diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx')
66 files changed, 45982 insertions, 0 deletions
diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig new file mode 100644 index 0000000000..faddae3d6a --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/Kconfig @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0 +config DRM_VMWGFX + tristate "DRM driver for VMware Virtual GPU" + depends on DRM && PCI && MMU + depends on X86 || ARM64 + select DRM_TTM + select DRM_TTM_HELPER + select MAPPING_DIRTY_HELPERS + # 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 + 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_MKSSTATS + bool "Enable mksGuestStats instrumentation of vmwgfx by default" + depends on DRM_VMWGFX + depends on X86 + default n + help + Choose this option to instrument the kernel driver for mksGuestStats. + diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile new file mode 100644 index 0000000000..e94479d9cd --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ + vmwgfx_ioctl.o vmwgfx_resource.o vmwgfx_ttm_buffer.o \ + vmwgfx_cmd.o vmwgfx_irq.o vmwgfx_ldu.o \ + vmwgfx_overlay.o vmwgfx_gmrid_manager.o vmwgfx_fence.o \ + vmwgfx_bo.o vmwgfx_scrn.o vmwgfx_context.o \ + vmwgfx_surface.o vmwgfx_prime.o vmwgfx_mob.o vmwgfx_shader.o \ + vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \ + vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o \ + vmwgfx_simple_resource.o vmwgfx_va.o vmwgfx_blit.o \ + vmwgfx_validation.o vmwgfx_page_dirty.o vmwgfx_streamoutput.o \ + vmwgfx_devcaps.o ttm_object.o vmwgfx_system_manager.o \ + vmwgfx_gem.o + +obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga3d_cmd.h b/drivers/gpu/drm/vmwgfx/device_include/svga3d_cmd.h new file mode 100644 index 0000000000..d90d940ad3 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/device_include/svga3d_cmd.h @@ -0,0 +1,1513 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2012-2021 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * svga3d_cmd.h -- + * + * SVGA 3d hardware cmd definitions + */ + + + +#ifndef _SVGA3D_CMD_H_ +#define _SVGA3D_CMD_H_ + +#include "svga3d_types.h" +#include "svga3d_limits.h" +#include "svga_reg.h" + +typedef enum SVGAFifo3dCmdId { + SVGA_3D_CMD_LEGACY_BASE = 1000, + SVGA_3D_CMD_BASE = 1040, + + SVGA_3D_CMD_SURFACE_DEFINE = 1040, + SVGA_3D_CMD_SURFACE_DESTROY = 1041, + SVGA_3D_CMD_SURFACE_COPY = 1042, + SVGA_3D_CMD_SURFACE_STRETCHBLT = 1043, + SVGA_3D_CMD_SURFACE_DMA = 1044, + SVGA_3D_CMD_CONTEXT_DEFINE = 1045, + SVGA_3D_CMD_CONTEXT_DESTROY = 1046, + SVGA_3D_CMD_SETTRANSFORM = 1047, + SVGA_3D_CMD_SETZRANGE = 1048, + SVGA_3D_CMD_SETRENDERSTATE = 1049, + SVGA_3D_CMD_SETRENDERTARGET = 1050, + SVGA_3D_CMD_SETTEXTURESTATE = 1051, + SVGA_3D_CMD_SETMATERIAL = 1052, + SVGA_3D_CMD_SETLIGHTDATA = 1053, + SVGA_3D_CMD_SETLIGHTENABLED = 1054, + SVGA_3D_CMD_SETVIEWPORT = 1055, + SVGA_3D_CMD_SETCLIPPLANE = 1056, + SVGA_3D_CMD_CLEAR = 1057, + SVGA_3D_CMD_PRESENT = 1058, + SVGA_3D_CMD_SHADER_DEFINE = 1059, + SVGA_3D_CMD_SHADER_DESTROY = 1060, + SVGA_3D_CMD_SET_SHADER = 1061, + SVGA_3D_CMD_SET_SHADER_CONST = 1062, + SVGA_3D_CMD_DRAW_PRIMITIVES = 1063, + SVGA_3D_CMD_SETSCISSORRECT = 1064, + SVGA_3D_CMD_BEGIN_QUERY = 1065, + SVGA_3D_CMD_END_QUERY = 1066, + SVGA_3D_CMD_WAIT_FOR_QUERY = 1067, + SVGA_3D_CMD_PRESENT_READBACK = 1068, + SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN = 1069, + SVGA_3D_CMD_SURFACE_DEFINE_V2 = 1070, + SVGA_3D_CMD_GENERATE_MIPMAPS = 1071, + SVGA_3D_CMD_DEAD4 = 1072, + SVGA_3D_CMD_DEAD5 = 1073, + SVGA_3D_CMD_DEAD6 = 1074, + SVGA_3D_CMD_DEAD7 = 1075, + SVGA_3D_CMD_DEAD8 = 1076, + SVGA_3D_CMD_DEAD9 = 1077, + SVGA_3D_CMD_DEAD10 = 1078, + SVGA_3D_CMD_DEAD11 = 1079, + SVGA_3D_CMD_ACTIVATE_SURFACE = 1080, + SVGA_3D_CMD_DEACTIVATE_SURFACE = 1081, + SVGA_3D_CMD_SCREEN_DMA = 1082, + SVGA_3D_CMD_DEAD1 = 1083, + SVGA_3D_CMD_DEAD2 = 1084, + + SVGA_3D_CMD_DEAD12 = 1085, + SVGA_3D_CMD_DEAD13 = 1086, + SVGA_3D_CMD_DEAD14 = 1087, + SVGA_3D_CMD_DEAD15 = 1088, + SVGA_3D_CMD_DEAD16 = 1089, + SVGA_3D_CMD_DEAD17 = 1090, + + SVGA_3D_CMD_SET_OTABLE_BASE = 1091, + SVGA_3D_CMD_READBACK_OTABLE = 1092, + + SVGA_3D_CMD_DEFINE_GB_MOB = 1093, + SVGA_3D_CMD_DESTROY_GB_MOB = 1094, + SVGA_3D_CMD_DEAD3 = 1095, + SVGA_3D_CMD_UPDATE_GB_MOB_MAPPING = 1096, + + SVGA_3D_CMD_DEFINE_GB_SURFACE = 1097, + SVGA_3D_CMD_DESTROY_GB_SURFACE = 1098, + SVGA_3D_CMD_BIND_GB_SURFACE = 1099, + SVGA_3D_CMD_COND_BIND_GB_SURFACE = 1100, + SVGA_3D_CMD_UPDATE_GB_IMAGE = 1101, + SVGA_3D_CMD_UPDATE_GB_SURFACE = 1102, + SVGA_3D_CMD_READBACK_GB_IMAGE = 1103, + SVGA_3D_CMD_READBACK_GB_SURFACE = 1104, + SVGA_3D_CMD_INVALIDATE_GB_IMAGE = 1105, + SVGA_3D_CMD_INVALIDATE_GB_SURFACE = 1106, + + SVGA_3D_CMD_DEFINE_GB_CONTEXT = 1107, + SVGA_3D_CMD_DESTROY_GB_CONTEXT = 1108, + SVGA_3D_CMD_BIND_GB_CONTEXT = 1109, + SVGA_3D_CMD_READBACK_GB_CONTEXT = 1110, + SVGA_3D_CMD_INVALIDATE_GB_CONTEXT = 1111, + + SVGA_3D_CMD_DEFINE_GB_SHADER = 1112, + SVGA_3D_CMD_DESTROY_GB_SHADER = 1113, + SVGA_3D_CMD_BIND_GB_SHADER = 1114, + + SVGA_3D_CMD_SET_OTABLE_BASE64 = 1115, + + SVGA_3D_CMD_BEGIN_GB_QUERY = 1116, + SVGA_3D_CMD_END_GB_QUERY = 1117, + SVGA_3D_CMD_WAIT_FOR_GB_QUERY = 1118, + + SVGA_3D_CMD_NOP = 1119, + + SVGA_3D_CMD_ENABLE_GART = 1120, + SVGA_3D_CMD_DISABLE_GART = 1121, + SVGA_3D_CMD_MAP_MOB_INTO_GART = 1122, + SVGA_3D_CMD_UNMAP_GART_RANGE = 1123, + + SVGA_3D_CMD_DEFINE_GB_SCREENTARGET = 1124, + SVGA_3D_CMD_DESTROY_GB_SCREENTARGET = 1125, + SVGA_3D_CMD_BIND_GB_SCREENTARGET = 1126, + SVGA_3D_CMD_UPDATE_GB_SCREENTARGET = 1127, + + SVGA_3D_CMD_READBACK_GB_IMAGE_PARTIAL = 1128, + SVGA_3D_CMD_INVALIDATE_GB_IMAGE_PARTIAL = 1129, + + SVGA_3D_CMD_SET_GB_SHADERCONSTS_INLINE = 1130, + + SVGA_3D_CMD_GB_SCREEN_DMA = 1131, + SVGA_3D_CMD_BIND_GB_SURFACE_WITH_PITCH = 1132, + SVGA_3D_CMD_GB_MOB_FENCE = 1133, + SVGA_3D_CMD_DEFINE_GB_SURFACE_V2 = 1134, + SVGA_3D_CMD_DEFINE_GB_MOB64 = 1135, + SVGA_3D_CMD_REDEFINE_GB_MOB64 = 1136, + SVGA_3D_CMD_NOP_ERROR = 1137, + + SVGA_3D_CMD_SET_VERTEX_STREAMS = 1138, + SVGA_3D_CMD_SET_VERTEX_DECLS = 1139, + SVGA_3D_CMD_SET_VERTEX_DIVISORS = 1140, + SVGA_3D_CMD_DRAW = 1141, + SVGA_3D_CMD_DRAW_INDEXED = 1142, + + SVGA_3D_CMD_DX_MIN = 1143, + SVGA_3D_CMD_DX_DEFINE_CONTEXT = 1143, + SVGA_3D_CMD_DX_DESTROY_CONTEXT = 1144, + SVGA_3D_CMD_DX_BIND_CONTEXT = 1145, + SVGA_3D_CMD_DX_READBACK_CONTEXT = 1146, + SVGA_3D_CMD_DX_INVALIDATE_CONTEXT = 1147, + SVGA_3D_CMD_DX_SET_SINGLE_CONSTANT_BUFFER = 1148, + SVGA_3D_CMD_DX_SET_SHADER_RESOURCES = 1149, + SVGA_3D_CMD_DX_SET_SHADER = 1150, + SVGA_3D_CMD_DX_SET_SAMPLERS = 1151, + SVGA_3D_CMD_DX_DRAW = 1152, + SVGA_3D_CMD_DX_DRAW_INDEXED = 1153, + SVGA_3D_CMD_DX_DRAW_INSTANCED = 1154, + SVGA_3D_CMD_DX_DRAW_INDEXED_INSTANCED = 1155, + SVGA_3D_CMD_DX_DRAW_AUTO = 1156, + SVGA_3D_CMD_DX_SET_INPUT_LAYOUT = 1157, + SVGA_3D_CMD_DX_SET_VERTEX_BUFFERS = 1158, + SVGA_3D_CMD_DX_SET_INDEX_BUFFER = 1159, + SVGA_3D_CMD_DX_SET_TOPOLOGY = 1160, + SVGA_3D_CMD_DX_SET_RENDERTARGETS = 1161, + SVGA_3D_CMD_DX_SET_BLEND_STATE = 1162, + SVGA_3D_CMD_DX_SET_DEPTHSTENCIL_STATE = 1163, + SVGA_3D_CMD_DX_SET_RASTERIZER_STATE = 1164, + SVGA_3D_CMD_DX_DEFINE_QUERY = 1165, + SVGA_3D_CMD_DX_DESTROY_QUERY = 1166, + SVGA_3D_CMD_DX_BIND_QUERY = 1167, + SVGA_3D_CMD_DX_SET_QUERY_OFFSET = 1168, + SVGA_3D_CMD_DX_BEGIN_QUERY = 1169, + SVGA_3D_CMD_DX_END_QUERY = 1170, + SVGA_3D_CMD_DX_READBACK_QUERY = 1171, + SVGA_3D_CMD_DX_SET_PREDICATION = 1172, + SVGA_3D_CMD_DX_SET_SOTARGETS = 1173, + SVGA_3D_CMD_DX_SET_VIEWPORTS = 1174, + SVGA_3D_CMD_DX_SET_SCISSORRECTS = 1175, + SVGA_3D_CMD_DX_CLEAR_RENDERTARGET_VIEW = 1176, + SVGA_3D_CMD_DX_CLEAR_DEPTHSTENCIL_VIEW = 1177, + SVGA_3D_CMD_DX_PRED_COPY_REGION = 1178, + SVGA_3D_CMD_DX_PRED_COPY = 1179, + SVGA_3D_CMD_DX_PRESENTBLT = 1180, + SVGA_3D_CMD_DX_GENMIPS = 1181, + SVGA_3D_CMD_DX_UPDATE_SUBRESOURCE = 1182, + SVGA_3D_CMD_DX_READBACK_SUBRESOURCE = 1183, + SVGA_3D_CMD_DX_INVALIDATE_SUBRESOURCE = 1184, + SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW = 1185, + SVGA_3D_CMD_DX_DESTROY_SHADERRESOURCE_VIEW = 1186, + SVGA_3D_CMD_DX_DEFINE_RENDERTARGET_VIEW = 1187, + SVGA_3D_CMD_DX_DESTROY_RENDERTARGET_VIEW = 1188, + SVGA_3D_CMD_DX_DEFINE_DEPTHSTENCIL_VIEW = 1189, + SVGA_3D_CMD_DX_DESTROY_DEPTHSTENCIL_VIEW = 1190, + SVGA_3D_CMD_DX_DEFINE_ELEMENTLAYOUT = 1191, + SVGA_3D_CMD_DX_DESTROY_ELEMENTLAYOUT = 1192, + SVGA_3D_CMD_DX_DEFINE_BLEND_STATE = 1193, + SVGA_3D_CMD_DX_DESTROY_BLEND_STATE = 1194, + SVGA_3D_CMD_DX_DEFINE_DEPTHSTENCIL_STATE = 1195, + SVGA_3D_CMD_DX_DESTROY_DEPTHSTENCIL_STATE = 1196, + SVGA_3D_CMD_DX_DEFINE_RASTERIZER_STATE = 1197, + SVGA_3D_CMD_DX_DESTROY_RASTERIZER_STATE = 1198, + SVGA_3D_CMD_DX_DEFINE_SAMPLER_STATE = 1199, + SVGA_3D_CMD_DX_DESTROY_SAMPLER_STATE = 1200, + SVGA_3D_CMD_DX_DEFINE_SHADER = 1201, + SVGA_3D_CMD_DX_DESTROY_SHADER = 1202, + SVGA_3D_CMD_DX_BIND_SHADER = 1203, + SVGA_3D_CMD_DX_DEFINE_STREAMOUTPUT = 1204, + SVGA_3D_CMD_DX_DESTROY_STREAMOUTPUT = 1205, + SVGA_3D_CMD_DX_SET_STREAMOUTPUT = 1206, + SVGA_3D_CMD_DX_SET_COTABLE = 1207, + SVGA_3D_CMD_DX_READBACK_COTABLE = 1208, + SVGA_3D_CMD_DX_BUFFER_COPY = 1209, + SVGA_3D_CMD_DX_TRANSFER_FROM_BUFFER = 1210, + SVGA_3D_CMD_DX_SURFACE_COPY_AND_READBACK = 1211, + SVGA_3D_CMD_DX_MOVE_QUERY = 1212, + SVGA_3D_CMD_DX_BIND_ALL_QUERY = 1213, + SVGA_3D_CMD_DX_READBACK_ALL_QUERY = 1214, + SVGA_3D_CMD_DX_PRED_TRANSFER_FROM_BUFFER = 1215, + SVGA_3D_CMD_DX_MOB_FENCE_64 = 1216, + SVGA_3D_CMD_DX_BIND_ALL_SHADER = 1217, + SVGA_3D_CMD_DX_HINT = 1218, + SVGA_3D_CMD_DX_BUFFER_UPDATE = 1219, + SVGA_3D_CMD_DX_SET_VS_CONSTANT_BUFFER_OFFSET = 1220, + SVGA_3D_CMD_DX_SET_PS_CONSTANT_BUFFER_OFFSET = 1221, + SVGA_3D_CMD_DX_SET_GS_CONSTANT_BUFFER_OFFSET = 1222, + SVGA_3D_CMD_DX_SET_HS_CONSTANT_BUFFER_OFFSET = 1223, + SVGA_3D_CMD_DX_SET_DS_CONSTANT_BUFFER_OFFSET = 1224, + SVGA_3D_CMD_DX_SET_CS_CONSTANT_BUFFER_OFFSET = 1225, + + SVGA_3D_CMD_DX_COND_BIND_ALL_SHADER = 1226, + SVGA_3D_CMD_DX_MAX = 1227, + + SVGA_3D_CMD_SCREEN_COPY = 1227, + + SVGA_3D_CMD_RESERVED1 = 1228, + SVGA_3D_CMD_RESERVED2 = 1229, + SVGA_3D_CMD_RESERVED3 = 1230, + SVGA_3D_CMD_RESERVED4 = 1231, + SVGA_3D_CMD_RESERVED5 = 1232, + SVGA_3D_CMD_RESERVED6 = 1233, + SVGA_3D_CMD_RESERVED7 = 1234, + SVGA_3D_CMD_RESERVED8 = 1235, + + SVGA_3D_CMD_GROW_OTABLE = 1236, + SVGA_3D_CMD_DX_GROW_COTABLE = 1237, + SVGA_3D_CMD_INTRA_SURFACE_COPY = 1238, + + SVGA_3D_CMD_DEFINE_GB_SURFACE_V3 = 1239, + + SVGA_3D_CMD_DX_RESOLVE_COPY = 1240, + SVGA_3D_CMD_DX_PRED_RESOLVE_COPY = 1241, + SVGA_3D_CMD_DX_PRED_CONVERT_REGION = 1242, + SVGA_3D_CMD_DX_PRED_CONVERT = 1243, + SVGA_3D_CMD_WHOLE_SURFACE_COPY = 1244, + + SVGA_3D_CMD_DX_DEFINE_UA_VIEW = 1245, + SVGA_3D_CMD_DX_DESTROY_UA_VIEW = 1246, + SVGA_3D_CMD_DX_CLEAR_UA_VIEW_UINT = 1247, + SVGA_3D_CMD_DX_CLEAR_UA_VIEW_FLOAT = 1248, + SVGA_3D_CMD_DX_COPY_STRUCTURE_COUNT = 1249, + SVGA_3D_CMD_DX_SET_UA_VIEWS = 1250, + + SVGA_3D_CMD_DX_DRAW_INDEXED_INSTANCED_INDIRECT = 1251, + SVGA_3D_CMD_DX_DRAW_INSTANCED_INDIRECT = 1252, + SVGA_3D_CMD_DX_DISPATCH = 1253, + SVGA_3D_CMD_DX_DISPATCH_INDIRECT = 1254, + + SVGA_3D_CMD_WRITE_ZERO_SURFACE = 1255, + SVGA_3D_CMD_UPDATE_ZERO_SURFACE = 1256, + SVGA_3D_CMD_DX_TRANSFER_TO_BUFFER = 1257, + SVGA_3D_CMD_DX_SET_STRUCTURE_COUNT = 1258, + + SVGA_3D_CMD_LOGICOPS_BITBLT = 1259, + SVGA_3D_CMD_LOGICOPS_TRANSBLT = 1260, + SVGA_3D_CMD_LOGICOPS_STRETCHBLT = 1261, + SVGA_3D_CMD_LOGICOPS_COLORFILL = 1262, + SVGA_3D_CMD_LOGICOPS_ALPHABLEND = 1263, + SVGA_3D_CMD_LOGICOPS_CLEARTYPEBLEND = 1264, + + SVGA_3D_CMD_DX_COPY_COTABLE_INTO_MOB = 1265, + + SVGA_3D_CMD_UPDATE_GB_SCREENTARGET_V2 = 1266, + + SVGA_3D_CMD_DEFINE_GB_SURFACE_V4 = 1267, + SVGA_3D_CMD_DX_SET_CS_UA_VIEWS = 1268, + SVGA_3D_CMD_DX_SET_MIN_LOD = 1269, + + SVGA_3D_CMD_DX_DEFINE_DEPTHSTENCIL_VIEW_V2 = 1272, + SVGA_3D_CMD_DX_DEFINE_STREAMOUTPUT_WITH_MOB = 1273, + SVGA_3D_CMD_DX_SET_SHADER_IFACE = 1274, + SVGA_3D_CMD_DX_BIND_STREAMOUTPUT = 1275, + SVGA_3D_CMD_SURFACE_STRETCHBLT_NON_MS_TO_MS = 1276, + SVGA_3D_CMD_DX_BIND_SHADER_IFACE = 1277, + + SVGA_3D_CMD_UPDATE_GB_SCREENTARGET_MOVE = 1278, + + SVGA_3D_CMD_DX_PRED_STAGING_COPY = 1281, + SVGA_3D_CMD_DX_STAGING_COPY = 1282, + SVGA_3D_CMD_DX_PRED_STAGING_COPY_REGION = 1283, + SVGA_3D_CMD_DX_SET_VERTEX_BUFFERS_V2 = 1284, + SVGA_3D_CMD_DX_SET_INDEX_BUFFER_V2 = 1285, + SVGA_3D_CMD_DX_SET_VERTEX_BUFFERS_OFFSET_AND_SIZE = 1286, + SVGA_3D_CMD_DX_SET_INDEX_BUFFER_OFFSET_AND_SIZE = 1287, + SVGA_3D_CMD_DX_DEFINE_RASTERIZER_STATE_V2 = 1288, + SVGA_3D_CMD_DX_PRED_STAGING_CONVERT_REGION = 1289, + SVGA_3D_CMD_DX_PRED_STAGING_CONVERT = 1290, + SVGA_3D_CMD_DX_STAGING_BUFFER_COPY = 1291, + + SVGA_3D_CMD_MAX = 1303, + SVGA_3D_CMD_FUTURE_MAX = 3000 +} SVGAFifo3dCmdId; + +#define SVGA_NUM_3D_CMD (SVGA_3D_CMD_MAX - SVGA_3D_CMD_BASE) + +#pragma pack(push, 1) +typedef struct { + uint32 id; + uint32 size; +} SVGA3dCmdHeader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 numMipLevels; +} SVGA3dSurfaceFace; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 sid; + SVGA3dSurface1Flags surfaceFlags; + SVGA3dSurfaceFormat format; + + SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES]; + +} SVGA3dCmdDefineSurface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 sid; + SVGA3dSurface1Flags surfaceFlags; + SVGA3dSurfaceFormat format; + + SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES]; + uint32 multisampleCount; + SVGA3dTextureFilter autogenFilter; + +} SVGA3dCmdDefineSurface_v2; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 sid; +} SVGA3dCmdDestroySurface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; +} SVGA3dCmdDefineContext; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; +} SVGA3dCmdDestroyContext; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dClearFlag clearFlag; + uint32 color; + float depth; + uint32 stencil; + +} SVGA3dCmdClear; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dLightType type; + SVGA3dBool inWorldSpace; + float diffuse[4]; + float specular[4]; + float ambient[4]; + float position[4]; + float direction[4]; + float range; + float falloff; + float attenuation0; + float attenuation1; + float attenuation2; + float theta; + float phi; +} SVGA3dLightData; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 sid; + +} SVGA3dCmdPresent; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dRenderStateName state; + union { + uint32 uintValue; + float floatValue; + }; +} SVGA3dRenderState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + +} SVGA3dCmdSetRenderState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dRenderTargetType type; + SVGA3dSurfaceImageId target; +} SVGA3dCmdSetRenderTarget; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceImageId src; + SVGA3dSurfaceImageId dest; + +} SVGA3dCmdSurfaceCopy; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceImageId surface; + SVGA3dCopyBox box; +} SVGA3dCmdIntraSurfaceCopy; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 srcSid; + uint32 destSid; +} SVGA3dCmdWholeSurfaceCopy; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceImageId src; + SVGA3dSurfaceImageId dest; + SVGA3dBox boxSrc; + SVGA3dBox boxDest; +} SVGA3dCmdSurfaceStretchBltNonMSToMS; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceImageId src; + SVGA3dSurfaceImageId dest; + SVGA3dBox boxSrc; + SVGA3dBox boxDest; + SVGA3dStretchBltMode mode; +} SVGA3dCmdSurfaceStretchBlt; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 discard : 1; + + uint32 unsynchronized : 1; + + uint32 reserved : 30; +} SVGA3dSurfaceDMAFlags; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAGuestImage guest; + SVGA3dSurfaceImageId host; + SVGA3dTransferType transfer; + +} SVGA3dCmdSurfaceDMA; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 suffixSize; + + uint32 maximumOffset; + + SVGA3dSurfaceDMAFlags flags; +} SVGA3dCmdSurfaceDMASuffix; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 first; + uint32 last; +} SVGA3dArrayRangeHint; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 surfaceId; + uint32 offset; + uint32 stride; +} SVGA3dArray; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dDeclType type; + SVGA3dDeclMethod method; + SVGA3dDeclUsage usage; + uint32 usageIndex; +} SVGA3dVertexArrayIdentity; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dVertexDecl { + SVGA3dVertexArrayIdentity identity; + SVGA3dArray array; + SVGA3dArrayRangeHint rangeHint; +} SVGA3dVertexDecl; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dPrimitiveRange { + SVGA3dPrimitiveType primType; + uint32 primitiveCount; + + SVGA3dArray indexArray; + uint32 indexWidth; + + int32 indexBias; +} SVGA3dPrimitiveRange; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + uint32 numVertexDecls; + uint32 numRanges; + +} SVGA3dCmdDrawPrimitives; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + + uint32 primitiveCount; + uint32 startVertexLocation; + + uint8 primitiveType; + uint8 padding[3]; +} SVGA3dCmdDraw; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + + uint8 primitiveType; + + uint32 indexBufferSid; + uint32 indexBufferOffset; + + uint8 indexBufferStride; + + int32 baseVertexLocation; + + uint32 primitiveCount; + uint32 pad0; + uint16 pad1; +} SVGA3dCmdDrawIndexed; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint16 streamOffset; + uint8 stream; + uint8 type; + uint8 method; + uint8 usage; + uint8 usageIndex; + uint8 padding; + +} SVGA3dVertexElement; +#pragma pack(pop) + +#define SVGA3D_VERTEX_ELEMENT_RESPECT_STREAM (1 << 7) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + + uint32 numElements; + +} SVGA3dCmdSetVertexDecls; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 sid; + uint32 stride; + uint32 offset; +} SVGA3dVertexStream; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + + uint32 numStreams; + +} SVGA3dCmdSetVertexStreams; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + uint32 numDivisors; +} SVGA3dCmdSetVertexDivisors; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 stage; + SVGA3dTextureStateName name; + union { + uint32 value; + float floatValue; + }; +} SVGA3dTextureState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + +} SVGA3dCmdSetTextureState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dTransformType type; + float matrix[16]; +} SVGA3dCmdSetTransform; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + float min; + float max; +} SVGA3dZRange; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dZRange zRange; +} SVGA3dCmdSetZRange; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + float diffuse[4]; + float ambient[4]; + float specular[4]; + float emissive[4]; + float shininess; +} SVGA3dMaterial; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dFace face; + SVGA3dMaterial material; +} SVGA3dCmdSetMaterial; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + uint32 index; + SVGA3dLightData data; +} SVGA3dCmdSetLightData; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + uint32 index; + uint32 enabled; +} SVGA3dCmdSetLightEnabled; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dRect rect; +} SVGA3dCmdSetViewport; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dRect rect; +} SVGA3dCmdSetScissorRect; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + uint32 index; + float plane[4]; +} SVGA3dCmdSetClipPlane; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + uint32 shid; + SVGA3dShaderType type; + +} SVGA3dCmdDefineShader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + uint32 shid; + SVGA3dShaderType type; +} SVGA3dCmdDestroyShader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + uint32 reg; + SVGA3dShaderType type; + SVGA3dShaderConstType ctype; + uint32 values[4]; + +} SVGA3dCmdSetShaderConst; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dShaderType type; + uint32 shid; +} SVGA3dCmdSetShader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dQueryType type; +} SVGA3dCmdBeginQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dQueryType type; + SVGAGuestPtr guestResult; +} SVGA3dCmdEndQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dQueryType type; + SVGAGuestPtr guestResult; +} SVGA3dCmdWaitForQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 totalSize; + SVGA3dQueryState state; + union { + uint32 result32; + uint32 queryCookie; + }; +} SVGA3dQueryResult; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceImageId srcImage; + SVGASignedRect srcRect; + uint32 destScreenId; + SVGASignedRect destRect; + +} SVGA3dCmdBlitSurfaceToScreen; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 sid; + SVGA3dTextureFilter filter; +} SVGA3dCmdGenerateMipmaps; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 sid; +} SVGA3dCmdActivateSurface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 sid; +} SVGA3dCmdDeactivateSurface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdScreenDMA { + uint32 screenId; + SVGAGuestImage refBuffer; + SVGAGuestImage destBuffer; + SVGAGuestImage changeMap; +} SVGA3dCmdScreenDMA; +#pragma pack(pop) + +#define SVGA3D_LOTRANSBLT_HONORALPHA (0x01) +#define SVGA3D_LOSTRETCHBLT_MIRRORX (0x01) +#define SVGA3D_LOSTRETCHBLT_MIRRORY (0x02) +#define SVGA3D_LOALPHABLEND_SRCHASALPHA (0x01) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdLogicOpsBitBlt { + SVGA3dSurfaceImageId src; + SVGA3dSurfaceImageId dst; + SVGA3dLogicOp logicOp; + SVGA3dLogicOpRop3 logicOpRop3; + +} SVGA3dCmdLogicOpsBitBlt; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdLogicOpsTransBlt { + SVGA3dSurfaceImageId src; + SVGA3dSurfaceImageId dst; + uint32 color; + uint32 flags; + SVGA3dBox srcBox; + SVGA3dSignedBox dstBox; + SVGA3dBox clipBox; +} SVGA3dCmdLogicOpsTransBlt; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdLogicOpsStretchBlt { + SVGA3dSurfaceImageId src; + SVGA3dSurfaceImageId dst; + uint16 mode; + uint16 flags; + SVGA3dBox srcBox; + SVGA3dSignedBox dstBox; + SVGA3dBox clipBox; +} SVGA3dCmdLogicOpsStretchBlt; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdLogicOpsColorFill { + SVGA3dSurfaceImageId dst; + uint32 color; + SVGA3dLogicOp logicOp; + SVGA3dLogicOpRop3 logicOpRop3; + +} SVGA3dCmdLogicOpsColorFill; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdLogicOpsAlphaBlend { + SVGA3dSurfaceImageId src; + SVGA3dSurfaceImageId dst; + uint32 alphaVal; + uint32 flags; + SVGA3dBox srcBox; + SVGA3dSignedBox dstBox; + SVGA3dBox clipBox; +} SVGA3dCmdLogicOpsAlphaBlend; +#pragma pack(pop) + +#define SVGA3D_CLEARTYPE_INVALID_GAMMA_INDEX 0xFFFFFFFF + +#define SVGA3D_CLEARTYPE_GAMMA_WIDTH 512 +#define SVGA3D_CLEARTYPE_GAMMA_HEIGHT 16 + +#pragma pack(push, 1) +typedef struct SVGA3dCmdLogicOpsClearTypeBlend { + SVGA3dSurfaceImageId tmp; + SVGA3dSurfaceImageId dst; + SVGA3dSurfaceImageId gammaSurf; + SVGA3dSurfaceImageId alphaSurf; + uint32 gamma; + uint32 color; + uint32 color2; + int32 alphaOffsetX; + int32 alphaOffsetY; + +} SVGA3dCmdLogicOpsClearTypeBlend; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAMobFormat ptDepth; + uint32 sizeInBytes; + PPN64 base; +} SVGAOTableMobEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceFormat format; + SVGA3dSurface1Flags surface1Flags; + uint32 numMipLevels; + uint32 multisampleCount; + SVGA3dTextureFilter autogenFilter; + SVGA3dSize size; + SVGAMobId mobid; + uint32 arraySize; + uint32 mobPitch; + SVGA3dSurface2Flags surface2Flags; + uint8 multisamplePattern; + uint8 qualityLevel; + uint16 bufferByteStride; + float minLOD; + uint32 pad0[2]; +} SVGAOTableSurfaceEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGAMobId mobid; +} SVGAOTableContextEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dShaderType type; + uint32 sizeInBytes; + uint32 offsetInBytes; + SVGAMobId mobid; +} SVGAOTableShaderEntry; +#pragma pack(pop) + +#define SVGA_STFLAG_PRIMARY (1 << 0) +#define SVGA_STFLAG_RESERVED (1 << 1) +typedef uint32 SVGAScreenTargetFlags; + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceImageId image; + uint32 width; + uint32 height; + int32 xRoot; + int32 yRoot; + SVGAScreenTargetFlags flags; + uint32 dpi; + uint32 pad[7]; +} SVGAOTableScreenTargetEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + float value[4]; +} SVGA3dShaderConstFloat; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + int32 value[4]; +} SVGA3dShaderConstInt; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 value; +} SVGA3dShaderConstBool; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint16 streamOffset; + uint8 stream; + uint8 type; + uint8 methodUsage; + uint8 usageIndex; +} SVGAGBVertexElement; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 sid; + uint16 stride; + uint32 offset; +} SVGAGBVertexStream; +#pragma pack(pop) +#pragma pack(push, 1) +typedef struct { + SVGA3dRect viewport; + SVGA3dRect scissorRect; + SVGA3dZRange zRange; + + SVGA3dSurfaceImageId renderTargets[SVGA3D_RT_MAX]; + SVGAGBVertexElement decl1[4]; + + uint32 renderStates[SVGA3D_RS_MAX]; + SVGAGBVertexElement decl2[18]; + uint32 pad0[2]; + + struct { + SVGA3dFace face; + SVGA3dMaterial material; + } material; + + float clipPlanes[SVGA3D_MAX_CLIP_PLANES][4]; + float matrices[SVGA3D_TRANSFORM_MAX][16]; + + SVGA3dBool lightEnabled[SVGA3D_NUM_LIGHTS]; + SVGA3dLightData lightData[SVGA3D_NUM_LIGHTS]; + + uint32 shaders[SVGA3D_NUM_SHADERTYPE_PREDX]; + SVGAGBVertexElement decl3[10]; + uint32 pad1[3]; + + uint32 occQueryActive; + uint32 occQueryValue; + + SVGA3dShaderConstInt pShaderIValues[SVGA3D_CONSTINTREG_MAX]; + SVGA3dShaderConstInt vShaderIValues[SVGA3D_CONSTINTREG_MAX]; + uint16 pShaderBValues; + uint16 vShaderBValues; + + SVGAGBVertexStream streams[SVGA3D_MAX_VERTEX_ARRAYS]; + SVGA3dVertexDivisor divisors[SVGA3D_MAX_VERTEX_ARRAYS]; + uint32 numVertexDecls; + uint32 numVertexStreams; + uint32 numVertexDivisors; + uint32 pad2[30]; + + uint32 tsColorKey[SVGA3D_NUM_TEXTURE_UNITS]; + uint32 textureStages[SVGA3D_NUM_TEXTURE_UNITS][SVGA3D_TS_CONSTANT + 1]; + uint32 tsColorKeyEnable[SVGA3D_NUM_TEXTURE_UNITS]; + + SVGA3dShaderConstFloat pShaderFValues[SVGA3D_CONSTREG_MAX]; + SVGA3dShaderConstFloat vShaderFValues[SVGA3D_CONSTREG_MAX]; +} SVGAGBContextData; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAOTableType type; + PPN32 baseAddress; + uint32 sizeInBytes; + uint32 validSizeInBytes; + SVGAMobFormat ptDepth; +} SVGA3dCmdSetOTableBase; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAOTableType type; + PPN64 baseAddress; + uint32 sizeInBytes; + uint32 validSizeInBytes; + SVGAMobFormat ptDepth; +} SVGA3dCmdSetOTableBase64; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAOTableType type; + PPN64 baseAddress; + uint32 sizeInBytes; + uint32 validSizeInBytes; + SVGAMobFormat ptDepth; +} SVGA3dCmdGrowOTable; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAOTableType type; +} SVGA3dCmdReadbackOTable; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDefineGBMob { + SVGAMobId mobid; + SVGAMobFormat ptDepth; + PPN32 base; + uint32 sizeInBytes; +} SVGA3dCmdDefineGBMob; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDestroyGBMob { + SVGAMobId mobid; +} SVGA3dCmdDestroyGBMob; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDefineGBMob64 { + SVGAMobId mobid; + SVGAMobFormat ptDepth; + PPN64 base; + uint32 sizeInBytes; +} SVGA3dCmdDefineGBMob64; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdRedefineGBMob64 { + SVGAMobId mobid; + SVGAMobFormat ptDepth; + PPN64 base; + uint32 sizeInBytes; +} SVGA3dCmdRedefineGBMob64; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdUpdateGBMobMapping { + SVGAMobId mobid; +} SVGA3dCmdUpdateGBMobMapping; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDefineGBSurface { + uint32 sid; + SVGA3dSurface1Flags surfaceFlags; + SVGA3dSurfaceFormat format; + uint32 numMipLevels; + uint32 multisampleCount; + SVGA3dTextureFilter autogenFilter; + SVGA3dSize size; +} SVGA3dCmdDefineGBSurface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDefineGBSurface_v2 { + uint32 sid; + SVGA3dSurface1Flags surfaceFlags; + SVGA3dSurfaceFormat format; + uint32 numMipLevels; + uint32 multisampleCount; + SVGA3dTextureFilter autogenFilter; + SVGA3dSize size; + uint32 arraySize; + uint32 pad; +} SVGA3dCmdDefineGBSurface_v2; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDefineGBSurface_v3 { + uint32 sid; + SVGA3dSurfaceAllFlags surfaceFlags; + SVGA3dSurfaceFormat format; + uint32 numMipLevels; + uint32 multisampleCount; + SVGA3dMSPattern multisamplePattern; + SVGA3dMSQualityLevel qualityLevel; + SVGA3dTextureFilter autogenFilter; + SVGA3dSize size; + uint32 arraySize; +} SVGA3dCmdDefineGBSurface_v3; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDefineGBSurface_v4 { + uint32 sid; + SVGA3dSurfaceAllFlags surfaceFlags; + SVGA3dSurfaceFormat format; + uint32 numMipLevels; + uint32 multisampleCount; + SVGA3dMSPattern multisamplePattern; + SVGA3dMSQualityLevel qualityLevel; + SVGA3dTextureFilter autogenFilter; + SVGA3dSize size; + uint32 arraySize; + uint32 bufferByteStride; +} SVGA3dCmdDefineGBSurface_v4; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDestroyGBSurface { + uint32 sid; +} SVGA3dCmdDestroyGBSurface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdBindGBSurface { + uint32 sid; + SVGAMobId mobid; +} SVGA3dCmdBindGBSurface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdBindGBSurfaceWithPitch { + uint32 sid; + SVGAMobId mobid; + uint32 baseLevelPitch; +} SVGA3dCmdBindGBSurfaceWithPitch; +#pragma pack(pop) + +#define SVGA3D_COND_BIND_GB_SURFACE_FLAG_READBACK (1 << 0) +#define SVGA3D_COND_BIND_GB_SURFACE_FLAG_UPDATE (1 << 1) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdCondBindGBSurface { + uint32 sid; + SVGAMobId testMobid; + SVGAMobId mobid; + uint32 flags; +} SVGA3dCmdCondBindGBSurface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdUpdateGBImage { + SVGA3dSurfaceImageId image; + SVGA3dBox box; +} SVGA3dCmdUpdateGBImage; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdUpdateGBSurface { + uint32 sid; +} SVGA3dCmdUpdateGBSurface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdReadbackGBImage { + SVGA3dSurfaceImageId image; +} SVGA3dCmdReadbackGBImage; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdReadbackGBSurface { + uint32 sid; +} SVGA3dCmdReadbackGBSurface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdReadbackGBImagePartial { + SVGA3dSurfaceImageId image; + SVGA3dBox box; + uint32 invertBox; +} SVGA3dCmdReadbackGBImagePartial; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdInvalidateGBImage { + SVGA3dSurfaceImageId image; +} SVGA3dCmdInvalidateGBImage; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdInvalidateGBSurface { + uint32 sid; +} SVGA3dCmdInvalidateGBSurface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdInvalidateGBImagePartial { + SVGA3dSurfaceImageId image; + SVGA3dBox box; + uint32 invertBox; +} SVGA3dCmdInvalidateGBImagePartial; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDefineGBContext { + uint32 cid; +} SVGA3dCmdDefineGBContext; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDestroyGBContext { + uint32 cid; +} SVGA3dCmdDestroyGBContext; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdBindGBContext { + uint32 cid; + SVGAMobId mobid; + uint32 validContents; +} SVGA3dCmdBindGBContext; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdReadbackGBContext { + uint32 cid; +} SVGA3dCmdReadbackGBContext; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdInvalidateGBContext { + uint32 cid; +} SVGA3dCmdInvalidateGBContext; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDefineGBShader { + uint32 shid; + SVGA3dShaderType type; + uint32 sizeInBytes; +} SVGA3dCmdDefineGBShader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdBindGBShader { + uint32 shid; + SVGAMobId mobid; + uint32 offsetInBytes; +} SVGA3dCmdBindGBShader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDestroyGBShader { + uint32 shid; +} SVGA3dCmdDestroyGBShader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + uint32 regStart; + SVGA3dShaderType shaderType; + SVGA3dShaderConstType constType; + +} SVGA3dCmdSetGBShaderConstInline; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dQueryType type; +} SVGA3dCmdBeginGBQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dQueryType type; + SVGAMobId mobid; + uint32 offset; +} SVGA3dCmdEndGBQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGA3dQueryType type; + SVGAMobId mobid; + uint32 offset; +} SVGA3dCmdWaitForGBQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAMobId mobid; + uint32 mustBeZero; + uint32 initialized; +} SVGA3dCmdEnableGart; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAMobId mobid; + uint32 gartOffset; +} SVGA3dCmdMapMobIntoGart; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 gartOffset; + uint32 numPages; +} SVGA3dCmdUnmapGartRange; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 stid; + uint32 width; + uint32 height; + int32 xRoot; + int32 yRoot; + SVGAScreenTargetFlags flags; + + uint32 dpi; +} SVGA3dCmdDefineGBScreenTarget; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 stid; +} SVGA3dCmdDestroyGBScreenTarget; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 stid; + SVGA3dSurfaceImageId image; +} SVGA3dCmdBindGBScreenTarget; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 stid; + SVGA3dRect rect; +} SVGA3dCmdUpdateGBScreenTarget; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 stid; + SVGA3dRect rect; + SVGA3dFrameUpdateType type; +} SVGA3dCmdUpdateGBScreenTarget_v2; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 stid; + SVGA3dRect rect; + SVGA3dFrameUpdateType type; + SVGAUnsignedPoint srcPoint; +} SVGA3dCmdUpdateGBScreenTargetMove; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdGBScreenDMA { + uint32 screenId; + uint32 dead; + SVGAMobId destMobID; + uint32 destPitch; + SVGAMobId changeMapMobID; +} SVGA3dCmdGBScreenDMA; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 value; + uint32 mobId; + uint32 mobOffset; +} SVGA3dCmdGBMobFence; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 stid; + SVGA3dSurfaceImageId dest; + + uint32 statusMobId; + uint32 statusMobOffset; + + uint32 mustBeInvalidId; + uint32 mustBeZero; +} SVGA3dCmdScreenCopy; +#pragma pack(pop) + +#define SVGA_SCREEN_COPY_STATUS_FAILURE 0x00 +#define SVGA_SCREEN_COPY_STATUS_SUCCESS 0x01 +#define SVGA_SCREEN_COPY_STATUS_INVALID 0xFFFFFFFF + +#pragma pack(push, 1) +typedef struct { + uint32 sid; +} SVGA3dCmdWriteZeroSurface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 sid; +} SVGA3dCmdUpdateZeroSurface; +#pragma pack(pop) + +#endif diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga3d_devcaps.h b/drivers/gpu/drm/vmwgfx/device_include/svga3d_devcaps.h new file mode 100644 index 0000000000..815d0ab005 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/device_include/svga3d_devcaps.h @@ -0,0 +1,375 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 1998-2021 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * svga3d_devcaps.h -- + * + * SVGA 3d caps definitions + */ + + + +#ifndef _SVGA3D_DEVCAPS_H_ +#define _SVGA3D_DEVCAPS_H_ + +#include "svga3d_types.h" + +#define SVGA3D_MAKE_HWVERSION(major, minor) (((major) << 16) | ((minor)&0xFF)) +#define SVGA3D_MAJOR_HWVERSION(version) ((version) >> 16) +#define SVGA3D_MINOR_HWVERSION(version) ((version)&0xFF) + +typedef enum { + SVGA3D_HWVERSION_WS5_RC1 = SVGA3D_MAKE_HWVERSION(0, 1), + SVGA3D_HWVERSION_WS5_RC2 = SVGA3D_MAKE_HWVERSION(0, 2), + SVGA3D_HWVERSION_WS51_RC1 = SVGA3D_MAKE_HWVERSION(0, 3), + SVGA3D_HWVERSION_WS6_B1 = SVGA3D_MAKE_HWVERSION(1, 1), + SVGA3D_HWVERSION_FUSION_11 = SVGA3D_MAKE_HWVERSION(1, 4), + SVGA3D_HWVERSION_WS65_B1 = SVGA3D_MAKE_HWVERSION(2, 0), + SVGA3D_HWVERSION_WS8_B1 = SVGA3D_MAKE_HWVERSION(2, 1), + SVGA3D_HWVERSION_CURRENT = SVGA3D_HWVERSION_WS8_B1, +} SVGA3dHardwareVersion; + +typedef uint32 SVGA3dDevCapIndex; + +#define SVGA3D_DEVCAP_INVALID ((uint32)-1) +#define SVGA3D_DEVCAP_3D 0 +#define SVGA3D_DEVCAP_MAX_LIGHTS 1 + +#define SVGA3D_DEVCAP_MAX_TEXTURES 2 +#define SVGA3D_DEVCAP_MAX_CLIP_PLANES 3 +#define SVGA3D_DEVCAP_VERTEX_SHADER_VERSION 4 +#define SVGA3D_DEVCAP_VERTEX_SHADER 5 +#define SVGA3D_DEVCAP_FRAGMENT_SHADER_VERSION 6 +#define SVGA3D_DEVCAP_FRAGMENT_SHADER 7 +#define SVGA3D_DEVCAP_MAX_RENDER_TARGETS 8 +#define SVGA3D_DEVCAP_S23E8_TEXTURES 9 +#define SVGA3D_DEVCAP_S10E5_TEXTURES 10 +#define SVGA3D_DEVCAP_MAX_FIXED_VERTEXBLEND 11 +#define SVGA3D_DEVCAP_D16_BUFFER_FORMAT 12 +#define SVGA3D_DEVCAP_D24S8_BUFFER_FORMAT 13 +#define SVGA3D_DEVCAP_D24X8_BUFFER_FORMAT 14 +#define SVGA3D_DEVCAP_QUERY_TYPES 15 +#define SVGA3D_DEVCAP_TEXTURE_GRADIENT_SAMPLING 16 +#define SVGA3D_DEVCAP_MAX_POINT_SIZE 17 +#define SVGA3D_DEVCAP_MAX_SHADER_TEXTURES 18 +#define SVGA3D_DEVCAP_MAX_TEXTURE_WIDTH 19 +#define SVGA3D_DEVCAP_MAX_TEXTURE_HEIGHT 20 +#define SVGA3D_DEVCAP_MAX_VOLUME_EXTENT 21 +#define SVGA3D_DEVCAP_MAX_TEXTURE_REPEAT 22 +#define SVGA3D_DEVCAP_MAX_TEXTURE_ASPECT_RATIO 23 +#define SVGA3D_DEVCAP_MAX_TEXTURE_ANISOTROPY 24 +#define SVGA3D_DEVCAP_MAX_PRIMITIVE_COUNT 25 +#define SVGA3D_DEVCAP_MAX_VERTEX_INDEX 26 +#define SVGA3D_DEVCAP_MAX_VERTEX_SHADER_INSTRUCTIONS 27 +#define SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_INSTRUCTIONS 28 +#define SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEMPS 29 +#define SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_TEMPS 30 +#define SVGA3D_DEVCAP_TEXTURE_OPS 31 +#define SVGA3D_DEVCAP_SURFACEFMT_X8R8G8B8 32 +#define SVGA3D_DEVCAP_SURFACEFMT_A8R8G8B8 33 +#define SVGA3D_DEVCAP_SURFACEFMT_A2R10G10B10 34 +#define SVGA3D_DEVCAP_SURFACEFMT_X1R5G5B5 35 +#define SVGA3D_DEVCAP_SURFACEFMT_A1R5G5B5 36 +#define SVGA3D_DEVCAP_SURFACEFMT_A4R4G4B4 37 +#define SVGA3D_DEVCAP_SURFACEFMT_R5G6B5 38 +#define SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE16 39 +#define SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8_ALPHA8 40 +#define SVGA3D_DEVCAP_SURFACEFMT_ALPHA8 41 +#define SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8 42 +#define SVGA3D_DEVCAP_SURFACEFMT_Z_D16 43 +#define SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8 44 +#define SVGA3D_DEVCAP_SURFACEFMT_Z_D24X8 45 +#define SVGA3D_DEVCAP_SURFACEFMT_DXT1 46 +#define SVGA3D_DEVCAP_SURFACEFMT_DXT2 47 +#define SVGA3D_DEVCAP_SURFACEFMT_DXT3 48 +#define SVGA3D_DEVCAP_SURFACEFMT_DXT4 49 +#define SVGA3D_DEVCAP_SURFACEFMT_DXT5 50 +#define SVGA3D_DEVCAP_SURFACEFMT_BUMPX8L8V8U8 51 +#define SVGA3D_DEVCAP_SURFACEFMT_A2W10V10U10 52 +#define SVGA3D_DEVCAP_SURFACEFMT_BUMPU8V8 53 +#define SVGA3D_DEVCAP_SURFACEFMT_Q8W8V8U8 54 +#define SVGA3D_DEVCAP_SURFACEFMT_CxV8U8 55 +#define SVGA3D_DEVCAP_SURFACEFMT_R_S10E5 56 +#define SVGA3D_DEVCAP_SURFACEFMT_R_S23E8 57 +#define SVGA3D_DEVCAP_SURFACEFMT_RG_S10E5 58 +#define SVGA3D_DEVCAP_SURFACEFMT_RG_S23E8 59 +#define SVGA3D_DEVCAP_SURFACEFMT_ARGB_S10E5 60 +#define SVGA3D_DEVCAP_SURFACEFMT_ARGB_S23E8 61 + +#define SVGA3D_DEVCAP_MISSING62 62 + +#define SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEXTURES 63 + +#define SVGA3D_DEVCAP_MAX_SIMULTANEOUS_RENDER_TARGETS 64 + +#define SVGA3D_DEVCAP_SURFACEFMT_V16U16 65 +#define SVGA3D_DEVCAP_SURFACEFMT_G16R16 66 +#define SVGA3D_DEVCAP_SURFACEFMT_A16B16G16R16 67 +#define SVGA3D_DEVCAP_SURFACEFMT_UYVY 68 +#define SVGA3D_DEVCAP_SURFACEFMT_YUY2 69 + +#define SVGA3D_DEVCAP_DEAD4 70 +#define SVGA3D_DEVCAP_DEAD5 71 +#define SVGA3D_DEVCAP_DEAD7 72 +#define SVGA3D_DEVCAP_DEAD6 73 + +#define SVGA3D_DEVCAP_AUTOGENMIPMAPS 74 +#define SVGA3D_DEVCAP_SURFACEFMT_NV12 75 +#define SVGA3D_DEVCAP_DEAD10 76 + +#define SVGA3D_DEVCAP_MAX_CONTEXT_IDS 77 + +#define SVGA3D_DEVCAP_MAX_SURFACE_IDS 78 + +#define SVGA3D_DEVCAP_SURFACEFMT_Z_DF16 79 +#define SVGA3D_DEVCAP_SURFACEFMT_Z_DF24 80 +#define SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8_INT 81 + +#define SVGA3D_DEVCAP_SURFACEFMT_ATI1 82 +#define SVGA3D_DEVCAP_SURFACEFMT_ATI2 83 + +#define SVGA3D_DEVCAP_DEAD1 84 +#define SVGA3D_DEVCAP_DEAD8 85 +#define SVGA3D_DEVCAP_DEAD9 86 + +#define SVGA3D_DEVCAP_LINE_AA 87 +#define SVGA3D_DEVCAP_LINE_STIPPLE 88 +#define SVGA3D_DEVCAP_MAX_LINE_WIDTH 89 +#define SVGA3D_DEVCAP_MAX_AA_LINE_WIDTH 90 + +#define SVGA3D_DEVCAP_SURFACEFMT_YV12 91 + +#define SVGA3D_DEVCAP_DEAD3 92 + +#define SVGA3D_DEVCAP_TS_COLOR_KEY 93 + +#define SVGA3D_DEVCAP_DEAD2 94 + +#define SVGA3D_DEVCAP_DXCONTEXT 95 + +#define SVGA3D_DEVCAP_DEAD11 96 + +#define SVGA3D_DEVCAP_DX_MAX_VERTEXBUFFERS 97 + +#define SVGA3D_DEVCAP_DX_MAX_CONSTANT_BUFFERS 98 + +#define SVGA3D_DEVCAP_DX_PROVOKING_VERTEX 99 + +#define SVGA3D_DEVCAP_DXFMT_X8R8G8B8 100 +#define SVGA3D_DEVCAP_DXFMT_A8R8G8B8 101 +#define SVGA3D_DEVCAP_DXFMT_R5G6B5 102 +#define SVGA3D_DEVCAP_DXFMT_X1R5G5B5 103 +#define SVGA3D_DEVCAP_DXFMT_A1R5G5B5 104 +#define SVGA3D_DEVCAP_DXFMT_A4R4G4B4 105 +#define SVGA3D_DEVCAP_DXFMT_Z_D32 106 +#define SVGA3D_DEVCAP_DXFMT_Z_D16 107 +#define SVGA3D_DEVCAP_DXFMT_Z_D24S8 108 +#define SVGA3D_DEVCAP_DXFMT_Z_D15S1 109 +#define SVGA3D_DEVCAP_DXFMT_LUMINANCE8 110 +#define SVGA3D_DEVCAP_DXFMT_LUMINANCE4_ALPHA4 111 +#define SVGA3D_DEVCAP_DXFMT_LUMINANCE16 112 +#define SVGA3D_DEVCAP_DXFMT_LUMINANCE8_ALPHA8 113 +#define SVGA3D_DEVCAP_DXFMT_DXT1 114 +#define SVGA3D_DEVCAP_DXFMT_DXT2 115 +#define SVGA3D_DEVCAP_DXFMT_DXT3 116 +#define SVGA3D_DEVCAP_DXFMT_DXT4 117 +#define SVGA3D_DEVCAP_DXFMT_DXT5 118 +#define SVGA3D_DEVCAP_DXFMT_BUMPU8V8 119 +#define SVGA3D_DEVCAP_DXFMT_BUMPL6V5U5 120 +#define SVGA3D_DEVCAP_DXFMT_BUMPX8L8V8U8 121 +#define SVGA3D_DEVCAP_DXFMT_FORMAT_DEAD1 122 +#define SVGA3D_DEVCAP_DXFMT_ARGB_S10E5 123 +#define SVGA3D_DEVCAP_DXFMT_ARGB_S23E8 124 +#define SVGA3D_DEVCAP_DXFMT_A2R10G10B10 125 +#define SVGA3D_DEVCAP_DXFMT_V8U8 126 +#define SVGA3D_DEVCAP_DXFMT_Q8W8V8U8 127 +#define SVGA3D_DEVCAP_DXFMT_CxV8U8 128 +#define SVGA3D_DEVCAP_DXFMT_X8L8V8U8 129 +#define SVGA3D_DEVCAP_DXFMT_A2W10V10U10 130 +#define SVGA3D_DEVCAP_DXFMT_ALPHA8 131 +#define SVGA3D_DEVCAP_DXFMT_R_S10E5 132 +#define SVGA3D_DEVCAP_DXFMT_R_S23E8 133 +#define SVGA3D_DEVCAP_DXFMT_RG_S10E5 134 +#define SVGA3D_DEVCAP_DXFMT_RG_S23E8 135 +#define SVGA3D_DEVCAP_DXFMT_BUFFER 136 +#define SVGA3D_DEVCAP_DXFMT_Z_D24X8 137 +#define SVGA3D_DEVCAP_DXFMT_V16U16 138 +#define SVGA3D_DEVCAP_DXFMT_G16R16 139 +#define SVGA3D_DEVCAP_DXFMT_A16B16G16R16 140 +#define SVGA3D_DEVCAP_DXFMT_UYVY 141 +#define SVGA3D_DEVCAP_DXFMT_YUY2 142 +#define SVGA3D_DEVCAP_DXFMT_NV12 143 +#define SVGA3D_DEVCAP_DXFMT_FORMAT_DEAD2 144 +#define SVGA3D_DEVCAP_DXFMT_R32G32B32A32_TYPELESS 145 +#define SVGA3D_DEVCAP_DXFMT_R32G32B32A32_UINT 146 +#define SVGA3D_DEVCAP_DXFMT_R32G32B32A32_SINT 147 +#define SVGA3D_DEVCAP_DXFMT_R32G32B32_TYPELESS 148 +#define SVGA3D_DEVCAP_DXFMT_R32G32B32_FLOAT 149 +#define SVGA3D_DEVCAP_DXFMT_R32G32B32_UINT 150 +#define SVGA3D_DEVCAP_DXFMT_R32G32B32_SINT 151 +#define SVGA3D_DEVCAP_DXFMT_R16G16B16A16_TYPELESS 152 +#define SVGA3D_DEVCAP_DXFMT_R16G16B16A16_UINT 153 +#define SVGA3D_DEVCAP_DXFMT_R16G16B16A16_SNORM 154 +#define SVGA3D_DEVCAP_DXFMT_R16G16B16A16_SINT 155 +#define SVGA3D_DEVCAP_DXFMT_R32G32_TYPELESS 156 +#define SVGA3D_DEVCAP_DXFMT_R32G32_UINT 157 +#define SVGA3D_DEVCAP_DXFMT_R32G32_SINT 158 +#define SVGA3D_DEVCAP_DXFMT_R32G8X24_TYPELESS 159 +#define SVGA3D_DEVCAP_DXFMT_D32_FLOAT_S8X24_UINT 160 +#define SVGA3D_DEVCAP_DXFMT_R32_FLOAT_X8X24 161 +#define SVGA3D_DEVCAP_DXFMT_X32_G8X24_UINT 162 +#define SVGA3D_DEVCAP_DXFMT_R10G10B10A2_TYPELESS 163 +#define SVGA3D_DEVCAP_DXFMT_R10G10B10A2_UINT 164 +#define SVGA3D_DEVCAP_DXFMT_R11G11B10_FLOAT 165 +#define SVGA3D_DEVCAP_DXFMT_R8G8B8A8_TYPELESS 166 +#define SVGA3D_DEVCAP_DXFMT_R8G8B8A8_UNORM 167 +#define SVGA3D_DEVCAP_DXFMT_R8G8B8A8_UNORM_SRGB 168 +#define SVGA3D_DEVCAP_DXFMT_R8G8B8A8_UINT 169 +#define SVGA3D_DEVCAP_DXFMT_R8G8B8A8_SINT 170 +#define SVGA3D_DEVCAP_DXFMT_R16G16_TYPELESS 171 +#define SVGA3D_DEVCAP_DXFMT_R16G16_UINT 172 +#define SVGA3D_DEVCAP_DXFMT_R16G16_SINT 173 +#define SVGA3D_DEVCAP_DXFMT_R32_TYPELESS 174 +#define SVGA3D_DEVCAP_DXFMT_D32_FLOAT 175 +#define SVGA3D_DEVCAP_DXFMT_R32_UINT 176 +#define SVGA3D_DEVCAP_DXFMT_R32_SINT 177 +#define SVGA3D_DEVCAP_DXFMT_R24G8_TYPELESS 178 +#define SVGA3D_DEVCAP_DXFMT_D24_UNORM_S8_UINT 179 +#define SVGA3D_DEVCAP_DXFMT_R24_UNORM_X8 180 +#define SVGA3D_DEVCAP_DXFMT_X24_G8_UINT 181 +#define SVGA3D_DEVCAP_DXFMT_R8G8_TYPELESS 182 +#define SVGA3D_DEVCAP_DXFMT_R8G8_UNORM 183 +#define SVGA3D_DEVCAP_DXFMT_R8G8_UINT 184 +#define SVGA3D_DEVCAP_DXFMT_R8G8_SINT 185 +#define SVGA3D_DEVCAP_DXFMT_R16_TYPELESS 186 +#define SVGA3D_DEVCAP_DXFMT_R16_UNORM 187 +#define SVGA3D_DEVCAP_DXFMT_R16_UINT 188 +#define SVGA3D_DEVCAP_DXFMT_R16_SNORM 189 +#define SVGA3D_DEVCAP_DXFMT_R16_SINT 190 +#define SVGA3D_DEVCAP_DXFMT_R8_TYPELESS 191 +#define SVGA3D_DEVCAP_DXFMT_R8_UNORM 192 +#define SVGA3D_DEVCAP_DXFMT_R8_UINT 193 +#define SVGA3D_DEVCAP_DXFMT_R8_SNORM 194 +#define SVGA3D_DEVCAP_DXFMT_R8_SINT 195 +#define SVGA3D_DEVCAP_DXFMT_P8 196 +#define SVGA3D_DEVCAP_DXFMT_R9G9B9E5_SHAREDEXP 197 +#define SVGA3D_DEVCAP_DXFMT_R8G8_B8G8_UNORM 198 +#define SVGA3D_DEVCAP_DXFMT_G8R8_G8B8_UNORM 199 +#define SVGA3D_DEVCAP_DXFMT_BC1_TYPELESS 200 +#define SVGA3D_DEVCAP_DXFMT_BC1_UNORM_SRGB 201 +#define SVGA3D_DEVCAP_DXFMT_BC2_TYPELESS 202 +#define SVGA3D_DEVCAP_DXFMT_BC2_UNORM_SRGB 203 +#define SVGA3D_DEVCAP_DXFMT_BC3_TYPELESS 204 +#define SVGA3D_DEVCAP_DXFMT_BC3_UNORM_SRGB 205 +#define SVGA3D_DEVCAP_DXFMT_BC4_TYPELESS 206 +#define SVGA3D_DEVCAP_DXFMT_ATI1 207 +#define SVGA3D_DEVCAP_DXFMT_BC4_SNORM 208 +#define SVGA3D_DEVCAP_DXFMT_BC5_TYPELESS 209 +#define SVGA3D_DEVCAP_DXFMT_ATI2 210 +#define SVGA3D_DEVCAP_DXFMT_BC5_SNORM 211 +#define SVGA3D_DEVCAP_DXFMT_R10G10B10_XR_BIAS_A2_UNORM 212 +#define SVGA3D_DEVCAP_DXFMT_B8G8R8A8_TYPELESS 213 +#define SVGA3D_DEVCAP_DXFMT_B8G8R8A8_UNORM_SRGB 214 +#define SVGA3D_DEVCAP_DXFMT_B8G8R8X8_TYPELESS 215 +#define SVGA3D_DEVCAP_DXFMT_B8G8R8X8_UNORM_SRGB 216 +#define SVGA3D_DEVCAP_DXFMT_Z_DF16 217 +#define SVGA3D_DEVCAP_DXFMT_Z_DF24 218 +#define SVGA3D_DEVCAP_DXFMT_Z_D24S8_INT 219 +#define SVGA3D_DEVCAP_DXFMT_YV12 220 +#define SVGA3D_DEVCAP_DXFMT_R32G32B32A32_FLOAT 221 +#define SVGA3D_DEVCAP_DXFMT_R16G16B16A16_FLOAT 222 +#define SVGA3D_DEVCAP_DXFMT_R16G16B16A16_UNORM 223 +#define SVGA3D_DEVCAP_DXFMT_R32G32_FLOAT 224 +#define SVGA3D_DEVCAP_DXFMT_R10G10B10A2_UNORM 225 +#define SVGA3D_DEVCAP_DXFMT_R8G8B8A8_SNORM 226 +#define SVGA3D_DEVCAP_DXFMT_R16G16_FLOAT 227 +#define SVGA3D_DEVCAP_DXFMT_R16G16_UNORM 228 +#define SVGA3D_DEVCAP_DXFMT_R16G16_SNORM 229 +#define SVGA3D_DEVCAP_DXFMT_R32_FLOAT 230 +#define SVGA3D_DEVCAP_DXFMT_R8G8_SNORM 231 +#define SVGA3D_DEVCAP_DXFMT_R16_FLOAT 232 +#define SVGA3D_DEVCAP_DXFMT_D16_UNORM 233 +#define SVGA3D_DEVCAP_DXFMT_A8_UNORM 234 +#define SVGA3D_DEVCAP_DXFMT_BC1_UNORM 235 +#define SVGA3D_DEVCAP_DXFMT_BC2_UNORM 236 +#define SVGA3D_DEVCAP_DXFMT_BC3_UNORM 237 +#define SVGA3D_DEVCAP_DXFMT_B5G6R5_UNORM 238 +#define SVGA3D_DEVCAP_DXFMT_B5G5R5A1_UNORM 239 +#define SVGA3D_DEVCAP_DXFMT_B8G8R8A8_UNORM 240 +#define SVGA3D_DEVCAP_DXFMT_B8G8R8X8_UNORM 241 +#define SVGA3D_DEVCAP_DXFMT_BC4_UNORM 242 +#define SVGA3D_DEVCAP_DXFMT_BC5_UNORM 243 + +#define SVGA3D_DEVCAP_SM41 244 +#define SVGA3D_DEVCAP_MULTISAMPLE_2X 245 +#define SVGA3D_DEVCAP_MULTISAMPLE_4X 246 + +#define SVGA3D_DEVCAP_MS_FULL_QUALITY 247 + +#define SVGA3D_DEVCAP_LOGICOPS 248 + +#define SVGA3D_DEVCAP_LOGIC_BLENDOPS 249 + +#define SVGA3D_DEVCAP_DEAD12 250 + +#define SVGA3D_DEVCAP_DXFMT_BC6H_TYPELESS 251 +#define SVGA3D_DEVCAP_DXFMT_BC6H_UF16 252 +#define SVGA3D_DEVCAP_DXFMT_BC6H_SF16 253 +#define SVGA3D_DEVCAP_DXFMT_BC7_TYPELESS 254 +#define SVGA3D_DEVCAP_DXFMT_BC7_UNORM 255 +#define SVGA3D_DEVCAP_DXFMT_BC7_UNORM_SRGB 256 + +#define SVGA3D_DEVCAP_DEAD13 257 + +#define SVGA3D_DEVCAP_SM5 258 +#define SVGA3D_DEVCAP_MULTISAMPLE_8X 259 + +#define SVGA3D_DEVCAP_MAX_FORCED_SAMPLE_COUNT 260 + +#define SVGA3D_DEVCAP_GL43 261 + +#define SVGA3D_DEVCAP_MAX 262 + +#define SVGA3D_DXFMT_SUPPORTED (1 << 0) +#define SVGA3D_DXFMT_SHADER_SAMPLE (1 << 1) +#define SVGA3D_DXFMT_COLOR_RENDERTARGET (1 << 2) +#define SVGA3D_DXFMT_DEPTH_RENDERTARGET (1 << 3) +#define SVGA3D_DXFMT_BLENDABLE (1 << 4) +#define SVGA3D_DXFMT_MIPS (1 << 5) +#define SVGA3D_DXFMT_ARRAY (1 << 6) +#define SVGA3D_DXFMT_VOLUME (1 << 7) +#define SVGA3D_DXFMT_DX_VERTEX_BUFFER (1 << 8) +#define SVGA3D_DXFMT_MULTISAMPLE (1 << 9) +#define SVGA3D_DXFMT_MAX (1 << 10) + +typedef union { + SVGA3dBool b; + uint32 u; + int32 i; + float f; +} SVGA3dDevCapResult; + +#endif diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga3d_dx.h b/drivers/gpu/drm/vmwgfx/device_include/svga3d_dx.h new file mode 100644 index 0000000000..925bf4b93f --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/device_include/svga3d_dx.h @@ -0,0 +1,1724 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2012-2021 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * svga3d_dx.h -- + * + * SVGA 3d hardware definitions for DX10 support. + */ + + + +#ifndef _SVGA3D_DX_H_ +#define _SVGA3D_DX_H_ + +#include "svga_reg.h" +#include "svga3d_limits.h" +#include "svga3d_types.h" + +#define SVGA3D_INPUT_MIN 0 +#define SVGA3D_INPUT_PER_VERTEX_DATA 0 +#define SVGA3D_INPUT_PER_INSTANCE_DATA 1 +#define SVGA3D_INPUT_MAX 2 +typedef uint32 SVGA3dInputClassification; + +#define SVGA3D_COLOR_WRITE_ENABLE_RED (1 << 0) +#define SVGA3D_COLOR_WRITE_ENABLE_GREEN (1 << 1) +#define SVGA3D_COLOR_WRITE_ENABLE_BLUE (1 << 2) +#define SVGA3D_COLOR_WRITE_ENABLE_ALPHA (1 << 3) +#define SVGA3D_COLOR_WRITE_ENABLE_ALL \ + (SVGA3D_COLOR_WRITE_ENABLE_RED | SVGA3D_COLOR_WRITE_ENABLE_GREEN | \ + SVGA3D_COLOR_WRITE_ENABLE_BLUE | SVGA3D_COLOR_WRITE_ENABLE_ALPHA) +typedef uint8 SVGA3dColorWriteEnable; + +#define SVGA3D_DEPTH_WRITE_MASK_ZERO 0 +#define SVGA3D_DEPTH_WRITE_MASK_ALL 1 +typedef uint8 SVGA3dDepthWriteMask; + +#define SVGA3D_FILTER_MIP_LINEAR (1 << 0) +#define SVGA3D_FILTER_MAG_LINEAR (1 << 2) +#define SVGA3D_FILTER_MIN_LINEAR (1 << 4) +#define SVGA3D_FILTER_ANISOTROPIC (1 << 6) +#define SVGA3D_FILTER_COMPARE (1 << 7) +typedef uint32 SVGA3dFilter; + +#define SVGA3D_CULL_INVALID 0 +#define SVGA3D_CULL_MIN 1 +#define SVGA3D_CULL_NONE 1 +#define SVGA3D_CULL_FRONT 2 +#define SVGA3D_CULL_BACK 3 +#define SVGA3D_CULL_MAX 4 +typedef uint8 SVGA3dCullMode; + +#define SVGA3D_COMPARISON_INVALID 0 +#define SVGA3D_COMPARISON_MIN 1 +#define SVGA3D_COMPARISON_NEVER 1 +#define SVGA3D_COMPARISON_LESS 2 +#define SVGA3D_COMPARISON_EQUAL 3 +#define SVGA3D_COMPARISON_LESS_EQUAL 4 +#define SVGA3D_COMPARISON_GREATER 5 +#define SVGA3D_COMPARISON_NOT_EQUAL 6 +#define SVGA3D_COMPARISON_GREATER_EQUAL 7 +#define SVGA3D_COMPARISON_ALWAYS 8 +#define SVGA3D_COMPARISON_MAX 9 +typedef uint8 SVGA3dComparisonFunc; + +#define SVGA3D_MULTISAMPLE_RAST_DISABLE 0 +#define SVGA3D_MULTISAMPLE_RAST_ENABLE 1 +#define SVGA3D_MULTISAMPLE_RAST_DX_MAX 1 +#define SVGA3D_MULTISAMPLE_RAST_DISABLE_LINE 2 +#define SVGA3D_MULTISAMPLE_RAST_MAX 2 +typedef uint8 SVGA3dMultisampleRastEnable; + +#define SVGA3D_DX_MAX_VERTEXBUFFERS 32 +#define SVGA3D_DX_MAX_VERTEXINPUTREGISTERS 16 +#define SVGA3D_DX_SM41_MAX_VERTEXINPUTREGISTERS 32 +#define SVGA3D_DX_MAX_SOTARGETS 4 +#define SVGA3D_DX_MAX_SRVIEWS 128 +#define SVGA3D_DX_MAX_CONSTBUFFERS 16 +#define SVGA3D_DX_MAX_SAMPLERS 16 +#define SVGA3D_DX_MAX_CLASS_INSTANCES 253 + +#define SVGA3D_DX_MAX_CONSTBUF_BINDING_SIZE (4096 * 4 * (uint32)sizeof(uint32)) + +typedef uint32 SVGA3dShaderResourceViewId; +typedef uint32 SVGA3dRenderTargetViewId; +typedef uint32 SVGA3dDepthStencilViewId; +typedef uint32 SVGA3dUAViewId; + +typedef uint32 SVGA3dShaderId; +typedef uint32 SVGA3dElementLayoutId; +typedef uint32 SVGA3dSamplerId; +typedef uint32 SVGA3dBlendStateId; +typedef uint32 SVGA3dDepthStencilStateId; +typedef uint32 SVGA3dRasterizerStateId; +typedef uint32 SVGA3dQueryId; +typedef uint32 SVGA3dStreamOutputId; + +typedef union { + struct { + uint32 r; + uint32 g; + uint32 b; + uint32 a; + }; + + uint32 value[4]; +} SVGA3dRGBAUint32; + +#pragma pack(push, 1) +typedef struct { + uint32 cid; + SVGAMobId mobid; +} SVGAOTableDXContextEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineContext { + uint32 cid; +} SVGA3dCmdDXDefineContext; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroyContext { + uint32 cid; +} SVGA3dCmdDXDestroyContext; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXBindContext { + uint32 cid; + SVGAMobId mobid; + uint32 validContents; +} SVGA3dCmdDXBindContext; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXReadbackContext { + uint32 cid; +} SVGA3dCmdDXReadbackContext; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXInvalidateContext { + uint32 cid; +} SVGA3dCmdDXInvalidateContext; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetSingleConstantBuffer { + uint32 slot; + SVGA3dShaderType type; + SVGA3dSurfaceId sid; + uint32 offsetInBytes; + uint32 sizeInBytes; +} SVGA3dCmdDXSetSingleConstantBuffer; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetShaderResources { + uint32 startView; + SVGA3dShaderType type; + +} SVGA3dCmdDXSetShaderResources; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetShader { + SVGA3dShaderId shaderId; + SVGA3dShaderType type; +} SVGA3dCmdDXSetShader; +#pragma pack(pop) + +typedef union { + struct { + uint32 cbOffset : 12; + uint32 cbId : 4; + uint32 baseSamp : 4; + uint32 baseTex : 7; + uint32 reserved : 5; + }; + uint32 value; +} SVGA3dIfaceData; + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetShaderIface { + SVGA3dShaderType type; + uint32 numClassInstances; + uint32 index; + uint32 iface; + SVGA3dIfaceData data; +} SVGA3dCmdDXSetShaderIface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXBindShaderIface { + uint32 cid; + SVGAMobId mobid; + uint32 offsetInBytes; +} SVGA3dCmdDXBindShaderIface; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetSamplers { + uint32 startSampler; + SVGA3dShaderType type; + +} SVGA3dCmdDXSetSamplers; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDraw { + uint32 vertexCount; + uint32 startVertexLocation; +} SVGA3dCmdDXDraw; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDrawIndexed { + uint32 indexCount; + uint32 startIndexLocation; + int32 baseVertexLocation; +} SVGA3dCmdDXDrawIndexed; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDrawInstanced { + uint32 vertexCountPerInstance; + uint32 instanceCount; + uint32 startVertexLocation; + uint32 startInstanceLocation; +} SVGA3dCmdDXDrawInstanced; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDrawIndexedInstanced { + uint32 indexCountPerInstance; + uint32 instanceCount; + uint32 startIndexLocation; + int32 baseVertexLocation; + uint32 startInstanceLocation; +} SVGA3dCmdDXDrawIndexedInstanced; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDrawIndexedInstancedIndirect { + SVGA3dSurfaceId argsBufferSid; + uint32 byteOffsetForArgs; +} SVGA3dCmdDXDrawIndexedInstancedIndirect; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDrawInstancedIndirect { + SVGA3dSurfaceId argsBufferSid; + uint32 byteOffsetForArgs; +} SVGA3dCmdDXDrawInstancedIndirect; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDrawAuto { + uint32 pad0; +} SVGA3dCmdDXDrawAuto; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDispatch { + uint32 threadGroupCountX; + uint32 threadGroupCountY; + uint32 threadGroupCountZ; +} SVGA3dCmdDXDispatch; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDispatchIndirect { + SVGA3dSurfaceId argsBufferSid; + uint32 byteOffsetForArgs; +} SVGA3dCmdDXDispatchIndirect; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetInputLayout { + SVGA3dElementLayoutId elementLayoutId; +} SVGA3dCmdDXSetInputLayout; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dVertexBuffer { + SVGA3dSurfaceId sid; + uint32 stride; + uint32 offset; +} SVGA3dVertexBuffer; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetVertexBuffers { + uint32 startBuffer; + +} SVGA3dCmdDXSetVertexBuffers; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dVertexBuffer_v2 { + SVGA3dSurfaceId sid; + uint32 stride; + uint32 offset; + uint32 sizeInBytes; +} SVGA3dVertexBuffer_v2; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetVertexBuffers_v2 { + uint32 startBuffer; + +} SVGA3dCmdDXSetVertexBuffers_v2; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dVertexBufferOffsetAndSize { + uint32 stride; + uint32 offset; + uint32 sizeInBytes; +} SVGA3dVertexBufferOffsetAndSize; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetVertexBuffersOffsetAndSize { + uint32 startBuffer; + +} SVGA3dCmdDXSetVertexBuffersOffsetAndSize; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetIndexBuffer { + SVGA3dSurfaceId sid; + SVGA3dSurfaceFormat format; + uint32 offset; +} SVGA3dCmdDXSetIndexBuffer; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetIndexBuffer_v2 { + SVGA3dSurfaceId sid; + SVGA3dSurfaceFormat format; + uint32 offset; + uint32 sizeInBytes; +} SVGA3dCmdDXSetIndexBuffer_v2; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetIndexBufferOffsetAndSize { + SVGA3dSurfaceFormat format; + uint32 offset; + uint32 sizeInBytes; +} SVGA3dCmdDXSetIndexBufferOffsetAndSize; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetTopology { + SVGA3dPrimitiveType topology; +} SVGA3dCmdDXSetTopology; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetRenderTargets { + SVGA3dDepthStencilViewId depthStencilViewId; + +} SVGA3dCmdDXSetRenderTargets; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetBlendState { + SVGA3dBlendStateId blendId; + float blendFactor[4]; + uint32 sampleMask; +} SVGA3dCmdDXSetBlendState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetDepthStencilState { + SVGA3dDepthStencilStateId depthStencilId; + uint32 stencilRef; +} SVGA3dCmdDXSetDepthStencilState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetRasterizerState { + SVGA3dRasterizerStateId rasterizerId; +} SVGA3dCmdDXSetRasterizerState; +#pragma pack(pop) + +#define SVGA3D_DXQUERY_FLAG_PREDICATEHINT (1 << 0) +typedef uint32 SVGA3dDXQueryFlags; + +#define SVGADX_QDSTATE_INVALID ((uint8)-1) +#define SVGADX_QDSTATE_MIN 0 +#define SVGADX_QDSTATE_IDLE 0 +#define SVGADX_QDSTATE_ACTIVE 1 +#define SVGADX_QDSTATE_PENDING 2 +#define SVGADX_QDSTATE_FINISHED 3 +#define SVGADX_QDSTATE_MAX 4 +typedef uint8 SVGADXQueryDeviceState; + +#pragma pack(push, 1) +typedef struct { + SVGA3dQueryTypeUint8 type; + uint16 pad0; + SVGADXQueryDeviceState state; + SVGA3dDXQueryFlags flags; + SVGAMobId mobid; + uint32 offset; +} SVGACOTableDXQueryEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineQuery { + SVGA3dQueryId queryId; + SVGA3dQueryType type; + SVGA3dDXQueryFlags flags; +} SVGA3dCmdDXDefineQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroyQuery { + SVGA3dQueryId queryId; +} SVGA3dCmdDXDestroyQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXBindQuery { + SVGA3dQueryId queryId; + SVGAMobId mobid; +} SVGA3dCmdDXBindQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetQueryOffset { + SVGA3dQueryId queryId; + uint32 mobOffset; +} SVGA3dCmdDXSetQueryOffset; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXBeginQuery { + SVGA3dQueryId queryId; +} SVGA3dCmdDXBeginQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXEndQuery { + SVGA3dQueryId queryId; +} SVGA3dCmdDXEndQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXReadbackQuery { + SVGA3dQueryId queryId; +} SVGA3dCmdDXReadbackQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXMoveQuery { + SVGA3dQueryId queryId; + SVGAMobId mobid; + uint32 mobOffset; +} SVGA3dCmdDXMoveQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXBindAllQuery { + uint32 cid; + SVGAMobId mobid; +} SVGA3dCmdDXBindAllQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXReadbackAllQuery { + uint32 cid; +} SVGA3dCmdDXReadbackAllQuery; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetPredication { + SVGA3dQueryId queryId; + uint32 predicateValue; +} SVGA3dCmdDXSetPredication; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dDXSOState { + uint32 offset; + uint32 intOffset; + uint32 dead1; + uint32 dead2; +} SVGA3dDXSOState; +#pragma pack(pop) + +#define SVGA3D_DX_SO_OFFSET_APPEND ((uint32)~0u) + +#pragma pack(push, 1) +typedef struct SVGA3dSoTarget { + SVGA3dSurfaceId sid; + uint32 offset; + uint32 sizeInBytes; +} SVGA3dSoTarget; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetSOTargets { + uint32 pad0; + +} SVGA3dCmdDXSetSOTargets; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dViewport { + float x; + float y; + float width; + float height; + float minDepth; + float maxDepth; +} SVGA3dViewport; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetViewports { + uint32 pad0; + +} SVGA3dCmdDXSetViewports; +#pragma pack(pop) + +#define SVGA3D_DX_MAX_VIEWPORTS 16 + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetScissorRects { + uint32 pad0; + +} SVGA3dCmdDXSetScissorRects; +#pragma pack(pop) + +#define SVGA3D_DX_MAX_SCISSORRECTS 16 + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXClearRenderTargetView { + SVGA3dRenderTargetViewId renderTargetViewId; + SVGA3dRGBAFloat rgba; +} SVGA3dCmdDXClearRenderTargetView; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXClearDepthStencilView { + uint16 flags; + uint16 stencil; + SVGA3dDepthStencilViewId depthStencilViewId; + float depth; +} SVGA3dCmdDXClearDepthStencilView; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXPredCopyRegion { + SVGA3dSurfaceId dstSid; + uint32 dstSubResource; + SVGA3dSurfaceId srcSid; + uint32 srcSubResource; + SVGA3dCopyBox box; +} SVGA3dCmdDXPredCopyRegion; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXPredStagingCopyRegion { + SVGA3dSurfaceId dstSid; + uint32 dstSubResource; + SVGA3dSurfaceId srcSid; + uint32 srcSubResource; + SVGA3dCopyBox box; + uint8 readback; + uint8 unsynchronized; + uint8 mustBeZero[2]; +} SVGA3dCmdDXPredStagingCopyRegion; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXPredCopy { + SVGA3dSurfaceId dstSid; + SVGA3dSurfaceId srcSid; +} SVGA3dCmdDXPredCopy; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXPredConvertRegion { + SVGA3dSurfaceId dstSid; + uint32 dstSubResource; + SVGA3dBox destBox; + SVGA3dSurfaceId srcSid; + uint32 srcSubResource; + SVGA3dBox srcBox; +} SVGA3dCmdDXPredConvertRegion; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXPredStagingConvertRegion { + SVGA3dSurfaceId dstSid; + uint32 dstSubResource; + SVGA3dBox destBox; + SVGA3dSurfaceId srcSid; + uint32 srcSubResource; + SVGA3dBox srcBox; + uint8 readback; + uint8 unsynchronized; + uint8 mustBeZero[2]; +} SVGA3dCmdDXPredStagingConvertRegion; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXPredConvert { + SVGA3dSurfaceId dstSid; + SVGA3dSurfaceId srcSid; +} SVGA3dCmdDXPredConvert; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXPredStagingConvert { + SVGA3dSurfaceId dstSid; + SVGA3dSurfaceId srcSid; + uint8 readback; + uint8 unsynchronized; + uint8 mustBeZero[2]; +} SVGA3dCmdDXPredStagingConvert; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXBufferCopy { + SVGA3dSurfaceId dest; + SVGA3dSurfaceId src; + uint32 destX; + uint32 srcX; + uint32 width; +} SVGA3dCmdDXBufferCopy; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXStagingBufferCopy { + SVGA3dSurfaceId dest; + SVGA3dSurfaceId src; + uint32 destX; + uint32 srcX; + uint32 width; + uint8 readback; + uint8 unsynchronized; + uint8 mustBeZero[2]; +} SVGA3dCmdDXStagingBufferCopy; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceId dstSid; + uint32 dstSubResource; + SVGA3dSurfaceId srcSid; + uint32 srcSubResource; + SVGA3dSurfaceFormat copyFormat; +} SVGA3dCmdDXResolveCopy; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceId dstSid; + uint32 dstSubResource; + SVGA3dSurfaceId srcSid; + uint32 srcSubResource; + SVGA3dSurfaceFormat copyFormat; +} SVGA3dCmdDXPredResolveCopy; +#pragma pack(pop) + +typedef uint32 SVGA3dDXPresentBltMode; +#define SVGADX_PRESENTBLT_LINEAR (1 << 0) +#define SVGADX_PRESENTBLT_FORCE_SRC_SRGB (1 << 1) +#define SVGADX_PRESENTBLT_FORCE_SRC_XRBIAS (1 << 2) +#define SVGADX_PRESENTBLT_MODE_MAX (1 << 3) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXPresentBlt { + SVGA3dSurfaceId srcSid; + uint32 srcSubResource; + SVGA3dSurfaceId dstSid; + uint32 destSubResource; + SVGA3dBox boxSrc; + SVGA3dBox boxDest; + SVGA3dDXPresentBltMode mode; +} SVGA3dCmdDXPresentBlt; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXGenMips { + SVGA3dShaderResourceViewId shaderResourceViewId; +} SVGA3dCmdDXGenMips; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXUpdateSubResource { + SVGA3dSurfaceId sid; + uint32 subResource; + SVGA3dBox box; +} SVGA3dCmdDXUpdateSubResource; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXReadbackSubResource { + SVGA3dSurfaceId sid; + uint32 subResource; +} SVGA3dCmdDXReadbackSubResource; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXInvalidateSubResource { + SVGA3dSurfaceId sid; + uint32 subResource; +} SVGA3dCmdDXInvalidateSubResource; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXTransferFromBuffer { + SVGA3dSurfaceId srcSid; + uint32 srcOffset; + uint32 srcPitch; + uint32 srcSlicePitch; + SVGA3dSurfaceId destSid; + uint32 destSubResource; + SVGA3dBox destBox; +} SVGA3dCmdDXTransferFromBuffer; +#pragma pack(pop) + +#define SVGA3D_TRANSFER_TO_BUFFER_READBACK (1 << 0) +#define SVGA3D_TRANSFER_TO_BUFFER_FLAGS_MASK (1 << 0) +typedef uint32 SVGA3dTransferToBufferFlags; + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXTransferToBuffer { + SVGA3dSurfaceId srcSid; + uint32 srcSubResource; + SVGA3dBox srcBox; + SVGA3dSurfaceId destSid; + uint32 destOffset; + uint32 destPitch; + uint32 destSlicePitch; + SVGA3dTransferToBufferFlags flags; +} SVGA3dCmdDXTransferToBuffer; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXPredTransferFromBuffer { + SVGA3dSurfaceId srcSid; + uint32 srcOffset; + uint32 srcPitch; + uint32 srcSlicePitch; + SVGA3dSurfaceId destSid; + uint32 destSubResource; + SVGA3dBox destBox; +} SVGA3dCmdDXPredTransferFromBuffer; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSurfaceCopyAndReadback { + SVGA3dSurfaceId srcSid; + SVGA3dSurfaceId destSid; + SVGA3dCopyBox box; +} SVGA3dCmdDXSurfaceCopyAndReadback; +#pragma pack(pop) + +typedef uint32 SVGADXHintId; +#define SVGA_DX_HINT_NONE 0 +#define SVGA_DX_HINT_PREFETCH_OBJECT 1 +#define SVGA_DX_HINT_PREEVICT_OBJECT 2 +#define SVGA_DX_HINT_PREFETCH_COBJECT 3 +#define SVGA_DX_HINT_PREEVICT_COBJECT 4 +#define SVGA_DX_HINT_MAX 5 + +#pragma pack(push, 1) +typedef struct SVGAObjectRef { + SVGAOTableType type; + uint32 id; +} SVGAObjectRef; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGACObjectRef { + SVGACOTableType type; + uint32 cid; + uint32 id; +} SVGACObjectRef; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXHint { + SVGADXHintId hintId; + +} SVGA3dCmdDXHint; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXBufferUpdate { + SVGA3dSurfaceId sid; + uint32 x; + uint32 width; +} SVGA3dCmdDXBufferUpdate; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetConstantBufferOffset { + uint32 slot; + uint32 offsetInBytes; +} SVGA3dCmdDXSetConstantBufferOffset; +#pragma pack(pop) + +typedef SVGA3dCmdDXSetConstantBufferOffset SVGA3dCmdDXSetVSConstantBufferOffset; + +typedef SVGA3dCmdDXSetConstantBufferOffset SVGA3dCmdDXSetPSConstantBufferOffset; + +typedef SVGA3dCmdDXSetConstantBufferOffset SVGA3dCmdDXSetGSConstantBufferOffset; + +typedef SVGA3dCmdDXSetConstantBufferOffset SVGA3dCmdDXSetHSConstantBufferOffset; + +typedef SVGA3dCmdDXSetConstantBufferOffset SVGA3dCmdDXSetDSConstantBufferOffset; + +typedef SVGA3dCmdDXSetConstantBufferOffset SVGA3dCmdDXSetCSConstantBufferOffset; + +#define SVGA3D_BUFFEREX_SRV_RAW (1 << 0) +#define SVGA3D_BUFFEREX_SRV_FLAGS_MAX (1 << 1) +#define SVGA3D_BUFFEREX_SRV_FLAGS_MASK (SVGA3D_BUFFEREX_SRV_FLAGS_MAX - 1) +typedef uint32 SVGA3dBufferExFlags; + +#pragma pack(push, 1) +typedef struct { + union { + struct { + uint32 firstElement; + uint32 numElements; + uint32 pad0; + uint32 pad1; + } buffer; + struct { + uint32 mostDetailedMip; + uint32 firstArraySlice; + uint32 mipLevels; + uint32 arraySize; + } tex; + struct { + uint32 firstElement; + uint32 numElements; + SVGA3dBufferExFlags flags; + uint32 pad0; + } bufferex; + }; +} SVGA3dShaderResourceViewDesc; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceId sid; + SVGA3dSurfaceFormat format; + SVGA3dResourceType resourceDimension; + SVGA3dShaderResourceViewDesc desc; + uint32 pad; +} SVGACOTableDXSRViewEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineShaderResourceView { + SVGA3dShaderResourceViewId shaderResourceViewId; + + SVGA3dSurfaceId sid; + SVGA3dSurfaceFormat format; + SVGA3dResourceType resourceDimension; + + SVGA3dShaderResourceViewDesc desc; +} SVGA3dCmdDXDefineShaderResourceView; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroyShaderResourceView { + SVGA3dShaderResourceViewId shaderResourceViewId; +} SVGA3dCmdDXDestroyShaderResourceView; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dRenderTargetViewDesc { + union { + struct { + uint32 firstElement; + uint32 numElements; + uint32 padding0; + } buffer; + struct { + uint32 mipSlice; + uint32 firstArraySlice; + uint32 arraySize; + } tex; + struct { + uint32 mipSlice; + uint32 firstW; + uint32 wSize; + } tex3D; + }; +} SVGA3dRenderTargetViewDesc; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceId sid; + SVGA3dSurfaceFormat format; + SVGA3dResourceType resourceDimension; + SVGA3dRenderTargetViewDesc desc; + uint32 pad[2]; +} SVGACOTableDXRTViewEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineRenderTargetView { + SVGA3dRenderTargetViewId renderTargetViewId; + + SVGA3dSurfaceId sid; + SVGA3dSurfaceFormat format; + SVGA3dResourceType resourceDimension; + + SVGA3dRenderTargetViewDesc desc; +} SVGA3dCmdDXDefineRenderTargetView; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroyRenderTargetView { + SVGA3dRenderTargetViewId renderTargetViewId; +} SVGA3dCmdDXDestroyRenderTargetView; +#pragma pack(pop) + +#define SVGA3D_DXDSVIEW_CREATE_READ_ONLY_DEPTH 0x01 +#define SVGA3D_DXDSVIEW_CREATE_READ_ONLY_STENCIL 0x02 +#define SVGA3D_DXDSVIEW_CREATE_FLAG_MASK 0x03 +typedef uint8 SVGA3DCreateDSViewFlags; + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceId sid; + SVGA3dSurfaceFormat format; + SVGA3dResourceType resourceDimension; + uint32 mipSlice; + uint32 firstArraySlice; + uint32 arraySize; + SVGA3DCreateDSViewFlags flags; + uint8 pad0; + uint16 pad1; + uint32 pad2; +} SVGACOTableDXDSViewEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineDepthStencilView { + SVGA3dDepthStencilViewId depthStencilViewId; + + SVGA3dSurfaceId sid; + SVGA3dSurfaceFormat format; + SVGA3dResourceType resourceDimension; + uint32 mipSlice; + uint32 firstArraySlice; + uint32 arraySize; + SVGA3DCreateDSViewFlags flags; + uint8 pad0; + uint16 pad1; +} SVGA3dCmdDXDefineDepthStencilView; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineDepthStencilView_v2 { + SVGA3dDepthStencilViewId depthStencilViewId; + + SVGA3dSurfaceId sid; + SVGA3dSurfaceFormat format; + SVGA3dResourceType resourceDimension; + uint32 mipSlice; + uint32 firstArraySlice; + uint32 arraySize; + SVGA3DCreateDSViewFlags flags; + uint8 pad0; + uint16 pad1; +} SVGA3dCmdDXDefineDepthStencilView_v2; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroyDepthStencilView { + SVGA3dDepthStencilViewId depthStencilViewId; +} SVGA3dCmdDXDestroyDepthStencilView; +#pragma pack(pop) + +#define SVGA3D_UABUFFER_RAW (1 << 0) +#define SVGA3D_UABUFFER_APPEND (1 << 1) +#define SVGA3D_UABUFFER_COUNTER (1 << 2) +typedef uint32 SVGA3dUABufferFlags; + +#pragma pack(push, 1) +typedef struct { + union { + struct { + uint32 firstElement; + uint32 numElements; + SVGA3dUABufferFlags flags; + uint32 padding0; + uint32 padding1; + } buffer; + struct { + uint32 mipSlice; + uint32 firstArraySlice; + uint32 arraySize; + uint32 padding0; + uint32 padding1; + } tex; + struct { + uint32 mipSlice; + uint32 firstW; + uint32 wSize; + uint32 padding0; + uint32 padding1; + } tex3D; + }; +} SVGA3dUAViewDesc; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dSurfaceId sid; + SVGA3dSurfaceFormat format; + SVGA3dResourceType resourceDimension; + SVGA3dUAViewDesc desc; + uint32 structureCount; + uint32 pad[7]; +} SVGACOTableDXUAViewEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineUAView { + SVGA3dUAViewId uaViewId; + + SVGA3dSurfaceId sid; + SVGA3dSurfaceFormat format; + SVGA3dResourceType resourceDimension; + + SVGA3dUAViewDesc desc; +} SVGA3dCmdDXDefineUAView; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroyUAView { + SVGA3dUAViewId uaViewId; +} SVGA3dCmdDXDestroyUAView; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXClearUAViewUint { + SVGA3dUAViewId uaViewId; + SVGA3dRGBAUint32 value; +} SVGA3dCmdDXClearUAViewUint; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXClearUAViewFloat { + SVGA3dUAViewId uaViewId; + SVGA3dRGBAFloat value; +} SVGA3dCmdDXClearUAViewFloat; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXCopyStructureCount { + SVGA3dUAViewId srcUAViewId; + SVGA3dSurfaceId destSid; + uint32 destByteOffset; +} SVGA3dCmdDXCopyStructureCount; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetStructureCount { + SVGA3dUAViewId uaViewId; + uint32 structureCount; +} SVGA3dCmdDXSetStructureCount; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetUAViews { + uint32 uavSpliceIndex; + +} SVGA3dCmdDXSetUAViews; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetCSUAViews { + uint32 startIndex; + +} SVGA3dCmdDXSetCSUAViews; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dInputElementDesc { + uint32 inputSlot; + uint32 alignedByteOffset; + SVGA3dSurfaceFormat format; + SVGA3dInputClassification inputSlotClass; + uint32 instanceDataStepRate; + uint32 inputRegister; +} SVGA3dInputElementDesc; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 elid; + uint32 numDescs; + SVGA3dInputElementDesc descs[32]; + uint32 pad[62]; +} SVGACOTableDXElementLayoutEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineElementLayout { + SVGA3dElementLayoutId elementLayoutId; + +} SVGA3dCmdDXDefineElementLayout; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroyElementLayout { + SVGA3dElementLayoutId elementLayoutId; +} SVGA3dCmdDXDestroyElementLayout; +#pragma pack(pop) + +#define SVGA3D_DX_MAX_RENDER_TARGETS 8 + +#pragma pack(push, 1) +typedef struct SVGA3dDXBlendStatePerRT { + uint8 blendEnable; + uint8 srcBlend; + uint8 destBlend; + uint8 blendOp; + uint8 srcBlendAlpha; + uint8 destBlendAlpha; + uint8 blendOpAlpha; + SVGA3dColorWriteEnable renderTargetWriteMask; + uint8 logicOpEnable; + uint8 logicOp; + uint16 pad0; +} SVGA3dDXBlendStatePerRT; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint8 alphaToCoverageEnable; + uint8 independentBlendEnable; + uint16 pad0; + SVGA3dDXBlendStatePerRT perRT[SVGA3D_DX_MAX_RENDER_TARGETS]; + uint32 pad1[7]; +} SVGACOTableDXBlendStateEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineBlendState { + SVGA3dBlendStateId blendId; + uint8 alphaToCoverageEnable; + uint8 independentBlendEnable; + uint16 pad0; + SVGA3dDXBlendStatePerRT perRT[SVGA3D_DX_MAX_RENDER_TARGETS]; +} SVGA3dCmdDXDefineBlendState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroyBlendState { + SVGA3dBlendStateId blendId; +} SVGA3dCmdDXDestroyBlendState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint8 depthEnable; + SVGA3dDepthWriteMask depthWriteMask; + SVGA3dComparisonFunc depthFunc; + uint8 stencilEnable; + uint8 frontEnable; + uint8 backEnable; + uint8 stencilReadMask; + uint8 stencilWriteMask; + + uint8 frontStencilFailOp; + uint8 frontStencilDepthFailOp; + uint8 frontStencilPassOp; + SVGA3dComparisonFunc frontStencilFunc; + + uint8 backStencilFailOp; + uint8 backStencilDepthFailOp; + uint8 backStencilPassOp; + SVGA3dComparisonFunc backStencilFunc; +} SVGACOTableDXDepthStencilEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineDepthStencilState { + SVGA3dDepthStencilStateId depthStencilId; + + uint8 depthEnable; + SVGA3dDepthWriteMask depthWriteMask; + SVGA3dComparisonFunc depthFunc; + uint8 stencilEnable; + uint8 frontEnable; + uint8 backEnable; + uint8 stencilReadMask; + uint8 stencilWriteMask; + + uint8 frontStencilFailOp; + uint8 frontStencilDepthFailOp; + uint8 frontStencilPassOp; + SVGA3dComparisonFunc frontStencilFunc; + + uint8 backStencilFailOp; + uint8 backStencilDepthFailOp; + uint8 backStencilPassOp; + SVGA3dComparisonFunc backStencilFunc; +} SVGA3dCmdDXDefineDepthStencilState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroyDepthStencilState { + SVGA3dDepthStencilStateId depthStencilId; +} SVGA3dCmdDXDestroyDepthStencilState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint8 fillMode; + SVGA3dCullMode cullMode; + uint8 frontCounterClockwise; + uint8 provokingVertexLast; + int32 depthBias; + float depthBiasClamp; + float slopeScaledDepthBias; + uint8 depthClipEnable; + uint8 scissorEnable; + SVGA3dMultisampleRastEnable multisampleEnable; + uint8 antialiasedLineEnable; + float lineWidth; + uint8 lineStippleEnable; + uint8 lineStippleFactor; + uint16 lineStipplePattern; + uint8 forcedSampleCount; + uint8 mustBeZero[3]; +} SVGACOTableDXRasterizerStateEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineRasterizerState { + SVGA3dRasterizerStateId rasterizerId; + + uint8 fillMode; + SVGA3dCullMode cullMode; + uint8 frontCounterClockwise; + uint8 provokingVertexLast; + int32 depthBias; + float depthBiasClamp; + float slopeScaledDepthBias; + uint8 depthClipEnable; + uint8 scissorEnable; + SVGA3dMultisampleRastEnable multisampleEnable; + uint8 antialiasedLineEnable; + float lineWidth; + uint8 lineStippleEnable; + uint8 lineStippleFactor; + uint16 lineStipplePattern; +} SVGA3dCmdDXDefineRasterizerState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineRasterizerState_v2 { + SVGA3dRasterizerStateId rasterizerId; + + uint8 fillMode; + SVGA3dCullMode cullMode; + uint8 frontCounterClockwise; + uint8 provokingVertexLast; + int32 depthBias; + float depthBiasClamp; + float slopeScaledDepthBias; + uint8 depthClipEnable; + uint8 scissorEnable; + SVGA3dMultisampleRastEnable multisampleEnable; + uint8 antialiasedLineEnable; + float lineWidth; + uint8 lineStippleEnable; + uint8 lineStippleFactor; + uint16 lineStipplePattern; + uint32 forcedSampleCount; +} SVGA3dCmdDXDefineRasterizerState_v2; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroyRasterizerState { + SVGA3dRasterizerStateId rasterizerId; +} SVGA3dCmdDXDestroyRasterizerState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGA3dFilter filter; + uint8 addressU; + uint8 addressV; + uint8 addressW; + uint8 pad0; + float mipLODBias; + uint8 maxAnisotropy; + SVGA3dComparisonFunc comparisonFunc; + uint16 pad1; + SVGA3dRGBAFloat borderColor; + float minLOD; + float maxLOD; + uint32 pad2[6]; +} SVGACOTableDXSamplerEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineSamplerState { + SVGA3dSamplerId samplerId; + SVGA3dFilter filter; + uint8 addressU; + uint8 addressV; + uint8 addressW; + uint8 pad0; + float mipLODBias; + uint8 maxAnisotropy; + SVGA3dComparisonFunc comparisonFunc; + uint16 pad1; + SVGA3dRGBAFloat borderColor; + float minLOD; + float maxLOD; +} SVGA3dCmdDXDefineSamplerState; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroySamplerState { + SVGA3dSamplerId samplerId; +} SVGA3dCmdDXDestroySamplerState; +#pragma pack(pop) + +#define SVGADX_SIGNATURE_SEMANTIC_NAME_UNDEFINED 0 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_POSITION 1 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_CLIP_DISTANCE 2 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_CULL_DISTANCE 3 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_RENDER_TARGET_ARRAY_INDEX 4 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_VIEWPORT_ARRAY_INDEX 5 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_VERTEX_ID 6 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_PRIMITIVE_ID 7 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_INSTANCE_ID 8 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_IS_FRONT_FACE 9 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_SAMPLE_INDEX 10 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_FINAL_QUAD_U_EQ_0_EDGE_TESSFACTOR 11 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_FINAL_QUAD_V_EQ_0_EDGE_TESSFACTOR 12 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_FINAL_QUAD_U_EQ_1_EDGE_TESSFACTOR 13 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_FINAL_QUAD_V_EQ_1_EDGE_TESSFACTOR 14 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_FINAL_QUAD_U_INSIDE_TESSFACTOR 15 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_FINAL_QUAD_V_INSIDE_TESSFACTOR 16 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_FINAL_TRI_U_EQ_0_EDGE_TESSFACTOR 17 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_FINAL_TRI_V_EQ_0_EDGE_TESSFACTOR 18 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_FINAL_TRI_W_EQ_0_EDGE_TESSFACTOR 19 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_FINAL_TRI_INSIDE_TESSFACTOR 20 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_FINAL_LINE_DETAIL_TESSFACTOR 21 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_FINAL_LINE_DENSITY_TESSFACTOR 22 +#define SVGADX_SIGNATURE_SEMANTIC_NAME_MAX 23 +typedef uint32 SVGA3dDXSignatureSemanticName; + +#define SVGADX_SIGNATURE_REGISTER_COMPONENT_UNKNOWN 0 +typedef uint32 SVGA3dDXSignatureRegisterComponentType; + +#define SVGADX_SIGNATURE_MIN_PRECISION_DEFAULT 0 +typedef uint32 SVGA3dDXSignatureMinPrecision; + +#pragma pack(push, 1) +typedef struct SVGA3dDXSignatureEntry { + uint32 registerIndex; + SVGA3dDXSignatureSemanticName semanticName; + uint32 mask; + SVGA3dDXSignatureRegisterComponentType componentType; + SVGA3dDXSignatureMinPrecision minPrecision; +} SVGA3dDXShaderSignatureEntry; +#pragma pack(pop) + +#define SVGADX_SIGNATURE_HEADER_VERSION_0 0x08a92d12 + +#pragma pack(push, 1) +typedef struct SVGA3dDXSignatureHeader { + uint32 headerVersion; + uint32 numInputSignatures; + uint32 numOutputSignatures; + uint32 numPatchConstantSignatures; +} SVGA3dDXShaderSignatureHeader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineShader { + SVGA3dShaderId shaderId; + SVGA3dShaderType type; + uint32 sizeInBytes; +} SVGA3dCmdDXDefineShader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGACOTableDXShaderEntry { + SVGA3dShaderType type; + uint32 sizeInBytes; + uint32 offsetInBytes; + SVGAMobId mobid; + uint32 pad[4]; +} SVGACOTableDXShaderEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroyShader { + SVGA3dShaderId shaderId; +} SVGA3dCmdDXDestroyShader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXBindShader { + uint32 cid; + uint32 shid; + SVGAMobId mobid; + uint32 offsetInBytes; +} SVGA3dCmdDXBindShader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXBindAllShader { + uint32 cid; + SVGAMobId mobid; +} SVGA3dCmdDXBindAllShader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXCondBindAllShader { + uint32 cid; + SVGAMobId testMobid; + SVGAMobId mobid; +} SVGA3dCmdDXCondBindAllShader; +#pragma pack(pop) + +#define SVGA3D_MAX_DX10_STREAMOUT_DECLS 64 +#define SVGA3D_MAX_STREAMOUT_DECLS 512 + +#pragma pack(push, 1) +typedef struct SVGA3dStreamOutputDeclarationEntry { + uint32 outputSlot; + uint32 registerIndex; + uint8 registerMask; + uint8 pad0; + uint16 pad1; + uint32 stream; +} SVGA3dStreamOutputDeclarationEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGAOTableStreamOutputEntry { + uint32 numOutputStreamEntries; + SVGA3dStreamOutputDeclarationEntry decl[SVGA3D_MAX_DX10_STREAMOUT_DECLS]; + uint32 streamOutputStrideInBytes[SVGA3D_DX_MAX_SOTARGETS]; + uint32 rasterizedStream; + uint32 numOutputStreamStrides; + uint32 mobid; + uint32 offsetInBytes; + uint8 usesMob; + uint8 pad0; + uint16 pad1; + uint32 pad2[246]; +} SVGACOTableDXStreamOutputEntry; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineStreamOutput { + SVGA3dStreamOutputId soid; + uint32 numOutputStreamEntries; + SVGA3dStreamOutputDeclarationEntry decl[SVGA3D_MAX_DX10_STREAMOUT_DECLS]; + uint32 streamOutputStrideInBytes[SVGA3D_DX_MAX_SOTARGETS]; + uint32 rasterizedStream; +} SVGA3dCmdDXDefineStreamOutput; +#pragma pack(pop) + +#define SVGA3D_DX_SO_NO_RASTERIZED_STREAM 0xFFFFFFFF + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDefineStreamOutputWithMob { + SVGA3dStreamOutputId soid; + uint32 numOutputStreamEntries; + uint32 numOutputStreamStrides; + uint32 streamOutputStrideInBytes[SVGA3D_DX_MAX_SOTARGETS]; + uint32 rasterizedStream; +} SVGA3dCmdDXDefineStreamOutputWithMob; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXBindStreamOutput { + SVGA3dStreamOutputId soid; + uint32 mobid; + uint32 offsetInBytes; + uint32 sizeInBytes; +} SVGA3dCmdDXBindStreamOutput; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXDestroyStreamOutput { + SVGA3dStreamOutputId soid; +} SVGA3dCmdDXDestroyStreamOutput; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetStreamOutput { + SVGA3dStreamOutputId soid; +} SVGA3dCmdDXSetStreamOutput; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetMinLOD { + SVGA3dSurfaceId sid; + float minLOD; +} SVGA3dCmdDXSetMinLOD; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint64 value; + uint32 mobId; + uint32 mobOffset; +} SVGA3dCmdDXMobFence64; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXSetCOTable { + uint32 cid; + uint32 mobid; + SVGACOTableType type; + uint32 validSizeInBytes; +} SVGA3dCmdDXSetCOTable; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXGrowCOTable { + uint32 cid; + uint32 mobid; + SVGACOTableType type; + uint32 validSizeInBytes; +} SVGA3dCmdDXGrowCOTable; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXReadbackCOTable { + uint32 cid; + SVGACOTableType type; +} SVGA3dCmdDXReadbackCOTable; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXCopyCOTableIntoMob { + uint32 cid; + SVGACOTableType type; + uint32 mobid; +} SVGA3dCmdDXCopyCOTableIntoMob; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXPredStagingCopy { + SVGA3dSurfaceId dstSid; + SVGA3dSurfaceId srcSid; + uint8 readback; + uint8 unsynchronized; + uint8 mustBeZero[2]; + +} SVGA3dCmdDXPredStagingCopy; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCmdDXStagingCopy { + SVGA3dSurfaceId dstSid; + SVGA3dSurfaceId srcSid; + uint8 readback; + uint8 unsynchronized; + uint8 mustBeZero[2]; + +} SVGA3dCmdDXStagingCopy; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCOTableData { + uint32 mobid; +} SVGA3dCOTableData; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dBufferBinding { + uint32 bufferId; + uint32 stride; + uint32 offset; +} SVGA3dBufferBinding; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dConstantBufferBinding { + uint32 sid; + uint32 offsetInBytes; + uint32 sizeInBytes; +} SVGA3dConstantBufferBinding; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGADXInputAssemblyMobFormat { + uint32 layoutId; + SVGA3dBufferBinding vertexBuffers[SVGA3D_DX_MAX_VERTEXBUFFERS]; + uint32 indexBufferSid; + uint32 pad; + uint32 indexBufferOffset; + uint32 indexBufferFormat; + uint32 topology; +} SVGADXInputAssemblyMobFormat; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGADXContextMobFormat { + SVGADXInputAssemblyMobFormat inputAssembly; + + struct { + uint32 blendStateId; + uint32 blendFactor[4]; + uint32 sampleMask; + uint32 depthStencilStateId; + uint32 stencilRef; + uint32 rasterizerStateId; + uint32 depthStencilViewId; + uint32 renderTargetViewIds[SVGA3D_DX_MAX_RENDER_TARGETS]; + } renderState; + + uint32 pad0[8]; + + struct { + uint32 targets[SVGA3D_DX_MAX_SOTARGETS]; + uint32 soid; + } streamOut; + + uint32 pad1[10]; + + uint32 uavSpliceIndex; + + uint8 numViewports; + uint8 numScissorRects; + uint16 pad2[1]; + + uint32 pad3[3]; + + SVGA3dViewport viewports[SVGA3D_DX_MAX_VIEWPORTS]; + uint32 pad4[32]; + + SVGASignedRect scissorRects[SVGA3D_DX_MAX_SCISSORRECTS]; + uint32 pad5[64]; + + struct { + uint32 queryID; + uint32 value; + } predication; + + SVGAMobId shaderIfaceMobid; + uint32 shaderIfaceOffset; + struct { + uint32 shaderId; + SVGA3dConstantBufferBinding + constantBuffers[SVGA3D_DX_MAX_CONSTBUFFERS]; + uint32 shaderResources[SVGA3D_DX_MAX_SRVIEWS]; + uint32 samplers[SVGA3D_DX_MAX_SAMPLERS]; + } shaderState[SVGA3D_NUM_SHADERTYPE]; + uint32 pad6[26]; + + SVGA3dQueryId queryID[SVGA3D_MAX_QUERY]; + + SVGA3dCOTableData cotables[SVGA_COTABLE_MAX]; + + uint32 pad7[64]; + + uint32 uaViewIds[SVGA3D_DX11_1_MAX_UAVIEWS]; + uint32 csuaViewIds[SVGA3D_DX11_1_MAX_UAVIEWS]; + + uint32 pad8[188]; +} SVGADXContextMobFormat; +#pragma pack(pop) + +#define SVGA3D_DX_MAX_CLASS_INSTANCES_PADDED 256 + +#pragma pack(push, 1) +typedef struct SVGADXShaderIfaceMobFormat { + struct { + uint32 numClassInstances; + uint32 iface[SVGA3D_DX_MAX_CLASS_INSTANCES_PADDED]; + SVGA3dIfaceData data[SVGA3D_DX_MAX_CLASS_INSTANCES_PADDED]; + } shaderIfaceState[SVGA3D_NUM_SHADERTYPE]; + + uint32 pad0[1018]; +} SVGADXShaderIfaceMobFormat; +#pragma pack(pop) + +#endif diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga3d_limits.h b/drivers/gpu/drm/vmwgfx/device_include/svga3d_limits.h new file mode 100644 index 0000000000..6103b41fe9 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/device_include/svga3d_limits.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2012-2021 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * svga3d_limits.h -- + * + * SVGA 3d hardware limits + */ + + + +#ifndef _SVGA3D_LIMITS_H_ +#define _SVGA3D_LIMITS_H_ + +#define SVGA3D_HB_MAX_CONTEXT_IDS 256 +#define SVGA3D_HB_MAX_SURFACE_IDS (32 * 1024) + +#define SVGA3D_DX_MAX_RENDER_TARGETS 8 +#define SVGA3D_DX11_MAX_UAVIEWS 8 +#define SVGA3D_DX11_1_MAX_UAVIEWS 64 +#define SVGA3D_MAX_UAVIEWS (SVGA3D_DX11_1_MAX_UAVIEWS) +#define SVGA3D_DX11_MAX_SIMULTANEOUS_RTUAV (SVGA3D_DX11_MAX_UAVIEWS) +#define SVGA3D_DX11_1_MAX_SIMULTANEOUS_RTUAV (SVGA3D_DX11_1_MAX_UAVIEWS) +#define SVGA3D_MAX_SIMULTANEOUS_RTUAV (SVGA3D_MAX_UAVIEWS) + +#define SVGA3D_HB_MAX_SURFACE_SIZE MBYTES_2_BYTES(128) + +#define SVGA3D_MAX_SHADERIDS 5000 + +#define SVGA3D_MAX_SIMULTANEOUS_SHADERS 20000 + +#define SVGA3D_NUM_TEXTURE_UNITS 32 +#define SVGA3D_NUM_LIGHTS 8 + +#define SVGA3D_MAX_VIDEOPROCESSOR_SAMPLERS 32 + +#define SVGA3D_MAX_SHADER_MEMORY_BYTES (8 * 1024 * 1024) +#define SVGA3D_MAX_SHADER_MEMORY \ + (SVGA3D_MAX_SHADER_MEMORY_BYTES / sizeof(uint32)) + +#define SVGA3D_MAX_SHADER_THREAD_GROUPS 65535 + +#define SVGA3D_MAX_CLIP_PLANES 6 + +#define SVGA3D_MAX_TEXTURE_COORDS 8 + +#define SVGA3D_MAX_SURFACE_FACES 6 + +#define SVGA3D_SM4_MAX_SURFACE_ARRAYSIZE 512 +#define SVGA3D_SM5_MAX_SURFACE_ARRAYSIZE 2048 +#define SVGA3D_MAX_SURFACE_ARRAYSIZE SVGA3D_SM5_MAX_SURFACE_ARRAYSIZE + +#define SVGA3D_MAX_VERTEX_ARRAYS 32 + +#define SVGA3D_MAX_DRAW_PRIMITIVE_RANGES 32 + +#define SVGA3D_MAX_SAMPLES 8 + +#define SVGA3D_MIN_SBX_DATA_SIZE (GBYTES_2_BYTES(1)) +#define SVGA3D_MAX_SBX_DATA_SIZE (GBYTES_2_BYTES(4)) + +#define SVGA3D_MIN_SBX_DATA_SIZE_DVM (MBYTES_2_BYTES(900)) +#define SVGA3D_MAX_SBX_DATA_SIZE_DVM (MBYTES_2_BYTES(910)) +#endif diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga3d_reg.h b/drivers/gpu/drm/vmwgfx/device_include/svga3d_reg.h new file mode 100644 index 0000000000..b24b4f55c9 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/device_include/svga3d_reg.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 1998-2015 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * svga3d_reg.h -- + * + * SVGA 3d hardware definitions + */ + + + +#ifndef _SVGA3D_REG_H_ +#define _SVGA3D_REG_H_ + +#include "svga_reg.h" + +#include "svga3d_types.h" +#include "svga3d_limits.h" +#include "svga3d_cmd.h" +#include "svga3d_dx.h" +#include "svga3d_devcaps.h" + +#endif diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h b/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h new file mode 100644 index 0000000000..7d98fc4841 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h @@ -0,0 +1,1561 @@ +/********************************************************** + * Copyright 2008-2021 VMware, Inc. + * SPDX-License-Identifier: GPL-2.0 OR MIT + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + **********************************************************/ + +/* + * svga3d_surfacedefs.h -- + * + * Surface definitions for SVGA3d. + */ + + + +#ifndef _SVGA3D_SURFACEDEFS_H_ +#define _SVGA3D_SURFACEDEFS_H_ + +#include "svga3d_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct SVGAUseCaps; + +#if defined(_WIN32) && !defined(__GNUC__) + +#define STATIC_CONST __declspec(selectany) extern const +#else +#define STATIC_CONST static const +#endif + +typedef enum SVGA3dBlockDesc { + + SVGA3DBLOCKDESC_NONE = 0, + + SVGA3DBLOCKDESC_BLUE = 1 << 0, + SVGA3DBLOCKDESC_W = 1 << 0, + SVGA3DBLOCKDESC_BUMP_L = 1 << 0, + + SVGA3DBLOCKDESC_GREEN = 1 << 1, + SVGA3DBLOCKDESC_V = 1 << 1, + + SVGA3DBLOCKDESC_RED = 1 << 2, + SVGA3DBLOCKDESC_U = 1 << 2, + SVGA3DBLOCKDESC_LUMINANCE = 1 << 2, + + SVGA3DBLOCKDESC_ALPHA = 1 << 3, + SVGA3DBLOCKDESC_Q = 1 << 3, + + SVGA3DBLOCKDESC_BUFFER = 1 << 4, + + SVGA3DBLOCKDESC_COMPRESSED = 1 << 5, + + SVGA3DBLOCKDESC_FP = 1 << 6, + + SVGA3DBLOCKDESC_PLANAR_YUV = 1 << 7, + + SVGA3DBLOCKDESC_2PLANAR_YUV = 1 << 8, + + SVGA3DBLOCKDESC_3PLANAR_YUV = 1 << 9, + + SVGA3DBLOCKDESC_STENCIL = 1 << 11, + + SVGA3DBLOCKDESC_TYPELESS = 1 << 12, + + SVGA3DBLOCKDESC_SINT = 1 << 13, + + SVGA3DBLOCKDESC_UINT = 1 << 14, + + SVGA3DBLOCKDESC_NORM = 1 << 15, + + SVGA3DBLOCKDESC_SRGB = 1 << 16, + + SVGA3DBLOCKDESC_EXP = 1 << 17, + + SVGA3DBLOCKDESC_COLOR = 1 << 18, + + SVGA3DBLOCKDESC_DEPTH = 1 << 19, + + SVGA3DBLOCKDESC_BUMP = 1 << 20, + + SVGA3DBLOCKDESC_YUV_VIDEO = 1 << 21, + + SVGA3DBLOCKDESC_MIXED = 1 << 22, + + SVGA3DBLOCKDESC_CX = 1 << 23, + + SVGA3DBLOCKDESC_BC1 = 1 << 24, + SVGA3DBLOCKDESC_BC2 = 1 << 25, + SVGA3DBLOCKDESC_BC3 = 1 << 26, + SVGA3DBLOCKDESC_BC4 = 1 << 27, + SVGA3DBLOCKDESC_BC5 = 1 << 28, + SVGA3DBLOCKDESC_BC6H = 1 << 29, + SVGA3DBLOCKDESC_BC7 = 1 << 30, + SVGA3DBLOCKDESC_COMPRESSED_MASK = + SVGA3DBLOCKDESC_BC1 | SVGA3DBLOCKDESC_BC2 | + SVGA3DBLOCKDESC_BC3 | SVGA3DBLOCKDESC_BC4 | + SVGA3DBLOCKDESC_BC5 | SVGA3DBLOCKDESC_BC6H | + SVGA3DBLOCKDESC_BC7, + + SVGA3DBLOCKDESC_A_UINT = SVGA3DBLOCKDESC_ALPHA | SVGA3DBLOCKDESC_UINT | + SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_A_UNORM = SVGA3DBLOCKDESC_A_UINT | SVGA3DBLOCKDESC_NORM, + SVGA3DBLOCKDESC_R_UINT = SVGA3DBLOCKDESC_RED | SVGA3DBLOCKDESC_UINT | + SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_R_UNORM = SVGA3DBLOCKDESC_R_UINT | SVGA3DBLOCKDESC_NORM, + SVGA3DBLOCKDESC_R_SINT = SVGA3DBLOCKDESC_RED | SVGA3DBLOCKDESC_SINT | + SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_R_SNORM = SVGA3DBLOCKDESC_R_SINT | SVGA3DBLOCKDESC_NORM, + SVGA3DBLOCKDESC_G_UINT = SVGA3DBLOCKDESC_GREEN | SVGA3DBLOCKDESC_UINT | + SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_RG_UINT = SVGA3DBLOCKDESC_RED | SVGA3DBLOCKDESC_GREEN | + SVGA3DBLOCKDESC_UINT | SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_RG_UNORM = + SVGA3DBLOCKDESC_RG_UINT | SVGA3DBLOCKDESC_NORM, + SVGA3DBLOCKDESC_RG_SINT = SVGA3DBLOCKDESC_RED | SVGA3DBLOCKDESC_GREEN | + SVGA3DBLOCKDESC_SINT | SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_RG_SNORM = + SVGA3DBLOCKDESC_RG_SINT | SVGA3DBLOCKDESC_NORM, + SVGA3DBLOCKDESC_RGB_UINT = SVGA3DBLOCKDESC_RED | SVGA3DBLOCKDESC_GREEN | + SVGA3DBLOCKDESC_BLUE | SVGA3DBLOCKDESC_UINT | + SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_RGB_SINT = SVGA3DBLOCKDESC_RED | SVGA3DBLOCKDESC_GREEN | + SVGA3DBLOCKDESC_BLUE | SVGA3DBLOCKDESC_SINT | + SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_RGB_UNORM = + SVGA3DBLOCKDESC_RGB_UINT | SVGA3DBLOCKDESC_NORM, + SVGA3DBLOCKDESC_RGB_UNORM_SRGB = + SVGA3DBLOCKDESC_RGB_UNORM | SVGA3DBLOCKDESC_SRGB, + SVGA3DBLOCKDESC_RGBA_UINT = + SVGA3DBLOCKDESC_RED | SVGA3DBLOCKDESC_GREEN | + SVGA3DBLOCKDESC_BLUE | SVGA3DBLOCKDESC_ALPHA | + SVGA3DBLOCKDESC_UINT | SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_RGBA_UNORM = + SVGA3DBLOCKDESC_RGBA_UINT | SVGA3DBLOCKDESC_NORM, + SVGA3DBLOCKDESC_RGBA_UNORM_SRGB = + SVGA3DBLOCKDESC_RGBA_UNORM | SVGA3DBLOCKDESC_SRGB, + SVGA3DBLOCKDESC_RGBA_SINT = + SVGA3DBLOCKDESC_RED | SVGA3DBLOCKDESC_GREEN | + SVGA3DBLOCKDESC_BLUE | SVGA3DBLOCKDESC_ALPHA | + SVGA3DBLOCKDESC_SINT | SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_RGBA_SNORM = + SVGA3DBLOCKDESC_RGBA_SINT | SVGA3DBLOCKDESC_NORM, + SVGA3DBLOCKDESC_RGBA_FP = SVGA3DBLOCKDESC_RED | SVGA3DBLOCKDESC_GREEN | + SVGA3DBLOCKDESC_BLUE | SVGA3DBLOCKDESC_ALPHA | + SVGA3DBLOCKDESC_FP | SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_UV = + SVGA3DBLOCKDESC_U | SVGA3DBLOCKDESC_V | SVGA3DBLOCKDESC_BUMP, + SVGA3DBLOCKDESC_UVL = SVGA3DBLOCKDESC_UV | SVGA3DBLOCKDESC_BUMP_L | + SVGA3DBLOCKDESC_MIXED | SVGA3DBLOCKDESC_BUMP, + SVGA3DBLOCKDESC_UVW = + SVGA3DBLOCKDESC_UV | SVGA3DBLOCKDESC_W | SVGA3DBLOCKDESC_BUMP, + SVGA3DBLOCKDESC_UVWA = SVGA3DBLOCKDESC_UVW | SVGA3DBLOCKDESC_ALPHA | + SVGA3DBLOCKDESC_MIXED | SVGA3DBLOCKDESC_BUMP, + SVGA3DBLOCKDESC_UVWQ = SVGA3DBLOCKDESC_U | SVGA3DBLOCKDESC_V | + SVGA3DBLOCKDESC_W | SVGA3DBLOCKDESC_Q | + SVGA3DBLOCKDESC_BUMP, + SVGA3DBLOCKDESC_L_UNORM = SVGA3DBLOCKDESC_LUMINANCE | + SVGA3DBLOCKDESC_UINT | SVGA3DBLOCKDESC_NORM | + SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_LA_UNORM = SVGA3DBLOCKDESC_LUMINANCE | + SVGA3DBLOCKDESC_ALPHA | + SVGA3DBLOCKDESC_UINT | SVGA3DBLOCKDESC_NORM | + SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_R_FP = SVGA3DBLOCKDESC_RED | SVGA3DBLOCKDESC_FP | + SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_RG_FP = SVGA3DBLOCKDESC_R_FP | SVGA3DBLOCKDESC_GREEN | + SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_RGB_FP = SVGA3DBLOCKDESC_RG_FP | SVGA3DBLOCKDESC_BLUE | + SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_YUV = SVGA3DBLOCKDESC_YUV_VIDEO | SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_AYUV = SVGA3DBLOCKDESC_ALPHA | + SVGA3DBLOCKDESC_YUV_VIDEO | + SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_RGB_EXP = SVGA3DBLOCKDESC_RED | SVGA3DBLOCKDESC_GREEN | + SVGA3DBLOCKDESC_BLUE | SVGA3DBLOCKDESC_EXP | + SVGA3DBLOCKDESC_COLOR, + + SVGA3DBLOCKDESC_COMP_TYPELESS = + SVGA3DBLOCKDESC_COMPRESSED | SVGA3DBLOCKDESC_TYPELESS, + SVGA3DBLOCKDESC_COMP_UNORM = + SVGA3DBLOCKDESC_COMPRESSED | SVGA3DBLOCKDESC_UINT | + SVGA3DBLOCKDESC_NORM | SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_COMP_SNORM = + SVGA3DBLOCKDESC_COMPRESSED | SVGA3DBLOCKDESC_SINT | + SVGA3DBLOCKDESC_NORM | SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_COMP_UNORM_SRGB = + SVGA3DBLOCKDESC_COMP_UNORM | SVGA3DBLOCKDESC_SRGB, + SVGA3DBLOCKDESC_BC1_COMP_TYPELESS = + SVGA3DBLOCKDESC_BC1 | SVGA3DBLOCKDESC_COMP_TYPELESS, + SVGA3DBLOCKDESC_BC1_COMP_UNORM = + SVGA3DBLOCKDESC_BC1 | SVGA3DBLOCKDESC_COMP_UNORM, + SVGA3DBLOCKDESC_BC1_COMP_UNORM_SRGB = + SVGA3DBLOCKDESC_BC1_COMP_UNORM | SVGA3DBLOCKDESC_SRGB, + SVGA3DBLOCKDESC_BC2_COMP_TYPELESS = + SVGA3DBLOCKDESC_BC2 | SVGA3DBLOCKDESC_COMP_TYPELESS, + SVGA3DBLOCKDESC_BC2_COMP_UNORM = + SVGA3DBLOCKDESC_BC2 | SVGA3DBLOCKDESC_COMP_UNORM, + SVGA3DBLOCKDESC_BC2_COMP_UNORM_SRGB = + SVGA3DBLOCKDESC_BC2_COMP_UNORM | SVGA3DBLOCKDESC_SRGB, + SVGA3DBLOCKDESC_BC3_COMP_TYPELESS = + SVGA3DBLOCKDESC_BC3 | SVGA3DBLOCKDESC_COMP_TYPELESS, + SVGA3DBLOCKDESC_BC3_COMP_UNORM = + SVGA3DBLOCKDESC_BC3 | SVGA3DBLOCKDESC_COMP_UNORM, + SVGA3DBLOCKDESC_BC3_COMP_UNORM_SRGB = + SVGA3DBLOCKDESC_BC3_COMP_UNORM | SVGA3DBLOCKDESC_SRGB, + SVGA3DBLOCKDESC_BC4_COMP_TYPELESS = + SVGA3DBLOCKDESC_BC4 | SVGA3DBLOCKDESC_COMP_TYPELESS, + SVGA3DBLOCKDESC_BC4_COMP_UNORM = + SVGA3DBLOCKDESC_BC4 | SVGA3DBLOCKDESC_COMP_UNORM, + SVGA3DBLOCKDESC_BC4_COMP_SNORM = + SVGA3DBLOCKDESC_BC4 | SVGA3DBLOCKDESC_COMP_SNORM, + SVGA3DBLOCKDESC_BC5_COMP_TYPELESS = + SVGA3DBLOCKDESC_BC5 | SVGA3DBLOCKDESC_COMP_TYPELESS, + SVGA3DBLOCKDESC_BC5_COMP_UNORM = + SVGA3DBLOCKDESC_BC5 | SVGA3DBLOCKDESC_COMP_UNORM, + SVGA3DBLOCKDESC_BC5_COMP_SNORM = + SVGA3DBLOCKDESC_BC5 | SVGA3DBLOCKDESC_COMP_SNORM, + SVGA3DBLOCKDESC_BC6H_COMP_TYPELESS = + SVGA3DBLOCKDESC_BC6H | SVGA3DBLOCKDESC_COMP_TYPELESS, + SVGA3DBLOCKDESC_BC6H_COMP_UF16 = + SVGA3DBLOCKDESC_BC6H | SVGA3DBLOCKDESC_COMPRESSED, + SVGA3DBLOCKDESC_BC6H_COMP_SF16 = + SVGA3DBLOCKDESC_BC6H | SVGA3DBLOCKDESC_COMPRESSED, + SVGA3DBLOCKDESC_BC7_COMP_TYPELESS = + SVGA3DBLOCKDESC_BC7 | SVGA3DBLOCKDESC_COMP_TYPELESS, + SVGA3DBLOCKDESC_BC7_COMP_UNORM = + SVGA3DBLOCKDESC_BC7 | SVGA3DBLOCKDESC_COMP_UNORM, + SVGA3DBLOCKDESC_BC7_COMP_UNORM_SRGB = + SVGA3DBLOCKDESC_BC7_COMP_UNORM | SVGA3DBLOCKDESC_SRGB, + + SVGA3DBLOCKDESC_NV12 = + SVGA3DBLOCKDESC_YUV_VIDEO | SVGA3DBLOCKDESC_PLANAR_YUV | + SVGA3DBLOCKDESC_2PLANAR_YUV | SVGA3DBLOCKDESC_COLOR, + SVGA3DBLOCKDESC_YV12 = + SVGA3DBLOCKDESC_YUV_VIDEO | SVGA3DBLOCKDESC_PLANAR_YUV | + SVGA3DBLOCKDESC_3PLANAR_YUV | SVGA3DBLOCKDESC_COLOR, + + SVGA3DBLOCKDESC_DEPTH_UINT = + SVGA3DBLOCKDESC_DEPTH | SVGA3DBLOCKDESC_UINT, + SVGA3DBLOCKDESC_DEPTH_UNORM = + SVGA3DBLOCKDESC_DEPTH_UINT | SVGA3DBLOCKDESC_NORM, + SVGA3DBLOCKDESC_DS = SVGA3DBLOCKDESC_DEPTH | SVGA3DBLOCKDESC_STENCIL, + SVGA3DBLOCKDESC_DS_UINT = SVGA3DBLOCKDESC_DEPTH | + SVGA3DBLOCKDESC_STENCIL | + SVGA3DBLOCKDESC_UINT, + SVGA3DBLOCKDESC_DS_UNORM = + SVGA3DBLOCKDESC_DS_UINT | SVGA3DBLOCKDESC_NORM, + SVGA3DBLOCKDESC_DEPTH_FP = SVGA3DBLOCKDESC_DEPTH | SVGA3DBLOCKDESC_FP, + + SVGA3DBLOCKDESC_UV_UINT = SVGA3DBLOCKDESC_UV | SVGA3DBLOCKDESC_UINT, + SVGA3DBLOCKDESC_UV_SNORM = SVGA3DBLOCKDESC_UV | SVGA3DBLOCKDESC_SINT | + SVGA3DBLOCKDESC_NORM, + SVGA3DBLOCKDESC_UVCX_SNORM = + SVGA3DBLOCKDESC_UV_SNORM | SVGA3DBLOCKDESC_CX, + SVGA3DBLOCKDESC_UVWQ_SNORM = SVGA3DBLOCKDESC_UVWQ | + SVGA3DBLOCKDESC_SINT | + SVGA3DBLOCKDESC_NORM, +} SVGA3dBlockDesc; + +typedef struct SVGA3dChannelDef { + union { + uint8 blue; + uint8 w_bump; + uint8 l_bump; + uint8 uv_video; + uint8 u_video; + }; + union { + uint8 green; + uint8 stencil; + uint8 v_bump; + uint8 v_video; + }; + union { + uint8 red; + uint8 u_bump; + uint8 luminance; + uint8 y_video; + uint8 depth; + uint8 data; + }; + union { + uint8 alpha; + uint8 q_bump; + uint8 exp; + }; +} SVGA3dChannelDef; + +typedef struct SVGA3dSurfaceDesc { + SVGA3dSurfaceFormat format; + SVGA3dBlockDesc blockDesc; + + SVGA3dSize blockSize; + uint32 bytesPerBlock; + uint32 pitchBytesPerBlock; + + SVGA3dChannelDef bitDepth; + SVGA3dChannelDef bitOffset; +} SVGA3dSurfaceDesc; + +STATIC_CONST SVGA3dSurfaceDesc g_SVGA3dSurfaceDescs[] = { + { SVGA3D_FORMAT_INVALID, + SVGA3DBLOCKDESC_NONE, + { 1, 1, 1 }, + 0, + 0, + { { 0 }, { 0 }, { 0 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_X8R8G8B8, + SVGA3DBLOCKDESC_RGB_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 16 }, { 24 } } }, + + { SVGA3D_A8R8G8B8, + SVGA3DBLOCKDESC_RGBA_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 0 }, { 8 }, { 16 }, { 24 } } }, + + { SVGA3D_R5G6B5, + SVGA3DBLOCKDESC_RGB_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 5 }, { 6 }, { 5 }, { 0 } }, + { { 0 }, { 5 }, { 11 }, { 0 } } }, + + { SVGA3D_X1R5G5B5, + SVGA3DBLOCKDESC_RGB_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 5 }, { 5 }, { 5 }, { 0 } }, + { { 0 }, { 5 }, { 10 }, { 0 } } }, + + { SVGA3D_A1R5G5B5, + SVGA3DBLOCKDESC_RGBA_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 5 }, { 5 }, { 5 }, { 1 } }, + { { 0 }, { 5 }, { 10 }, { 15 } } }, + + { SVGA3D_A4R4G4B4, + SVGA3DBLOCKDESC_RGBA_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 4 }, { 4 }, { 4 }, { 4 } }, + { { 0 }, { 4 }, { 8 }, { 12 } } }, + + { SVGA3D_Z_D32, + SVGA3DBLOCKDESC_DEPTH_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 0 }, { 32 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_Z_D16, + SVGA3DBLOCKDESC_DEPTH_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 0 }, { 16 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_Z_D24S8, + SVGA3DBLOCKDESC_DS_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 8 }, { 24 }, { 0 } }, + { { 0 }, { 0 }, { 8 }, { 0 } } }, + + { SVGA3D_Z_D15S1, + SVGA3DBLOCKDESC_DS_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 1 }, { 15 }, { 0 } }, + { { 0 }, { 0 }, { 1 }, { 0 } } }, + + { SVGA3D_LUMINANCE8, + SVGA3DBLOCKDESC_L_UNORM, + { 1, 1, 1 }, + 1, + 1, + { { 0 }, { 0 }, { 8 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_LUMINANCE4_ALPHA4, + SVGA3DBLOCKDESC_LA_UNORM, + { 1, 1, 1 }, + 1, + 1, + { { 0 }, { 0 }, { 4 }, { 4 } }, + { { 0 }, { 0 }, { 0 }, { 4 } } }, + + { SVGA3D_LUMINANCE16, + SVGA3DBLOCKDESC_L_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 0 }, { 16 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_LUMINANCE8_ALPHA8, + SVGA3DBLOCKDESC_LA_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 0 }, { 8 }, { 8 } }, + { { 0 }, { 0 }, { 0 }, { 8 } } }, + + { SVGA3D_DXT1, + SVGA3DBLOCKDESC_BC1_COMP_UNORM, + { 4, 4, 1 }, + 8, + 8, + { { 0 }, { 0 }, { 64 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_DXT2, + SVGA3DBLOCKDESC_BC2_COMP_UNORM, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_DXT3, + SVGA3DBLOCKDESC_BC2_COMP_UNORM, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_DXT4, + SVGA3DBLOCKDESC_BC3_COMP_UNORM, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_DXT5, + SVGA3DBLOCKDESC_BC3_COMP_UNORM, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BUMPU8V8, + SVGA3DBLOCKDESC_UV_SNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 0 }, { 0 } } }, + + { SVGA3D_BUMPL6V5U5, + SVGA3DBLOCKDESC_UVL, + { 1, 1, 1 }, + 2, + 2, + { { 6 }, { 5 }, { 5 }, { 0 } }, + { { 10 }, { 5 }, { 0 }, { 0 } } }, + + { SVGA3D_BUMPX8L8V8U8, + SVGA3DBLOCKDESC_UVL, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 0 } }, + { { 16 }, { 8 }, { 0 }, { 0 } } }, + + { SVGA3D_FORMAT_DEAD1, + SVGA3DBLOCKDESC_NONE, + { 1, 1, 1 }, + 3, + 3, + { { 8 }, { 8 }, { 8 }, { 0 } }, + { { 16 }, { 8 }, { 0 }, { 0 } } }, + + { SVGA3D_ARGB_S10E5, + SVGA3DBLOCKDESC_RGBA_FP, + { 1, 1, 1 }, + 8, + 8, + { { 16 }, { 16 }, { 16 }, { 16 } }, + { { 32 }, { 16 }, { 0 }, { 48 } } }, + + { SVGA3D_ARGB_S23E8, + SVGA3DBLOCKDESC_RGBA_FP, + { 1, 1, 1 }, + 16, + 16, + { { 32 }, { 32 }, { 32 }, { 32 } }, + { { 64 }, { 32 }, { 0 }, { 96 } } }, + + { SVGA3D_A2R10G10B10, + SVGA3DBLOCKDESC_RGBA_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 10 }, { 10 }, { 10 }, { 2 } }, + { { 0 }, { 10 }, { 20 }, { 30 } } }, + + { SVGA3D_V8U8, + SVGA3DBLOCKDESC_UV_SNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 0 }, { 0 } } }, + + { SVGA3D_Q8W8V8U8, + SVGA3DBLOCKDESC_UVWQ_SNORM, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 16 }, { 8 }, { 0 }, { 24 } } }, + + { SVGA3D_CxV8U8, + SVGA3DBLOCKDESC_UVCX_SNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 0 }, { 0 } } }, + + { SVGA3D_X8L8V8U8, + SVGA3DBLOCKDESC_UVL, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 0 } }, + { { 16 }, { 8 }, { 0 }, { 0 } } }, + + { SVGA3D_A2W10V10U10, + SVGA3DBLOCKDESC_UVWA, + { 1, 1, 1 }, + 4, + 4, + { { 10 }, { 10 }, { 10 }, { 2 } }, + { { 20 }, { 10 }, { 0 }, { 30 } } }, + + { SVGA3D_ALPHA8, + SVGA3DBLOCKDESC_A_UNORM, + { 1, 1, 1 }, + 1, + 1, + { { 0 }, { 0 }, { 0 }, { 8 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R_S10E5, + SVGA3DBLOCKDESC_R_FP, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 0 }, { 16 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R_S23E8, + SVGA3DBLOCKDESC_R_FP, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 0 }, { 32 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_RG_S10E5, + SVGA3DBLOCKDESC_RG_FP, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 16 }, { 16 }, { 0 } }, + { { 0 }, { 16 }, { 0 }, { 0 } } }, + + { SVGA3D_RG_S23E8, + SVGA3DBLOCKDESC_RG_FP, + { 1, 1, 1 }, + 8, + 8, + { { 0 }, { 32 }, { 32 }, { 0 } }, + { { 0 }, { 32 }, { 0 }, { 0 } } }, + + { SVGA3D_BUFFER, + SVGA3DBLOCKDESC_BUFFER, + { 1, 1, 1 }, + 1, + 1, + { { 0 }, { 0 }, { 8 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_Z_D24X8, + SVGA3DBLOCKDESC_DEPTH_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 0 }, { 24 }, { 0 } }, + { { 0 }, { 0 }, { 8 }, { 0 } } }, + + { SVGA3D_V16U16, + SVGA3DBLOCKDESC_UV_SNORM, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 16 }, { 16 }, { 0 } }, + { { 0 }, { 16 }, { 0 }, { 0 } } }, + + { SVGA3D_G16R16, + SVGA3DBLOCKDESC_RG_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 16 }, { 16 }, { 0 } }, + { { 0 }, { 16 }, { 0 }, { 0 } } }, + + { SVGA3D_A16B16G16R16, + SVGA3DBLOCKDESC_RGBA_UNORM, + { 1, 1, 1 }, + 8, + 8, + { { 16 }, { 16 }, { 16 }, { 16 } }, + { { 32 }, { 16 }, { 0 }, { 48 } } }, + + { SVGA3D_UYVY, + SVGA3DBLOCKDESC_YUV, + { 2, 1, 1 }, + 4, + 4, + { { 8 }, { 0 }, { 8 }, { 0 } }, + { { 0 }, { 0 }, { 8 }, { 0 } } }, + + { SVGA3D_YUY2, + SVGA3DBLOCKDESC_YUV, + { 2, 1, 1 }, + 4, + 4, + { { 8 }, { 0 }, { 8 }, { 0 } }, + { { 8 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_NV12, + SVGA3DBLOCKDESC_NV12, + { 2, 2, 1 }, + 6, + 2, + { { 0 }, { 0 }, { 48 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_FORMAT_DEAD2, + SVGA3DBLOCKDESC_NONE, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 0 }, { 8 }, { 16 }, { 24 } } }, + + { SVGA3D_R32G32B32A32_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 16, + 16, + { { 32 }, { 32 }, { 32 }, { 32 } }, + { { 64 }, { 32 }, { 0 }, { 96 } } }, + + { SVGA3D_R32G32B32A32_UINT, + SVGA3DBLOCKDESC_RGBA_UINT, + { 1, 1, 1 }, + 16, + 16, + { { 32 }, { 32 }, { 32 }, { 32 } }, + { { 64 }, { 32 }, { 0 }, { 96 } } }, + + { SVGA3D_R32G32B32A32_SINT, + SVGA3DBLOCKDESC_RGBA_SINT, + { 1, 1, 1 }, + 16, + 16, + { { 32 }, { 32 }, { 32 }, { 32 } }, + { { 64 }, { 32 }, { 0 }, { 96 } } }, + + { SVGA3D_R32G32B32_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 12, + 12, + { { 32 }, { 32 }, { 32 }, { 0 } }, + { { 64 }, { 32 }, { 0 }, { 0 } } }, + + { SVGA3D_R32G32B32_FLOAT, + SVGA3DBLOCKDESC_RGB_FP, + { 1, 1, 1 }, + 12, + 12, + { { 32 }, { 32 }, { 32 }, { 0 } }, + { { 64 }, { 32 }, { 0 }, { 0 } } }, + + { SVGA3D_R32G32B32_UINT, + SVGA3DBLOCKDESC_RGB_UINT, + { 1, 1, 1 }, + 12, + 12, + { { 32 }, { 32 }, { 32 }, { 0 } }, + { { 64 }, { 32 }, { 0 }, { 0 } } }, + + { SVGA3D_R32G32B32_SINT, + SVGA3DBLOCKDESC_RGB_SINT, + { 1, 1, 1 }, + 12, + 12, + { { 32 }, { 32 }, { 32 }, { 0 } }, + { { 64 }, { 32 }, { 0 }, { 0 } } }, + + { SVGA3D_R16G16B16A16_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 8, + 8, + { { 16 }, { 16 }, { 16 }, { 16 } }, + { { 32 }, { 16 }, { 0 }, { 48 } } }, + + { SVGA3D_R16G16B16A16_UINT, + SVGA3DBLOCKDESC_RGBA_UINT, + { 1, 1, 1 }, + 8, + 8, + { { 16 }, { 16 }, { 16 }, { 16 } }, + { { 32 }, { 16 }, { 0 }, { 48 } } }, + + { SVGA3D_R16G16B16A16_SNORM, + SVGA3DBLOCKDESC_RGBA_SNORM, + { 1, 1, 1 }, + 8, + 8, + { { 16 }, { 16 }, { 16 }, { 16 } }, + { { 32 }, { 16 }, { 0 }, { 48 } } }, + + { SVGA3D_R16G16B16A16_SINT, + SVGA3DBLOCKDESC_RGBA_SINT, + { 1, 1, 1 }, + 8, + 8, + { { 16 }, { 16 }, { 16 }, { 16 } }, + { { 32 }, { 16 }, { 0 }, { 48 } } }, + + { SVGA3D_R32G32_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 8, + 8, + { { 0 }, { 32 }, { 32 }, { 0 } }, + { { 0 }, { 32 }, { 0 }, { 0 } } }, + + { SVGA3D_R32G32_UINT, + SVGA3DBLOCKDESC_RG_UINT, + { 1, 1, 1 }, + 8, + 8, + { { 0 }, { 32 }, { 32 }, { 0 } }, + { { 0 }, { 32 }, { 0 }, { 0 } } }, + + { SVGA3D_R32G32_SINT, + SVGA3DBLOCKDESC_RG_SINT, + { 1, 1, 1 }, + 8, + 8, + { { 0 }, { 32 }, { 32 }, { 0 } }, + { { 0 }, { 32 }, { 0 }, { 0 } } }, + + { SVGA3D_R32G8X24_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 8, + 8, + { { 0 }, { 8 }, { 32 }, { 0 } }, + { { 0 }, { 32 }, { 0 }, { 0 } } }, + + { SVGA3D_D32_FLOAT_S8X24_UINT, + SVGA3DBLOCKDESC_DS, + { 1, 1, 1 }, + 8, + 8, + { { 0 }, { 8 }, { 32 }, { 0 } }, + { { 0 }, { 32 }, { 0 }, { 0 } } }, + + { SVGA3D_R32_FLOAT_X8X24, + SVGA3DBLOCKDESC_R_FP, + { 1, 1, 1 }, + 8, + 8, + { { 0 }, { 0 }, { 32 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_X32_G8X24_UINT, + SVGA3DBLOCKDESC_G_UINT, + { 1, 1, 1 }, + 8, + 8, + { { 0 }, { 8 }, { 0 }, { 0 } }, + { { 0 }, { 32 }, { 0 }, { 0 } } }, + + { SVGA3D_R10G10B10A2_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 4, + 4, + { { 10 }, { 10 }, { 10 }, { 2 } }, + { { 20 }, { 10 }, { 0 }, { 30 } } }, + + { SVGA3D_R10G10B10A2_UINT, + SVGA3DBLOCKDESC_RGBA_UINT, + { 1, 1, 1 }, + 4, + 4, + { { 10 }, { 10 }, { 10 }, { 2 } }, + { { 20 }, { 10 }, { 0 }, { 30 } } }, + + { SVGA3D_R11G11B10_FLOAT, + SVGA3DBLOCKDESC_RGB_FP, + { 1, 1, 1 }, + 4, + 4, + { { 10 }, { 11 }, { 11 }, { 0 } }, + { { 22 }, { 11 }, { 0 }, { 0 } } }, + + { SVGA3D_R8G8B8A8_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 16 }, { 8 }, { 0 }, { 24 } } }, + + { SVGA3D_R8G8B8A8_UNORM, + SVGA3DBLOCKDESC_RGBA_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 16 }, { 8 }, { 0 }, { 24 } } }, + + { SVGA3D_R8G8B8A8_UNORM_SRGB, + SVGA3DBLOCKDESC_RGBA_UNORM_SRGB, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 16 }, { 8 }, { 0 }, { 24 } } }, + + { SVGA3D_R8G8B8A8_UINT, + SVGA3DBLOCKDESC_RGBA_UINT, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 16 }, { 8 }, { 0 }, { 24 } } }, + + { SVGA3D_R8G8B8A8_SINT, + SVGA3DBLOCKDESC_RGBA_SINT, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 16 }, { 8 }, { 0 }, { 24 } } }, + + { SVGA3D_R16G16_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 16 }, { 16 }, { 0 } }, + { { 0 }, { 16 }, { 0 }, { 0 } } }, + + { SVGA3D_R16G16_UINT, + SVGA3DBLOCKDESC_RG_UINT, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 16 }, { 16 }, { 0 } }, + { { 0 }, { 16 }, { 0 }, { 0 } } }, + + { SVGA3D_R16G16_SINT, + SVGA3DBLOCKDESC_RG_SINT, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 16 }, { 16 }, { 0 } }, + { { 0 }, { 16 }, { 0 }, { 0 } } }, + + { SVGA3D_R32_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 0 }, { 32 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_D32_FLOAT, + SVGA3DBLOCKDESC_DEPTH_FP, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 0 }, { 32 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R32_UINT, + SVGA3DBLOCKDESC_R_UINT, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 0 }, { 32 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R32_SINT, + SVGA3DBLOCKDESC_R_SINT, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 0 }, { 32 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R24G8_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 8 }, { 24 }, { 0 } }, + { { 0 }, { 24 }, { 0 }, { 0 } } }, + + { SVGA3D_D24_UNORM_S8_UINT, + SVGA3DBLOCKDESC_DS_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 8 }, { 24 }, { 0 } }, + { { 0 }, { 24 }, { 0 }, { 0 } } }, + + { SVGA3D_R24_UNORM_X8, + SVGA3DBLOCKDESC_R_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 0 }, { 24 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_X24_G8_UINT, + SVGA3DBLOCKDESC_G_UINT, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 8 }, { 0 }, { 0 } }, + { { 0 }, { 24 }, { 0 }, { 0 } } }, + + { SVGA3D_R8G8_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 0 }, { 0 } } }, + + { SVGA3D_R8G8_UNORM, + SVGA3DBLOCKDESC_RG_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 0 }, { 0 } } }, + + { SVGA3D_R8G8_UINT, + SVGA3DBLOCKDESC_RG_UINT, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 0 }, { 0 } } }, + + { SVGA3D_R8G8_SINT, + SVGA3DBLOCKDESC_RG_SINT, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 0 }, { 0 } } }, + + { SVGA3D_R16_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 0 }, { 16 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R16_UNORM, + SVGA3DBLOCKDESC_R_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 0 }, { 16 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R16_UINT, + SVGA3DBLOCKDESC_R_UINT, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 0 }, { 16 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R16_SNORM, + SVGA3DBLOCKDESC_R_SNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 0 }, { 16 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R16_SINT, + SVGA3DBLOCKDESC_R_SINT, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 0 }, { 16 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R8_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 1, + 1, + { { 0 }, { 0 }, { 8 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R8_UNORM, + SVGA3DBLOCKDESC_R_UNORM, + { 1, 1, 1 }, + 1, + 1, + { { 0 }, { 0 }, { 8 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R8_UINT, + SVGA3DBLOCKDESC_R_UINT, + { 1, 1, 1 }, + 1, + 1, + { { 0 }, { 0 }, { 8 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R8_SNORM, + SVGA3DBLOCKDESC_R_SNORM, + { 1, 1, 1 }, + 1, + 1, + { { 0 }, { 0 }, { 8 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R8_SINT, + SVGA3DBLOCKDESC_R_SINT, + { 1, 1, 1 }, + 1, + 1, + { { 0 }, { 0 }, { 8 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_P8, + SVGA3DBLOCKDESC_NONE, + { 1, 1, 1 }, + 1, + 1, + { { 0 }, { 0 }, { 8 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R9G9B9E5_SHAREDEXP, + SVGA3DBLOCKDESC_RGB_EXP, + { 1, 1, 1 }, + 4, + 4, + { { 9 }, { 9 }, { 9 }, { 5 } }, + { { 18 }, { 9 }, { 0 }, { 27 } } }, + + { SVGA3D_R8G8_B8G8_UNORM, + SVGA3DBLOCKDESC_NONE, + { 2, 1, 1 }, + 4, + 4, + { { 0 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 0 }, { 8 }, { 0 } } }, + + { SVGA3D_G8R8_G8B8_UNORM, + SVGA3DBLOCKDESC_NONE, + { 2, 1, 1 }, + 4, + 4, + { { 0 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 0 }, { 0 } } }, + + { SVGA3D_BC1_TYPELESS, + SVGA3DBLOCKDESC_BC1_COMP_TYPELESS, + { 4, 4, 1 }, + 8, + 8, + { { 0 }, { 0 }, { 64 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC1_UNORM_SRGB, + SVGA3DBLOCKDESC_BC1_COMP_UNORM_SRGB, + { 4, 4, 1 }, + 8, + 8, + { { 0 }, { 0 }, { 64 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC2_TYPELESS, + SVGA3DBLOCKDESC_BC2_COMP_TYPELESS, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC2_UNORM_SRGB, + SVGA3DBLOCKDESC_BC2_COMP_UNORM_SRGB, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC3_TYPELESS, + SVGA3DBLOCKDESC_BC3_COMP_TYPELESS, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC3_UNORM_SRGB, + SVGA3DBLOCKDESC_BC3_COMP_UNORM_SRGB, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC4_TYPELESS, + SVGA3DBLOCKDESC_BC4_COMP_TYPELESS, + { 4, 4, 1 }, + 8, + 8, + { { 0 }, { 0 }, { 64 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_ATI1, + SVGA3DBLOCKDESC_BC4_COMP_UNORM, + { 4, 4, 1 }, + 8, + 8, + { { 0 }, { 0 }, { 64 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC4_SNORM, + SVGA3DBLOCKDESC_BC4_COMP_SNORM, + { 4, 4, 1 }, + 8, + 8, + { { 0 }, { 0 }, { 64 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC5_TYPELESS, + SVGA3DBLOCKDESC_BC5_COMP_TYPELESS, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_ATI2, + SVGA3DBLOCKDESC_BC5_COMP_UNORM, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC5_SNORM, + SVGA3DBLOCKDESC_BC5_COMP_SNORM, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R10G10B10_XR_BIAS_A2_UNORM, + SVGA3DBLOCKDESC_RGBA_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 10 }, { 10 }, { 10 }, { 2 } }, + { { 20 }, { 10 }, { 0 }, { 30 } } }, + + { SVGA3D_B8G8R8A8_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 0 }, { 8 }, { 16 }, { 24 } } }, + + { SVGA3D_B8G8R8A8_UNORM_SRGB, + SVGA3DBLOCKDESC_RGBA_UNORM_SRGB, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 0 }, { 8 }, { 16 }, { 24 } } }, + + { SVGA3D_B8G8R8X8_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 16 }, { 24 } } }, + + { SVGA3D_B8G8R8X8_UNORM_SRGB, + SVGA3DBLOCKDESC_RGB_UNORM_SRGB, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 16 }, { 24 } } }, + + { SVGA3D_Z_DF16, + SVGA3DBLOCKDESC_DEPTH_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 0 }, { 16 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_Z_DF24, + SVGA3DBLOCKDESC_DEPTH_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 0 }, { 24 }, { 0 } }, + { { 0 }, { 0 }, { 8 }, { 0 } } }, + + { SVGA3D_Z_D24S8_INT, + SVGA3DBLOCKDESC_DS_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 8 }, { 24 }, { 0 } }, + { { 0 }, { 0 }, { 8 }, { 0 } } }, + + { SVGA3D_YV12, + SVGA3DBLOCKDESC_YV12, + { 2, 2, 1 }, + 6, + 2, + { { 0 }, { 0 }, { 48 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R32G32B32A32_FLOAT, + SVGA3DBLOCKDESC_RGBA_FP, + { 1, 1, 1 }, + 16, + 16, + { { 32 }, { 32 }, { 32 }, { 32 } }, + { { 64 }, { 32 }, { 0 }, { 96 } } }, + + { SVGA3D_R16G16B16A16_FLOAT, + SVGA3DBLOCKDESC_RGBA_FP, + { 1, 1, 1 }, + 8, + 8, + { { 16 }, { 16 }, { 16 }, { 16 } }, + { { 32 }, { 16 }, { 0 }, { 48 } } }, + + { SVGA3D_R16G16B16A16_UNORM, + SVGA3DBLOCKDESC_RGBA_UNORM, + { 1, 1, 1 }, + 8, + 8, + { { 16 }, { 16 }, { 16 }, { 16 } }, + { { 32 }, { 16 }, { 0 }, { 48 } } }, + + { SVGA3D_R32G32_FLOAT, + SVGA3DBLOCKDESC_RG_FP, + { 1, 1, 1 }, + 8, + 8, + { { 0 }, { 32 }, { 32 }, { 0 } }, + { { 0 }, { 32 }, { 0 }, { 0 } } }, + + { SVGA3D_R10G10B10A2_UNORM, + SVGA3DBLOCKDESC_RGBA_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 10 }, { 10 }, { 10 }, { 2 } }, + { { 20 }, { 10 }, { 0 }, { 30 } } }, + + { SVGA3D_R8G8B8A8_SNORM, + SVGA3DBLOCKDESC_RGBA_SNORM, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 16 }, { 8 }, { 0 }, { 24 } } }, + + { SVGA3D_R16G16_FLOAT, + SVGA3DBLOCKDESC_RG_FP, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 16 }, { 16 }, { 0 } }, + { { 0 }, { 16 }, { 0 }, { 0 } } }, + + { SVGA3D_R16G16_UNORM, + SVGA3DBLOCKDESC_RG_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 16 }, { 16 }, { 0 } }, + { { 0 }, { 16 }, { 0 }, { 0 } } }, + + { SVGA3D_R16G16_SNORM, + SVGA3DBLOCKDESC_RG_SNORM, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 16 }, { 16 }, { 0 } }, + { { 0 }, { 16 }, { 0 }, { 0 } } }, + + { SVGA3D_R32_FLOAT, + SVGA3DBLOCKDESC_R_FP, + { 1, 1, 1 }, + 4, + 4, + { { 0 }, { 0 }, { 32 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_R8G8_SNORM, + SVGA3DBLOCKDESC_RG_SNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 0 }, { 0 } } }, + + { SVGA3D_R16_FLOAT, + SVGA3DBLOCKDESC_R_FP, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 0 }, { 16 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_D16_UNORM, + SVGA3DBLOCKDESC_DEPTH_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 0 }, { 0 }, { 16 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_A8_UNORM, + SVGA3DBLOCKDESC_A_UNORM, + { 1, 1, 1 }, + 1, + 1, + { { 0 }, { 0 }, { 0 }, { 8 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC1_UNORM, + SVGA3DBLOCKDESC_BC1_COMP_UNORM, + { 4, 4, 1 }, + 8, + 8, + { { 0 }, { 0 }, { 64 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC2_UNORM, + SVGA3DBLOCKDESC_BC2_COMP_UNORM, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC3_UNORM, + SVGA3DBLOCKDESC_BC3_COMP_UNORM, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_B5G6R5_UNORM, + SVGA3DBLOCKDESC_RGB_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 5 }, { 6 }, { 5 }, { 0 } }, + { { 0 }, { 5 }, { 11 }, { 0 } } }, + + { SVGA3D_B5G5R5A1_UNORM, + SVGA3DBLOCKDESC_RGBA_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 5 }, { 5 }, { 5 }, { 1 } }, + { { 0 }, { 5 }, { 10 }, { 15 } } }, + + { SVGA3D_B8G8R8A8_UNORM, + SVGA3DBLOCKDESC_RGBA_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 0 }, { 8 }, { 16 }, { 24 } } }, + + { SVGA3D_B8G8R8X8_UNORM, + SVGA3DBLOCKDESC_RGB_UNORM, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 0 } }, + { { 0 }, { 8 }, { 16 }, { 24 } } }, + + { SVGA3D_BC4_UNORM, + SVGA3DBLOCKDESC_BC4_COMP_UNORM, + { 4, 4, 1 }, + 8, + 8, + { { 0 }, { 0 }, { 64 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC5_UNORM, + SVGA3DBLOCKDESC_BC5_COMP_UNORM, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_B4G4R4A4_UNORM, + SVGA3DBLOCKDESC_RGBA_UNORM, + { 1, 1, 1 }, + 2, + 2, + { { 4 }, { 4 }, { 4 }, { 4 } }, + { { 0 }, { 4 }, { 8 }, { 12 } } }, + + { SVGA3D_BC6H_TYPELESS, + SVGA3DBLOCKDESC_BC6H_COMP_TYPELESS, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC6H_UF16, + SVGA3DBLOCKDESC_BC6H_COMP_UF16, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC6H_SF16, + SVGA3DBLOCKDESC_BC6H_COMP_SF16, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC7_TYPELESS, + SVGA3DBLOCKDESC_BC7_COMP_TYPELESS, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC7_UNORM, + SVGA3DBLOCKDESC_BC7_COMP_UNORM, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_BC7_UNORM_SRGB, + SVGA3DBLOCKDESC_BC7_COMP_UNORM_SRGB, + { 4, 4, 1 }, + 16, + 16, + { { 0 }, { 0 }, { 128 }, { 0 } }, + { { 0 }, { 0 }, { 0 }, { 0 } } }, + + { SVGA3D_AYUV, + SVGA3DBLOCKDESC_AYUV, + { 1, 1, 1 }, + 4, + 4, + { { 8 }, { 8 }, { 8 }, { 8 } }, + { { 0 }, { 8 }, { 16 }, { 24 } } }, + + { SVGA3D_R11G11B10_TYPELESS, + SVGA3DBLOCKDESC_TYPELESS, + { 1, 1, 1 }, + 4, + 4, + { { 10 }, { 11 }, { 11 }, { 0 } }, + { { 22 }, { 11 }, { 0 }, { 0 } } }, +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga3d_types.h b/drivers/gpu/drm/vmwgfx/device_include/svga3d_types.h new file mode 100644 index 0000000000..e9219eb380 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/device_include/svga3d_types.h @@ -0,0 +1,1555 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2012-2021 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * svga3d_types.h -- + * + * SVGA 3d hardware definitions for basic types + */ + + + +#ifndef _SVGA3D_TYPES_H_ +#define _SVGA3D_TYPES_H_ + +#include "vm_basic_types.h" + +#define SVGA3D_INVALID_ID ((uint32)-1) + +#define SVGA3D_RESOURCE_TYPE_MIN 1 +#define SVGA3D_RESOURCE_BUFFER 1 +#define SVGA3D_RESOURCE_TEXTURE1D 2 +#define SVGA3D_RESOURCE_TEXTURE2D 3 +#define SVGA3D_RESOURCE_TEXTURE3D 4 +#define SVGA3D_RESOURCE_TEXTURECUBE 5 +#define SVGA3D_RESOURCE_TYPE_DX10_MAX 6 +#define SVGA3D_RESOURCE_BUFFEREX 6 +#define SVGA3D_RESOURCE_TYPE_MAX 7 +typedef uint32 SVGA3dResourceType; + +typedef uint8 SVGABool8; +typedef uint32 SVGA3dBool; +typedef uint32 SVGA3dColor; + +typedef uint32 SVGA3dSurfaceId; + +#pragma pack(push, 1) +typedef struct { + uint32 numerator; + uint32 denominator; +} SVGA3dFraction64; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCopyRect { + uint32 x; + uint32 y; + uint32 w; + uint32 h; + uint32 srcx; + uint32 srcy; +} SVGA3dCopyRect; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dCopyBox { + uint32 x; + uint32 y; + uint32 z; + uint32 w; + uint32 h; + uint32 d; + uint32 srcx; + uint32 srcy; + uint32 srcz; +} SVGA3dCopyBox; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dRect { + uint32 x; + uint32 y; + uint32 w; + uint32 h; +} SVGA3dRect; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 x; + uint32 y; + uint32 z; + uint32 w; + uint32 h; + uint32 d; +} SVGA3dBox; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + int32 x; + int32 y; + int32 z; + int32 w; + int32 h; + int32 d; +} SVGA3dSignedBox; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 x; + uint32 y; + uint32 z; +} SVGA3dPoint; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef union { + struct { + float r; + float g; + float b; + float a; + }; + + float value[4]; +} SVGA3dRGBAFloat; +#pragma pack(pop) + +typedef enum SVGA3dSurfaceFormat { + SVGA3D_FORMAT_INVALID = 0, + + SVGA3D_X8R8G8B8 = 1, + SVGA3D_FORMAT_MIN = 1, + + SVGA3D_A8R8G8B8 = 2, + + SVGA3D_R5G6B5 = 3, + SVGA3D_X1R5G5B5 = 4, + SVGA3D_A1R5G5B5 = 5, + SVGA3D_A4R4G4B4 = 6, + + SVGA3D_Z_D32 = 7, + SVGA3D_Z_D16 = 8, + SVGA3D_Z_D24S8 = 9, + SVGA3D_Z_D15S1 = 10, + + SVGA3D_LUMINANCE8 = 11, + SVGA3D_LUMINANCE4_ALPHA4 = 12, + SVGA3D_LUMINANCE16 = 13, + SVGA3D_LUMINANCE8_ALPHA8 = 14, + + SVGA3D_DXT1 = 15, + SVGA3D_DXT2 = 16, + SVGA3D_DXT3 = 17, + SVGA3D_DXT4 = 18, + SVGA3D_DXT5 = 19, + + SVGA3D_BUMPU8V8 = 20, + SVGA3D_BUMPL6V5U5 = 21, + SVGA3D_BUMPX8L8V8U8 = 22, + SVGA3D_FORMAT_DEAD1 = 23, + + SVGA3D_ARGB_S10E5 = 24, + SVGA3D_ARGB_S23E8 = 25, + + SVGA3D_A2R10G10B10 = 26, + + SVGA3D_V8U8 = 27, + SVGA3D_Q8W8V8U8 = 28, + SVGA3D_CxV8U8 = 29, + + SVGA3D_X8L8V8U8 = 30, + SVGA3D_A2W10V10U10 = 31, + + SVGA3D_ALPHA8 = 32, + + SVGA3D_R_S10E5 = 33, + SVGA3D_R_S23E8 = 34, + SVGA3D_RG_S10E5 = 35, + SVGA3D_RG_S23E8 = 36, + + SVGA3D_BUFFER = 37, + + SVGA3D_Z_D24X8 = 38, + + SVGA3D_V16U16 = 39, + + SVGA3D_G16R16 = 40, + SVGA3D_A16B16G16R16 = 41, + + SVGA3D_UYVY = 42, + SVGA3D_YUY2 = 43, + + SVGA3D_NV12 = 44, + + SVGA3D_FORMAT_DEAD2 = 45, + + SVGA3D_R32G32B32A32_TYPELESS = 46, + 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_UINT = 54, + SVGA3D_R16G16B16A16_SNORM = 55, + SVGA3D_R16G16B16A16_SINT = 56, + SVGA3D_R32G32_TYPELESS = 57, + SVGA3D_R32G32_UINT = 58, + SVGA3D_R32G32_SINT = 59, + SVGA3D_R32G8X24_TYPELESS = 60, + SVGA3D_D32_FLOAT_S8X24_UINT = 61, + SVGA3D_R32_FLOAT_X8X24 = 62, + SVGA3D_X32_G8X24_UINT = 63, + SVGA3D_R10G10B10A2_TYPELESS = 64, + 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_SINT = 71, + SVGA3D_R16G16_TYPELESS = 72, + SVGA3D_R16G16_UINT = 73, + SVGA3D_R16G16_SINT = 74, + SVGA3D_R32_TYPELESS = 75, + SVGA3D_D32_FLOAT = 76, + SVGA3D_R32_UINT = 77, + SVGA3D_R32_SINT = 78, + SVGA3D_R24G8_TYPELESS = 79, + SVGA3D_D24_UNORM_S8_UINT = 80, + SVGA3D_R24_UNORM_X8 = 81, + SVGA3D_X24_G8_UINT = 82, + SVGA3D_R8G8_TYPELESS = 83, + SVGA3D_R8G8_UNORM = 84, + SVGA3D_R8G8_UINT = 85, + SVGA3D_R8G8_SINT = 86, + SVGA3D_R16_TYPELESS = 87, + 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_P8 = 97, + SVGA3D_R9G9B9E5_SHAREDEXP = 98, + SVGA3D_R8G8_B8G8_UNORM = 99, + SVGA3D_G8R8_G8B8_UNORM = 100, + SVGA3D_BC1_TYPELESS = 101, + SVGA3D_BC1_UNORM_SRGB = 102, + SVGA3D_BC2_TYPELESS = 103, + SVGA3D_BC2_UNORM_SRGB = 104, + SVGA3D_BC3_TYPELESS = 105, + SVGA3D_BC3_UNORM_SRGB = 106, + SVGA3D_BC4_TYPELESS = 107, + SVGA3D_ATI1 = 108, + SVGA3D_BC4_SNORM = 109, + SVGA3D_BC5_TYPELESS = 110, + SVGA3D_ATI2 = 111, + SVGA3D_BC5_SNORM = 112, + 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, + + SVGA3D_Z_DF16 = 118, + SVGA3D_Z_DF24 = 119, + SVGA3D_Z_D24S8_INT = 120, + + SVGA3D_YV12 = 121, + + SVGA3D_R32G32B32A32_FLOAT = 122, + SVGA3D_R16G16B16A16_FLOAT = 123, + SVGA3D_R16G16B16A16_UNORM = 124, + SVGA3D_R32G32_FLOAT = 125, + SVGA3D_R10G10B10A2_UNORM = 126, + SVGA3D_R8G8B8A8_SNORM = 127, + SVGA3D_R16G16_FLOAT = 128, + SVGA3D_R16G16_UNORM = 129, + SVGA3D_R16G16_SNORM = 130, + SVGA3D_R32_FLOAT = 131, + SVGA3D_R8G8_SNORM = 132, + SVGA3D_R16_FLOAT = 133, + SVGA3D_D16_UNORM = 134, + SVGA3D_A8_UNORM = 135, + SVGA3D_BC1_UNORM = 136, + SVGA3D_BC2_UNORM = 137, + SVGA3D_BC3_UNORM = 138, + SVGA3D_B5G6R5_UNORM = 139, + SVGA3D_B5G5R5A1_UNORM = 140, + SVGA3D_B8G8R8A8_UNORM = 141, + SVGA3D_B8G8R8X8_UNORM = 142, + SVGA3D_BC4_UNORM = 143, + SVGA3D_BC5_UNORM = 144, + SVGA3D_B4G4R4A4_UNORM = 145, + + SVGA3D_BC6H_TYPELESS = 146, + SVGA3D_BC6H_UF16 = 147, + SVGA3D_BC6H_SF16 = 148, + SVGA3D_BC7_TYPELESS = 149, + SVGA3D_BC7_UNORM = 150, + SVGA3D_BC7_UNORM_SRGB = 151, + + SVGA3D_AYUV = 152, + + SVGA3D_R11G11B10_TYPELESS = 153, + + SVGA3D_FORMAT_MAX +} SVGA3dSurfaceFormat; + +#define SVGA3D_SURFACE_CUBEMAP (1 << 0) + +#define SVGA3D_SURFACE_HINT_STATIC (CONST64U(1) << 1) +#define SVGA3D_SURFACE_HINT_DYNAMIC (CONST64U(1) << 2) +#define SVGA3D_SURFACE_HINT_INDEXBUFFER (CONST64U(1) << 3) +#define SVGA3D_SURFACE_HINT_VERTEXBUFFER (CONST64U(1) << 4) +#define SVGA3D_SURFACE_HINT_TEXTURE (CONST64U(1) << 5) +#define SVGA3D_SURFACE_HINT_RENDERTARGET (CONST64U(1) << 6) +#define SVGA3D_SURFACE_HINT_DEPTHSTENCIL (CONST64U(1) << 7) +#define SVGA3D_SURFACE_HINT_WRITEONLY (CONST64U(1) << 8) +#define SVGA3D_SURFACE_DEAD2 (CONST64U(1) << 9) +#define SVGA3D_SURFACE_AUTOGENMIPMAPS (CONST64U(1) << 10) + +#define SVGA3D_SURFACE_DEAD1 (CONST64U(1) << 11) + +#define SVGA3D_SURFACE_MOB_PITCH (CONST64U(1) << 12) + +#define SVGA3D_SURFACE_INACTIVE (CONST64U(1) << 13) +#define SVGA3D_SURFACE_HINT_RT_LOCKABLE (CONST64U(1) << 14) +#define SVGA3D_SURFACE_VOLUME (CONST64U(1) << 15) + +#define SVGA3D_SURFACE_SCREENTARGET (CONST64U(1) << 16) + +#define SVGA3D_SURFACE_ALIGN16 (CONST64U(1) << 17) + +#define SVGA3D_SURFACE_1D (CONST64U(1) << 18) +#define SVGA3D_SURFACE_ARRAY (CONST64U(1) << 19) + +#define SVGA3D_SURFACE_BIND_VERTEX_BUFFER (CONST64U(1) << 20) +#define SVGA3D_SURFACE_BIND_INDEX_BUFFER (CONST64U(1) << 21) +#define SVGA3D_SURFACE_BIND_CONSTANT_BUFFER (CONST64U(1) << 22) +#define SVGA3D_SURFACE_BIND_SHADER_RESOURCE (CONST64U(1) << 23) +#define SVGA3D_SURFACE_BIND_RENDER_TARGET (CONST64U(1) << 24) +#define SVGA3D_SURFACE_BIND_DEPTH_STENCIL (CONST64U(1) << 25) +#define SVGA3D_SURFACE_BIND_STREAM_OUTPUT (CONST64U(1) << 26) + +#define SVGA3D_SURFACE_STAGING_UPLOAD (CONST64U(1) << 27) +#define SVGA3D_SURFACE_STAGING_DOWNLOAD (CONST64U(1) << 28) +#define SVGA3D_SURFACE_HINT_INDIRECT_UPDATE (CONST64U(1) << 29) + +#define SVGA3D_SURFACE_TRANSFER_FROM_BUFFER (CONST64U(1) << 30) + +#define SVGA3D_SURFACE_RESERVED1 (CONST64U(1) << 31) + +#define SVGA3D_SURFACE_MULTISAMPLE (CONST64U(1) << 32) + +#define SVGA3D_SURFACE_BIND_UAVIEW (CONST64U(1) << 33) + +#define SVGA3D_SURFACE_TRANSFER_TO_BUFFER (CONST64U(1) << 34) + +#define SVGA3D_SURFACE_BIND_LOGICOPS (CONST64U(1) << 35) + +#define SVGA3D_SURFACE_BIND_RAW_VIEWS (CONST64U(1) << 36) +#define SVGA3D_SURFACE_BUFFER_STRUCTURED (CONST64U(1) << 37) + +#define SVGA3D_SURFACE_DRAWINDIRECT_ARGS (CONST64U(1) << 38) +#define SVGA3D_SURFACE_RESOURCE_CLAMP (CONST64U(1) << 39) + +#define SVGA3D_SURFACE_STAGING_COPY (CONST64U(1) << 40) + +#define SVGA3D_SURFACE_FLAG_MAX (CONST64U(1) << 44) + +typedef uint32 SVGA3dSurface1Flags; +typedef uint32 SVGA3dSurface2Flags; +typedef uint64 SVGA3dSurfaceAllFlags; + +#define SVGA3D_SURFACE_FLAGS1_MASK ((uint64)MAX_UINT32) +#define SVGA3D_SURFACE_FLAGS2_MASK (MAX_UINT64 & ~SVGA3D_SURFACE_FLAGS1_MASK) + +#define SVGA3D_SURFACE_HB_DISALLOWED_MASK \ + (SVGA3D_SURFACE_MOB_PITCH | SVGA3D_SURFACE_SCREENTARGET | \ + SVGA3D_SURFACE_ALIGN16 | SVGA3D_SURFACE_BIND_CONSTANT_BUFFER | \ + SVGA3D_SURFACE_BIND_STREAM_OUTPUT | SVGA3D_SURFACE_STAGING_UPLOAD | \ + SVGA3D_SURFACE_STAGING_DOWNLOAD | \ + SVGA3D_SURFACE_HINT_INDIRECT_UPDATE | \ + SVGA3D_SURFACE_TRANSFER_FROM_BUFFER | SVGA3D_SURFACE_MULTISAMPLE | \ + SVGA3D_SURFACE_BIND_UAVIEW | SVGA3D_SURFACE_TRANSFER_TO_BUFFER | \ + SVGA3D_SURFACE_BIND_LOGICOPS | SVGA3D_SURFACE_BIND_RAW_VIEWS | \ + SVGA3D_SURFACE_BUFFER_STRUCTURED | SVGA3D_SURFACE_DRAWINDIRECT_ARGS | \ + SVGA3D_SURFACE_RESOURCE_CLAMP | SVGA3D_SURFACE_STAGING_COPY | \ + SVGA3D_SURFACE_RESTRICT_UPDATE | SVGA3D_SURFACE_BIND_TENSOR | \ + SVGA3D_SURFACE_LO_STAGING) + +#define SVGA3D_SURFACE_HB_PRESENT_DISALLOWED_MASK \ + (SVGA3D_SURFACE_1D | SVGA3D_SURFACE_MULTISAMPLE | \ + SVGA3D_SURFACE_STAGING_COPY) + +#define SVGA3D_SURFACE_2D_DISALLOWED_MASK \ + (SVGA3D_SURFACE_CUBEMAP | SVGA3D_SURFACE_AUTOGENMIPMAPS | \ + SVGA3D_SURFACE_VOLUME | SVGA3D_SURFACE_1D | \ + SVGA3D_SURFACE_BIND_VERTEX_BUFFER | \ + SVGA3D_SURFACE_BIND_INDEX_BUFFER | \ + SVGA3D_SURFACE_BIND_CONSTANT_BUFFER | \ + SVGA3D_SURFACE_BIND_DEPTH_STENCIL | \ + SVGA3D_SURFACE_BIND_STREAM_OUTPUT | \ + SVGA3D_SURFACE_TRANSFER_FROM_BUFFER | SVGA3D_SURFACE_MULTISAMPLE | \ + SVGA3D_SURFACE_BIND_UAVIEW | SVGA3D_SURFACE_TRANSFER_TO_BUFFER | \ + SVGA3D_SURFACE_BIND_RAW_VIEWS | SVGA3D_SURFACE_BUFFER_STRUCTURED | \ + SVGA3D_SURFACE_DRAWINDIRECT_ARGS | SVGA3D_SURFACE_RESOURCE_CLAMP | \ + SVGA3D_SURFACE_BIND_TENSOR) + +#define SVGA3D_SURFACE_BASICOPS_DISALLOWED_MASK \ + (SVGA3D_SURFACE_CUBEMAP | SVGA3D_SURFACE_AUTOGENMIPMAPS | \ + SVGA3D_SURFACE_VOLUME | SVGA3D_SURFACE_1D | \ + SVGA3D_SURFACE_MULTISAMPLE) + +#define SVGA3D_SURFACE_SCREENTARGET_DISALLOWED_MASK \ + (SVGA3D_SURFACE_CUBEMAP | SVGA3D_SURFACE_AUTOGENMIPMAPS | \ + SVGA3D_SURFACE_VOLUME | SVGA3D_SURFACE_1D | \ + SVGA3D_SURFACE_BIND_VERTEX_BUFFER | \ + SVGA3D_SURFACE_BIND_INDEX_BUFFER | \ + SVGA3D_SURFACE_BIND_CONSTANT_BUFFER | \ + SVGA3D_SURFACE_BIND_DEPTH_STENCIL | \ + SVGA3D_SURFACE_BIND_STREAM_OUTPUT | SVGA3D_SURFACE_INACTIVE | \ + SVGA3D_SURFACE_STAGING_UPLOAD | SVGA3D_SURFACE_STAGING_DOWNLOAD | \ + SVGA3D_SURFACE_HINT_INDIRECT_UPDATE | \ + SVGA3D_SURFACE_TRANSFER_FROM_BUFFER | SVGA3D_SURFACE_MULTISAMPLE | \ + SVGA3D_SURFACE_TRANSFER_TO_BUFFER | SVGA3D_SURFACE_BIND_RAW_VIEWS | \ + SVGA3D_SURFACE_BUFFER_STRUCTURED | SVGA3D_SURFACE_DRAWINDIRECT_ARGS | \ + SVGA3D_SURFACE_RESOURCE_CLAMP | SVGA3D_SURFACE_STAGING_COPY | \ + SVGA3D_SURFACE_BIND_TENSOR | SVGA3D_SURFACE_LO_STAGING) + +#define SVGA3D_SURFACE_BUFFER_DISALLOWED_MASK \ + (SVGA3D_SURFACE_CUBEMAP | SVGA3D_SURFACE_AUTOGENMIPMAPS | \ + SVGA3D_SURFACE_VOLUME | SVGA3D_SURFACE_1D | SVGA3D_SURFACE_DEAD2 | \ + SVGA3D_SURFACE_ARRAY | SVGA3D_SURFACE_MULTISAMPLE | \ + SVGA3D_SURFACE_MOB_PITCH | SVGA3D_SURFACE_RESOURCE_CLAMP) + +#define SVGA3D_SURFACE_MULTISAMPLE_DISALLOWED_MASK \ + (SVGA3D_SURFACE_CUBEMAP | SVGA3D_SURFACE_AUTOGENMIPMAPS | \ + SVGA3D_SURFACE_VOLUME | SVGA3D_SURFACE_1D | \ + SVGA3D_SURFACE_SCREENTARGET | SVGA3D_SURFACE_MOB_PITCH | \ + SVGA3D_SURFACE_TRANSFER_FROM_BUFFER | SVGA3D_SURFACE_BIND_UAVIEW | \ + SVGA3D_SURFACE_TRANSFER_TO_BUFFER | SVGA3D_SURFACE_BIND_LOGICOPS | \ + SVGA3D_SURFACE_BIND_RAW_VIEWS | SVGA3D_SURFACE_BUFFER_STRUCTURED | \ + SVGA3D_SURFACE_DRAWINDIRECT_ARGS | SVGA3D_SURFACE_STAGING_COPY) + +#define SVGA3D_SURFACE_DX_ONLY_MASK \ + (SVGA3D_SURFACE_BIND_STREAM_OUTPUT | SVGA3D_SURFACE_STAGING_UPLOAD | \ + SVGA3D_SURFACE_STAGING_DOWNLOAD | \ + SVGA3D_SURFACE_TRANSFER_FROM_BUFFER | \ + SVGA3D_SURFACE_TRANSFER_TO_BUFFER) + +#define SVGA3D_SURFACE_ANY_STAGING_MASK \ + (SVGA3D_SURFACE_STAGING_UPLOAD | SVGA3D_SURFACE_STAGING_DOWNLOAD | \ + SVGA3D_SURFACE_STAGING_COPY | SVGA3D_SURFACE_LO_STAGING) + +#define SVGA3D_SURFACE_ANY_NONHINT_STAGING_MASK \ + (SVGA3D_SURFACE_ANY_STAGING_MASK & ~(SVGA3D_SURFACE_LO_STAGING)) + +#define SVGA3D_SURFACE_BIND_MASK \ + (SVGA3D_SURFACE_BIND_VERTEX_BUFFER | \ + SVGA3D_SURFACE_BIND_INDEX_BUFFER | \ + SVGA3D_SURFACE_BIND_CONSTANT_BUFFER | \ + SVGA3D_SURFACE_BIND_SHADER_RESOURCE | \ + SVGA3D_SURFACE_BIND_RENDER_TARGET | \ + SVGA3D_SURFACE_BIND_DEPTH_STENCIL | \ + SVGA3D_SURFACE_BIND_STREAM_OUTPUT | SVGA3D_SURFACE_BIND_UAVIEW | \ + SVGA3D_SURFACE_BIND_LOGICOPS | SVGA3D_SURFACE_BIND_RAW_VIEWS | \ + SVGA3D_SURFACE_BIND_TENSOR) + +#define SVGA3D_SURFACE_STAGING_DISALLOWED_MASK \ + (SVGA3D_SURFACE_BIND_MASK | SVGA3D_SURFACE_AUTOGENMIPMAPS | \ + SVGA3D_SURFACE_SCREENTARGET | SVGA3D_SURFACE_HINT_RENDERTARGET | \ + SVGA3D_SURFACE_HINT_INDIRECT_UPDATE | SVGA3D_SURFACE_MULTISAMPLE | \ + SVGA3D_SURFACE_DRAWINDIRECT_ARGS | SVGA3D_SURFACE_RESOURCE_CLAMP | \ + SVGA3D_SURFACE_BIND_TENSOR) + +#define SVGA3D_SURFACE_STAGING_COPY_DISALLOWED_MASK \ + (SVGA3D_SURFACE_STAGING_DISALLOWED_MASK | \ + SVGA3D_SURFACE_TRANSFER_TO_BUFFER | \ + SVGA3D_SURFACE_TRANSFER_FROM_BUFFER) + +#define SVGA3D_SURFACE_LOGICOPS_DISALLOWED_MASK \ + (SVGA3D_SURFACE_CUBEMAP | SVGA3D_SURFACE_DEAD2 | \ + SVGA3D_SURFACE_AUTOGENMIPMAPS | SVGA3D_SURFACE_VOLUME | \ + SVGA3D_SURFACE_1D | SVGA3D_SURFACE_BIND_VERTEX_BUFFER | \ + SVGA3D_SURFACE_BIND_INDEX_BUFFER | \ + SVGA3D_SURFACE_BIND_CONSTANT_BUFFER | \ + SVGA3D_SURFACE_BIND_DEPTH_STENCIL | \ + SVGA3D_SURFACE_BIND_STREAM_OUTPUT | \ + SVGA3D_SURFACE_TRANSFER_FROM_BUFFER | SVGA3D_SURFACE_MULTISAMPLE | \ + SVGA3D_SURFACE_BIND_UAVIEW | SVGA3D_SURFACE_TRANSFER_TO_BUFFER | \ + SVGA3D_SURFACE_BIND_RAW_VIEWS | SVGA3D_SURFACE_BUFFER_STRUCTURED | \ + SVGA3D_SURFACE_DRAWINDIRECT_ARGS | SVGA3D_SURFACE_RESOURCE_CLAMP | \ + SVGA3D_SURFACE_STAGING_COPY) + +#define SVGA3D_SURFACE_SM5_MASK \ + (SVGA3D_SURFACE_DRAWINDIRECT_ARGS | SVGA3D_SURFACE_BUFFER_STRUCTURED | \ + SVGA3D_SURFACE_BIND_RAW_VIEWS | SVGA3D_SURFACE_BIND_UAVIEW | \ + SVGA3D_SURFACE_RESOURCE_CLAMP) + +#define SVGA3D_BUFFER_STRUCTURED_STRIDE_MAX 2048 + +typedef enum { + SVGA3DFORMAT_OP_TEXTURE = 0x00000001, + SVGA3DFORMAT_OP_VOLUMETEXTURE = 0x00000002, + SVGA3DFORMAT_OP_CUBETEXTURE = 0x00000004, + SVGA3DFORMAT_OP_OFFSCREEN_RENDERTARGET = 0x00000008, + SVGA3DFORMAT_OP_SAME_FORMAT_RENDERTARGET = 0x00000010, + SVGA3DFORMAT_OP_ZSTENCIL = 0x00000040, + SVGA3DFORMAT_OP_ZSTENCIL_WITH_ARBITRARY_COLOR_DEPTH = 0x00000080, + + SVGA3DFORMAT_OP_SAME_FORMAT_UP_TO_ALPHA_RENDERTARGET = 0x00000100, + + SVGA3DFORMAT_OP_DISPLAYMODE = 0x00000400, + + SVGA3DFORMAT_OP_3DACCELERATION = 0x00000800, + + SVGA3DFORMAT_OP_PIXELSIZE = 0x00001000, + + SVGA3DFORMAT_OP_CONVERT_TO_ARGB = 0x00002000, + + SVGA3DFORMAT_OP_OFFSCREENPLAIN = 0x00004000, + + SVGA3DFORMAT_OP_SRGBREAD = 0x00008000, + + SVGA3DFORMAT_OP_BUMPMAP = 0x00010000, + + SVGA3DFORMAT_OP_DMAP = 0x00020000, + + SVGA3DFORMAT_OP_NOFILTER = 0x00040000, + + SVGA3DFORMAT_OP_MEMBEROFGROUP_ARGB = 0x00080000, + + SVGA3DFORMAT_OP_SRGBWRITE = 0x00100000, + + SVGA3DFORMAT_OP_NOALPHABLEND = 0x00200000, + + SVGA3DFORMAT_OP_AUTOGENMIPMAP = 0x00400000, + + SVGA3DFORMAT_OP_VERTEXTEXTURE = 0x00800000, + + SVGA3DFORMAT_OP_NOTEXCOORDWRAPNORMIP = 0x01000000 +} SVGA3dFormatOp; + +#define SVGA3D_FORMAT_POSITIVE \ + (SVGA3DFORMAT_OP_TEXTURE | SVGA3DFORMAT_OP_VOLUMETEXTURE | \ + SVGA3DFORMAT_OP_CUBETEXTURE | \ + SVGA3DFORMAT_OP_OFFSCREEN_RENDERTARGET | \ + SVGA3DFORMAT_OP_SAME_FORMAT_RENDERTARGET | SVGA3DFORMAT_OP_ZSTENCIL | \ + SVGA3DFORMAT_OP_ZSTENCIL_WITH_ARBITRARY_COLOR_DEPTH | \ + SVGA3DFORMAT_OP_SAME_FORMAT_UP_TO_ALPHA_RENDERTARGET | \ + SVGA3DFORMAT_OP_DISPLAYMODE | SVGA3DFORMAT_OP_3DACCELERATION | \ + SVGA3DFORMAT_OP_PIXELSIZE | SVGA3DFORMAT_OP_CONVERT_TO_ARGB | \ + SVGA3DFORMAT_OP_OFFSCREENPLAIN | SVGA3DFORMAT_OP_SRGBREAD | \ + SVGA3DFORMAT_OP_BUMPMAP | SVGA3DFORMAT_OP_DMAP | \ + SVGA3DFORMAT_OP_MEMBEROFGROUP_ARGB | SVGA3DFORMAT_OP_SRGBWRITE | \ + SVGA3DFORMAT_OP_AUTOGENMIPMAP | SVGA3DFORMAT_OP_VERTEXTEXTURE) + +#define SVGA3D_FORMAT_NEGATIVE \ + (SVGA3DFORMAT_OP_NOFILTER | SVGA3DFORMAT_OP_NOALPHABLEND | \ + SVGA3DFORMAT_OP_NOTEXCOORDWRAPNORMIP) + +typedef union { + uint32 value; + struct { + uint32 texture : 1; + uint32 volumeTexture : 1; + uint32 cubeTexture : 1; + uint32 offscreenRenderTarget : 1; + uint32 sameFormatRenderTarget : 1; + uint32 unknown1 : 1; + uint32 zStencil : 1; + uint32 zStencilArbitraryDepth : 1; + uint32 sameFormatUpToAlpha : 1; + uint32 unknown2 : 1; + uint32 displayMode : 1; + uint32 acceleration3d : 1; + uint32 pixelSize : 1; + uint32 convertToARGB : 1; + uint32 offscreenPlain : 1; + uint32 sRGBRead : 1; + uint32 bumpMap : 1; + uint32 dmap : 1; + uint32 noFilter : 1; + uint32 memberOfGroupARGB : 1; + uint32 sRGBWrite : 1; + uint32 noAlphaBlend : 1; + uint32 autoGenMipMap : 1; + uint32 vertexTexture : 1; + uint32 noTexCoordWrapNorMip : 1; + }; +} SVGA3dSurfaceFormatCaps; + +typedef enum { + SVGA3D_RS_INVALID = 0, + SVGA3D_RS_MIN = 1, + SVGA3D_RS_ZENABLE = 1, + SVGA3D_RS_ZWRITEENABLE = 2, + SVGA3D_RS_ALPHATESTENABLE = 3, + SVGA3D_RS_DITHERENABLE = 4, + SVGA3D_RS_BLENDENABLE = 5, + SVGA3D_RS_FOGENABLE = 6, + SVGA3D_RS_SPECULARENABLE = 7, + SVGA3D_RS_STENCILENABLE = 8, + SVGA3D_RS_LIGHTINGENABLE = 9, + SVGA3D_RS_NORMALIZENORMALS = 10, + SVGA3D_RS_POINTSPRITEENABLE = 11, + SVGA3D_RS_POINTSCALEENABLE = 12, + SVGA3D_RS_STENCILREF = 13, + SVGA3D_RS_STENCILMASK = 14, + SVGA3D_RS_STENCILWRITEMASK = 15, + SVGA3D_RS_FOGSTART = 16, + SVGA3D_RS_FOGEND = 17, + SVGA3D_RS_FOGDENSITY = 18, + SVGA3D_RS_POINTSIZE = 19, + SVGA3D_RS_POINTSIZEMIN = 20, + SVGA3D_RS_POINTSIZEMAX = 21, + SVGA3D_RS_POINTSCALE_A = 22, + SVGA3D_RS_POINTSCALE_B = 23, + SVGA3D_RS_POINTSCALE_C = 24, + SVGA3D_RS_FOGCOLOR = 25, + SVGA3D_RS_AMBIENT = 26, + SVGA3D_RS_CLIPPLANEENABLE = 27, + SVGA3D_RS_FOGMODE = 28, + SVGA3D_RS_FILLMODE = 29, + SVGA3D_RS_SHADEMODE = 30, + SVGA3D_RS_LINEPATTERN = 31, + SVGA3D_RS_SRCBLEND = 32, + SVGA3D_RS_DSTBLEND = 33, + SVGA3D_RS_BLENDEQUATION = 34, + SVGA3D_RS_CULLMODE = 35, + SVGA3D_RS_ZFUNC = 36, + SVGA3D_RS_ALPHAFUNC = 37, + SVGA3D_RS_STENCILFUNC = 38, + SVGA3D_RS_STENCILFAIL = 39, + SVGA3D_RS_STENCILZFAIL = 40, + SVGA3D_RS_STENCILPASS = 41, + SVGA3D_RS_ALPHAREF = 42, + SVGA3D_RS_FRONTWINDING = 43, + SVGA3D_RS_COORDINATETYPE = 44, + SVGA3D_RS_ZBIAS = 45, + SVGA3D_RS_RANGEFOGENABLE = 46, + SVGA3D_RS_COLORWRITEENABLE = 47, + SVGA3D_RS_VERTEXMATERIALENABLE = 48, + SVGA3D_RS_DIFFUSEMATERIALSOURCE = 49, + SVGA3D_RS_SPECULARMATERIALSOURCE = 50, + SVGA3D_RS_AMBIENTMATERIALSOURCE = 51, + SVGA3D_RS_EMISSIVEMATERIALSOURCE = 52, + SVGA3D_RS_TEXTUREFACTOR = 53, + SVGA3D_RS_LOCALVIEWER = 54, + SVGA3D_RS_SCISSORTESTENABLE = 55, + SVGA3D_RS_BLENDCOLOR = 56, + SVGA3D_RS_STENCILENABLE2SIDED = 57, + SVGA3D_RS_CCWSTENCILFUNC = 58, + SVGA3D_RS_CCWSTENCILFAIL = 59, + SVGA3D_RS_CCWSTENCILZFAIL = 60, + SVGA3D_RS_CCWSTENCILPASS = 61, + SVGA3D_RS_VERTEXBLEND = 62, + SVGA3D_RS_SLOPESCALEDEPTHBIAS = 63, + SVGA3D_RS_DEPTHBIAS = 64, + + SVGA3D_RS_OUTPUTGAMMA = 65, + SVGA3D_RS_ZVISIBLE = 66, + SVGA3D_RS_LASTPIXEL = 67, + SVGA3D_RS_CLIPPING = 68, + SVGA3D_RS_WRAP0 = 69, + SVGA3D_RS_WRAP1 = 70, + SVGA3D_RS_WRAP2 = 71, + SVGA3D_RS_WRAP3 = 72, + SVGA3D_RS_WRAP4 = 73, + SVGA3D_RS_WRAP5 = 74, + SVGA3D_RS_WRAP6 = 75, + SVGA3D_RS_WRAP7 = 76, + SVGA3D_RS_WRAP8 = 77, + SVGA3D_RS_WRAP9 = 78, + SVGA3D_RS_WRAP10 = 79, + SVGA3D_RS_WRAP11 = 80, + SVGA3D_RS_WRAP12 = 81, + SVGA3D_RS_WRAP13 = 82, + SVGA3D_RS_WRAP14 = 83, + SVGA3D_RS_WRAP15 = 84, + SVGA3D_RS_MULTISAMPLEANTIALIAS = 85, + SVGA3D_RS_MULTISAMPLEMASK = 86, + SVGA3D_RS_INDEXEDVERTEXBLENDENABLE = 87, + SVGA3D_RS_TWEENFACTOR = 88, + SVGA3D_RS_ANTIALIASEDLINEENABLE = 89, + SVGA3D_RS_COLORWRITEENABLE1 = 90, + SVGA3D_RS_COLORWRITEENABLE2 = 91, + SVGA3D_RS_COLORWRITEENABLE3 = 92, + SVGA3D_RS_SEPARATEALPHABLENDENABLE = 93, + SVGA3D_RS_SRCBLENDALPHA = 94, + SVGA3D_RS_DSTBLENDALPHA = 95, + SVGA3D_RS_BLENDEQUATIONALPHA = 96, + SVGA3D_RS_TRANSPARENCYANTIALIAS = 97, + SVGA3D_RS_LINEWIDTH = 98, + 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, + SVGA3D_VERTEXMATERIAL_DIFFUSE = 1, + SVGA3D_VERTEXMATERIAL_SPECULAR = 2, + SVGA3D_VERTEXMATERIAL_MAX = 3, +} SVGA3dVertexMaterial; + +typedef enum { + SVGA3D_FILLMODE_INVALID = 0, + SVGA3D_FILLMODE_MIN = 1, + SVGA3D_FILLMODE_POINT = 1, + SVGA3D_FILLMODE_LINE = 2, + SVGA3D_FILLMODE_FILL = 3, + SVGA3D_FILLMODE_MAX +} SVGA3dFillModeType; + +#pragma pack(push, 1) +typedef union { + struct { + uint16 mode; + uint16 face; + }; + uint32 uintValue; +} SVGA3dFillMode; +#pragma pack(pop) + +typedef enum { + SVGA3D_SHADEMODE_INVALID = 0, + SVGA3D_SHADEMODE_FLAT = 1, + SVGA3D_SHADEMODE_SMOOTH = 2, + SVGA3D_SHADEMODE_PHONG = 3, + SVGA3D_SHADEMODE_MAX +} SVGA3dShadeMode; + +#pragma pack(push, 1) +typedef union { + struct { + uint16 repeat; + uint16 pattern; + }; + uint32 uintValue; +} SVGA3dLinePattern; +#pragma pack(pop) + +typedef enum { + SVGA3D_BLENDOP_INVALID = 0, + SVGA3D_BLENDOP_MIN = 1, + SVGA3D_BLENDOP_ZERO = 1, + SVGA3D_BLENDOP_ONE = 2, + SVGA3D_BLENDOP_SRCCOLOR = 3, + SVGA3D_BLENDOP_INVSRCCOLOR = 4, + SVGA3D_BLENDOP_SRCALPHA = 5, + SVGA3D_BLENDOP_INVSRCALPHA = 6, + SVGA3D_BLENDOP_DESTALPHA = 7, + SVGA3D_BLENDOP_INVDESTALPHA = 8, + SVGA3D_BLENDOP_DESTCOLOR = 9, + SVGA3D_BLENDOP_INVDESTCOLOR = 10, + SVGA3D_BLENDOP_SRCALPHASAT = 11, + SVGA3D_BLENDOP_BLENDFACTOR = 12, + SVGA3D_BLENDOP_INVBLENDFACTOR = 13, + SVGA3D_BLENDOP_SRC1COLOR = 14, + SVGA3D_BLENDOP_INVSRC1COLOR = 15, + SVGA3D_BLENDOP_SRC1ALPHA = 16, + SVGA3D_BLENDOP_INVSRC1ALPHA = 17, + SVGA3D_BLENDOP_BLENDFACTORALPHA = 18, + SVGA3D_BLENDOP_INVBLENDFACTORALPHA = 19, + SVGA3D_BLENDOP_MAX +} SVGA3dBlendOp; + +typedef enum { + SVGA3D_BLENDEQ_INVALID = 0, + SVGA3D_BLENDEQ_MIN = 1, + SVGA3D_BLENDEQ_ADD = 1, + SVGA3D_BLENDEQ_SUBTRACT = 2, + SVGA3D_BLENDEQ_REVSUBTRACT = 3, + SVGA3D_BLENDEQ_MINIMUM = 4, + SVGA3D_BLENDEQ_MAXIMUM = 5, + SVGA3D_BLENDEQ_MAX +} SVGA3dBlendEquation; + +typedef enum { + SVGA3D_DX11_LOGICOP_MIN = 0, + SVGA3D_DX11_LOGICOP_CLEAR = 0, + SVGA3D_DX11_LOGICOP_SET = 1, + SVGA3D_DX11_LOGICOP_COPY = 2, + SVGA3D_DX11_LOGICOP_COPY_INVERTED = 3, + SVGA3D_DX11_LOGICOP_NOOP = 4, + SVGA3D_DX11_LOGICOP_INVERT = 5, + SVGA3D_DX11_LOGICOP_AND = 6, + SVGA3D_DX11_LOGICOP_NAND = 7, + SVGA3D_DX11_LOGICOP_OR = 8, + SVGA3D_DX11_LOGICOP_NOR = 9, + SVGA3D_DX11_LOGICOP_XOR = 10, + SVGA3D_DX11_LOGICOP_EQUIV = 11, + SVGA3D_DX11_LOGICOP_AND_REVERSE = 12, + SVGA3D_DX11_LOGICOP_AND_INVERTED = 13, + SVGA3D_DX11_LOGICOP_OR_REVERSE = 14, + SVGA3D_DX11_LOGICOP_OR_INVERTED = 15, + SVGA3D_DX11_LOGICOP_MAX +} SVGA3dDX11LogicOp; + +typedef enum { + SVGA3D_FRONTWINDING_INVALID = 0, + SVGA3D_FRONTWINDING_CW = 1, + SVGA3D_FRONTWINDING_MIN = 1, + SVGA3D_FRONTWINDING_CCW = 2, + SVGA3D_FRONTWINDING_MAX +} SVGA3dFrontWinding; + +typedef enum { + SVGA3D_FACE_INVALID = 0, + SVGA3D_FACE_NONE = 1, + SVGA3D_FACE_MIN = 1, + SVGA3D_FACE_FRONT = 2, + SVGA3D_FACE_BACK = 3, + SVGA3D_FACE_FRONT_BACK = 4, + SVGA3D_FACE_MAX +} SVGA3dFace; + +typedef enum { + SVGA3D_CMP_INVALID = 0, + SVGA3D_CMP_NEVER = 1, + SVGA3D_CMP_LESS = 2, + SVGA3D_CMP_EQUAL = 3, + SVGA3D_CMP_LESSEQUAL = 4, + SVGA3D_CMP_GREATER = 5, + SVGA3D_CMP_NOTEQUAL = 6, + SVGA3D_CMP_GREATEREQUAL = 7, + SVGA3D_CMP_ALWAYS = 8, + SVGA3D_CMP_MAX +} SVGA3dCmpFunc; + +typedef enum { + SVGA3D_FOGFUNC_INVALID = 0, + SVGA3D_FOGFUNC_EXP = 1, + SVGA3D_FOGFUNC_EXP2 = 2, + SVGA3D_FOGFUNC_LINEAR = 3, + SVGA3D_FOGFUNC_PER_VERTEX = 4 +} SVGA3dFogFunction; + +typedef enum { + SVGA3D_FOGTYPE_INVALID = 0, + SVGA3D_FOGTYPE_VERTEX = 1, + SVGA3D_FOGTYPE_PIXEL = 2, + SVGA3D_FOGTYPE_MAX = 3 +} SVGA3dFogType; + +typedef enum { + SVGA3D_FOGBASE_INVALID = 0, + SVGA3D_FOGBASE_DEPTHBASED = 1, + SVGA3D_FOGBASE_RANGEBASED = 2, + SVGA3D_FOGBASE_MAX = 3 +} SVGA3dFogBase; + +typedef enum { + SVGA3D_STENCILOP_INVALID = 0, + SVGA3D_STENCILOP_MIN = 1, + SVGA3D_STENCILOP_KEEP = 1, + SVGA3D_STENCILOP_ZERO = 2, + SVGA3D_STENCILOP_REPLACE = 3, + SVGA3D_STENCILOP_INCRSAT = 4, + SVGA3D_STENCILOP_DECRSAT = 5, + SVGA3D_STENCILOP_INVERT = 6, + SVGA3D_STENCILOP_INCR = 7, + SVGA3D_STENCILOP_DECR = 8, + SVGA3D_STENCILOP_MAX +} SVGA3dStencilOp; + +typedef enum { + SVGA3D_CLIPPLANE_0 = (1 << 0), + SVGA3D_CLIPPLANE_1 = (1 << 1), + SVGA3D_CLIPPLANE_2 = (1 << 2), + SVGA3D_CLIPPLANE_3 = (1 << 3), + SVGA3D_CLIPPLANE_4 = (1 << 4), + SVGA3D_CLIPPLANE_5 = (1 << 5), +} SVGA3dClipPlanes; + +typedef enum { + SVGA3D_CLEAR_COLOR = 0x1, + SVGA3D_CLEAR_DEPTH = 0x2, + SVGA3D_CLEAR_STENCIL = 0x4, + + SVGA3D_CLEAR_COLORFILL = 0x8 +} SVGA3dClearFlag; + +typedef enum { + SVGA3D_RT_DEPTH = 0, + SVGA3D_RT_MIN = 0, + SVGA3D_RT_STENCIL = 1, + SVGA3D_RT_COLOR0 = 2, + SVGA3D_RT_COLOR1 = 3, + SVGA3D_RT_COLOR2 = 4, + SVGA3D_RT_COLOR3 = 5, + SVGA3D_RT_COLOR4 = 6, + SVGA3D_RT_COLOR5 = 7, + SVGA3D_RT_COLOR6 = 8, + SVGA3D_RT_COLOR7 = 9, + SVGA3D_RT_MAX, + SVGA3D_RT_INVALID = ((uint32)-1), +} SVGA3dRenderTargetType; + +#define SVGA3D_MAX_RT_COLOR (SVGA3D_RT_COLOR7 - SVGA3D_RT_COLOR0 + 1) + +#pragma pack(push, 1) +typedef union { + struct { + uint32 red : 1; + uint32 green : 1; + uint32 blue : 1; + uint32 alpha : 1; + }; + uint32 uintValue; +} SVGA3dColorMask; +#pragma pack(pop) + +typedef enum { + SVGA3D_VBLEND_DISABLE = 0, + SVGA3D_VBLEND_1WEIGHT = 1, + SVGA3D_VBLEND_2WEIGHT = 2, + SVGA3D_VBLEND_3WEIGHT = 3, + SVGA3D_VBLEND_MAX = 4, +} SVGA3dVertexBlendFlags; + +typedef enum { + SVGA3D_WRAPCOORD_0 = 1 << 0, + SVGA3D_WRAPCOORD_1 = 1 << 1, + SVGA3D_WRAPCOORD_2 = 1 << 2, + SVGA3D_WRAPCOORD_3 = 1 << 3, + SVGA3D_WRAPCOORD_ALL = 0xF, +} SVGA3dWrapFlags; + +typedef enum { + SVGA3D_TS_INVALID = 0, + SVGA3D_TS_MIN = 1, + SVGA3D_TS_BIND_TEXTURE = 1, + SVGA3D_TS_COLOROP = 2, + SVGA3D_TS_COLORARG1 = 3, + SVGA3D_TS_COLORARG2 = 4, + SVGA3D_TS_ALPHAOP = 5, + SVGA3D_TS_ALPHAARG1 = 6, + SVGA3D_TS_ALPHAARG2 = 7, + SVGA3D_TS_ADDRESSU = 8, + SVGA3D_TS_ADDRESSV = 9, + SVGA3D_TS_MIPFILTER = 10, + SVGA3D_TS_MAGFILTER = 11, + SVGA3D_TS_MINFILTER = 12, + SVGA3D_TS_BORDERCOLOR = 13, + SVGA3D_TS_TEXCOORDINDEX = 14, + SVGA3D_TS_TEXTURETRANSFORMFLAGS = 15, + SVGA3D_TS_TEXCOORDGEN = 16, + SVGA3D_TS_BUMPENVMAT00 = 17, + SVGA3D_TS_BUMPENVMAT01 = 18, + SVGA3D_TS_BUMPENVMAT10 = 19, + SVGA3D_TS_BUMPENVMAT11 = 20, + SVGA3D_TS_TEXTURE_MIPMAP_LEVEL = 21, + SVGA3D_TS_TEXTURE_LOD_BIAS = 22, + SVGA3D_TS_TEXTURE_ANISOTROPIC_LEVEL = 23, + SVGA3D_TS_ADDRESSW = 24, + + SVGA3D_TS_GAMMA = 25, + SVGA3D_TS_BUMPENVLSCALE = 26, + SVGA3D_TS_BUMPENVLOFFSET = 27, + SVGA3D_TS_COLORARG0 = 28, + SVGA3D_TS_ALPHAARG0 = 29, + SVGA3D_TS_PREGB_MAX = 30, + SVGA3D_TS_CONSTANT = 30, + SVGA3D_TS_COLOR_KEY_ENABLE = 31, + SVGA3D_TS_COLOR_KEY = 32, + SVGA3D_TS_MAX +} SVGA3dTextureStateName; + +typedef enum { + SVGA3D_TC_INVALID = 0, + SVGA3D_TC_DISABLE = 1, + SVGA3D_TC_SELECTARG1 = 2, + SVGA3D_TC_SELECTARG2 = 3, + SVGA3D_TC_MODULATE = 4, + SVGA3D_TC_ADD = 5, + SVGA3D_TC_ADDSIGNED = 6, + SVGA3D_TC_SUBTRACT = 7, + SVGA3D_TC_BLENDTEXTUREALPHA = 8, + SVGA3D_TC_BLENDDIFFUSEALPHA = 9, + SVGA3D_TC_BLENDCURRENTALPHA = 10, + SVGA3D_TC_BLENDFACTORALPHA = 11, + SVGA3D_TC_MODULATE2X = 12, + SVGA3D_TC_MODULATE4X = 13, + SVGA3D_TC_DSDT = 14, + SVGA3D_TC_DOTPRODUCT3 = 15, + SVGA3D_TC_BLENDTEXTUREALPHAPM = 16, + SVGA3D_TC_ADDSIGNED2X = 17, + SVGA3D_TC_ADDSMOOTH = 18, + SVGA3D_TC_PREMODULATE = 19, + SVGA3D_TC_MODULATEALPHA_ADDCOLOR = 20, + SVGA3D_TC_MODULATECOLOR_ADDALPHA = 21, + SVGA3D_TC_MODULATEINVALPHA_ADDCOLOR = 22, + SVGA3D_TC_MODULATEINVCOLOR_ADDALPHA = 23, + SVGA3D_TC_BUMPENVMAPLUMINANCE = 24, + SVGA3D_TC_MULTIPLYADD = 25, + SVGA3D_TC_LERP = 26, + SVGA3D_TC_MAX +} SVGA3dTextureCombiner; + +#define SVGA3D_TC_CAP_BIT(svga3d_tc_op) \ + (svga3d_tc_op ? (1 << (svga3d_tc_op - 1)) : 0) + +typedef enum { + SVGA3D_TEX_ADDRESS_INVALID = 0, + SVGA3D_TEX_ADDRESS_MIN = 1, + SVGA3D_TEX_ADDRESS_WRAP = 1, + SVGA3D_TEX_ADDRESS_MIRROR = 2, + SVGA3D_TEX_ADDRESS_CLAMP = 3, + SVGA3D_TEX_ADDRESS_BORDER = 4, + SVGA3D_TEX_ADDRESS_MIRRORONCE = 5, + SVGA3D_TEX_ADDRESS_EDGE = 6, + SVGA3D_TEX_ADDRESS_MAX +} SVGA3dTextureAddress; + +typedef enum { + SVGA3D_TEX_FILTER_NONE = 0, + SVGA3D_TEX_FILTER_MIN = 0, + SVGA3D_TEX_FILTER_NEAREST = 1, + SVGA3D_TEX_FILTER_LINEAR = 2, + SVGA3D_TEX_FILTER_ANISOTROPIC = 3, + SVGA3D_TEX_FILTER_FLATCUBIC = 4, + SVGA3D_TEX_FILTER_GAUSSIANCUBIC = 5, + SVGA3D_TEX_FILTER_PYRAMIDALQUAD = 6, + SVGA3D_TEX_FILTER_GAUSSIANQUAD = 7, + SVGA3D_TEX_FILTER_MAX +} SVGA3dTextureFilter; + +typedef enum { + SVGA3D_TEX_TRANSFORM_OFF = 0, + SVGA3D_TEX_TRANSFORM_S = (1 << 0), + SVGA3D_TEX_TRANSFORM_T = (1 << 1), + SVGA3D_TEX_TRANSFORM_R = (1 << 2), + SVGA3D_TEX_TRANSFORM_Q = (1 << 3), + SVGA3D_TEX_PROJECTED = (1 << 15), +} SVGA3dTexTransformFlags; + +typedef enum { + SVGA3D_TEXCOORD_GEN_OFF = 0, + SVGA3D_TEXCOORD_GEN_EYE_POSITION = 1, + SVGA3D_TEXCOORD_GEN_EYE_NORMAL = 2, + SVGA3D_TEXCOORD_GEN_REFLECTIONVECTOR = 3, + SVGA3D_TEXCOORD_GEN_SPHERE = 4, + SVGA3D_TEXCOORD_GEN_MAX +} SVGA3dTextureCoordGen; + +typedef enum { + SVGA3D_TA_INVALID = 0, + SVGA3D_TA_TFACTOR = 1, + SVGA3D_TA_PREVIOUS = 2, + SVGA3D_TA_DIFFUSE = 3, + SVGA3D_TA_TEXTURE = 4, + SVGA3D_TA_SPECULAR = 5, + SVGA3D_TA_CONSTANT = 6, + SVGA3D_TA_MAX +} SVGA3dTextureArgData; + +#define SVGA3D_TM_MASK_LEN 4 + +typedef enum { + SVGA3D_TM_NONE = 0, + SVGA3D_TM_ALPHA = (1 << SVGA3D_TM_MASK_LEN), + SVGA3D_TM_ONE_MINUS = (2 << SVGA3D_TM_MASK_LEN), +} SVGA3dTextureArgModifier; + +typedef enum { + SVGA3D_DECLUSAGE_POSITION = 0, + SVGA3D_DECLUSAGE_BLENDWEIGHT, + SVGA3D_DECLUSAGE_BLENDINDICES, + SVGA3D_DECLUSAGE_NORMAL, + SVGA3D_DECLUSAGE_PSIZE, + SVGA3D_DECLUSAGE_TEXCOORD, + SVGA3D_DECLUSAGE_TANGENT, + SVGA3D_DECLUSAGE_BINORMAL, + SVGA3D_DECLUSAGE_TESSFACTOR, + SVGA3D_DECLUSAGE_POSITIONT, + SVGA3D_DECLUSAGE_COLOR, + SVGA3D_DECLUSAGE_FOG, + SVGA3D_DECLUSAGE_DEPTH, + SVGA3D_DECLUSAGE_SAMPLE, + SVGA3D_DECLUSAGE_MAX +} SVGA3dDeclUsage; + +typedef enum { + SVGA3D_DECLMETHOD_DEFAULT = 0, + SVGA3D_DECLMETHOD_PARTIALU, + SVGA3D_DECLMETHOD_PARTIALV, + SVGA3D_DECLMETHOD_CROSSUV, + SVGA3D_DECLMETHOD_UV, + SVGA3D_DECLMETHOD_LOOKUP, + SVGA3D_DECLMETHOD_LOOKUPPRESAMPLED, +} SVGA3dDeclMethod; + +typedef enum { + SVGA3D_DECLTYPE_FLOAT1 = 0, + SVGA3D_DECLTYPE_FLOAT2 = 1, + SVGA3D_DECLTYPE_FLOAT3 = 2, + SVGA3D_DECLTYPE_FLOAT4 = 3, + SVGA3D_DECLTYPE_D3DCOLOR = 4, + SVGA3D_DECLTYPE_UBYTE4 = 5, + SVGA3D_DECLTYPE_SHORT2 = 6, + SVGA3D_DECLTYPE_SHORT4 = 7, + SVGA3D_DECLTYPE_UBYTE4N = 8, + SVGA3D_DECLTYPE_SHORT2N = 9, + SVGA3D_DECLTYPE_SHORT4N = 10, + SVGA3D_DECLTYPE_USHORT2N = 11, + SVGA3D_DECLTYPE_USHORT4N = 12, + SVGA3D_DECLTYPE_UDEC3 = 13, + SVGA3D_DECLTYPE_DEC3N = 14, + SVGA3D_DECLTYPE_FLOAT16_2 = 15, + SVGA3D_DECLTYPE_FLOAT16_4 = 16, + SVGA3D_DECLTYPE_MAX, +} SVGA3dDeclType; + +typedef union { + struct { + uint32 count : 30; + + uint32 indexedData : 1; + + uint32 instanceData : 1; + }; + + uint32 value; +} SVGA3dVertexDivisor; + +typedef enum { + + SVGA3D_PRIMITIVE_INVALID = 0, + SVGA3D_PRIMITIVE_MIN = 0, + SVGA3D_PRIMITIVE_TRIANGLELIST = 1, + SVGA3D_PRIMITIVE_POINTLIST = 2, + SVGA3D_PRIMITIVE_LINELIST = 3, + SVGA3D_PRIMITIVE_LINESTRIP = 4, + SVGA3D_PRIMITIVE_TRIANGLESTRIP = 5, + SVGA3D_PRIMITIVE_TRIANGLEFAN = 6, + SVGA3D_PRIMITIVE_LINELIST_ADJ = 7, + SVGA3D_PRIMITIVE_PREDX_MAX = 7, + SVGA3D_PRIMITIVE_LINESTRIP_ADJ = 8, + SVGA3D_PRIMITIVE_TRIANGLELIST_ADJ = 9, + SVGA3D_PRIMITIVE_TRIANGLESTRIP_ADJ = 10, + SVGA3D_PRIMITIVE_DX10_MAX = 11, + SVGA3D_PRIMITIVE_1_CONTROL_POINT_PATCH = 11, + SVGA3D_PRIMITIVE_2_CONTROL_POINT_PATCH = 12, + SVGA3D_PRIMITIVE_3_CONTROL_POINT_PATCH = 13, + SVGA3D_PRIMITIVE_4_CONTROL_POINT_PATCH = 14, + SVGA3D_PRIMITIVE_5_CONTROL_POINT_PATCH = 15, + SVGA3D_PRIMITIVE_6_CONTROL_POINT_PATCH = 16, + SVGA3D_PRIMITIVE_7_CONTROL_POINT_PATCH = 17, + SVGA3D_PRIMITIVE_8_CONTROL_POINT_PATCH = 18, + SVGA3D_PRIMITIVE_9_CONTROL_POINT_PATCH = 19, + SVGA3D_PRIMITIVE_10_CONTROL_POINT_PATCH = 20, + SVGA3D_PRIMITIVE_11_CONTROL_POINT_PATCH = 21, + SVGA3D_PRIMITIVE_12_CONTROL_POINT_PATCH = 22, + SVGA3D_PRIMITIVE_13_CONTROL_POINT_PATCH = 23, + SVGA3D_PRIMITIVE_14_CONTROL_POINT_PATCH = 24, + SVGA3D_PRIMITIVE_15_CONTROL_POINT_PATCH = 25, + SVGA3D_PRIMITIVE_16_CONTROL_POINT_PATCH = 26, + SVGA3D_PRIMITIVE_17_CONTROL_POINT_PATCH = 27, + SVGA3D_PRIMITIVE_18_CONTROL_POINT_PATCH = 28, + SVGA3D_PRIMITIVE_19_CONTROL_POINT_PATCH = 29, + SVGA3D_PRIMITIVE_20_CONTROL_POINT_PATCH = 30, + SVGA3D_PRIMITIVE_21_CONTROL_POINT_PATCH = 31, + SVGA3D_PRIMITIVE_22_CONTROL_POINT_PATCH = 32, + SVGA3D_PRIMITIVE_23_CONTROL_POINT_PATCH = 33, + SVGA3D_PRIMITIVE_24_CONTROL_POINT_PATCH = 34, + SVGA3D_PRIMITIVE_25_CONTROL_POINT_PATCH = 35, + SVGA3D_PRIMITIVE_26_CONTROL_POINT_PATCH = 36, + SVGA3D_PRIMITIVE_27_CONTROL_POINT_PATCH = 37, + SVGA3D_PRIMITIVE_28_CONTROL_POINT_PATCH = 38, + SVGA3D_PRIMITIVE_29_CONTROL_POINT_PATCH = 39, + SVGA3D_PRIMITIVE_30_CONTROL_POINT_PATCH = 40, + SVGA3D_PRIMITIVE_31_CONTROL_POINT_PATCH = 41, + SVGA3D_PRIMITIVE_32_CONTROL_POINT_PATCH = 42, + SVGA3D_PRIMITIVE_MAX = 43 +} SVGA3dPrimitiveType; + +typedef enum { + SVGA3D_COORDINATE_INVALID = 0, + SVGA3D_COORDINATE_LEFTHANDED = 1, + SVGA3D_COORDINATE_RIGHTHANDED = 2, + SVGA3D_COORDINATE_MAX +} SVGA3dCoordinateType; + +typedef enum { + SVGA3D_TRANSFORM_INVALID = 0, + SVGA3D_TRANSFORM_WORLD = 1, + SVGA3D_TRANSFORM_MIN = 1, + SVGA3D_TRANSFORM_VIEW = 2, + SVGA3D_TRANSFORM_PROJECTION = 3, + SVGA3D_TRANSFORM_TEXTURE0 = 4, + SVGA3D_TRANSFORM_TEXTURE1 = 5, + SVGA3D_TRANSFORM_TEXTURE2 = 6, + SVGA3D_TRANSFORM_TEXTURE3 = 7, + SVGA3D_TRANSFORM_TEXTURE4 = 8, + SVGA3D_TRANSFORM_TEXTURE5 = 9, + SVGA3D_TRANSFORM_TEXTURE6 = 10, + SVGA3D_TRANSFORM_TEXTURE7 = 11, + SVGA3D_TRANSFORM_WORLD1 = 12, + SVGA3D_TRANSFORM_WORLD2 = 13, + SVGA3D_TRANSFORM_WORLD3 = 14, + SVGA3D_TRANSFORM_MAX +} SVGA3dTransformType; + +typedef enum { + SVGA3D_LIGHTTYPE_INVALID = 0, + SVGA3D_LIGHTTYPE_MIN = 1, + SVGA3D_LIGHTTYPE_POINT = 1, + SVGA3D_LIGHTTYPE_SPOT1 = 2, + SVGA3D_LIGHTTYPE_SPOT2 = 3, + SVGA3D_LIGHTTYPE_DIRECTIONAL = 4, + SVGA3D_LIGHTTYPE_MAX +} SVGA3dLightType; + +typedef enum { + SVGA3D_CUBEFACE_POSX = 0, + SVGA3D_CUBEFACE_NEGX = 1, + SVGA3D_CUBEFACE_POSY = 2, + SVGA3D_CUBEFACE_NEGY = 3, + SVGA3D_CUBEFACE_POSZ = 4, + SVGA3D_CUBEFACE_NEGZ = 5, +} SVGA3dCubeFace; + +typedef enum { + SVGA3D_SHADERTYPE_INVALID = 0, + SVGA3D_SHADERTYPE_MIN = 1, + SVGA3D_SHADERTYPE_VS = 1, + SVGA3D_SHADERTYPE_PS = 2, + SVGA3D_SHADERTYPE_PREDX_MAX = 3, + SVGA3D_SHADERTYPE_GS = 3, + SVGA3D_SHADERTYPE_DX10_MAX = 4, + SVGA3D_SHADERTYPE_HS = 4, + SVGA3D_SHADERTYPE_DS = 5, + SVGA3D_SHADERTYPE_CS = 6, + SVGA3D_SHADERTYPE_MAX = 7 +} SVGA3dShaderType; + +#define SVGA3D_NUM_SHADERTYPE_PREDX \ + (SVGA3D_SHADERTYPE_PREDX_MAX - SVGA3D_SHADERTYPE_MIN) + +#define SVGA3D_NUM_SHADERTYPE_DX10 \ + (SVGA3D_SHADERTYPE_DX10_MAX - SVGA3D_SHADERTYPE_MIN) + +#define SVGA3D_NUM_SHADERTYPE (SVGA3D_SHADERTYPE_MAX - SVGA3D_SHADERTYPE_MIN) + +typedef enum { + SVGA3D_CONST_TYPE_MIN = 0, + SVGA3D_CONST_TYPE_FLOAT = 0, + SVGA3D_CONST_TYPE_INT = 1, + SVGA3D_CONST_TYPE_BOOL = 2, + SVGA3D_CONST_TYPE_MAX = 3, +} SVGA3dShaderConstType; + +#define SVGA3D_CONSTREG_MAX 256 +#define SVGA3D_CONSTINTREG_MAX 16 +#define SVGA3D_CONSTBOOLREG_MAX 16 + +typedef enum { + SVGA3D_STRETCH_BLT_POINT = 0, + SVGA3D_STRETCH_BLT_LINEAR = 1, + SVGA3D_STRETCH_BLT_MAX +} SVGA3dStretchBltMode; + +typedef enum { + SVGA3D_QUERYTYPE_INVALID = ((uint8)-1), + SVGA3D_QUERYTYPE_MIN = 0, + SVGA3D_QUERYTYPE_OCCLUSION = 0, + SVGA3D_QUERYTYPE_TIMESTAMP = 1, + SVGA3D_QUERYTYPE_TIMESTAMPDISJOINT = 2, + SVGA3D_QUERYTYPE_PIPELINESTATS = 3, + SVGA3D_QUERYTYPE_OCCLUSIONPREDICATE = 4, + SVGA3D_QUERYTYPE_STREAMOUTPUTSTATS = 5, + SVGA3D_QUERYTYPE_STREAMOVERFLOWPREDICATE = 6, + SVGA3D_QUERYTYPE_OCCLUSION64 = 7, + SVGA3D_QUERYTYPE_DX10_MAX = 8, + SVGA3D_QUERYTYPE_SOSTATS_STREAM0 = 8, + SVGA3D_QUERYTYPE_SOSTATS_STREAM1 = 9, + SVGA3D_QUERYTYPE_SOSTATS_STREAM2 = 10, + SVGA3D_QUERYTYPE_SOSTATS_STREAM3 = 11, + SVGA3D_QUERYTYPE_SOP_STREAM0 = 12, + SVGA3D_QUERYTYPE_SOP_STREAM1 = 13, + SVGA3D_QUERYTYPE_SOP_STREAM2 = 14, + SVGA3D_QUERYTYPE_SOP_STREAM3 = 15, + SVGA3D_QUERYTYPE_MAX +} SVGA3dQueryType; + +typedef uint8 SVGA3dQueryTypeUint8; + +#define SVGA3D_NUM_QUERYTYPE (SVGA3D_QUERYTYPE_MAX - SVGA3D_QUERYTYPE_MIN) + +#define SVGA3D_MAX_QUERY 64 + +#pragma pack(push, 1) +typedef struct { + uint32 samplesRendered; +} SVGADXOcclusionQueryResult; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 passed; +} SVGADXEventQueryResult; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint64 timestamp; +} SVGADXTimestampQueryResult; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint64 realFrequency; + uint32 disjoint; +} SVGADXTimestampDisjointQueryResult; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint64 inputAssemblyVertices; + uint64 inputAssemblyPrimitives; + uint64 vertexShaderInvocations; + uint64 geometryShaderInvocations; + uint64 geometryShaderPrimitives; + uint64 clipperInvocations; + uint64 clipperPrimitives; + uint64 pixelShaderInvocations; + uint64 hullShaderInvocations; + uint64 domainShaderInvocations; + uint64 computeShaderInvocations; +} SVGADXPipelineStatisticsQueryResult; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 anySamplesRendered; +} SVGADXOcclusionPredicateQueryResult; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint64 numPrimitivesWritten; + uint64 numPrimitivesRequired; +} SVGADXStreamOutStatisticsQueryResult; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 overflowed; +} SVGADXStreamOutPredicateQueryResult; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint64 samplesRendered; +} SVGADXOcclusion64QueryResult; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef union SVGADXQueryResultUnion { + SVGADXOcclusionQueryResult occ; + SVGADXEventQueryResult event; + SVGADXTimestampQueryResult ts; + SVGADXTimestampDisjointQueryResult tsDisjoint; + SVGADXPipelineStatisticsQueryResult pipelineStats; + SVGADXOcclusionPredicateQueryResult occPred; + SVGADXStreamOutStatisticsQueryResult soStats; + SVGADXStreamOutPredicateQueryResult soPred; + SVGADXOcclusion64QueryResult occ64; +} SVGADXQueryResultUnion; +#pragma pack(pop) + +typedef enum { + SVGA3D_QUERYSTATE_PENDING = 0, + SVGA3D_QUERYSTATE_SUCCEEDED = 1, + SVGA3D_QUERYSTATE_FAILED = 2, + SVGA3D_QUERYSTATE_NEW = 3, +} SVGA3dQueryState; + +typedef enum { + SVGA3D_WRITE_HOST_VRAM = 1, + SVGA3D_READ_HOST_VRAM = 2, +} SVGA3dTransferType; + +#define SVGA3D_LOGICOP_INVALID 0 +#define SVGA3D_LOGICOP_COPY 1 + +#define SVGA3D_LOGICOP_MIN 1 +#define SVGA3D_LOGICOP_NOT 2 +#define SVGA3D_LOGICOP_AND 3 +#define SVGA3D_LOGICOP_OR 4 +#define SVGA3D_LOGICOP_XOR 5 +#define SVGA3D_LOGICOP_NXOR 6 +#define SVGA3D_LOGICOP_ROP3 7 + +#define SVGA3D_LOGICOP_MAX 8 + +typedef uint16 SVGA3dLogicOp; + +#define SVGA3D_LOGICOP_ROP3_INVALID ((uint16)-1) +#define SVGA3D_LOGICOP_ROP3_MIN 0 +#define SVGA3D_LOGICOP_ROP3_MAX 256 + +typedef uint16 SVGA3dLogicOpRop3; + +#pragma pack(push, 1) +typedef struct { + union { + struct { + uint16 function; + uint8 type; + uint8 base; + }; + uint32 uintValue; + }; +} SVGA3dFogMode; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dSurfaceImageId { + uint32 sid; + uint32 face; + uint32 mipmap; +} SVGA3dSurfaceImageId; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGA3dSubSurfaceId { + uint32 sid; + uint32 subResourceId; +} SVGA3dSubSurfaceId; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 width; + uint32 height; + uint32 depth; +} SVGA3dSize; +#pragma pack(pop) + +typedef enum { + SVGA_OTABLE_MOB = 0, + SVGA_OTABLE_MIN = 0, + SVGA_OTABLE_SURFACE = 1, + SVGA_OTABLE_CONTEXT = 2, + SVGA_OTABLE_SHADER = 3, + SVGA_OTABLE_SCREENTARGET = 4, + + SVGA_OTABLE_DX9_MAX = 5, + + SVGA_OTABLE_DXCONTEXT = 5, + SVGA_OTABLE_DX_MAX = 6, + + SVGA_OTABLE_DEVEL_MAX = 6, + SVGA_OTABLE_MAX = 6, + + SVGA_OTABLE_RESERVED1 = 6, + SVGA_OTABLE_RESERVED2 = 7, + SVGA_OTABLE_BUG_1952836_MAX = 8, +} SVGAOTableType; + +typedef enum { + SVGA_COTABLE_MIN = 0, + SVGA_COTABLE_RTVIEW = 0, + SVGA_COTABLE_DSVIEW = 1, + SVGA_COTABLE_SRVIEW = 2, + SVGA_COTABLE_ELEMENTLAYOUT = 3, + SVGA_COTABLE_BLENDSTATE = 4, + SVGA_COTABLE_DEPTHSTENCIL = 5, + SVGA_COTABLE_RASTERIZERSTATE = 6, + SVGA_COTABLE_SAMPLER = 7, + SVGA_COTABLE_STREAMOUTPUT = 8, + SVGA_COTABLE_DXQUERY = 9, + SVGA_COTABLE_DXSHADER = 10, + SVGA_COTABLE_DX10_MAX = 11, + SVGA_COTABLE_UAVIEW = 11, + SVGA_COTABLE_MAX = 12, +} SVGACOTableType; + +#define SVGA_COTABLE_MAX_IDS (MAX_UINT16 - 2) + +typedef enum SVGAMobFormat { + SVGA3D_MOBFMT_INVALID = SVGA3D_INVALID_ID, + SVGA3D_MOBFMT_PT_0 = 0, + SVGA3D_MOBFMT_MIN = 0, + SVGA3D_MOBFMT_PT_1 = 1, + SVGA3D_MOBFMT_PT_2 = 2, + SVGA3D_MOBFMT_RANGE = 3, + SVGA3D_MOBFMT_PT64_0 = 4, + SVGA3D_MOBFMT_PT64_1 = 5, + SVGA3D_MOBFMT_PT64_2 = 6, + SVGA3D_MOBFMT_PREDX_MAX = 7, + SVGA3D_MOBFMT_EMPTY = 7, + + SVGA3D_MOBFMT_MAX, + + SVGA3D_MOBFMT_HB, +} SVGAMobFormat; + +#define SVGA3D_MOB_EMPTY_BASE 1 + +typedef enum SVGA3dMSPattern { + SVGA3D_MS_PATTERN_NONE = 0, + SVGA3D_MS_PATTERN_MIN = 0, + SVGA3D_MS_PATTERN_STANDARD = 1, + SVGA3D_MS_PATTERN_CENTER = 2, + SVGA3D_MS_PATTERN_MAX = 3, +} SVGA3dMSPattern; + +typedef enum SVGA3dMSQualityLevel { + SVGA3D_MS_QUALITY_NONE = 0, + SVGA3D_MS_QUALITY_MIN = 0, + SVGA3D_MS_QUALITY_FULL = 1, + SVGA3D_MS_QUALITY_RESOLVED = 2, + SVGA3D_MS_QUALITY_MAX = 3, +} SVGA3dMSQualityLevel; + +typedef enum SVGA3dFrameUpdateType { + SVGA3D_FRAME_END = 0, + SVGA3D_FRAME_MIN = 0, + SVGA3D_FRAME_PARTIAL = 1, + SVGA3D_FRAME_UNKNOWN = 2, + SVGA3D_FRAME_MAX = 3, +} SVGA3dFrameUpdateType; + +#endif diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga_escape.h b/drivers/gpu/drm/vmwgfx/device_include/svga_escape.h new file mode 100644 index 0000000000..405f20fc26 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/device_include/svga_escape.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2007,2020 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * svga_escape.h -- + * + * Definitions for our own (vendor-specific) SVGA Escape commands. + */ + + + +#ifndef _SVGA_ESCAPE_H_ +#define _SVGA_ESCAPE_H_ + +#define SVGA_ESCAPE_NSID_VMWARE 0x00000000 +#define SVGA_ESCAPE_NSID_DEVEL 0xFFFFFFFF + +#define SVGA_ESCAPE_VMWARE_MAJOR_MASK 0xFFFF0000 + +#define SVGA_ESCAPE_VMWARE_HINT 0x00030000 +#define SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN 0x00030001 + +#pragma pack(push, 1) +typedef struct { + uint32 command; + uint32 fullscreen; + struct { + int32 x, y; + } monitorPosition; +} SVGAEscapeHintFullscreen; +#pragma pack(pop) + +#endif diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga_overlay.h b/drivers/gpu/drm/vmwgfx/device_include/svga_overlay.h new file mode 100644 index 0000000000..691f48f77e --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/device_include/svga_overlay.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2007-2021 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * svga_overlay.h -- + * + * Definitions for video-overlay support. + */ + + + +#ifndef _SVGA_OVERLAY_H_ +#define _SVGA_OVERLAY_H_ + +#include "svga_reg.h" + +#if defined __cplusplus +extern "C" { +#endif + +#define VMWARE_FOURCC_YV12 0x32315659 +#define VMWARE_FOURCC_YUY2 0x32595559 +#define VMWARE_FOURCC_UYVY 0x59565955 + +typedef enum { + SVGA_OVERLAY_FORMAT_INVALID = 0, + SVGA_OVERLAY_FORMAT_YV12 = VMWARE_FOURCC_YV12, + SVGA_OVERLAY_FORMAT_YUY2 = VMWARE_FOURCC_YUY2, + SVGA_OVERLAY_FORMAT_UYVY = VMWARE_FOURCC_UYVY, +} SVGAOverlayFormat; + +#define SVGA_VIDEO_COLORKEY_MASK 0x00ffffff + +#define SVGA_ESCAPE_VMWARE_VIDEO 0x00020000 + +#define SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS 0x00020001 + +#define SVGA_ESCAPE_VMWARE_VIDEO_FLUSH 0x00020002 + +typedef struct SVGAEscapeVideoSetRegs { + struct { + uint32 cmdType; + uint32 streamId; + } header; + + struct { + uint32 registerId; + uint32 value; + } items[1]; +} SVGAEscapeVideoSetRegs; + +typedef struct SVGAEscapeVideoFlush { + uint32 cmdType; + uint32 streamId; +} SVGAEscapeVideoFlush; + +#pragma pack(push, 1) +typedef struct { + uint32 command; + uint32 overlay; +} SVGAFifoEscapeCmdVideoBase; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAFifoEscapeCmdVideoBase videoCmd; +} SVGAFifoEscapeCmdVideoFlush; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAFifoEscapeCmdVideoBase videoCmd; + struct { + uint32 regId; + uint32 value; + } items[1]; +} SVGAFifoEscapeCmdVideoSetRegs; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAFifoEscapeCmdVideoBase videoCmd; + struct { + uint32 regId; + uint32 value; + } items[SVGA_VIDEO_NUM_REGS]; +} SVGAFifoEscapeCmdVideoSetAllRegs; +#pragma pack(pop) + +#if defined __cplusplus +} +#endif + +#endif diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga_reg.h b/drivers/gpu/drm/vmwgfx/device_include/svga_reg.h new file mode 100644 index 0000000000..acabdb550c --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/device_include/svga_reg.h @@ -0,0 +1,901 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 1998-2021 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +/* + * svga_reg.h -- + * + * Virtual hardware definitions for the VMware SVGA II device. + */ + + + +#ifndef _SVGA_REG_H_ +#define _SVGA_REG_H_ + +#include "vm_basic_types.h" + +typedef enum { + SVGA_REG_ENABLE_DISABLE = 0, + SVGA_REG_ENABLE_ENABLE = (1 << 0), + SVGA_REG_ENABLE_HIDE = (1 << 1), +} SvgaRegEnable; + +typedef uint32 SVGAMobId; + +#define SVGA_MAX_WIDTH 2560 +#define SVGA_MAX_HEIGHT 1600 + +#define SVGA_MAX_BITS_PER_PIXEL 32 +#define SVGA_MAX_DEPTH 24 +#define SVGA_MAX_DISPLAYS 10 +#define SVGA_MAX_SCREEN_SIZE 8192 +#define SVGA_SCREEN_ROOT_LIMIT (SVGA_MAX_SCREEN_SIZE * SVGA_MAX_DISPLAYS) + +#define SVGA_CURSOR_ON_HIDE 0x0 +#define SVGA_CURSOR_ON_SHOW 0x1 + +#define SVGA_CURSOR_ON_REMOVE_FROM_FB 0x2 + +#define SVGA_CURSOR_ON_RESTORE_TO_FB 0x3 + +#define SVGA_FB_MAX_TRACEABLE_SIZE 0x1000000 + +#define SVGA_MAX_PSEUDOCOLOR_DEPTH 8 +#define SVGA_MAX_PSEUDOCOLORS (1 << SVGA_MAX_PSEUDOCOLOR_DEPTH) +#define SVGA_NUM_PALETTE_REGS (3 * SVGA_MAX_PSEUDOCOLORS) + +#define SVGA_MAGIC 0x900000UL +#define SVGA_MAKE_ID(ver) (SVGA_MAGIC << 8 | (ver)) + +#define SVGA_VERSION_3 3 +#define SVGA_ID_3 SVGA_MAKE_ID(SVGA_VERSION_3) + +#define SVGA_VERSION_2 2 +#define SVGA_ID_2 SVGA_MAKE_ID(SVGA_VERSION_2) + +#define SVGA_VERSION_1 1 +#define SVGA_ID_1 SVGA_MAKE_ID(SVGA_VERSION_1) + +#define SVGA_VERSION_0 0 +#define SVGA_ID_0 SVGA_MAKE_ID(SVGA_VERSION_0) + +#define SVGA_ID_INVALID 0xFFFFFFFF + +#define SVGA_INDEX_PORT 0x0 +#define SVGA_VALUE_PORT 0x1 +#define SVGA_BIOS_PORT 0x2 +#define SVGA_IRQSTATUS_PORT 0x8 + +#define SVGA_IRQFLAG_ANY_FENCE (1 << 0) +#define SVGA_IRQFLAG_FIFO_PROGRESS (1 << 1) +#define SVGA_IRQFLAG_FENCE_GOAL (1 << 2) +#define SVGA_IRQFLAG_COMMAND_BUFFER (1 << 3) +#define SVGA_IRQFLAG_ERROR (1 << 4) +#define SVGA_IRQFLAG_REG_FENCE_GOAL (1 << 5) +#define SVGA_IRQFLAG_MAX (1 << 6) + +#define SVGA_MAX_CURSOR_CMD_BYTES (40 * 1024) +#define SVGA_MAX_CURSOR_CMD_DIMENSION 1024 + +enum { + SVGA_REG_ID = 0, + SVGA_REG_ENABLE = 1, + SVGA_REG_WIDTH = 2, + SVGA_REG_HEIGHT = 3, + SVGA_REG_MAX_WIDTH = 4, + SVGA_REG_MAX_HEIGHT = 5, + SVGA_REG_DEPTH = 6, + SVGA_REG_BITS_PER_PIXEL = 7, + SVGA_REG_PSEUDOCOLOR = 8, + SVGA_REG_RED_MASK = 9, + SVGA_REG_GREEN_MASK = 10, + SVGA_REG_BLUE_MASK = 11, + SVGA_REG_BYTES_PER_LINE = 12, + SVGA_REG_FB_START = 13, + SVGA_REG_FB_OFFSET = 14, + SVGA_REG_VRAM_SIZE = 15, + SVGA_REG_FB_SIZE = 16, + + SVGA_REG_ID_0_TOP = 17, + + SVGA_REG_CAPABILITIES = 17, + SVGA_REG_MEM_START = 18, + SVGA_REG_MEM_SIZE = 19, + SVGA_REG_CONFIG_DONE = 20, + SVGA_REG_SYNC = 21, + SVGA_REG_BUSY = 22, + SVGA_REG_GUEST_ID = 23, + SVGA_REG_DEAD = 24, + SVGA_REG_CURSOR_X = 25, + SVGA_REG_CURSOR_Y = 26, + SVGA_REG_CURSOR_ON = 27, + SVGA_REG_HOST_BITS_PER_PIXEL = 28, + SVGA_REG_SCRATCH_SIZE = 29, + SVGA_REG_MEM_REGS = 30, + SVGA_REG_NUM_DISPLAYS = 31, + SVGA_REG_PITCHLOCK = 32, + SVGA_REG_IRQMASK = 33, + + SVGA_REG_NUM_GUEST_DISPLAYS = 34, + SVGA_REG_DISPLAY_ID = 35, + SVGA_REG_DISPLAY_IS_PRIMARY = 36, + SVGA_REG_DISPLAY_POSITION_X = 37, + SVGA_REG_DISPLAY_POSITION_Y = 38, + SVGA_REG_DISPLAY_WIDTH = 39, + SVGA_REG_DISPLAY_HEIGHT = 40, + + SVGA_REG_GMR_ID = 41, + SVGA_REG_GMR_DESCRIPTOR = 42, + SVGA_REG_GMR_MAX_IDS = 43, + SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH = 44, + + SVGA_REG_TRACES = 45, + SVGA_REG_GMRS_MAX_PAGES = 46, + SVGA_REG_MEMORY_SIZE = 47, + SVGA_REG_COMMAND_LOW = 48, + SVGA_REG_COMMAND_HIGH = 49, + + SVGA_REG_MAX_PRIMARY_MEM = 50, + + SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB = 51, + + SVGA_REG_DEV_CAP = 52, + 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_BLANK_SCREEN_TARGETS = 58, + SVGA_REG_CAP2 = 59, + SVGA_REG_DEVEL_CAP = 60, + + SVGA_REG_GUEST_DRIVER_ID = 61, + SVGA_REG_GUEST_DRIVER_VERSION1 = 62, + SVGA_REG_GUEST_DRIVER_VERSION2 = 63, + SVGA_REG_GUEST_DRIVER_VERSION3 = 64, + + SVGA_REG_CURSOR_MOBID = 65, + SVGA_REG_CURSOR_MAX_BYTE_SIZE = 66, + SVGA_REG_CURSOR_MAX_DIMENSION = 67, + + SVGA_REG_FIFO_CAPS = 68, + SVGA_REG_FENCE = 69, + + SVGA_REG_CURSOR4_ON = 70, + SVGA_REG_CURSOR4_X = 71, + SVGA_REG_CURSOR4_Y = 72, + SVGA_REG_CURSOR4_SCREEN_ID = 73, + SVGA_REG_CURSOR4_SUBMIT = 74, + + SVGA_REG_SCREENDMA = 75, + + SVGA_REG_GBOBJECT_MEM_SIZE_KB = 76, + + SVGA_REG_REGS_START_HIGH32 = 77, + SVGA_REG_REGS_START_LOW32 = 78, + SVGA_REG_FB_START_HIGH32 = 79, + SVGA_REG_FB_START_LOW32 = 80, + + SVGA_REG_MSHINT = 81, + + SVGA_REG_IRQ_STATUS = 82, + + SVGA_REG_DIRTY_TRACKING = 83, + SVGA_REG_FENCE_GOAL = 84, + + SVGA_REG_TOP = 85, + + SVGA_PALETTE_BASE = 1024, + + SVGA_SCRATCH_BASE = SVGA_PALETTE_BASE + SVGA_NUM_PALETTE_REGS + +}; + +typedef enum SVGARegGuestDriverId { + SVGA_REG_GUEST_DRIVER_ID_UNKNOWN = 0, + SVGA_REG_GUEST_DRIVER_ID_WDDM = 1, + SVGA_REG_GUEST_DRIVER_ID_LINUX = 2, + SVGA_REG_GUEST_DRIVER_ID_MAX, + + SVGA_REG_GUEST_DRIVER_ID_SUBMIT = MAX_UINT32, +} SVGARegGuestDriverId; + +typedef enum SVGARegMSHint { + SVGA_REG_MSHINT_DISABLED = 0, + SVGA_REG_MSHINT_FULL = 1, + SVGA_REG_MSHINT_RESOLVED = 2, +} SVGARegMSHint; + +typedef enum SVGARegDirtyTracking { + SVGA_REG_DIRTY_TRACKING_PER_IMAGE = 0, + SVGA_REG_DIRTY_TRACKING_PER_SURFACE = 1, +} SVGARegDirtyTracking; + +#define SVGA_GMR_NULL ((uint32)-1) +#define SVGA_GMR_FRAMEBUFFER ((uint32)-2) + +#pragma pack(push, 1) +typedef struct SVGAGuestMemDescriptor { + uint32 ppn; + uint32 numPages; +} SVGAGuestMemDescriptor; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct SVGAGuestPtr { + uint32 gmrId; + uint32 offset; +} SVGAGuestPtr; +#pragma pack(pop) + +#define SVGA_CB_MAX_SIZE_DEFAULT (KBYTES_2_BYTES(512)) +#define SVGA_CB_MAX_SIZE_4MB (MBYTES_2_BYTES(4)) +#define SVGA_CB_MAX_SIZE SVGA_CB_MAX_SIZE_4MB +#define SVGA_CB_MAX_QUEUED_PER_CONTEXT 32 +#define SVGA_CB_MAX_COMMAND_SIZE (32 * 1024) + +#define SVGA_CB_CONTEXT_MASK 0x3f +typedef enum { + SVGA_CB_CONTEXT_DEVICE = 0x3f, + SVGA_CB_CONTEXT_0 = 0x0, + SVGA_CB_CONTEXT_1 = 0x1, + SVGA_CB_CONTEXT_MAX = 0x2, +} SVGACBContext; + +typedef enum { + + SVGA_CB_STATUS_NONE = 0, + + SVGA_CB_STATUS_COMPLETED = 1, + + SVGA_CB_STATUS_QUEUE_FULL = 2, + + SVGA_CB_STATUS_COMMAND_ERROR = 3, + + SVGA_CB_STATUS_CB_HEADER_ERROR = 4, + + SVGA_CB_STATUS_PREEMPTED = 5, + + SVGA_CB_STATUS_SUBMISSION_ERROR = 6, + + SVGA_CB_STATUS_PARTIAL_COMPLETE = 7, +} SVGACBStatus; + +typedef enum { + SVGA_CB_FLAG_NONE = 0, + SVGA_CB_FLAG_NO_IRQ = 1 << 0, + SVGA_CB_FLAG_DX_CONTEXT = 1 << 1, + SVGA_CB_FLAG_MOB = 1 << 2, +} SVGACBFlags; + +#pragma pack(push, 1) +typedef struct { + volatile SVGACBStatus status; + volatile uint32 errorOffset; + uint64 id; + SVGACBFlags flags; + uint32 length; + union { + PA pa; + struct { + SVGAMobId mobid; + uint32 mobOffset; + } mob; + } ptr; + uint32 offset; + uint32 dxContext; + uint32 mustBeZero[6]; +} SVGACBHeader; +#pragma pack(pop) + +typedef enum { + SVGA_DC_CMD_NOP = 0, + SVGA_DC_CMD_START_STOP_CONTEXT = 1, + SVGA_DC_CMD_PREEMPT = 2, + SVGA_DC_CMD_START_QUEUE = 3, + SVGA_DC_CMD_ASYNC_STOP_QUEUE = 4, + SVGA_DC_CMD_EMPTY_CONTEXT_QUEUE = 5, + SVGA_DC_CMD_MAX = 6 +} SVGADeviceContextCmdId; + +typedef struct SVGADCCmdStartStop { + uint32 enable; + SVGACBContext context; +} SVGADCCmdStartStop; + +typedef struct SVGADCCmdPreempt { + SVGACBContext context; + uint32 ignoreIDZero; +} SVGADCCmdPreempt; + +typedef struct SVGADCCmdStartQueue { + SVGACBContext context; +} SVGADCCmdStartQueue; + +typedef struct SVGADCCmdAsyncStopQueue { + SVGACBContext context; +} SVGADCCmdAsyncStopQueue; + +typedef struct SVGADCCmdEmptyQueue { + SVGACBContext context; +} SVGADCCmdEmptyQueue; + +typedef struct SVGAGMRImageFormat { + union { + struct { + uint32 bitsPerPixel : 8; + uint32 colorDepth : 8; + uint32 reserved : 16; + }; + + uint32 value; + }; +} SVGAGMRImageFormat; + +#pragma pack(push, 1) +typedef struct SVGAGuestImage { + SVGAGuestPtr ptr; + + uint32 pitch; +} SVGAGuestImage; +#pragma pack(pop) + +typedef struct SVGAColorBGRX { + union { + struct { + uint32 b : 8; + uint32 g : 8; + uint32 r : 8; + uint32 x : 8; + }; + + uint32 value; + }; +} SVGAColorBGRX; + +#pragma pack(push, 1) +typedef struct { + int32 left; + int32 top; + int32 right; + int32 bottom; +} SVGASignedRect; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + int32 x; + int32 y; +} SVGASignedPoint; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 x; + uint32 y; +} SVGAUnsignedPoint; +#pragma pack(pop) + +#define SVGA_CAP_NONE 0x00000000 +#define SVGA_CAP_RECT_COPY 0x00000002 +#define SVGA_CAP_CURSOR 0x00000020 +#define SVGA_CAP_CURSOR_BYPASS 0x00000040 +#define SVGA_CAP_CURSOR_BYPASS_2 0x00000080 +#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 +#define SVGA_CAP_PITCHLOCK 0x00020000 +#define SVGA_CAP_IRQMASK 0x00040000 +#define SVGA_CAP_DISPLAY_TOPOLOGY 0x00080000 +#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 +#define SVGA_CAP_DX 0x10000000 +#define SVGA_CAP_HP_CMD_QUEUE 0x20000000 +#define SVGA_CAP_NO_BB_RESTRICTION 0x40000000 +#define SVGA_CAP_CAP2_REGISTER 0x80000000 + +#define SVGA_CAP2_NONE 0x00000000 +#define SVGA_CAP2_GROW_OTABLE 0x00000001 +#define SVGA_CAP2_INTRA_SURFACE_COPY 0x00000002 +#define SVGA_CAP2_DX2 0x00000004 +#define SVGA_CAP2_GB_MEMSIZE_2 0x00000008 +#define SVGA_CAP2_SCREENDMA_REG 0x00000010 +#define SVGA_CAP2_OTABLE_PTDEPTH_2 0x00000020 +#define SVGA_CAP2_NON_MS_TO_MS_STRETCHBLT 0x00000040 +#define SVGA_CAP2_CURSOR_MOB 0x00000080 +#define SVGA_CAP2_MSHINT 0x00000100 +#define SVGA_CAP2_CB_MAX_SIZE_4MB 0x00000200 +#define SVGA_CAP2_DX3 0x00000400 +#define SVGA_CAP2_FRAME_TYPE 0x00000800 +#define SVGA_CAP2_COTABLE_COPY 0x00001000 +#define SVGA_CAP2_TRACE_FULL_FB 0x00002000 +#define SVGA_CAP2_EXTRA_REGS 0x00004000 +#define SVGA_CAP2_LO_STAGING 0x00008000 +#define SVGA_CAP2_VIDEO_BLT 0x00010000 +#define SVGA_CAP2_RESERVED 0x80000000 + +typedef enum { + SVGABackdoorCapDeviceCaps = 0, + SVGABackdoorCapFifoCaps = 1, + SVGABackdoorCap3dHWVersion = 2, + SVGABackdoorCapDeviceCaps2 = 3, + SVGABackdoorCapDevelCaps = 4, + SVGABackdoorCapDevCaps = 5, + SVGABackdoorDevelRenderer = 6, + SVGABackdoorDevelUsingISB = 7, + SVGABackdoorCapMax = 8, +} SVGABackdoorCapType; + +enum { + + SVGA_FIFO_MIN = 0, + SVGA_FIFO_MAX, + SVGA_FIFO_NEXT_CMD, + SVGA_FIFO_STOP, + + SVGA_FIFO_CAPABILITIES = 4, + SVGA_FIFO_FLAGS, + + SVGA_FIFO_FENCE, + + SVGA_FIFO_3D_HWVERSION, + + SVGA_FIFO_PITCHLOCK, + + SVGA_FIFO_CURSOR_ON, + SVGA_FIFO_CURSOR_X, + SVGA_FIFO_CURSOR_Y, + SVGA_FIFO_CURSOR_COUNT, + SVGA_FIFO_CURSOR_LAST_UPDATED, + + SVGA_FIFO_RESERVED, + + SVGA_FIFO_CURSOR_SCREEN_ID, + + SVGA_FIFO_DEAD, + + SVGA_FIFO_3D_HWVERSION_REVISED, + + SVGA_FIFO_3D_CAPS = 32, + SVGA_FIFO_3D_CAPS_LAST = 32 + 255, + + SVGA_FIFO_GUEST_3D_HWVERSION, + SVGA_FIFO_FENCE_GOAL, + SVGA_FIFO_BUSY, + + SVGA_FIFO_NUM_REGS +}; + +#define SVGA_FIFO_3D_CAPS_SIZE (SVGA_FIFO_3D_CAPS_LAST - SVGA_FIFO_3D_CAPS + 1) + +#define SVGA3D_FIFO_CAPS_RECORD_DEVCAPS 0x100 +typedef uint32 SVGA3dFifoCapsRecordType; + +typedef uint32 SVGA3dFifoCapPair[2]; + +#pragma pack(push, 1) +typedef struct SVGA3dFifoCapsRecordHeader { + uint32 length; + SVGA3dFifoCapsRecordType type; + +} SVGA3dFifoCapsRecordHeader; +#pragma pack(pop) + +#define SVGA_FIFO_EXTENDED_MANDATORY_REGS (SVGA_FIFO_3D_CAPS_LAST + 1) + +#define SVGA_FIFO_CAP_NONE 0 +#define SVGA_FIFO_CAP_FENCE (1 << 0) +#define SVGA_FIFO_CAP_ACCELFRONT (1 << 1) +#define SVGA_FIFO_CAP_PITCHLOCK (1 << 2) +#define SVGA_FIFO_CAP_VIDEO (1 << 3) +#define SVGA_FIFO_CAP_CURSOR_BYPASS_3 (1 << 4) +#define SVGA_FIFO_CAP_ESCAPE (1 << 5) +#define SVGA_FIFO_CAP_RESERVE (1 << 6) +#define SVGA_FIFO_CAP_SCREEN_OBJECT (1 << 7) +#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) + +#define SVGA_FIFO_FLAG_NONE 0 +#define SVGA_FIFO_FLAG_ACCELFRONT (1 << 0) +#define SVGA_FIFO_FLAG_RESERVED (1 << 31) + +#define SVGA_FIFO_RESERVED_UNKNOWN 0xffffffff + +#define SVGA_SCREENDMA_REG_UNDEFINED 0 +#define SVGA_SCREENDMA_REG_NOT_PRESENT 1 +#define SVGA_SCREENDMA_REG_PRESENT 2 +#define SVGA_SCREENDMA_REG_MAX 3 + +#define SVGA_NUM_OVERLAY_UNITS 32 + +#define SVGA_VIDEO_FLAG_COLORKEY 0x0001 + +enum { + SVGA_VIDEO_ENABLED = 0, + SVGA_VIDEO_FLAGS, + SVGA_VIDEO_DATA_OFFSET, + SVGA_VIDEO_FORMAT, + SVGA_VIDEO_COLORKEY, + SVGA_VIDEO_SIZE, + 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, + SVGA_VIDEO_DST_Y, + SVGA_VIDEO_DST_WIDTH, + SVGA_VIDEO_DST_HEIGHT, + SVGA_VIDEO_PITCH_1, + SVGA_VIDEO_PITCH_2, + SVGA_VIDEO_PITCH_3, + SVGA_VIDEO_DATA_GMRID, + SVGA_VIDEO_DST_SCREEN_ID, + SVGA_VIDEO_NUM_REGS +}; + +#pragma pack(push, 1) +typedef struct SVGAOverlayUnit { + uint32 enabled; + uint32 flags; + uint32 dataOffset; + uint32 format; + uint32 colorKey; + uint32 size; + uint32 width; + uint32 height; + uint32 srcX; + uint32 srcY; + uint32 srcWidth; + uint32 srcHeight; + int32 dstX; + int32 dstY; + uint32 dstWidth; + uint32 dstHeight; + uint32 pitches[3]; + uint32 dataGMRId; + uint32 dstScreenId; +} SVGAOverlayUnit; +#pragma pack(pop) + +#define SVGA_INVALID_DISPLAY_ID ((uint32)-1) + +typedef struct SVGADisplayTopology { + uint16 displayId; + uint16 isPrimary; + uint32 width; + uint32 height; + uint32 positionX; + uint32 positionY; +} SVGADisplayTopology; + +#define SVGA_SCREEN_MUST_BE_SET (1 << 0) +#define SVGA_SCREEN_HAS_ROOT SVGA_SCREEN_MUST_BE_SET +#define SVGA_SCREEN_IS_PRIMARY (1 << 1) +#define SVGA_SCREEN_FULLSCREEN_HINT (1 << 2) + +#define SVGA_SCREEN_DEACTIVATE (1 << 3) + +#define SVGA_SCREEN_BLANKING (1 << 4) + +#pragma pack(push, 1) +typedef struct { + uint32 structSize; + uint32 id; + uint32 flags; + struct { + uint32 width; + uint32 height; + } size; + struct { + int32 x; + int32 y; + } root; + + SVGAGuestImage backingStore; + + uint32 cloneCount; +} SVGAScreenObject; +#pragma pack(pop) + +typedef enum { + SVGA_CMD_INVALID_CMD = 0, + SVGA_CMD_UPDATE = 1, + SVGA_CMD_RECT_COPY = 3, + SVGA_CMD_RECT_ROP_COPY = 14, + SVGA_CMD_DEFINE_CURSOR = 19, + SVGA_CMD_DEFINE_ALPHA_CURSOR = 22, + SVGA_CMD_UPDATE_VERBOSE = 25, + SVGA_CMD_FRONT_ROP_FILL = 29, + SVGA_CMD_FENCE = 30, + SVGA_CMD_ESCAPE = 33, + SVGA_CMD_DEFINE_SCREEN = 34, + SVGA_CMD_DESTROY_SCREEN = 35, + SVGA_CMD_DEFINE_GMRFB = 36, + SVGA_CMD_BLIT_GMRFB_TO_SCREEN = 37, + SVGA_CMD_BLIT_SCREEN_TO_GMRFB = 38, + SVGA_CMD_ANNOTATION_FILL = 39, + SVGA_CMD_ANNOTATION_COPY = 40, + SVGA_CMD_DEFINE_GMR2 = 41, + SVGA_CMD_REMAP_GMR2 = 42, + SVGA_CMD_DEAD = 43, + SVGA_CMD_DEAD_2 = 44, + SVGA_CMD_NOP = 45, + SVGA_CMD_NOP_ERROR = 46, + SVGA_CMD_MAX +} SVGAFifoCmdId; + +#define SVGA_CMD_MAX_DATASIZE (256 * 1024) +#define SVGA_CMD_MAX_ARGS 64 + +#pragma pack(push, 1) +typedef struct { + uint32 x; + uint32 y; + uint32 width; + uint32 height; +} SVGAFifoCmdUpdate; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 srcX; + uint32 srcY; + uint32 destX; + uint32 destY; + uint32 width; + uint32 height; +} SVGAFifoCmdRectCopy; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 srcX; + uint32 srcY; + uint32 destX; + uint32 destY; + uint32 width; + uint32 height; + uint32 rop; +} SVGAFifoCmdRectRopCopy; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 id; + uint32 hotspotX; + uint32 hotspotY; + uint32 width; + uint32 height; + uint32 andMaskDepth; + uint32 xorMaskDepth; + +} SVGAFifoCmdDefineCursor; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 id; + uint32 hotspotX; + uint32 hotspotY; + uint32 width; + uint32 height; + +} SVGAFifoCmdDefineAlphaCursor; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 hotspotX; + uint32 hotspotY; + uint32 width; + uint32 height; + uint32 andMaskDepth; + uint32 xorMaskDepth; + +} SVGAGBColorCursorHeader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 hotspotX; + uint32 hotspotY; + uint32 width; + uint32 height; + +} SVGAGBAlphaCursorHeader; +#pragma pack(pop) + +typedef enum { + SVGA_COLOR_CURSOR = 0, + SVGA_ALPHA_CURSOR = 1, +} SVGAGBCursorType; + +#pragma pack(push, 1) +typedef struct { + SVGAGBCursorType type; + union { + SVGAGBColorCursorHeader colorHeader; + SVGAGBAlphaCursorHeader alphaHeader; + } header; + uint32 sizeInBytes; + +} SVGAGBCursorHeader; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 x; + uint32 y; + uint32 width; + uint32 height; + uint32 reason; +} SVGAFifoCmdUpdateVerbose; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 color; + uint32 x; + uint32 y; + uint32 width; + uint32 height; + uint32 rop; +} SVGAFifoCmdFrontRopFill; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 fence; +} SVGAFifoCmdFence; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 nsid; + uint32 size; + +} SVGAFifoCmdEscape; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAScreenObject screen; +} SVGAFifoCmdDefineScreen; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 screenId; +} SVGAFifoCmdDestroyScreen; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAGuestPtr ptr; + uint32 bytesPerLine; + SVGAGMRImageFormat format; +} SVGAFifoCmdDefineGMRFB; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGASignedPoint srcOrigin; + SVGASignedRect destRect; + uint32 destScreenId; +} SVGAFifoCmdBlitGMRFBToScreen; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGASignedPoint destOrigin; + SVGASignedRect srcRect; + uint32 srcScreenId; +} SVGAFifoCmdBlitScreenToGMRFB; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGAColorBGRX color; +} SVGAFifoCmdAnnotationFill; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + SVGASignedPoint srcOrigin; + uint32 srcScreenId; +} SVGAFifoCmdAnnotationCopy; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct { + uint32 gmrId; + uint32 numPages; +} SVGAFifoCmdDefineGMR2; +#pragma pack(pop) + +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; + +#pragma pack(push, 1) +typedef struct { + uint32 gmrId; + SVGARemapGMR2Flags flags; + uint32 offsetPages; + uint32 numPages; + +} SVGAFifoCmdRemapGMR2; +#pragma pack(pop) + +#define SVGA_VRAM_MIN_SIZE (4 * 640 * 480) +#define SVGA_VRAM_MIN_SIZE_3D (16 * 1024 * 1024) +#define SVGA_VRAM_MAX_SIZE (128 * 1024 * 1024) +#define SVGA_MEMORY_SIZE_MAX (1024 * 1024 * 1024) +#define SVGA_FIFO_SIZE_MAX (2 * 1024 * 1024) +#define SVGA_GRAPHICS_MEMORY_KB_MIN (32 * 1024) +#define SVGA_GRAPHICS_MEMORY_KB_MAX_2GB (2 * 1024 * 1024) +#define SVGA_GRAPHICS_MEMORY_KB_MAX_3GB (3 * 1024 * 1024) +#define SVGA_GRAPHICS_MEMORY_KB_MAX_4GB (4 * 1024 * 1024) +#define SVGA_GRAPHICS_MEMORY_KB_MAX_8GB (8 * 1024 * 1024) +#define SVGA_GRAPHICS_MEMORY_KB_DEFAULT (256 * 1024) + +#define SVGA_VRAM_SIZE_W2K (64 * 1024 * 1024) + +#if defined(VMX86_SERVER) +#define SVGA_VRAM_SIZE (4 * 1024 * 1024) +#define SVGA_VRAM_SIZE_3D (64 * 1024 * 1024) +#define SVGA_FIFO_SIZE (256 * 1024) +#define SVGA_FIFO_SIZE_3D (516 * 1024) +#define SVGA_MEMORY_SIZE_DEFAULT (160 * 1024 * 1024) +#define SVGA_AUTODETECT_DEFAULT FALSE +#else +#define SVGA_VRAM_SIZE (16 * 1024 * 1024) +#define SVGA_VRAM_SIZE_3D SVGA_VRAM_MAX_SIZE +#define SVGA_FIFO_SIZE (2 * 1024 * 1024) +#define SVGA_FIFO_SIZE_3D SVGA_FIFO_SIZE +#define SVGA_MEMORY_SIZE_DEFAULT (768 * 1024 * 1024) +#define SVGA_AUTODETECT_DEFAULT TRUE +#endif + +#define SVGA_FIFO_SIZE_GBOBJECTS (256 * 1024) +#define SVGA_VRAM_SIZE_GBOBJECTS (4 * 1024 * 1024) + +#endif diff --git a/drivers/gpu/drm/vmwgfx/device_include/vm_basic_types.h b/drivers/gpu/drm/vmwgfx/device_include/vm_basic_types.h new file mode 100644 index 0000000000..f843767180 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/device_include/vm_basic_types.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/********************************************************** + * Copyright 2015-2021 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + **********************************************************/ +#ifndef VM_BASIC_TYPES_H +#define VM_BASIC_TYPES_H + +#include <linux/kernel.h> +#include <linux/mm.h> +#include <asm/page.h> + +typedef u32 uint32; +typedef s32 int32; +typedef u64 uint64; +typedef u16 uint16; +typedef s16 int16; +typedef u8 uint8; +typedef s8 int8; + +typedef uint64 PA; +typedef uint32 PPN; +typedef uint32 PPN32; +typedef uint64 PPN64; + +typedef bool Bool; + +#define MAX_UINT64 U64_MAX +#define MAX_UINT32 U32_MAX +#define MAX_UINT16 U16_MAX + +#define CONST64U(x) x##ULL + +#ifndef MBYTES_SHIFT +#define MBYTES_SHIFT 20 +#endif +#ifndef MBYTES_2_BYTES +#define MBYTES_2_BYTES(_nbytes) ((uint64)(_nbytes) << MBYTES_SHIFT) +#endif + +/* + * MKS Guest Stats types + */ + +typedef struct MKSGuestStatCounter { + atomic64_t count; +} MKSGuestStatCounter; + +typedef struct MKSGuestStatCounterTime { + MKSGuestStatCounter counter; + atomic64_t selfCycles; + atomic64_t totalCycles; +} MKSGuestStatCounterTime; + +/* + * Flags for MKSGuestStatInfoEntry::flags below + */ + +#define MKS_GUEST_STAT_FLAG_NONE 0 +#define MKS_GUEST_STAT_FLAG_TIME (1U << 0) + +typedef __attribute__((aligned(32))) struct MKSGuestStatInfoEntry { + union { + const char *s; + uint64 u; + } name; + union { + const char *s; + uint64 u; + } description; + uint64 flags; + union { + MKSGuestStatCounter *counter; + MKSGuestStatCounterTime *counterTime; + uint64 u; + } stat; +} MKSGuestStatInfoEntry; + +#define INVALID_PPN64 ((PPN64)0x000fffffffffffffULL) + +#define MKS_GUEST_STAT_INSTANCE_DESC_LENGTH 1024 +#define MKS_GUEST_STAT_INSTANCE_MAX_STATS 4096 +#define MKS_GUEST_STAT_INSTANCE_MAX_STAT_PPNS \ + (PFN_UP(MKS_GUEST_STAT_INSTANCE_MAX_STATS * \ + sizeof(MKSGuestStatCounterTime))) +#define MKS_GUEST_STAT_INSTANCE_MAX_INFO_PPNS \ + (PFN_UP(MKS_GUEST_STAT_INSTANCE_MAX_STATS * \ + sizeof(MKSGuestStatInfoEntry))) +#define MKS_GUEST_STAT_AVERAGE_NAME_LENGTH 40 +#define MKS_GUEST_STAT_INSTANCE_MAX_STRS_PPNS \ + (PFN_UP(MKS_GUEST_STAT_INSTANCE_MAX_STATS * \ + MKS_GUEST_STAT_AVERAGE_NAME_LENGTH)) + +/* + * The MKSGuestStatInstanceDescriptor is used as main interface to + * communicate guest stats back to the host code. The guest must + * allocate an instance of this structure at the start of a page and + * provide the physical address to the host. From there the host code + * can walk this structure to find other (pinned) pages containing the + * stats data. + * + * Since the MKSGuestStatInfoEntry structures contain userlevel + * pointers, the InstanceDescriptor also contains pointers to the + * beginning of these sections allowing the host side code to correctly + * interpret the pointers. + * + * Because the host side code never acknowledges anything back to the + * guest there is no strict requirement to maintain compatability + * across releases. If the interface changes the host might not be + * able to log stats, but the guest will continue to run normally. + */ + +typedef struct MKSGuestStatInstanceDescriptor { + uint64 reservedMBZ; /* must be zero for now. */ + uint64 statStartVA; /* VA of the start of the stats section. */ + uint64 strsStartVA; /* VA of the start of the strings section. */ + uint64 statLength; /* length of the stats section in bytes. */ + uint64 infoLength; /* length of the info entry section in bytes. */ + uint64 strsLength; /* length of the strings section in bytes. */ + PPN64 statPPNs[MKS_GUEST_STAT_INSTANCE_MAX_STAT_PPNS]; /* stat counters */ + PPN64 infoPPNs[MKS_GUEST_STAT_INSTANCE_MAX_INFO_PPNS]; /* stat info */ + PPN64 strsPPNs[MKS_GUEST_STAT_INSTANCE_MAX_STRS_PPNS]; /* strings */ + char description[MKS_GUEST_STAT_INSTANCE_DESC_LENGTH]; +} MKSGuestStatInstanceDescriptor; + +#endif diff --git a/drivers/gpu/drm/vmwgfx/ttm_object.c b/drivers/gpu/drm/vmwgfx/ttm_object.c new file mode 100644 index 0000000000..ddf8373c1d --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/ttm_object.c @@ -0,0 +1,671 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright (c) 2009-2022 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-at-vmware-dot-com> + * + * While no substantial code is shared, the prime code is inspired by + * drm_prime.c, with + * Authors: + * Dave Airlie <airlied@redhat.com> + * Rob Clark <rob.clark@linaro.org> + */ +/** @file ttm_ref_object.c + * + * Base- and reference object implementation for the various + * ttm objects. Implements reference counting, minimal security checks + * and release on file close. + */ + + +#define pr_fmt(fmt) "[TTM] " fmt + +#include "ttm_object.h" +#include "vmwgfx_drv.h" + +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/atomic.h> +#include <linux/module.h> +#include <linux/hashtable.h> + +MODULE_IMPORT_NS(DMA_BUF); + +#define VMW_TTM_OBJECT_REF_HT_ORDER 10 + +/** + * struct ttm_object_file + * + * @tdev: Pointer to the ttm_object_device. + * + * @lock: Lock that protects the ref_list list and the + * ref_hash hash tables. + * + * @ref_list: List of ttm_ref_objects to be destroyed at + * file release. + * + * @ref_hash: Hash tables of ref objects, one per ttm_ref_type, + * for fast lookup of ref objects given a base object. + * + * @refcount: reference/usage count + */ +struct ttm_object_file { + struct ttm_object_device *tdev; + spinlock_t lock; + struct list_head ref_list; + DECLARE_HASHTABLE(ref_hash, VMW_TTM_OBJECT_REF_HT_ORDER); + struct kref refcount; +}; + +/* + * struct ttm_object_device + * + * @object_lock: lock that protects idr. + * + * @object_count: Per device object count. + * + * This is the per-device data structure needed for ttm object management. + */ + +struct ttm_object_device { + spinlock_t object_lock; + atomic_t object_count; + struct dma_buf_ops ops; + void (*dmabuf_release)(struct dma_buf *dma_buf); + struct idr idr; +}; + +/* + * struct ttm_ref_object + * + * @hash: Hash entry for the per-file object reference hash. + * + * @head: List entry for the per-file list of ref-objects. + * + * @kref: Ref count. + * + * @obj: Base object this ref object is referencing. + * + * @ref_type: Type of ref object. + * + * This is similar to an idr object, but it also has a hash table entry + * that allows lookup with a pointer to the referenced object as a key. In + * that way, one can easily detect whether a base object is referenced by + * a particular ttm_object_file. It also carries a ref count to avoid creating + * multiple ref objects if a ttm_object_file references the same base + * object more than once. + */ + +struct ttm_ref_object { + struct rcu_head rcu_head; + struct vmwgfx_hash_item hash; + struct list_head head; + struct kref kref; + struct ttm_base_object *obj; + struct ttm_object_file *tfile; +}; + +static void ttm_prime_dmabuf_release(struct dma_buf *dma_buf); + +static inline struct ttm_object_file * +ttm_object_file_ref(struct ttm_object_file *tfile) +{ + kref_get(&tfile->refcount); + return tfile; +} + +static int ttm_tfile_find_ref_rcu(struct ttm_object_file *tfile, + uint64_t key, + struct vmwgfx_hash_item **p_hash) +{ + struct vmwgfx_hash_item *hash; + + hash_for_each_possible_rcu(tfile->ref_hash, hash, head, key) { + if (hash->key == key) { + *p_hash = hash; + return 0; + } + } + return -EINVAL; +} + +static int ttm_tfile_find_ref(struct ttm_object_file *tfile, + uint64_t key, + struct vmwgfx_hash_item **p_hash) +{ + struct vmwgfx_hash_item *hash; + + hash_for_each_possible(tfile->ref_hash, hash, head, key) { + if (hash->key == key) { + *p_hash = hash; + return 0; + } + } + return -EINVAL; +} + +static void ttm_object_file_destroy(struct kref *kref) +{ + struct ttm_object_file *tfile = + container_of(kref, struct ttm_object_file, refcount); + + kfree(tfile); +} + + +static inline void ttm_object_file_unref(struct ttm_object_file **p_tfile) +{ + struct ttm_object_file *tfile = *p_tfile; + + *p_tfile = NULL; + kref_put(&tfile->refcount, ttm_object_file_destroy); +} + + +int ttm_base_object_init(struct ttm_object_file *tfile, + struct ttm_base_object *base, + bool shareable, + enum ttm_object_type object_type, + void (*refcount_release) (struct ttm_base_object **)) +{ + struct ttm_object_device *tdev = tfile->tdev; + int ret; + + base->shareable = shareable; + base->tfile = ttm_object_file_ref(tfile); + base->refcount_release = refcount_release; + base->object_type = object_type; + kref_init(&base->refcount); + idr_preload(GFP_KERNEL); + spin_lock(&tdev->object_lock); + ret = idr_alloc(&tdev->idr, base, 1, 0, GFP_NOWAIT); + spin_unlock(&tdev->object_lock); + idr_preload_end(); + if (ret < 0) + return ret; + + base->handle = ret; + ret = ttm_ref_object_add(tfile, base, NULL, false); + if (unlikely(ret != 0)) + goto out_err1; + + ttm_base_object_unref(&base); + + return 0; +out_err1: + spin_lock(&tdev->object_lock); + idr_remove(&tdev->idr, base->handle); + spin_unlock(&tdev->object_lock); + return ret; +} + +static void ttm_release_base(struct kref *kref) +{ + struct ttm_base_object *base = + container_of(kref, struct ttm_base_object, refcount); + struct ttm_object_device *tdev = base->tfile->tdev; + + spin_lock(&tdev->object_lock); + idr_remove(&tdev->idr, base->handle); + spin_unlock(&tdev->object_lock); + + /* + * Note: We don't use synchronize_rcu() here because it's far + * too slow. It's up to the user to free the object using + * call_rcu() or ttm_base_object_kfree(). + */ + + ttm_object_file_unref(&base->tfile); + if (base->refcount_release) + base->refcount_release(&base); +} + +void ttm_base_object_unref(struct ttm_base_object **p_base) +{ + struct ttm_base_object *base = *p_base; + + *p_base = NULL; + + kref_put(&base->refcount, ttm_release_base); +} + +struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile, + uint64_t key) +{ + struct ttm_base_object *base = NULL; + struct vmwgfx_hash_item *hash; + int ret; + + spin_lock(&tfile->lock); + ret = ttm_tfile_find_ref(tfile, key, &hash); + + if (likely(ret == 0)) { + base = hlist_entry(hash, struct ttm_ref_object, hash)->obj; + if (!kref_get_unless_zero(&base->refcount)) + base = NULL; + } + spin_unlock(&tfile->lock); + + + return base; +} + +struct ttm_base_object * +ttm_base_object_lookup_for_ref(struct ttm_object_device *tdev, uint64_t key) +{ + struct ttm_base_object *base; + + rcu_read_lock(); + base = idr_find(&tdev->idr, key); + + if (base && !kref_get_unless_zero(&base->refcount)) + base = NULL; + rcu_read_unlock(); + + return base; +} + +int ttm_ref_object_add(struct ttm_object_file *tfile, + struct ttm_base_object *base, + bool *existed, + bool require_existed) +{ + struct ttm_ref_object *ref; + struct vmwgfx_hash_item *hash; + int ret = -EINVAL; + + if (base->tfile != tfile && !base->shareable) + return -EPERM; + + if (existed != NULL) + *existed = true; + + while (ret == -EINVAL) { + rcu_read_lock(); + ret = ttm_tfile_find_ref_rcu(tfile, base->handle, &hash); + + if (ret == 0) { + ref = hlist_entry(hash, struct ttm_ref_object, hash); + if (kref_get_unless_zero(&ref->kref)) { + rcu_read_unlock(); + break; + } + } + + rcu_read_unlock(); + if (require_existed) + return -EPERM; + + ref = kmalloc(sizeof(*ref), GFP_KERNEL); + if (unlikely(ref == NULL)) { + return -ENOMEM; + } + + ref->hash.key = base->handle; + ref->obj = base; + ref->tfile = tfile; + kref_init(&ref->kref); + + spin_lock(&tfile->lock); + hash_add_rcu(tfile->ref_hash, &ref->hash.head, ref->hash.key); + ret = 0; + + list_add_tail(&ref->head, &tfile->ref_list); + kref_get(&base->refcount); + spin_unlock(&tfile->lock); + if (existed != NULL) + *existed = false; + } + + return ret; +} + +static void __releases(tfile->lock) __acquires(tfile->lock) +ttm_ref_object_release(struct kref *kref) +{ + struct ttm_ref_object *ref = + container_of(kref, struct ttm_ref_object, kref); + struct ttm_object_file *tfile = ref->tfile; + + hash_del_rcu(&ref->hash.head); + list_del(&ref->head); + spin_unlock(&tfile->lock); + + ttm_base_object_unref(&ref->obj); + kfree_rcu(ref, rcu_head); + spin_lock(&tfile->lock); +} + +int ttm_ref_object_base_unref(struct ttm_object_file *tfile, + unsigned long key) +{ + struct ttm_ref_object *ref; + struct vmwgfx_hash_item *hash; + int ret; + + spin_lock(&tfile->lock); + ret = ttm_tfile_find_ref(tfile, key, &hash); + if (unlikely(ret != 0)) { + spin_unlock(&tfile->lock); + return -EINVAL; + } + ref = hlist_entry(hash, struct ttm_ref_object, hash); + kref_put(&ref->kref, ttm_ref_object_release); + spin_unlock(&tfile->lock); + return 0; +} + +void ttm_object_file_release(struct ttm_object_file **p_tfile) +{ + struct ttm_ref_object *ref; + struct list_head *list; + struct ttm_object_file *tfile = *p_tfile; + + *p_tfile = NULL; + spin_lock(&tfile->lock); + + /* + * Since we release the lock within the loop, we have to + * restart it from the beginning each time. + */ + + while (!list_empty(&tfile->ref_list)) { + list = tfile->ref_list.next; + ref = list_entry(list, struct ttm_ref_object, head); + ttm_ref_object_release(&ref->kref); + } + + spin_unlock(&tfile->lock); + + ttm_object_file_unref(&tfile); +} + +struct ttm_object_file *ttm_object_file_init(struct ttm_object_device *tdev) +{ + struct ttm_object_file *tfile = kmalloc(sizeof(*tfile), GFP_KERNEL); + + if (unlikely(tfile == NULL)) + return NULL; + + spin_lock_init(&tfile->lock); + tfile->tdev = tdev; + kref_init(&tfile->refcount); + INIT_LIST_HEAD(&tfile->ref_list); + + hash_init(tfile->ref_hash); + + return tfile; +} + +struct ttm_object_device * +ttm_object_device_init(const struct dma_buf_ops *ops) +{ + struct ttm_object_device *tdev = kmalloc(sizeof(*tdev), GFP_KERNEL); + + if (unlikely(tdev == NULL)) + return NULL; + + spin_lock_init(&tdev->object_lock); + atomic_set(&tdev->object_count, 0); + + /* + * Our base is at VMWGFX_NUM_MOB + 1 because we want to create + * a seperate namespace for GEM handles (which are + * 1..VMWGFX_NUM_MOB) and the surface handles. Some ioctl's + * can take either handle as an argument so we want to + * easily be able to tell whether the handle refers to a + * GEM buffer or a surface. + */ + idr_init_base(&tdev->idr, VMWGFX_NUM_MOB + 1); + tdev->ops = *ops; + tdev->dmabuf_release = tdev->ops.release; + tdev->ops.release = ttm_prime_dmabuf_release; + return tdev; +} + +void ttm_object_device_release(struct ttm_object_device **p_tdev) +{ + struct ttm_object_device *tdev = *p_tdev; + + *p_tdev = NULL; + + WARN_ON_ONCE(!idr_is_empty(&tdev->idr)); + idr_destroy(&tdev->idr); + + kfree(tdev); +} + +/** + * get_dma_buf_unless_doomed - get a dma_buf reference if possible. + * + * @dmabuf: Non-refcounted pointer to a struct dma-buf. + * + * Obtain a file reference from a lookup structure that doesn't refcount + * the file, but synchronizes with its release method to make sure it has + * not been freed yet. See for example kref_get_unless_zero documentation. + * Returns true if refcounting succeeds, false otherwise. + * + * Nobody really wants this as a public API yet, so let it mature here + * for some time... + */ +static bool __must_check get_dma_buf_unless_doomed(struct dma_buf *dmabuf) +{ + return atomic_long_inc_not_zero(&dmabuf->file->f_count) != 0L; +} + +/** + * ttm_prime_refcount_release - refcount release method for a prime object. + * + * @p_base: Pointer to ttm_base_object pointer. + * + * This is a wrapper that calls the refcount_release founction of the + * underlying object. At the same time it cleans up the prime object. + * This function is called when all references to the base object we + * derive from are gone. + */ +static void ttm_prime_refcount_release(struct ttm_base_object **p_base) +{ + struct ttm_base_object *base = *p_base; + struct ttm_prime_object *prime; + + *p_base = NULL; + prime = container_of(base, struct ttm_prime_object, base); + BUG_ON(prime->dma_buf != NULL); + mutex_destroy(&prime->mutex); + if (prime->refcount_release) + prime->refcount_release(&base); +} + +/** + * ttm_prime_dmabuf_release - Release method for the dma-bufs we export + * + * @dma_buf: + * + * This function first calls the dma_buf release method the driver + * provides. Then it cleans up our dma_buf pointer used for lookup, + * and finally releases the reference the dma_buf has on our base + * object. + */ +static void ttm_prime_dmabuf_release(struct dma_buf *dma_buf) +{ + struct ttm_prime_object *prime = + (struct ttm_prime_object *) dma_buf->priv; + struct ttm_base_object *base = &prime->base; + struct ttm_object_device *tdev = base->tfile->tdev; + + if (tdev->dmabuf_release) + tdev->dmabuf_release(dma_buf); + mutex_lock(&prime->mutex); + if (prime->dma_buf == dma_buf) + prime->dma_buf = NULL; + mutex_unlock(&prime->mutex); + ttm_base_object_unref(&base); +} + +/** + * ttm_prime_fd_to_handle - Get a base object handle from a prime fd + * + * @tfile: A struct ttm_object_file identifying the caller. + * @fd: The prime / dmabuf fd. + * @handle: The returned handle. + * + * This function returns a handle to an object that previously exported + * a dma-buf. Note that we don't handle imports yet, because we simply + * have no consumers of that implementation. + */ +int ttm_prime_fd_to_handle(struct ttm_object_file *tfile, + int fd, u32 *handle) +{ + struct ttm_object_device *tdev = tfile->tdev; + struct dma_buf *dma_buf; + struct ttm_prime_object *prime; + struct ttm_base_object *base; + int ret; + + dma_buf = dma_buf_get(fd); + if (IS_ERR(dma_buf)) + return PTR_ERR(dma_buf); + + if (dma_buf->ops != &tdev->ops) + return -ENOSYS; + + prime = (struct ttm_prime_object *) dma_buf->priv; + base = &prime->base; + *handle = base->handle; + ret = ttm_ref_object_add(tfile, base, NULL, false); + + dma_buf_put(dma_buf); + + return ret; +} + +/** + * ttm_prime_handle_to_fd - Return a dma_buf fd from a ttm prime object + * + * @tfile: Struct ttm_object_file identifying the caller. + * @handle: Handle to the object we're exporting from. + * @flags: flags for dma-buf creation. We just pass them on. + * @prime_fd: The returned file descriptor. + * + */ +int ttm_prime_handle_to_fd(struct ttm_object_file *tfile, + uint32_t handle, uint32_t flags, + int *prime_fd) +{ + struct ttm_object_device *tdev = tfile->tdev; + struct ttm_base_object *base; + struct dma_buf *dma_buf; + struct ttm_prime_object *prime; + int ret; + + base = ttm_base_object_lookup(tfile, handle); + if (unlikely(base == NULL || + base->object_type != ttm_prime_type)) { + ret = -ENOENT; + goto out_unref; + } + + prime = container_of(base, struct ttm_prime_object, base); + if (unlikely(!base->shareable)) { + ret = -EPERM; + goto out_unref; + } + + ret = mutex_lock_interruptible(&prime->mutex); + if (unlikely(ret != 0)) { + ret = -ERESTARTSYS; + goto out_unref; + } + + dma_buf = prime->dma_buf; + if (!dma_buf || !get_dma_buf_unless_doomed(dma_buf)) { + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + exp_info.ops = &tdev->ops; + exp_info.size = prime->size; + exp_info.flags = flags; + exp_info.priv = prime; + + /* + * Need to create a new dma_buf + */ + + dma_buf = dma_buf_export(&exp_info); + if (IS_ERR(dma_buf)) { + ret = PTR_ERR(dma_buf); + mutex_unlock(&prime->mutex); + goto out_unref; + } + + /* + * dma_buf has taken the base object reference + */ + base = NULL; + prime->dma_buf = dma_buf; + } + mutex_unlock(&prime->mutex); + + ret = dma_buf_fd(dma_buf, flags); + if (ret >= 0) { + *prime_fd = ret; + ret = 0; + } else + dma_buf_put(dma_buf); + +out_unref: + if (base) + ttm_base_object_unref(&base); + return ret; +} + +/** + * ttm_prime_object_init - Initialize a ttm_prime_object + * + * @tfile: struct ttm_object_file identifying the caller + * @size: The size of the dma_bufs we export. + * @prime: The object to be initialized. + * @shareable: See ttm_base_object_init + * @type: See ttm_base_object_init + * @refcount_release: See ttm_base_object_init + * + * Initializes an object which is compatible with the drm_prime model + * for data sharing between processes and devices. + */ +int ttm_prime_object_init(struct ttm_object_file *tfile, size_t size, + struct ttm_prime_object *prime, bool shareable, + enum ttm_object_type type, + void (*refcount_release) (struct ttm_base_object **)) +{ + mutex_init(&prime->mutex); + prime->size = PAGE_ALIGN(size); + prime->real_type = type; + prime->dma_buf = NULL; + prime->refcount_release = refcount_release; + return ttm_base_object_init(tfile, &prime->base, shareable, + ttm_prime_type, + ttm_prime_refcount_release); +} diff --git a/drivers/gpu/drm/vmwgfx/ttm_object.h b/drivers/gpu/drm/vmwgfx/ttm_object.h new file mode 100644 index 0000000000..e6b77ee33e --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/ttm_object.h @@ -0,0 +1,320 @@ +/************************************************************************** + * + * Copyright (c) 2006-2022 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-at-vmware-dot-com> + */ +/** @file ttm_object.h + * + * Base- and reference object implementation for the various + * ttm objects. Implements reference counting, minimal security checks + * and release on file close. + */ + +#ifndef _TTM_OBJECT_H_ +#define _TTM_OBJECT_H_ + +#include <linux/dma-buf.h> +#include <linux/kref.h> +#include <linux/list.h> +#include <linux/rcupdate.h> + +#include <drm/ttm/ttm_bo.h> + +/** + * enum ttm_object_type + * + * One entry per ttm object type. + * Device-specific types should use the + * ttm_driver_typex types. + */ + +enum ttm_object_type { + ttm_fence_type, + ttm_lock_type, + ttm_prime_type, + ttm_driver_type0 = 256, + ttm_driver_type1, + ttm_driver_type2, + ttm_driver_type3, + ttm_driver_type4, + ttm_driver_type5 +}; + +struct ttm_object_file; +struct ttm_object_device; + +/** + * struct ttm_base_object + * + * @hash: hash entry for the per-device object hash. + * @type: derived type this object is base class for. + * @shareable: Other ttm_object_files can access this object. + * + * @tfile: Pointer to ttm_object_file of the creator. + * NULL if the object was not created by a user request. + * (kernel object). + * + * @refcount: Number of references to this object, not + * including the hash entry. A reference to a base object can + * only be held by a ref object. + * + * @refcount_release: A function to be called when there are + * no more references to this object. This function should + * destroy the object (or make sure destruction eventually happens), + * and when it is called, the object has + * already been taken out of the per-device hash. The parameter + * "base" should be set to NULL by the function. + * + * @ref_obj_release: A function to be called when a reference object + * with another ttm_ref_type than TTM_REF_USAGE is deleted. + * This function may, for example, release a lock held by a user-space + * process. + * + * This struct is intended to be used as a base struct for objects that + * are visible to user-space. It provides a global name, race-safe + * access and refcounting, minimal access control and hooks for unref actions. + */ + +struct ttm_base_object { + struct rcu_head rhead; + struct ttm_object_file *tfile; + struct kref refcount; + void (*refcount_release) (struct ttm_base_object **base); + u64 handle; + enum ttm_object_type object_type; + u32 shareable; +}; + + +/** + * struct ttm_prime_object - Modified base object that is prime-aware + * + * @base: struct ttm_base_object that we derive from + * @mutex: Mutex protecting the @dma_buf member. + * @size: Size of the dma_buf associated with this object + * @real_type: Type of the underlying object. Needed since we're setting + * the value of @base::object_type to ttm_prime_type + * @dma_buf: Non ref-coutned pointer to a struct dma_buf created from this + * object. + * @refcount_release: The underlying object's release method. Needed since + * we set @base::refcount_release to our own release method. + */ + +struct ttm_prime_object { + struct ttm_base_object base; + struct mutex mutex; + size_t size; + enum ttm_object_type real_type; + struct dma_buf *dma_buf; + void (*refcount_release) (struct ttm_base_object **); +}; + +/** + * ttm_base_object_init + * + * @tfile: Pointer to a struct ttm_object_file. + * @base: The struct ttm_base_object to initialize. + * @shareable: This object is shareable with other applications. + * (different @tfile pointers.) + * @type: The object type. + * @refcount_release: See the struct ttm_base_object description. + * @ref_obj_release: See the struct ttm_base_object description. + * + * Initializes a struct ttm_base_object. + */ + +extern int ttm_base_object_init(struct ttm_object_file *tfile, + struct ttm_base_object *base, + bool shareable, + enum ttm_object_type type, + void (*refcount_release) (struct ttm_base_object + **)); + +/** + * ttm_base_object_lookup + * + * @tfile: Pointer to a struct ttm_object_file. + * @key: Hash key + * + * Looks up a struct ttm_base_object with the key @key. + */ + +extern struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file + *tfile, uint64_t key); + +/** + * ttm_base_object_lookup_for_ref + * + * @tdev: Pointer to a struct ttm_object_device. + * @key: Hash key + * + * Looks up a struct ttm_base_object with the key @key. + * This function should only be used when the struct tfile associated with the + * caller doesn't yet have a reference to the base object. + */ + +extern struct ttm_base_object * +ttm_base_object_lookup_for_ref(struct ttm_object_device *tdev, uint64_t key); + +/** + * ttm_base_object_unref + * + * @p_base: Pointer to a pointer referencing a struct ttm_base_object. + * + * Decrements the base object refcount and clears the pointer pointed to by + * p_base. + */ + +extern void ttm_base_object_unref(struct ttm_base_object **p_base); + +/** + * ttm_ref_object_add. + * + * @tfile: A struct ttm_object_file representing the application owning the + * ref_object. + * @base: The base object to reference. + * @ref_type: The type of reference. + * @existed: Upon completion, indicates that an identical reference object + * already existed, and the refcount was upped on that object instead. + * @require_existed: Fail with -EPERM if an identical ref object didn't + * already exist. + * + * Checks that the base object is shareable and adds a ref object to it. + * + * Adding a ref object to a base object is basically like referencing the + * base object, but a user-space application holds the reference. When the + * file corresponding to @tfile is closed, all its reference objects are + * deleted. A reference object can have different types depending on what + * it's intended for. It can be refcounting to prevent object destruction, + * When user-space takes a lock, it can add a ref object to that lock to + * make sure the lock is released if the application dies. A ref object + * will hold a single reference on a base object. + */ +extern int ttm_ref_object_add(struct ttm_object_file *tfile, + struct ttm_base_object *base, + bool *existed, + bool require_existed); + +/** + * ttm_ref_object_base_unref + * + * @key: Key representing the base object. + * @ref_type: Ref type of the ref object to be dereferenced. + * + * Unreference a ref object with type @ref_type + * on the base object identified by @key. If there are no duplicate + * references, the ref object will be destroyed and the base object + * will be unreferenced. + */ +extern int ttm_ref_object_base_unref(struct ttm_object_file *tfile, + unsigned long key); + +/** + * ttm_object_file_init - initialize a struct ttm_object file + * + * @tdev: A struct ttm_object device this file is initialized on. + * + * This is typically called by the file_ops::open function. + */ + +extern struct ttm_object_file *ttm_object_file_init(struct ttm_object_device + *tdev); + +/** + * ttm_object_file_release - release data held by a ttm_object_file + * + * @p_tfile: Pointer to pointer to the ttm_object_file object to release. + * *p_tfile will be set to NULL by this function. + * + * Releases all data associated by a ttm_object_file. + * Typically called from file_ops::release. The caller must + * ensure that there are no concurrent users of tfile. + */ + +extern void ttm_object_file_release(struct ttm_object_file **p_tfile); + +/** + * ttm_object device init - initialize a struct ttm_object_device + * + * @ops: DMA buf ops for prime objects of this device. + * + * This function is typically called on device initialization to prepare + * data structures needed for ttm base and ref objects. + */ + +extern struct ttm_object_device * +ttm_object_device_init(const struct dma_buf_ops *ops); + +/** + * ttm_object_device_release - release data held by a ttm_object_device + * + * @p_tdev: Pointer to pointer to the ttm_object_device object to release. + * *p_tdev will be set to NULL by this function. + * + * Releases all data associated by a ttm_object_device. + * Typically called from driver::unload before the destruction of the + * device private data structure. + */ + +extern void ttm_object_device_release(struct ttm_object_device **p_tdev); + +#define ttm_base_object_kfree(__object, __base)\ + kfree_rcu(__object, __base.rhead) + +extern int ttm_prime_object_init(struct ttm_object_file *tfile, + size_t size, + struct ttm_prime_object *prime, + bool shareable, + enum ttm_object_type type, + void (*refcount_release) + (struct ttm_base_object **)); + +static inline enum ttm_object_type +ttm_base_object_type(struct ttm_base_object *base) +{ + return (base->object_type == ttm_prime_type) ? + container_of(base, struct ttm_prime_object, base)->real_type : + base->object_type; +} +extern int ttm_prime_fd_to_handle(struct ttm_object_file *tfile, + int fd, u32 *handle); +extern int ttm_prime_handle_to_fd(struct ttm_object_file *tfile, + uint32_t handle, uint32_t flags, + int *prime_fd); + +#define ttm_prime_object_kfree(__obj, __prime) \ + kfree_rcu(__obj, __prime.base.rhead) + +static inline int ttm_bo_wait(struct ttm_buffer_object *bo, bool intr, + bool no_wait) +{ + struct ttm_operation_ctx ctx = { intr, no_wait }; + + return ttm_bo_wait_ctx(bo, &ctx); +} + +#endif diff --git a/drivers/gpu/drm/vmwgfx/vmw_surface_cache.h b/drivers/gpu/drm/vmwgfx/vmw_surface_cache.h new file mode 100644 index 0000000000..b0d87c5f58 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmw_surface_cache.h @@ -0,0 +1,539 @@ +/********************************************************** + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: GPL-2.0 OR MIT + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + **********************************************************/ + +#ifndef VMW_SURFACE_CACHE_H +#define VMW_SURFACE_CACHE_H + +#include "device_include/svga3d_surfacedefs.h" + +#include <drm/vmwgfx_drm.h> + +static inline u32 clamped_umul32(u32 a, u32 b) +{ + uint64_t tmp = (uint64_t) a*b; + return (tmp > (uint64_t) ((u32) -1)) ? (u32) -1 : tmp; +} + +/** + * vmw_surface_get_desc - Look up the appropriate SVGA3dSurfaceDesc for the + * given format. + */ +static inline const SVGA3dSurfaceDesc * +vmw_surface_get_desc(SVGA3dSurfaceFormat format) +{ + if (format < ARRAY_SIZE(g_SVGA3dSurfaceDescs)) + return &g_SVGA3dSurfaceDescs[format]; + + return &g_SVGA3dSurfaceDescs[SVGA3D_FORMAT_INVALID]; +} + +/** + * vmw_surface_get_mip_size - Given a base level size and the mip level, + * compute the size of the mip level. + */ +static inline struct drm_vmw_size +vmw_surface_get_mip_size(struct drm_vmw_size base_level, u32 mip_level) +{ + struct drm_vmw_size size = { + .width = max_t(u32, base_level.width >> mip_level, 1), + .height = max_t(u32, base_level.height >> mip_level, 1), + .depth = max_t(u32, base_level.depth >> mip_level, 1) + }; + + return size; +} + +static inline void +vmw_surface_get_size_in_blocks(const SVGA3dSurfaceDesc *desc, + const struct drm_vmw_size *pixel_size, + SVGA3dSize *block_size) +{ + block_size->width = __KERNEL_DIV_ROUND_UP(pixel_size->width, + desc->blockSize.width); + block_size->height = __KERNEL_DIV_ROUND_UP(pixel_size->height, + desc->blockSize.height); + block_size->depth = __KERNEL_DIV_ROUND_UP(pixel_size->depth, + desc->blockSize.depth); +} + +static inline bool +vmw_surface_is_planar_surface(const SVGA3dSurfaceDesc *desc) +{ + return (desc->blockDesc & SVGA3DBLOCKDESC_PLANAR_YUV) != 0; +} + +static inline u32 +vmw_surface_calculate_pitch(const SVGA3dSurfaceDesc *desc, + const struct drm_vmw_size *size) +{ + u32 pitch; + SVGA3dSize blocks; + + vmw_surface_get_size_in_blocks(desc, size, &blocks); + + pitch = blocks.width * desc->pitchBytesPerBlock; + + return pitch; +} + +/** + * vmw_surface_get_image_buffer_size - Calculates 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. + */ +static inline u32 +vmw_surface_get_image_buffer_size(const SVGA3dSurfaceDesc *desc, + const struct drm_vmw_size *size, + u32 pitch) +{ + SVGA3dSize image_blocks; + u32 slice_size, total_size; + + vmw_surface_get_size_in_blocks(desc, size, &image_blocks); + + if (vmw_surface_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->bytesPerBlock); + return total_size; + } + + if (pitch == 0) + pitch = vmw_surface_calculate_pitch(desc, size); + + slice_size = clamped_umul32(image_blocks.height, pitch); + total_size = clamped_umul32(slice_size, image_blocks.depth); + + return total_size; +} + +/** + * vmw_surface_get_serialized_size - Get the serialized size for the image. + */ +static inline u32 +vmw_surface_get_serialized_size(SVGA3dSurfaceFormat format, + struct drm_vmw_size base_level_size, + u32 num_mip_levels, + u32 num_layers) +{ + const SVGA3dSurfaceDesc *desc = vmw_surface_get_desc(format); + u32 total_size = 0; + u32 mip; + + for (mip = 0; mip < num_mip_levels; mip++) { + struct drm_vmw_size size = + vmw_surface_get_mip_size(base_level_size, mip); + total_size += vmw_surface_get_image_buffer_size(desc, + &size, 0); + } + + return total_size * num_layers; +} + +/** + * vmw_surface_get_serialized_size_extended - Returns the number of bytes + * required for a surface with given parameters. Support for sample count. + */ +static inline u32 +vmw_surface_get_serialized_size_extended(SVGA3dSurfaceFormat format, + struct drm_vmw_size base_level_size, + u32 num_mip_levels, + u32 num_layers, + u32 num_samples) +{ + uint64_t total_size = + vmw_surface_get_serialized_size(format, + base_level_size, + num_mip_levels, + num_layers); + total_size *= max_t(u32, 1, num_samples); + + return min_t(uint64_t, total_size, (uint64_t)U32_MAX); +} + +/** + * vmw_surface_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 +vmw_surface_get_pixel_offset(SVGA3dSurfaceFormat format, + u32 width, u32 height, + u32 x, u32 y, u32 z) +{ + const SVGA3dSurfaceDesc *desc = vmw_surface_get_desc(format); + const u32 bw = desc->blockSize.width, bh = desc->blockSize.height; + const u32 bd = desc->blockSize.depth; + const u32 rowstride = __KERNEL_DIV_ROUND_UP(width, bw) * + desc->bytesPerBlock; + const u32 imgstride = __KERNEL_DIV_ROUND_UP(height, bh) * rowstride; + const u32 offset = (z / bd * imgstride + + y / bh * rowstride + + x / bw * desc->bytesPerBlock); + return offset; +} + +static inline u32 +vmw_surface_get_image_offset(SVGA3dSurfaceFormat format, + struct drm_vmw_size baseLevelSize, + u32 numMipLevels, + u32 face, + u32 mip) + +{ + u32 offset; + u32 mipChainBytes; + u32 mipChainBytesToLevel; + u32 i; + const SVGA3dSurfaceDesc *desc; + struct drm_vmw_size mipSize; + u32 bytes; + + desc = vmw_surface_get_desc(format); + + mipChainBytes = 0; + mipChainBytesToLevel = 0; + for (i = 0; i < numMipLevels; i++) { + mipSize = vmw_surface_get_mip_size(baseLevelSize, i); + bytes = vmw_surface_get_image_buffer_size(desc, &mipSize, 0); + mipChainBytes += bytes; + if (i < mip) + mipChainBytesToLevel += bytes; + } + + offset = mipChainBytes * face + mipChainBytesToLevel; + + return offset; +} + + +/** + * vmw_surface_is_gb_screen_target_format - Is the specified format usable as + * a ScreenTarget? + * (with just the GBObjects cap-bit + * set) + * @format: format to queried + * + * RETURNS: + * true if queried format is valid for screen targets + */ +static inline bool +vmw_surface_is_gb_screen_target_format(SVGA3dSurfaceFormat format) +{ + return (format == SVGA3D_X8R8G8B8 || + format == SVGA3D_A8R8G8B8 || + format == SVGA3D_R5G6B5 || + format == SVGA3D_X1R5G5B5 || + format == SVGA3D_A1R5G5B5 || + format == SVGA3D_P8); +} + + +/** + * vmw_surface_is_dx_screen_target_format - Is the specified format usable as + * a ScreenTarget? + * (with DX10 enabled) + * + * @format: format to queried + * + * Results: + * true if queried format is valid for screen targets + */ +static inline bool +vmw_surface_is_dx_screen_target_format(SVGA3dSurfaceFormat format) +{ + return (format == SVGA3D_R8G8B8A8_UNORM || + format == SVGA3D_B8G8R8A8_UNORM || + format == SVGA3D_B8G8R8X8_UNORM); +} + + +/** + * vmw_surface_is_screen_target_format - Is the specified format usable as a + * ScreenTarget? + * (for some combination of caps) + * + * @format: format to queried + * + * Results: + * true if queried format is valid for screen targets + */ +static inline bool +vmw_surface_is_screen_target_format(SVGA3dSurfaceFormat format) +{ + if (vmw_surface_is_gb_screen_target_format(format)) { + return true; + } + return vmw_surface_is_dx_screen_target_format(format); +} + +/** + * struct vmw_surface_mip - Mimpmap level information + * @bytes: Bytes required in the backing store of this mipmap level. + * @img_stride: Byte stride per image. + * @row_stride: Byte stride per block row. + * @size: The size of the mipmap. + */ +struct vmw_surface_mip { + size_t bytes; + size_t img_stride; + size_t row_stride; + struct drm_vmw_size size; + +}; + +/** + * struct vmw_surface_cache - Cached surface information + * @desc: Pointer to the surface descriptor + * @mip: Array of mipmap level information. Valid size is @num_mip_levels. + * @mip_chain_bytes: Bytes required in the backing store for the whole chain + * of mip levels. + * @sheet_bytes: Bytes required in the backing store for a sheet + * representing a single sample. + * @num_mip_levels: Valid size of the @mip array. Number of mipmap levels in + * a chain. + * @num_layers: Number of slices in an array texture or number of faces in + * a cubemap texture. + */ +struct vmw_surface_cache { + const SVGA3dSurfaceDesc *desc; + struct vmw_surface_mip mip[DRM_VMW_MAX_MIP_LEVELS]; + size_t mip_chain_bytes; + size_t sheet_bytes; + u32 num_mip_levels; + u32 num_layers; +}; + +/** + * struct vmw_surface_loc - Surface location + * @sheet: The multisample sheet. + * @sub_resource: Surface subresource. Defined as layer * num_mip_levels + + * mip_level. + * @x: X coordinate. + * @y: Y coordinate. + * @z: Z coordinate. + */ +struct vmw_surface_loc { + u32 sheet; + u32 sub_resource; + u32 x, y, z; +}; + +/** + * vmw_surface_subres - Compute the subresource from layer and mipmap. + * @cache: Surface layout data. + * @mip_level: The mipmap level. + * @layer: The surface layer (face or array slice). + * + * Return: The subresource. + */ +static inline u32 vmw_surface_subres(const struct vmw_surface_cache *cache, + u32 mip_level, u32 layer) +{ + return cache->num_mip_levels * layer + mip_level; +} + +/** + * vmw_surface_setup_cache - Build a surface cache entry + * @size: The surface base level dimensions. + * @format: The surface format. + * @num_mip_levels: Number of mipmap levels. + * @num_layers: Number of layers. + * @cache: Pointer to a struct vmw_surface_cach object to be filled in. + * + * Return: Zero on success, -EINVAL on invalid surface layout. + */ +static inline int vmw_surface_setup_cache(const struct drm_vmw_size *size, + SVGA3dSurfaceFormat format, + u32 num_mip_levels, + u32 num_layers, + u32 num_samples, + struct vmw_surface_cache *cache) +{ + const SVGA3dSurfaceDesc *desc; + u32 i; + + memset(cache, 0, sizeof(*cache)); + cache->desc = desc = vmw_surface_get_desc(format); + cache->num_mip_levels = num_mip_levels; + cache->num_layers = num_layers; + for (i = 0; i < cache->num_mip_levels; i++) { + struct vmw_surface_mip *mip = &cache->mip[i]; + + mip->size = vmw_surface_get_mip_size(*size, i); + mip->bytes = vmw_surface_get_image_buffer_size + (desc, &mip->size, 0); + mip->row_stride = + __KERNEL_DIV_ROUND_UP(mip->size.width, + desc->blockSize.width) * + desc->bytesPerBlock * num_samples; + if (!mip->row_stride) + goto invalid_dim; + + mip->img_stride = + __KERNEL_DIV_ROUND_UP(mip->size.height, + desc->blockSize.height) * + mip->row_stride; + if (!mip->img_stride) + goto invalid_dim; + + cache->mip_chain_bytes += mip->bytes; + } + cache->sheet_bytes = cache->mip_chain_bytes * num_layers; + if (!cache->sheet_bytes) + goto invalid_dim; + + return 0; + +invalid_dim: + VMW_DEBUG_USER("Invalid surface layout for dirty tracking.\n"); + return -EINVAL; +} + +/** + * vmw_surface_get_loc - Get a surface location from an offset into the + * backing store + * @cache: Surface layout data. + * @loc: Pointer to a struct vmw_surface_loc to be filled in. + * @offset: Offset into the surface backing store. + */ +static inline void +vmw_surface_get_loc(const struct vmw_surface_cache *cache, + struct vmw_surface_loc *loc, + size_t offset) +{ + const struct vmw_surface_mip *mip = &cache->mip[0]; + const SVGA3dSurfaceDesc *desc = cache->desc; + u32 layer; + int i; + + loc->sheet = offset / cache->sheet_bytes; + offset -= loc->sheet * cache->sheet_bytes; + + layer = offset / cache->mip_chain_bytes; + offset -= layer * cache->mip_chain_bytes; + for (i = 0; i < cache->num_mip_levels; ++i, ++mip) { + if (mip->bytes > offset) + break; + offset -= mip->bytes; + } + + loc->sub_resource = vmw_surface_subres(cache, i, layer); + loc->z = offset / mip->img_stride; + offset -= loc->z * mip->img_stride; + loc->z *= desc->blockSize.depth; + loc->y = offset / mip->row_stride; + offset -= loc->y * mip->row_stride; + loc->y *= desc->blockSize.height; + loc->x = offset / desc->bytesPerBlock; + loc->x *= desc->blockSize.width; +} + +/** + * vmw_surface_inc_loc - Clamp increment a surface location with one block + * size + * in each dimension. + * @loc: Pointer to a struct vmw_surface_loc to be incremented. + * + * When computing the size of a range as size = end - start, the range does not + * include the end element. However a location representing the last byte + * of a touched region in the backing store *is* included in the range. + * This function modifies such a location to match the end definition + * given as start + size which is the one used in a SVGA3dBox. + */ +static inline void +vmw_surface_inc_loc(const struct vmw_surface_cache *cache, + struct vmw_surface_loc *loc) +{ + const SVGA3dSurfaceDesc *desc = cache->desc; + u32 mip = loc->sub_resource % cache->num_mip_levels; + const struct drm_vmw_size *size = &cache->mip[mip].size; + + loc->sub_resource++; + loc->x += desc->blockSize.width; + if (loc->x > size->width) + loc->x = size->width; + loc->y += desc->blockSize.height; + if (loc->y > size->height) + loc->y = size->height; + loc->z += desc->blockSize.depth; + if (loc->z > size->depth) + loc->z = size->depth; +} + +/** + * vmw_surface_min_loc - The start location in a subresource + * @cache: Surface layout data. + * @sub_resource: The subresource. + * @loc: Pointer to a struct vmw_surface_loc to be filled in. + */ +static inline void +vmw_surface_min_loc(const struct vmw_surface_cache *cache, + u32 sub_resource, + struct vmw_surface_loc *loc) +{ + loc->sheet = 0; + loc->sub_resource = sub_resource; + loc->x = loc->y = loc->z = 0; +} + +/** + * vmw_surface_min_loc - The end location in a subresource + * @cache: Surface layout data. + * @sub_resource: The subresource. + * @loc: Pointer to a struct vmw_surface_loc to be filled in. + * + * Following the end definition given in vmw_surface_inc_loc(), + * Compute the end location of a surface subresource. + */ +static inline void +vmw_surface_max_loc(const struct vmw_surface_cache *cache, + u32 sub_resource, + struct vmw_surface_loc *loc) +{ + const struct drm_vmw_size *size; + u32 mip; + + loc->sheet = 0; + loc->sub_resource = sub_resource + 1; + mip = sub_resource % cache->num_mip_levels; + size = &cache->mip[mip].size; + loc->x = size->width; + loc->y = size->height; + loc->z = size->depth; +} + + +#endif /* VMW_SURFACE_CACHE_H */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_binding.c b/drivers/gpu/drm/vmwgfx/vmwgfx_binding.c new file mode 100644 index 0000000000..ae2de914eb --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_binding.c @@ -0,0 +1,1467 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2015 VMware, Inc., Palo Alto, CA., USA + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +/* + * This file implements the vmwgfx context binding manager, + * The sole reason for having to use this code is that vmware guest + * backed contexts can be swapped out to their backing mobs by the device + * at any time, also swapped in at any time. At swapin time, the device + * validates the context bindings to make sure they point to valid resources. + * It's this outside-of-drawcall validation (that can happen at any time), + * that makes this code necessary. + * + * We therefore need to kill any context bindings pointing to a resource + * when the resource is swapped out. Furthermore, if the vmwgfx driver has + * swapped out the context we can't swap it in again to kill bindings because + * of backing mob reservation lockdep violations, so as part of + * context swapout, also kill all bindings of a context, so that they are + * already killed if a resource to which a binding points + * needs to be swapped out. + * + * Note that a resource can be pointed to by bindings from multiple contexts, + * Therefore we can't easily protect this data by a per context mutex + * (unless we use deadlock-safe WW mutexes). So we use a global binding_mutex + * to protect all binding manager data. + * + * Finally, any association between a context and a global resource + * (surface, shader or even DX query) is conceptually a context binding that + * needs to be tracked by this code. + */ + +#include "vmwgfx_drv.h" +#include "vmwgfx_binding.h" +#include "device_include/svga3d_reg.h" + +#define VMW_BINDING_RT_BIT 0 +#define VMW_BINDING_PS_BIT 1 +#define VMW_BINDING_SO_T_BIT 2 +#define VMW_BINDING_VB_BIT 3 +#define VMW_BINDING_UAV_BIT 4 +#define VMW_BINDING_CS_UAV_BIT 5 +#define VMW_BINDING_NUM_BITS 6 + +#define VMW_BINDING_PS_SR_BIT 0 + +/** + * struct vmw_ctx_binding_state - per context binding state + * + * @dev_priv: Pointer to device private structure. + * @list: linked list of individual active bindings. + * @render_targets: Render target bindings. + * @texture_units: Texture units bindings. + * @ds_view: Depth-stencil view binding. + * @so_targets: StreamOutput target bindings. + * @vertex_buffers: Vertex buffer bindings. + * @index_buffer: Index buffer binding. + * @per_shader: Per shader-type bindings. + * @ua_views: UAV bindings. + * @so_state: StreamOutput bindings. + * @dirty: Bitmap tracking per binding-type changes that have not yet + * been emitted to the device. + * @dirty_vb: Bitmap tracking individual vertex buffer binding changes that + * have not yet been emitted to the device. + * @bind_cmd_buffer: Scratch space used to construct binding commands. + * @bind_cmd_count: Number of binding command data entries in @bind_cmd_buffer + * @bind_first_slot: Used together with @bind_cmd_buffer to indicate the + * device binding slot of the first command data entry in @bind_cmd_buffer. + * + * 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 vmw_private *dev_priv; + struct list_head list; + struct vmw_ctx_bindinfo_view render_targets[SVGA3D_RT_MAX]; + struct vmw_ctx_bindinfo_tex texture_units[SVGA3D_NUM_TEXTURE_UNITS]; + struct vmw_ctx_bindinfo_view ds_view; + struct vmw_ctx_bindinfo_so_target so_targets[SVGA3D_DX_MAX_SOTARGETS]; + struct vmw_ctx_bindinfo_vb vertex_buffers[SVGA3D_DX_MAX_VERTEXBUFFERS]; + struct vmw_ctx_bindinfo_ib index_buffer; + struct vmw_dx_shader_bindings per_shader[SVGA3D_NUM_SHADERTYPE]; + struct vmw_ctx_bindinfo_uav ua_views[VMW_MAX_UAV_BIND_TYPE]; + struct vmw_ctx_bindinfo_so so_state; + + unsigned long dirty; + DECLARE_BITMAP(dirty_vb, SVGA3D_DX_MAX_VERTEXBUFFERS); + + u32 bind_cmd_buffer[VMW_MAX_VIEW_BINDINGS]; + u32 bind_cmd_count; + u32 bind_first_slot; +}; + +static int vmw_binding_scrub_shader(struct vmw_ctx_bindinfo *bi, bool rebind); +static int vmw_binding_scrub_render_target(struct vmw_ctx_bindinfo *bi, + bool rebind); +static int vmw_binding_scrub_texture(struct vmw_ctx_bindinfo *bi, bool rebind); +static int vmw_binding_scrub_cb(struct vmw_ctx_bindinfo *bi, bool rebind); +static int vmw_binding_scrub_dx_rt(struct vmw_ctx_bindinfo *bi, bool rebind); +static int vmw_binding_scrub_sr(struct vmw_ctx_bindinfo *bi, bool rebind); +static int vmw_binding_scrub_so_target(struct vmw_ctx_bindinfo *bi, bool rebind); +static int vmw_binding_emit_dirty(struct vmw_ctx_binding_state *cbs); +static int vmw_binding_scrub_dx_shader(struct vmw_ctx_bindinfo *bi, + bool rebind); +static int vmw_binding_scrub_ib(struct vmw_ctx_bindinfo *bi, bool rebind); +static int vmw_binding_scrub_vb(struct vmw_ctx_bindinfo *bi, bool rebind); +static int vmw_binding_scrub_uav(struct vmw_ctx_bindinfo *bi, bool rebind); +static int vmw_binding_scrub_cs_uav(struct vmw_ctx_bindinfo *bi, bool rebind); +static int vmw_binding_scrub_so(struct vmw_ctx_bindinfo *bi, bool rebind); + +static void vmw_binding_build_asserts(void) __attribute__ ((unused)); + +typedef int (*vmw_scrub_func)(struct vmw_ctx_bindinfo *, bool); + +/** + * struct vmw_binding_info - Per binding type information for the binding + * manager + * + * @size: The size of the struct binding derived from a struct vmw_ctx_bindinfo. + * @offsets: array[shader_slot] of offsets to the array[slot] + * of struct bindings for the binding type. + * @scrub_func: Pointer to the scrub function for this binding type. + * + * Holds static information to help optimize the binding manager and avoid + * an excessive amount of switch statements. + */ +struct vmw_binding_info { + size_t size; + const size_t *offsets; + vmw_scrub_func scrub_func; +}; + +/* + * A number of static variables that help determine the scrub func and the + * location of the struct vmw_ctx_bindinfo slots for each binding type. + */ +static const size_t vmw_binding_shader_offsets[] = { + offsetof(struct vmw_ctx_binding_state, per_shader[0].shader), + offsetof(struct vmw_ctx_binding_state, per_shader[1].shader), + offsetof(struct vmw_ctx_binding_state, per_shader[2].shader), + offsetof(struct vmw_ctx_binding_state, per_shader[3].shader), + offsetof(struct vmw_ctx_binding_state, per_shader[4].shader), + offsetof(struct vmw_ctx_binding_state, per_shader[5].shader), +}; +static const size_t vmw_binding_rt_offsets[] = { + offsetof(struct vmw_ctx_binding_state, render_targets), +}; +static const size_t vmw_binding_tex_offsets[] = { + offsetof(struct vmw_ctx_binding_state, texture_units), +}; +static const size_t vmw_binding_cb_offsets[] = { + offsetof(struct vmw_ctx_binding_state, per_shader[0].const_buffers), + offsetof(struct vmw_ctx_binding_state, per_shader[1].const_buffers), + offsetof(struct vmw_ctx_binding_state, per_shader[2].const_buffers), + offsetof(struct vmw_ctx_binding_state, per_shader[3].const_buffers), + offsetof(struct vmw_ctx_binding_state, per_shader[4].const_buffers), + offsetof(struct vmw_ctx_binding_state, per_shader[5].const_buffers), +}; +static const size_t vmw_binding_dx_ds_offsets[] = { + offsetof(struct vmw_ctx_binding_state, ds_view), +}; +static const size_t vmw_binding_sr_offsets[] = { + offsetof(struct vmw_ctx_binding_state, per_shader[0].shader_res), + offsetof(struct vmw_ctx_binding_state, per_shader[1].shader_res), + offsetof(struct vmw_ctx_binding_state, per_shader[2].shader_res), + offsetof(struct vmw_ctx_binding_state, per_shader[3].shader_res), + offsetof(struct vmw_ctx_binding_state, per_shader[4].shader_res), + offsetof(struct vmw_ctx_binding_state, per_shader[5].shader_res), +}; +static const size_t vmw_binding_so_target_offsets[] = { + offsetof(struct vmw_ctx_binding_state, so_targets), +}; +static const size_t vmw_binding_vb_offsets[] = { + offsetof(struct vmw_ctx_binding_state, vertex_buffers), +}; +static const size_t vmw_binding_ib_offsets[] = { + offsetof(struct vmw_ctx_binding_state, index_buffer), +}; +static const size_t vmw_binding_uav_offsets[] = { + offsetof(struct vmw_ctx_binding_state, ua_views[0].views), +}; +static const size_t vmw_binding_cs_uav_offsets[] = { + offsetof(struct vmw_ctx_binding_state, ua_views[1].views), +}; +static const size_t vmw_binding_so_offsets[] = { + offsetof(struct vmw_ctx_binding_state, so_state), +}; + +static const struct vmw_binding_info vmw_binding_infos[] = { + [vmw_ctx_binding_shader] = { + .size = sizeof(struct vmw_ctx_bindinfo_shader), + .offsets = vmw_binding_shader_offsets, + .scrub_func = vmw_binding_scrub_shader}, + [vmw_ctx_binding_rt] = { + .size = sizeof(struct vmw_ctx_bindinfo_view), + .offsets = vmw_binding_rt_offsets, + .scrub_func = vmw_binding_scrub_render_target}, + [vmw_ctx_binding_tex] = { + .size = sizeof(struct vmw_ctx_bindinfo_tex), + .offsets = vmw_binding_tex_offsets, + .scrub_func = vmw_binding_scrub_texture}, + [vmw_ctx_binding_cb] = { + .size = sizeof(struct vmw_ctx_bindinfo_cb), + .offsets = vmw_binding_cb_offsets, + .scrub_func = vmw_binding_scrub_cb}, + [vmw_ctx_binding_dx_shader] = { + .size = sizeof(struct vmw_ctx_bindinfo_shader), + .offsets = vmw_binding_shader_offsets, + .scrub_func = vmw_binding_scrub_dx_shader}, + [vmw_ctx_binding_dx_rt] = { + .size = sizeof(struct vmw_ctx_bindinfo_view), + .offsets = vmw_binding_rt_offsets, + .scrub_func = vmw_binding_scrub_dx_rt}, + [vmw_ctx_binding_sr] = { + .size = sizeof(struct vmw_ctx_bindinfo_view), + .offsets = vmw_binding_sr_offsets, + .scrub_func = vmw_binding_scrub_sr}, + [vmw_ctx_binding_ds] = { + .size = sizeof(struct vmw_ctx_bindinfo_view), + .offsets = vmw_binding_dx_ds_offsets, + .scrub_func = vmw_binding_scrub_dx_rt}, + [vmw_ctx_binding_so_target] = { + .size = sizeof(struct vmw_ctx_bindinfo_so_target), + .offsets = vmw_binding_so_target_offsets, + .scrub_func = vmw_binding_scrub_so_target}, + [vmw_ctx_binding_vb] = { + .size = sizeof(struct vmw_ctx_bindinfo_vb), + .offsets = vmw_binding_vb_offsets, + .scrub_func = vmw_binding_scrub_vb}, + [vmw_ctx_binding_ib] = { + .size = sizeof(struct vmw_ctx_bindinfo_ib), + .offsets = vmw_binding_ib_offsets, + .scrub_func = vmw_binding_scrub_ib}, + [vmw_ctx_binding_uav] = { + .size = sizeof(struct vmw_ctx_bindinfo_view), + .offsets = vmw_binding_uav_offsets, + .scrub_func = vmw_binding_scrub_uav}, + [vmw_ctx_binding_cs_uav] = { + .size = sizeof(struct vmw_ctx_bindinfo_view), + .offsets = vmw_binding_cs_uav_offsets, + .scrub_func = vmw_binding_scrub_cs_uav}, + [vmw_ctx_binding_so] = { + .size = sizeof(struct vmw_ctx_bindinfo_so), + .offsets = vmw_binding_so_offsets, + .scrub_func = vmw_binding_scrub_so}, +}; + +/** + * vmw_cbs_context - Return a pointer to the context resource of a + * context binding state tracker. + * + * @cbs: The context binding state tracker. + * + * Provided there are any active bindings, this function will return an + * unreferenced pointer to the context resource that owns the context + * binding state tracker. If there are no active bindings, this function + * will return NULL. Note that the caller must somehow ensure that a reference + * is held on the context resource prior to calling this function. + */ +static const struct vmw_resource * +vmw_cbs_context(const struct vmw_ctx_binding_state *cbs) +{ + if (list_empty(&cbs->list)) + return NULL; + + return list_first_entry(&cbs->list, struct vmw_ctx_bindinfo, + ctx_list)->ctx; +} + +/** + * vmw_binding_loc - determine the struct vmw_ctx_bindinfo slot location. + * + * @cbs: Pointer to a struct vmw_ctx_binding state which holds the slot. + * @bt: The binding type. + * @shader_slot: The shader slot of the binding. If none, then set to 0. + * @slot: The slot of the binding. + */ +static struct vmw_ctx_bindinfo * +vmw_binding_loc(struct vmw_ctx_binding_state *cbs, + enum vmw_ctx_binding_type bt, u32 shader_slot, u32 slot) +{ + const struct vmw_binding_info *b = &vmw_binding_infos[bt]; + size_t offset = b->offsets[shader_slot] + b->size*slot; + + return (struct vmw_ctx_bindinfo *)((u8 *) cbs + offset); +} + +/** + * vmw_binding_drop: Stop tracking a context binding + * + * @bi: 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_binding_drop(struct vmw_ctx_bindinfo *bi) +{ + list_del(&bi->ctx_list); + if (!list_empty(&bi->res_list)) + list_del(&bi->res_list); + bi->ctx = NULL; +} + +/** + * vmw_binding_add: Start tracking a context binding + * + * @cbs: Pointer to the context binding state tracker. + * @bi: Information about the binding to track. + * @shader_slot: The shader slot of the binding. + * @slot: The slot of the binding. + * + * Starts tracking the binding in the context binding + * state structure @cbs. + */ +void vmw_binding_add(struct vmw_ctx_binding_state *cbs, + const struct vmw_ctx_bindinfo *bi, + u32 shader_slot, u32 slot) +{ + struct vmw_ctx_bindinfo *loc = + vmw_binding_loc(cbs, bi->bt, shader_slot, slot); + const struct vmw_binding_info *b = &vmw_binding_infos[bi->bt]; + + if (loc->ctx != NULL) + vmw_binding_drop(loc); + + memcpy(loc, bi, b->size); + loc->scrubbed = false; + list_add(&loc->ctx_list, &cbs->list); + INIT_LIST_HEAD(&loc->res_list); +} + +/** + * vmw_binding_cb_offset_update: Update the offset of a cb binding + * + * @cbs: Pointer to the context binding state tracker. + * @shader_slot: The shader slot of the binding. + * @slot: The slot of the binding. + * @offsetInBytes: The new offset of the binding. + * + * Updates the offset of an existing cb binding in the context binding + * state structure @cbs. + */ +void vmw_binding_cb_offset_update(struct vmw_ctx_binding_state *cbs, + u32 shader_slot, u32 slot, u32 offsetInBytes) +{ + struct vmw_ctx_bindinfo *loc = + vmw_binding_loc(cbs, vmw_ctx_binding_cb, shader_slot, slot); + struct vmw_ctx_bindinfo_cb *loc_cb = + (struct vmw_ctx_bindinfo_cb *)((u8 *) loc); + loc_cb->offset = offsetInBytes; +} + +/** + * vmw_binding_add_uav_index - Add UAV index for tracking. + * @cbs: Pointer to the context binding state tracker. + * @slot: UAV type to which bind this index. + * @index: The splice index to track. + */ +void vmw_binding_add_uav_index(struct vmw_ctx_binding_state *cbs, uint32 slot, + uint32 index) +{ + cbs->ua_views[slot].index = index; +} + +/** + * vmw_binding_transfer: Transfer a context binding tracking entry. + * + * @cbs: Pointer to the persistent context binding state tracker. + * @from: Staged binding info built during execbuf + * @bi: Information about the binding to track. + * + */ +static void vmw_binding_transfer(struct vmw_ctx_binding_state *cbs, + const struct vmw_ctx_binding_state *from, + const struct vmw_ctx_bindinfo *bi) +{ + size_t offset = (unsigned long)bi - (unsigned long)from; + struct vmw_ctx_bindinfo *loc = (struct vmw_ctx_bindinfo *) + ((unsigned long) cbs + offset); + + if (loc->ctx != NULL) { + WARN_ON(bi->scrubbed); + + vmw_binding_drop(loc); + } + + if (bi->res != NULL) { + memcpy(loc, bi, vmw_binding_infos[bi->bt].size); + list_add_tail(&loc->ctx_list, &cbs->list); + list_add_tail(&loc->res_list, &loc->res->binding_head); + } +} + +/** + * vmw_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. + */ +void vmw_binding_state_kill(struct vmw_ctx_binding_state *cbs) +{ + struct vmw_ctx_bindinfo *entry, *next; + + vmw_binding_state_scrub(cbs); + list_for_each_entry_safe(entry, next, &cbs->list, ctx_list) + vmw_binding_drop(entry); +} + +/** + * vmw_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. + */ +void vmw_binding_state_scrub(struct vmw_ctx_binding_state *cbs) +{ + struct vmw_ctx_bindinfo *entry; + + list_for_each_entry(entry, &cbs->list, ctx_list) { + if (!entry->scrubbed) { + (void) vmw_binding_infos[entry->bt].scrub_func + (entry, false); + entry->scrubbed = true; + } + } + + (void) vmw_binding_emit_dirty(cbs); +} + +/** + * vmw_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_binding_res_list_kill(struct list_head *head) +{ + struct vmw_ctx_bindinfo *entry, *next; + + vmw_binding_res_list_scrub(head); + list_for_each_entry_safe(entry, next, head, res_list) + vmw_binding_drop(entry); +} + +/** + * vmw_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_binding_res_list_scrub(struct list_head *head) +{ + struct vmw_ctx_bindinfo *entry; + + list_for_each_entry(entry, head, res_list) { + if (!entry->scrubbed) { + (void) vmw_binding_infos[entry->bt].scrub_func + (entry, false); + entry->scrubbed = true; + } + } + + list_for_each_entry(entry, head, res_list) { + struct vmw_ctx_binding_state *cbs = + vmw_context_binding_state(entry->ctx); + + (void) vmw_binding_emit_dirty(cbs); + } +} + + +/** + * vmw_binding_state_commit - Commit staged binding info + * + * @to: Staged binding info area to copy into to. + * @from: Staged binding info built during execbuf. + * + * Transfers binding info from a temporary structure + * (typically used by execbuf) to the persistent + * structure in the context. This can be done once commands have been + * submitted to hardware + */ +void vmw_binding_state_commit(struct vmw_ctx_binding_state *to, + struct vmw_ctx_binding_state *from) +{ + struct vmw_ctx_bindinfo *entry, *next; + + list_for_each_entry_safe(entry, next, &from->list, ctx_list) { + vmw_binding_transfer(to, from, entry); + vmw_binding_drop(entry); + } + + /* Also transfer uav splice indices */ + to->ua_views[0].index = from->ua_views[0].index; + to->ua_views[1].index = from->ua_views[1].index; +} + +/** + * vmw_binding_rebind_all - Rebind all scrubbed bindings of a context + * + * @cbs: Pointer to the context binding state tracker. + * + * Walks through the context binding list and rebinds all scrubbed + * resources. + */ +int vmw_binding_rebind_all(struct vmw_ctx_binding_state *cbs) +{ + struct vmw_ctx_bindinfo *entry; + int ret; + + list_for_each_entry(entry, &cbs->list, ctx_list) { + if (likely(!entry->scrubbed)) + continue; + + if ((entry->res == NULL || entry->res->id == + SVGA3D_INVALID_ID)) + continue; + + ret = vmw_binding_infos[entry->bt].scrub_func(entry, true); + if (unlikely(ret != 0)) + return ret; + + entry->scrubbed = false; + } + + return vmw_binding_emit_dirty(cbs); +} + +/** + * vmw_binding_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_binding_scrub_shader(struct vmw_ctx_bindinfo *bi, bool rebind) +{ + struct vmw_ctx_bindinfo_shader *binding = + container_of(bi, typeof(*binding), bi); + struct vmw_private *dev_priv = bi->ctx->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdSetShader body; + } *cmd; + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + 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 = binding->shader_slot + SVGA3D_SHADERTYPE_MIN; + cmd->body.shid = ((rebind) ? bi->res->id : SVGA3D_INVALID_ID); + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; +} + +/** + * vmw_binding_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_binding_scrub_render_target(struct vmw_ctx_bindinfo *bi, + bool rebind) +{ + struct vmw_ctx_bindinfo_view *binding = + container_of(bi, typeof(*binding), bi); + struct vmw_private *dev_priv = bi->ctx->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdSetRenderTarget body; + } *cmd; + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_SETRENDERTARGET; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = bi->ctx->id; + cmd->body.type = binding->slot; + cmd->body.target.sid = ((rebind) ? bi->res->id : SVGA3D_INVALID_ID); + cmd->body.target.face = 0; + cmd->body.target.mipmap = 0; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; +} + +/** + * vmw_binding_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_binding_scrub_texture(struct vmw_ctx_bindinfo *bi, + bool rebind) +{ + struct vmw_ctx_bindinfo_tex *binding = + container_of(bi, typeof(*binding), bi); + struct vmw_private *dev_priv = bi->ctx->dev_priv; + struct { + SVGA3dCmdHeader header; + struct { + SVGA3dCmdSetTextureState c; + SVGA3dTextureState s1; + } body; + } *cmd; + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + 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 = binding->texture_stage; + cmd->body.s1.name = SVGA3D_TS_BIND_TEXTURE; + cmd->body.s1.value = ((rebind) ? bi->res->id : SVGA3D_INVALID_ID); + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; +} + +/** + * vmw_binding_scrub_dx_shader - scrub a dx shader binding from a context. + * + * @bi: single binding information. + * @rebind: Whether to issue a bind instead of scrub command. + */ +static int vmw_binding_scrub_dx_shader(struct vmw_ctx_bindinfo *bi, bool rebind) +{ + struct vmw_ctx_bindinfo_shader *binding = + container_of(bi, typeof(*binding), bi); + struct vmw_private *dev_priv = bi->ctx->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetShader body; + } *cmd; + + cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), bi->ctx->id); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_SET_SHADER; + cmd->header.size = sizeof(cmd->body); + cmd->body.type = binding->shader_slot + SVGA3D_SHADERTYPE_MIN; + cmd->body.shaderId = ((rebind) ? bi->res->id : SVGA3D_INVALID_ID); + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; +} + +/** + * vmw_binding_scrub_cb - scrub a constant buffer binding from a context. + * + * @bi: single binding information. + * @rebind: Whether to issue a bind instead of scrub command. + */ +static int vmw_binding_scrub_cb(struct vmw_ctx_bindinfo *bi, bool rebind) +{ + struct vmw_ctx_bindinfo_cb *binding = + container_of(bi, typeof(*binding), bi); + struct vmw_private *dev_priv = bi->ctx->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetSingleConstantBuffer body; + } *cmd; + + cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), bi->ctx->id); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_SET_SINGLE_CONSTANT_BUFFER; + cmd->header.size = sizeof(cmd->body); + cmd->body.slot = binding->slot; + cmd->body.type = binding->shader_slot + SVGA3D_SHADERTYPE_MIN; + if (rebind) { + cmd->body.offsetInBytes = binding->offset; + cmd->body.sizeInBytes = binding->size; + cmd->body.sid = bi->res->id; + } else { + cmd->body.offsetInBytes = 0; + cmd->body.sizeInBytes = 0; + cmd->body.sid = SVGA3D_INVALID_ID; + } + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; +} + +/** + * vmw_collect_view_ids - Build view id data for a view binding command + * without checking which bindings actually need to be emitted + * + * @cbs: Pointer to the context's struct vmw_ctx_binding_state + * @biv: Pointer to where the binding info array is stored in @cbs + * @max_num: Maximum number of entries in the @bi array. + * + * Scans the @bi array for bindings and builds a buffer of view id data. + * Stops at the first non-existing binding in the @bi array. + * On output, @cbs->bind_cmd_count contains the number of bindings to be + * emitted, @cbs->bind_first_slot is set to zero, and @cbs->bind_cmd_buffer + * contains the command data. + */ +static void vmw_collect_view_ids(struct vmw_ctx_binding_state *cbs, + const struct vmw_ctx_bindinfo_view *biv, + u32 max_num) +{ + unsigned long i; + + cbs->bind_cmd_count = 0; + cbs->bind_first_slot = 0; + + for (i = 0; i < max_num; ++i, ++biv) { + if (!biv->bi.ctx) + break; + + cbs->bind_cmd_buffer[cbs->bind_cmd_count++] = + ((biv->bi.scrubbed) ? + SVGA3D_INVALID_ID : biv->bi.res->id); + } +} + +/** + * vmw_collect_dirty_view_ids - Build view id data for a view binding command + * + * @cbs: Pointer to the context's struct vmw_ctx_binding_state + * @bi: Pointer to where the binding info array is stored in @cbs + * @dirty: Bitmap indicating which bindings need to be emitted. + * @max_num: Maximum number of entries in the @bi array. + * + * Scans the @bi array for bindings that need to be emitted and + * builds a buffer of view id data. + * On output, @cbs->bind_cmd_count contains the number of bindings to be + * emitted, @cbs->bind_first_slot indicates the index of the first emitted + * binding, and @cbs->bind_cmd_buffer contains the command data. + */ +static void vmw_collect_dirty_view_ids(struct vmw_ctx_binding_state *cbs, + const struct vmw_ctx_bindinfo *bi, + unsigned long *dirty, + u32 max_num) +{ + const struct vmw_ctx_bindinfo_view *biv = + container_of(bi, struct vmw_ctx_bindinfo_view, bi); + unsigned long i, next_bit; + + cbs->bind_cmd_count = 0; + i = find_first_bit(dirty, max_num); + next_bit = i; + cbs->bind_first_slot = i; + + biv += i; + for (; i < max_num; ++i, ++biv) { + cbs->bind_cmd_buffer[cbs->bind_cmd_count++] = + ((!biv->bi.ctx || biv->bi.scrubbed) ? + SVGA3D_INVALID_ID : biv->bi.res->id); + + if (next_bit == i) { + next_bit = find_next_bit(dirty, max_num, i + 1); + if (next_bit >= max_num) + break; + } + } +} + +/** + * vmw_emit_set_sr - Issue delayed DX shader resource binding commands + * + * @cbs: Pointer to the context's struct vmw_ctx_binding_state + * @shader_slot: The shader slot of the binding. + */ +static int vmw_emit_set_sr(struct vmw_ctx_binding_state *cbs, + int shader_slot) +{ + const struct vmw_ctx_bindinfo *loc = + &cbs->per_shader[shader_slot].shader_res[0].bi; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetShaderResources body; + } *cmd; + size_t cmd_size, view_id_size; + const struct vmw_resource *ctx = vmw_cbs_context(cbs); + + vmw_collect_dirty_view_ids(cbs, loc, + cbs->per_shader[shader_slot].dirty_sr, + SVGA3D_DX_MAX_SRVIEWS); + if (cbs->bind_cmd_count == 0) + return 0; + + view_id_size = cbs->bind_cmd_count*sizeof(uint32); + cmd_size = sizeof(*cmd) + view_id_size; + cmd = VMW_CMD_CTX_RESERVE(ctx->dev_priv, cmd_size, ctx->id); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_SET_SHADER_RESOURCES; + cmd->header.size = sizeof(cmd->body) + view_id_size; + cmd->body.type = shader_slot + SVGA3D_SHADERTYPE_MIN; + cmd->body.startView = cbs->bind_first_slot; + + memcpy(&cmd[1], cbs->bind_cmd_buffer, view_id_size); + + vmw_cmd_commit(ctx->dev_priv, cmd_size); + bitmap_clear(cbs->per_shader[shader_slot].dirty_sr, + cbs->bind_first_slot, cbs->bind_cmd_count); + + return 0; +} + +/** + * vmw_emit_set_rt - Issue delayed DX rendertarget binding commands + * + * @cbs: Pointer to the context's struct vmw_ctx_binding_state + */ +static int vmw_emit_set_rt(struct vmw_ctx_binding_state *cbs) +{ + const struct vmw_ctx_bindinfo_view *loc = &cbs->render_targets[0]; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetRenderTargets body; + } *cmd; + size_t cmd_size, view_id_size; + const struct vmw_resource *ctx = vmw_cbs_context(cbs); + + vmw_collect_view_ids(cbs, loc, SVGA3D_DX_MAX_RENDER_TARGETS); + view_id_size = cbs->bind_cmd_count*sizeof(uint32); + cmd_size = sizeof(*cmd) + view_id_size; + cmd = VMW_CMD_CTX_RESERVE(ctx->dev_priv, cmd_size, ctx->id); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_SET_RENDERTARGETS; + cmd->header.size = sizeof(cmd->body) + view_id_size; + + if (cbs->ds_view.bi.ctx && !cbs->ds_view.bi.scrubbed) + cmd->body.depthStencilViewId = cbs->ds_view.bi.res->id; + else + cmd->body.depthStencilViewId = SVGA3D_INVALID_ID; + + memcpy(&cmd[1], cbs->bind_cmd_buffer, view_id_size); + + vmw_cmd_commit(ctx->dev_priv, cmd_size); + + return 0; + +} + +/** + * vmw_collect_so_targets - Build SVGA3dSoTarget data for a binding command + * without checking which bindings actually need to be emitted + * + * @cbs: Pointer to the context's struct vmw_ctx_binding_state + * @biso: Pointer to where the binding info array is stored in @cbs + * @max_num: Maximum number of entries in the @bi array. + * + * Scans the @bi array for bindings and builds a buffer of SVGA3dSoTarget data. + * Stops at the first non-existing binding in the @bi array. + * On output, @cbs->bind_cmd_count contains the number of bindings to be + * emitted, @cbs->bind_first_slot is set to zero, and @cbs->bind_cmd_buffer + * contains the command data. + */ +static void vmw_collect_so_targets(struct vmw_ctx_binding_state *cbs, + const struct vmw_ctx_bindinfo_so_target *biso, + u32 max_num) +{ + unsigned long i; + SVGA3dSoTarget *so_buffer = (SVGA3dSoTarget *) cbs->bind_cmd_buffer; + + cbs->bind_cmd_count = 0; + cbs->bind_first_slot = 0; + + for (i = 0; i < max_num; ++i, ++biso, ++so_buffer, + ++cbs->bind_cmd_count) { + if (!biso->bi.ctx) + break; + + if (!biso->bi.scrubbed) { + so_buffer->sid = biso->bi.res->id; + so_buffer->offset = biso->offset; + so_buffer->sizeInBytes = biso->size; + } else { + so_buffer->sid = SVGA3D_INVALID_ID; + so_buffer->offset = 0; + so_buffer->sizeInBytes = 0; + } + } +} + +/** + * vmw_emit_set_so_target - Issue delayed streamout binding commands + * + * @cbs: Pointer to the context's struct vmw_ctx_binding_state + */ +static int vmw_emit_set_so_target(struct vmw_ctx_binding_state *cbs) +{ + const struct vmw_ctx_bindinfo_so_target *loc = &cbs->so_targets[0]; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetSOTargets body; + } *cmd; + size_t cmd_size, so_target_size; + const struct vmw_resource *ctx = vmw_cbs_context(cbs); + + vmw_collect_so_targets(cbs, loc, SVGA3D_DX_MAX_SOTARGETS); + if (cbs->bind_cmd_count == 0) + return 0; + + so_target_size = cbs->bind_cmd_count*sizeof(SVGA3dSoTarget); + cmd_size = sizeof(*cmd) + so_target_size; + cmd = VMW_CMD_CTX_RESERVE(ctx->dev_priv, cmd_size, ctx->id); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_SET_SOTARGETS; + cmd->header.size = sizeof(cmd->body) + so_target_size; + memcpy(&cmd[1], cbs->bind_cmd_buffer, so_target_size); + + vmw_cmd_commit(ctx->dev_priv, cmd_size); + + return 0; + +} + +/** + * vmw_binding_emit_dirty_ps - Issue delayed per shader binding commands + * + * @cbs: Pointer to the context's struct vmw_ctx_binding_state + * + */ +static int vmw_binding_emit_dirty_ps(struct vmw_ctx_binding_state *cbs) +{ + struct vmw_dx_shader_bindings *sb = &cbs->per_shader[0]; + u32 i; + int ret; + + for (i = 0; i < SVGA3D_NUM_SHADERTYPE_DX10; ++i, ++sb) { + if (!test_bit(VMW_BINDING_PS_SR_BIT, &sb->dirty)) + continue; + + ret = vmw_emit_set_sr(cbs, i); + if (ret) + break; + + __clear_bit(VMW_BINDING_PS_SR_BIT, &sb->dirty); + } + + return 0; +} + +/** + * vmw_collect_dirty_vbs - Build SVGA3dVertexBuffer data for a + * SVGA3dCmdDXSetVertexBuffers command + * + * @cbs: Pointer to the context's struct vmw_ctx_binding_state + * @bi: Pointer to where the binding info array is stored in @cbs + * @dirty: Bitmap indicating which bindings need to be emitted. + * @max_num: Maximum number of entries in the @bi array. + * + * Scans the @bi array for bindings that need to be emitted and + * builds a buffer of SVGA3dVertexBuffer data. + * On output, @cbs->bind_cmd_count contains the number of bindings to be + * emitted, @cbs->bind_first_slot indicates the index of the first emitted + * binding, and @cbs->bind_cmd_buffer contains the command data. + */ +static void vmw_collect_dirty_vbs(struct vmw_ctx_binding_state *cbs, + const struct vmw_ctx_bindinfo *bi, + unsigned long *dirty, + u32 max_num) +{ + const struct vmw_ctx_bindinfo_vb *biv = + container_of(bi, struct vmw_ctx_bindinfo_vb, bi); + unsigned long i, next_bit; + SVGA3dVertexBuffer *vbs = (SVGA3dVertexBuffer *) &cbs->bind_cmd_buffer; + + cbs->bind_cmd_count = 0; + i = find_first_bit(dirty, max_num); + next_bit = i; + cbs->bind_first_slot = i; + + biv += i; + for (; i < max_num; ++i, ++biv, ++vbs) { + if (!biv->bi.ctx || biv->bi.scrubbed) { + vbs->sid = SVGA3D_INVALID_ID; + vbs->stride = 0; + vbs->offset = 0; + } else { + vbs->sid = biv->bi.res->id; + vbs->stride = biv->stride; + vbs->offset = biv->offset; + } + cbs->bind_cmd_count++; + if (next_bit == i) { + next_bit = find_next_bit(dirty, max_num, i + 1); + if (next_bit >= max_num) + break; + } + } +} + +/** + * vmw_emit_set_vb - Issue delayed vertex buffer binding commands + * + * @cbs: Pointer to the context's struct vmw_ctx_binding_state + * + */ +static int vmw_emit_set_vb(struct vmw_ctx_binding_state *cbs) +{ + const struct vmw_ctx_bindinfo *loc = + &cbs->vertex_buffers[0].bi; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetVertexBuffers body; + } *cmd; + size_t cmd_size, set_vb_size; + const struct vmw_resource *ctx = vmw_cbs_context(cbs); + + vmw_collect_dirty_vbs(cbs, loc, cbs->dirty_vb, + SVGA3D_DX_MAX_VERTEXBUFFERS); + if (cbs->bind_cmd_count == 0) + return 0; + + set_vb_size = cbs->bind_cmd_count*sizeof(SVGA3dVertexBuffer); + cmd_size = sizeof(*cmd) + set_vb_size; + cmd = VMW_CMD_CTX_RESERVE(ctx->dev_priv, cmd_size, ctx->id); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_SET_VERTEX_BUFFERS; + cmd->header.size = sizeof(cmd->body) + set_vb_size; + cmd->body.startBuffer = cbs->bind_first_slot; + + memcpy(&cmd[1], cbs->bind_cmd_buffer, set_vb_size); + + vmw_cmd_commit(ctx->dev_priv, cmd_size); + bitmap_clear(cbs->dirty_vb, + cbs->bind_first_slot, cbs->bind_cmd_count); + + return 0; +} + +static int vmw_emit_set_uav(struct vmw_ctx_binding_state *cbs) +{ + const struct vmw_ctx_bindinfo_view *loc = &cbs->ua_views[0].views[0]; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetUAViews body; + } *cmd; + size_t cmd_size, view_id_size; + const struct vmw_resource *ctx = vmw_cbs_context(cbs); + + vmw_collect_view_ids(cbs, loc, vmw_max_num_uavs(cbs->dev_priv)); + view_id_size = cbs->bind_cmd_count*sizeof(uint32); + cmd_size = sizeof(*cmd) + view_id_size; + cmd = VMW_CMD_CTX_RESERVE(ctx->dev_priv, cmd_size, ctx->id); + if (!cmd) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_SET_UA_VIEWS; + cmd->header.size = sizeof(cmd->body) + view_id_size; + + /* Splice index is specified user-space */ + cmd->body.uavSpliceIndex = cbs->ua_views[0].index; + + memcpy(&cmd[1], cbs->bind_cmd_buffer, view_id_size); + + vmw_cmd_commit(ctx->dev_priv, cmd_size); + + return 0; +} + +static int vmw_emit_set_cs_uav(struct vmw_ctx_binding_state *cbs) +{ + const struct vmw_ctx_bindinfo_view *loc = &cbs->ua_views[1].views[0]; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetCSUAViews body; + } *cmd; + size_t cmd_size, view_id_size; + const struct vmw_resource *ctx = vmw_cbs_context(cbs); + + vmw_collect_view_ids(cbs, loc, vmw_max_num_uavs(cbs->dev_priv)); + view_id_size = cbs->bind_cmd_count*sizeof(uint32); + cmd_size = sizeof(*cmd) + view_id_size; + cmd = VMW_CMD_CTX_RESERVE(ctx->dev_priv, cmd_size, ctx->id); + if (!cmd) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_SET_CS_UA_VIEWS; + cmd->header.size = sizeof(cmd->body) + view_id_size; + + /* Start index is specified user-space */ + cmd->body.startIndex = cbs->ua_views[1].index; + + memcpy(&cmd[1], cbs->bind_cmd_buffer, view_id_size); + + vmw_cmd_commit(ctx->dev_priv, cmd_size); + + return 0; +} + +/** + * vmw_binding_emit_dirty - Issue delayed binding commands + * + * @cbs: Pointer to the context's struct vmw_ctx_binding_state + * + * This function issues the delayed binding commands that arise from + * previous scrub / unscrub calls. These binding commands are typically + * commands that batch a number of bindings and therefore it makes sense + * to delay them. + */ +static int vmw_binding_emit_dirty(struct vmw_ctx_binding_state *cbs) +{ + int ret = 0; + unsigned long hit = 0; + + while ((hit = find_next_bit(&cbs->dirty, VMW_BINDING_NUM_BITS, hit)) + < VMW_BINDING_NUM_BITS) { + + switch (hit) { + case VMW_BINDING_RT_BIT: + ret = vmw_emit_set_rt(cbs); + break; + case VMW_BINDING_PS_BIT: + ret = vmw_binding_emit_dirty_ps(cbs); + break; + case VMW_BINDING_SO_T_BIT: + ret = vmw_emit_set_so_target(cbs); + break; + case VMW_BINDING_VB_BIT: + ret = vmw_emit_set_vb(cbs); + break; + case VMW_BINDING_UAV_BIT: + ret = vmw_emit_set_uav(cbs); + break; + case VMW_BINDING_CS_UAV_BIT: + ret = vmw_emit_set_cs_uav(cbs); + break; + default: + BUG(); + } + if (ret) + return ret; + + __clear_bit(hit, &cbs->dirty); + hit++; + } + + return 0; +} + +/** + * vmw_binding_scrub_sr - Schedule a dx shaderresource binding + * scrub from a context + * + * @bi: single binding information. + * @rebind: Whether to issue a bind instead of scrub command. + */ +static int vmw_binding_scrub_sr(struct vmw_ctx_bindinfo *bi, bool rebind) +{ + struct vmw_ctx_bindinfo_view *biv = + container_of(bi, struct vmw_ctx_bindinfo_view, bi); + struct vmw_ctx_binding_state *cbs = + vmw_context_binding_state(bi->ctx); + + __set_bit(biv->slot, cbs->per_shader[biv->shader_slot].dirty_sr); + __set_bit(VMW_BINDING_PS_SR_BIT, + &cbs->per_shader[biv->shader_slot].dirty); + __set_bit(VMW_BINDING_PS_BIT, &cbs->dirty); + + return 0; +} + +/** + * vmw_binding_scrub_dx_rt - Schedule a dx rendertarget binding + * scrub from a context + * + * @bi: single binding information. + * @rebind: Whether to issue a bind instead of scrub command. + */ +static int vmw_binding_scrub_dx_rt(struct vmw_ctx_bindinfo *bi, bool rebind) +{ + struct vmw_ctx_binding_state *cbs = + vmw_context_binding_state(bi->ctx); + + __set_bit(VMW_BINDING_RT_BIT, &cbs->dirty); + + return 0; +} + +/** + * vmw_binding_scrub_so_target - Schedule a dx streamoutput buffer binding + * scrub from a context + * + * @bi: single binding information. + * @rebind: Whether to issue a bind instead of scrub command. + */ +static int vmw_binding_scrub_so_target(struct vmw_ctx_bindinfo *bi, bool rebind) +{ + struct vmw_ctx_binding_state *cbs = + vmw_context_binding_state(bi->ctx); + + __set_bit(VMW_BINDING_SO_T_BIT, &cbs->dirty); + + return 0; +} + +/** + * vmw_binding_scrub_vb - Schedule a dx vertex buffer binding + * scrub from a context + * + * @bi: single binding information. + * @rebind: Whether to issue a bind instead of scrub command. + */ +static int vmw_binding_scrub_vb(struct vmw_ctx_bindinfo *bi, bool rebind) +{ + struct vmw_ctx_bindinfo_vb *bivb = + container_of(bi, struct vmw_ctx_bindinfo_vb, bi); + struct vmw_ctx_binding_state *cbs = + vmw_context_binding_state(bi->ctx); + + __set_bit(bivb->slot, cbs->dirty_vb); + __set_bit(VMW_BINDING_VB_BIT, &cbs->dirty); + + return 0; +} + +/** + * vmw_binding_scrub_ib - scrub a dx index buffer binding from a context + * + * @bi: single binding information. + * @rebind: Whether to issue a bind instead of scrub command. + */ +static int vmw_binding_scrub_ib(struct vmw_ctx_bindinfo *bi, bool rebind) +{ + struct vmw_ctx_bindinfo_ib *binding = + container_of(bi, typeof(*binding), bi); + struct vmw_private *dev_priv = bi->ctx->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetIndexBuffer body; + } *cmd; + + cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), bi->ctx->id); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_SET_INDEX_BUFFER; + cmd->header.size = sizeof(cmd->body); + if (rebind) { + cmd->body.sid = bi->res->id; + cmd->body.format = binding->format; + cmd->body.offset = binding->offset; + } else { + cmd->body.sid = SVGA3D_INVALID_ID; + cmd->body.format = 0; + cmd->body.offset = 0; + } + + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; +} + +static int vmw_binding_scrub_uav(struct vmw_ctx_bindinfo *bi, bool rebind) +{ + struct vmw_ctx_binding_state *cbs = vmw_context_binding_state(bi->ctx); + + __set_bit(VMW_BINDING_UAV_BIT, &cbs->dirty); + return 0; +} + +static int vmw_binding_scrub_cs_uav(struct vmw_ctx_bindinfo *bi, bool rebind) +{ + struct vmw_ctx_binding_state *cbs = vmw_context_binding_state(bi->ctx); + + __set_bit(VMW_BINDING_CS_UAV_BIT, &cbs->dirty); + return 0; +} + +/** + * vmw_binding_scrub_so - Scrub a streamoutput binding from context. + * @bi: Single binding information. + * @rebind: Whether to issue a bind instead of scrub command. + */ +static int vmw_binding_scrub_so(struct vmw_ctx_bindinfo *bi, bool rebind) +{ + struct vmw_ctx_bindinfo_so *binding = + container_of(bi, typeof(*binding), bi); + struct vmw_private *dev_priv = bi->ctx->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetStreamOutput body; + } *cmd; + + cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), bi->ctx->id); + if (!cmd) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_SET_STREAMOUTPUT; + cmd->header.size = sizeof(cmd->body); + cmd->body.soid = rebind ? bi->res->id : SVGA3D_INVALID_ID; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; +} + +/** + * vmw_binding_state_alloc - Allocate a struct vmw_ctx_binding_state. + * + * @dev_priv: Pointer to a device private structure. + * + * Returns a pointer to a newly allocated struct or an error pointer on error. + */ +struct vmw_ctx_binding_state * +vmw_binding_state_alloc(struct vmw_private *dev_priv) +{ + struct vmw_ctx_binding_state *cbs; + + cbs = vzalloc(sizeof(*cbs)); + if (!cbs) { + return ERR_PTR(-ENOMEM); + } + + cbs->dev_priv = dev_priv; + INIT_LIST_HEAD(&cbs->list); + + return cbs; +} + +/** + * vmw_binding_state_free - Free a struct vmw_ctx_binding_state. + * + * @cbs: Pointer to the struct vmw_ctx_binding_state to be freed. + */ +void vmw_binding_state_free(struct vmw_ctx_binding_state *cbs) +{ + vfree(cbs); +} + +/** + * vmw_binding_state_list - Get the binding list of a + * struct vmw_ctx_binding_state + * + * @cbs: Pointer to the struct vmw_ctx_binding_state + * + * Returns the binding list which can be used to traverse through the bindings + * and access the resource information of all bindings. + */ +struct list_head *vmw_binding_state_list(struct vmw_ctx_binding_state *cbs) +{ + return &cbs->list; +} + +/** + * vmw_binding_state_reset - clear a struct vmw_ctx_binding_state + * + * @cbs: Pointer to the struct vmw_ctx_binding_state to be cleared + * + * Drops all bindings registered in @cbs. No device binding actions are + * performed. + */ +void vmw_binding_state_reset(struct vmw_ctx_binding_state *cbs) +{ + struct vmw_ctx_bindinfo *entry, *next; + + list_for_each_entry_safe(entry, next, &cbs->list, ctx_list) + vmw_binding_drop(entry); +} + +/** + * vmw_binding_dirtying - Return whether a binding type is dirtying its resource + * @binding_type: The binding type + * + * Each time a resource is put on the validation list as the result of a + * context binding referencing it, we need to determine whether that resource + * will be dirtied (written to by the GPU) as a result of the corresponding + * GPU operation. Currently rendertarget-, depth-stencil-, stream-output-target + * and unordered access view bindings are capable of dirtying its resource. + * + * Return: Whether the binding type dirties the resource its binding points to. + */ +u32 vmw_binding_dirtying(enum vmw_ctx_binding_type binding_type) +{ + static u32 is_binding_dirtying[vmw_ctx_binding_max] = { + [vmw_ctx_binding_rt] = VMW_RES_DIRTY_SET, + [vmw_ctx_binding_dx_rt] = VMW_RES_DIRTY_SET, + [vmw_ctx_binding_ds] = VMW_RES_DIRTY_SET, + [vmw_ctx_binding_so_target] = VMW_RES_DIRTY_SET, + [vmw_ctx_binding_uav] = VMW_RES_DIRTY_SET, + [vmw_ctx_binding_cs_uav] = VMW_RES_DIRTY_SET, + }; + + /* Review this function as new bindings are added. */ + BUILD_BUG_ON(vmw_ctx_binding_max != 14); + return is_binding_dirtying[binding_type]; +} + +/* + * This function is unused at run-time, and only used to hold various build + * asserts important for code optimization assumptions. + */ +static void vmw_binding_build_asserts(void) +{ + BUILD_BUG_ON(SVGA3D_NUM_SHADERTYPE_DX10 != 3); + BUILD_BUG_ON(SVGA3D_DX_MAX_RENDER_TARGETS > SVGA3D_RT_MAX); + BUILD_BUG_ON(sizeof(uint32) != sizeof(u32)); + + /* + * struct vmw_ctx_binding_state::bind_cmd_buffer is used for various + * view id arrays. + */ + BUILD_BUG_ON(VMW_MAX_VIEW_BINDINGS < SVGA3D_RT_MAX); + BUILD_BUG_ON(VMW_MAX_VIEW_BINDINGS < SVGA3D_DX_MAX_SRVIEWS); + BUILD_BUG_ON(VMW_MAX_VIEW_BINDINGS < SVGA3D_DX_MAX_CONSTBUFFERS); + + /* + * struct vmw_ctx_binding_state::bind_cmd_buffer is used for + * u32 view ids, SVGA3dSoTargets and SVGA3dVertexBuffers + */ + BUILD_BUG_ON(SVGA3D_DX_MAX_SOTARGETS*sizeof(SVGA3dSoTarget) > + VMW_MAX_VIEW_BINDINGS*sizeof(u32)); + BUILD_BUG_ON(SVGA3D_DX_MAX_VERTEXBUFFERS*sizeof(SVGA3dVertexBuffer) > + VMW_MAX_VIEW_BINDINGS*sizeof(u32)); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_binding.h b/drivers/gpu/drm/vmwgfx/vmwgfx_binding.h new file mode 100644 index 0000000000..85b90f7d39 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_binding.h @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright 2015 VMware, Inc., Palo Alto, CA., USA + * + * 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_BINDING_H_ +#define _VMWGFX_BINDING_H_ + +#include <linux/list.h> + +#include "device_include/svga3d_reg.h" + +#define VMW_MAX_VIEW_BINDINGS 128 + +#define VMW_MAX_UAV_BIND_TYPE 2 + +struct vmw_private; +struct vmw_ctx_binding_state; + +/* + * 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_cb, + vmw_ctx_binding_dx_shader, + vmw_ctx_binding_dx_rt, + vmw_ctx_binding_sr, + vmw_ctx_binding_ds, + vmw_ctx_binding_so_target, + vmw_ctx_binding_vb, + vmw_ctx_binding_ib, + vmw_ctx_binding_uav, + vmw_ctx_binding_cs_uav, + vmw_ctx_binding_so, + vmw_ctx_binding_max +}; + +/** + * struct vmw_ctx_bindinfo - single binding metadata + * + * @ctx_list: List head for the context's list of bindings. + * @res_list: List head for a resource's list of bindings. + * @ctx: Non-refcounted pointer to the context that owns the binding. NULL + * indicates no binding present. + * @res: Non-refcounted pointer to the resource the binding points to. This + * is typically a surface or a view. + * @bt: Binding type. + * @scrubbed: Whether the binding has been scrubbed from the context. + */ +struct vmw_ctx_bindinfo { + struct list_head ctx_list; + struct list_head res_list; + struct vmw_resource *ctx; + struct vmw_resource *res; + enum vmw_ctx_binding_type bt; + bool scrubbed; +}; + +/** + * struct vmw_ctx_bindinfo_tex - texture stage binding metadata + * + * @bi: struct vmw_ctx_bindinfo we derive from. + * @texture_stage: Device data used to reconstruct binding command. + */ +struct vmw_ctx_bindinfo_tex { + struct vmw_ctx_bindinfo bi; + uint32 texture_stage; +}; + +/** + * struct vmw_ctx_bindinfo_shader - Shader binding metadata + * + * @bi: struct vmw_ctx_bindinfo we derive from. + * @shader_slot: Device data used to reconstruct binding command. + */ +struct vmw_ctx_bindinfo_shader { + struct vmw_ctx_bindinfo bi; + SVGA3dShaderType shader_slot; +}; + +/** + * struct vmw_ctx_bindinfo_cb - Constant buffer binding metadata + * + * @bi: struct vmw_ctx_bindinfo we derive from. + * @shader_slot: Device data used to reconstruct binding command. + * @offset: Device data used to reconstruct binding command. + * @size: Device data used to reconstruct binding command. + * @slot: Device data used to reconstruct binding command. + */ +struct vmw_ctx_bindinfo_cb { + struct vmw_ctx_bindinfo bi; + SVGA3dShaderType shader_slot; + uint32 offset; + uint32 size; + uint32 slot; +}; + +/** + * struct vmw_ctx_bindinfo_view - View binding metadata + * + * @bi: struct vmw_ctx_bindinfo we derive from. + * @shader_slot: Device data used to reconstruct binding command. + * @slot: Device data used to reconstruct binding command. + */ +struct vmw_ctx_bindinfo_view { + struct vmw_ctx_bindinfo bi; + SVGA3dShaderType shader_slot; + uint32 slot; +}; + +/** + * struct vmw_ctx_bindinfo_so_target - StreamOutput binding metadata + * + * @bi: struct vmw_ctx_bindinfo we derive from. + * @offset: Device data used to reconstruct binding command. + * @size: Device data used to reconstruct binding command. + * @slot: Device data used to reconstruct binding command. + */ +struct vmw_ctx_bindinfo_so_target { + struct vmw_ctx_bindinfo bi; + uint32 offset; + uint32 size; + uint32 slot; +}; + +/** + * struct vmw_ctx_bindinfo_vb - Vertex buffer binding metadata + * + * @bi: struct vmw_ctx_bindinfo we derive from. + * @offset: Device data used to reconstruct binding command. + * @stride: Device data used to reconstruct binding command. + * @slot: Device data used to reconstruct binding command. + */ +struct vmw_ctx_bindinfo_vb { + struct vmw_ctx_bindinfo bi; + uint32 offset; + uint32 stride; + uint32 slot; +}; + +/** + * struct vmw_ctx_bindinfo_ib - StreamOutput binding metadata + * + * @bi: struct vmw_ctx_bindinfo we derive from. + * @offset: Device data used to reconstruct binding command. + * @format: Device data used to reconstruct binding command. + */ +struct vmw_ctx_bindinfo_ib { + struct vmw_ctx_bindinfo bi; + uint32 offset; + uint32 format; +}; + +/** + * struct vmw_dx_shader_bindings - per shader type context binding state + * + * @shader: The shader binding for this shader type + * @const_buffer: Const buffer bindings for this shader type. + * @shader_res: Shader resource view bindings for this shader type. + * @dirty_sr: Bitmap tracking individual shader resource bindings changes + * that have not yet been emitted to the device. + * @dirty: Bitmap tracking per-binding type binding changes that have not + * yet been emitted to the device. + */ +struct vmw_dx_shader_bindings { + struct vmw_ctx_bindinfo_shader shader; + struct vmw_ctx_bindinfo_cb const_buffers[SVGA3D_DX_MAX_CONSTBUFFERS]; + struct vmw_ctx_bindinfo_view shader_res[SVGA3D_DX_MAX_SRVIEWS]; + DECLARE_BITMAP(dirty_sr, SVGA3D_DX_MAX_SRVIEWS); + unsigned long dirty; +}; + +/** + * struct vmw_ctx_bindinfo_uav - UAV context binding state. + * @views: UAV view bindings. + * @splice_index: The device splice index set by user-space. + */ +struct vmw_ctx_bindinfo_uav { + struct vmw_ctx_bindinfo_view views[SVGA3D_DX11_1_MAX_UAVIEWS]; + uint32 index; +}; + +/** + * struct vmw_ctx_bindinfo_so - Stream output binding metadata. + * @bi: struct vmw_ctx_bindinfo we derive from. + * @slot: Device data used to reconstruct binding command. + */ +struct vmw_ctx_bindinfo_so { + struct vmw_ctx_bindinfo bi; + uint32 slot; +}; + +extern void vmw_binding_add(struct vmw_ctx_binding_state *cbs, + const struct vmw_ctx_bindinfo *ci, + u32 shader_slot, u32 slot); +extern void vmw_binding_cb_offset_update(struct vmw_ctx_binding_state *cbs, + u32 shader_slot, u32 slot, u32 offsetInBytes); +extern void vmw_binding_add_uav_index(struct vmw_ctx_binding_state *cbs, + uint32 slot, uint32 splice_index); +extern void +vmw_binding_state_commit(struct vmw_ctx_binding_state *to, + struct vmw_ctx_binding_state *from); +extern void vmw_binding_res_list_kill(struct list_head *head); +extern void vmw_binding_res_list_scrub(struct list_head *head); +extern int vmw_binding_rebind_all(struct vmw_ctx_binding_state *cbs); +extern void vmw_binding_state_kill(struct vmw_ctx_binding_state *cbs); +extern void vmw_binding_state_scrub(struct vmw_ctx_binding_state *cbs); +extern struct vmw_ctx_binding_state * +vmw_binding_state_alloc(struct vmw_private *dev_priv); +extern void vmw_binding_state_free(struct vmw_ctx_binding_state *cbs); +extern struct list_head * +vmw_binding_state_list(struct vmw_ctx_binding_state *cbs); +extern void vmw_binding_state_reset(struct vmw_ctx_binding_state *cbs); +extern u32 vmw_binding_dirtying(enum vmw_ctx_binding_type binding_type); + + +#endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c new file mode 100644 index 0000000000..c52c7bf148 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2017 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 <linux/highmem.h> + +/* + * Template that implements find_first_diff() for a generic + * unsigned integer type. @size and return value are in bytes. + */ +#define VMW_FIND_FIRST_DIFF(_type) \ +static size_t vmw_find_first_diff_ ## _type \ + (const _type * dst, const _type * src, size_t size)\ +{ \ + size_t i; \ + \ + for (i = 0; i < size; i += sizeof(_type)) { \ + if (*dst++ != *src++) \ + break; \ + } \ + \ + return i; \ +} + + +/* + * Template that implements find_last_diff() for a generic + * unsigned integer type. Pointers point to the item following the + * *end* of the area to be examined. @size and return value are in + * bytes. + */ +#define VMW_FIND_LAST_DIFF(_type) \ +static ssize_t vmw_find_last_diff_ ## _type( \ + const _type * dst, const _type * src, size_t size) \ +{ \ + while (size) { \ + if (*--dst != *--src) \ + break; \ + \ + size -= sizeof(_type); \ + } \ + return size; \ +} + + +/* + * Instantiate find diff functions for relevant unsigned integer sizes, + * assuming that wider integers are faster (including aligning) up to the + * architecture native width, which is assumed to be 32 bit unless + * CONFIG_64BIT is defined. + */ +VMW_FIND_FIRST_DIFF(u8); +VMW_FIND_LAST_DIFF(u8); + +VMW_FIND_FIRST_DIFF(u16); +VMW_FIND_LAST_DIFF(u16); + +VMW_FIND_FIRST_DIFF(u32); +VMW_FIND_LAST_DIFF(u32); + +#ifdef CONFIG_64BIT +VMW_FIND_FIRST_DIFF(u64); +VMW_FIND_LAST_DIFF(u64); +#endif + + +/* We use size aligned copies. This computes (addr - align(addr)) */ +#define SPILL(_var, _type) ((unsigned long) _var & (sizeof(_type) - 1)) + + +/* + * Template to compute find_first_diff() for a certain integer type + * including a head copy for alignment, and adjustment of parameters + * for tail find or increased resolution find using an unsigned integer find + * of smaller width. If finding is complete, and resolution is sufficient, + * the macro executes a return statement. Otherwise it falls through. + */ +#define VMW_TRY_FIND_FIRST_DIFF(_type) \ +do { \ + unsigned int spill = SPILL(dst, _type); \ + size_t diff_offs; \ + \ + if (spill && spill == SPILL(src, _type) && \ + sizeof(_type) - spill <= size) { \ + spill = sizeof(_type) - spill; \ + diff_offs = vmw_find_first_diff_u8(dst, src, spill); \ + if (diff_offs < spill) \ + return round_down(offset + diff_offs, granularity); \ + \ + dst += spill; \ + src += spill; \ + size -= spill; \ + offset += spill; \ + spill = 0; \ + } \ + if (!spill && !SPILL(src, _type)) { \ + size_t to_copy = size & ~(sizeof(_type) - 1); \ + \ + diff_offs = vmw_find_first_diff_ ## _type \ + ((_type *) dst, (_type *) src, to_copy); \ + if (diff_offs >= size || granularity == sizeof(_type)) \ + return (offset + diff_offs); \ + \ + dst += diff_offs; \ + src += diff_offs; \ + size -= diff_offs; \ + offset += diff_offs; \ + } \ +} while (0) \ + + +/** + * vmw_find_first_diff - find the first difference between dst and src + * + * @dst: The destination address + * @src: The source address + * @size: Number of bytes to compare + * @granularity: The granularity needed for the return value in bytes. + * return: The offset from find start where the first difference was + * encountered in bytes. If no difference was found, the function returns + * a value >= @size. + */ +static size_t vmw_find_first_diff(const u8 *dst, const u8 *src, size_t size, + size_t granularity) +{ + size_t offset = 0; + + /* + * Try finding with large integers if alignment allows, or we can + * fix it. Fall through if we need better resolution or alignment + * was bad. + */ +#ifdef CONFIG_64BIT + VMW_TRY_FIND_FIRST_DIFF(u64); +#endif + VMW_TRY_FIND_FIRST_DIFF(u32); + VMW_TRY_FIND_FIRST_DIFF(u16); + + return round_down(offset + vmw_find_first_diff_u8(dst, src, size), + granularity); +} + + +/* + * Template to compute find_last_diff() for a certain integer type + * including a tail copy for alignment, and adjustment of parameters + * for head find or increased resolution find using an unsigned integer find + * of smaller width. If finding is complete, and resolution is sufficient, + * the macro executes a return statement. Otherwise it falls through. + */ +#define VMW_TRY_FIND_LAST_DIFF(_type) \ +do { \ + unsigned int spill = SPILL(dst, _type); \ + ssize_t location; \ + ssize_t diff_offs; \ + \ + if (spill && spill <= size && spill == SPILL(src, _type)) { \ + diff_offs = vmw_find_last_diff_u8(dst, src, spill); \ + if (diff_offs) { \ + location = size - spill + diff_offs - 1; \ + return round_down(location, granularity); \ + } \ + \ + dst -= spill; \ + src -= spill; \ + size -= spill; \ + spill = 0; \ + } \ + if (!spill && !SPILL(src, _type)) { \ + size_t to_copy = round_down(size, sizeof(_type)); \ + \ + diff_offs = vmw_find_last_diff_ ## _type \ + ((_type *) dst, (_type *) src, to_copy); \ + location = size - to_copy + diff_offs - sizeof(_type); \ + if (location < 0 || granularity == sizeof(_type)) \ + return location; \ + \ + dst -= to_copy - diff_offs; \ + src -= to_copy - diff_offs; \ + size -= to_copy - diff_offs; \ + } \ +} while (0) + + +/** + * vmw_find_last_diff - find the last difference between dst and src + * + * @dst: The destination address + * @src: The source address + * @size: Number of bytes to compare + * @granularity: The granularity needed for the return value in bytes. + * return: The offset from find start where the last difference was + * encountered in bytes, or a negative value if no difference was found. + */ +static ssize_t vmw_find_last_diff(const u8 *dst, const u8 *src, size_t size, + size_t granularity) +{ + dst += size; + src += size; + +#ifdef CONFIG_64BIT + VMW_TRY_FIND_LAST_DIFF(u64); +#endif + VMW_TRY_FIND_LAST_DIFF(u32); + VMW_TRY_FIND_LAST_DIFF(u16); + + return round_down(vmw_find_last_diff_u8(dst, src, size) - 1, + granularity); +} + + +/** + * vmw_memcpy - A wrapper around kernel memcpy with allowing to plug it into a + * struct vmw_diff_cpy. + * + * @diff: The struct vmw_diff_cpy closure argument (unused). + * @dest: The copy destination. + * @src: The copy source. + * @n: Number of bytes to copy. + */ +void vmw_memcpy(struct vmw_diff_cpy *diff, u8 *dest, const u8 *src, size_t n) +{ + memcpy(dest, src, n); +} + + +/** + * vmw_adjust_rect - Adjust rectangle coordinates for newly found difference + * + * @diff: The struct vmw_diff_cpy used to track the modified bounding box. + * @diff_offs: The offset from @diff->line_offset where the difference was + * found. + */ +static void vmw_adjust_rect(struct vmw_diff_cpy *diff, size_t diff_offs) +{ + size_t offs = (diff_offs + diff->line_offset) / diff->cpp; + struct drm_rect *rect = &diff->rect; + + rect->x1 = min_t(int, rect->x1, offs); + rect->x2 = max_t(int, rect->x2, offs + 1); + rect->y1 = min_t(int, rect->y1, diff->line); + rect->y2 = max_t(int, rect->y2, diff->line + 1); +} + +/** + * vmw_diff_memcpy - memcpy that creates a bounding box of modified content. + * + * @diff: The struct vmw_diff_cpy used to track the modified bounding box. + * @dest: The copy destination. + * @src: The copy source. + * @n: Number of bytes to copy. + * + * In order to correctly track the modified content, the field @diff->line must + * be pre-loaded with the current line number, the field @diff->line_offset must + * be pre-loaded with the line offset in bytes where the copy starts, and + * finally the field @diff->cpp need to be preloaded with the number of bytes + * per unit in the horizontal direction of the area we're examining. + * Typically bytes per pixel. + * This is needed to know the needed granularity of the difference computing + * operations. A higher cpp generally leads to faster execution at the cost of + * bounding box width precision. + */ +void vmw_diff_memcpy(struct vmw_diff_cpy *diff, u8 *dest, const u8 *src, + size_t n) +{ + ssize_t csize, byte_len; + + if (WARN_ON_ONCE(round_down(n, diff->cpp) != n)) + return; + + /* TODO: Possibly use a single vmw_find_first_diff per line? */ + csize = vmw_find_first_diff(dest, src, n, diff->cpp); + if (csize < n) { + vmw_adjust_rect(diff, csize); + byte_len = diff->cpp; + + /* + * Starting from where first difference was found, find + * location of last difference, and then copy. + */ + diff->line_offset += csize; + dest += csize; + src += csize; + n -= csize; + csize = vmw_find_last_diff(dest, src, n, diff->cpp); + if (csize >= 0) { + byte_len += csize; + vmw_adjust_rect(diff, csize); + } + memcpy(dest, src, byte_len); + } + diff->line_offset += n; +} + +/** + * struct vmw_bo_blit_line_data - Convenience argument to vmw_bo_cpu_blit_line + * + * @mapped_dst: Already mapped destination page index in @dst_pages. + * @dst_addr: Kernel virtual address of mapped destination page. + * @dst_pages: Array of destination bo pages. + * @dst_num_pages: Number of destination bo pages. + * @dst_prot: Destination bo page protection. + * @mapped_src: Already mapped source page index in @dst_pages. + * @src_addr: Kernel virtual address of mapped source page. + * @src_pages: Array of source bo pages. + * @src_num_pages: Number of source bo pages. + * @src_prot: Source bo page protection. + * @diff: Struct vmw_diff_cpy, in the end forwarded to the memcpy routine. + */ +struct vmw_bo_blit_line_data { + u32 mapped_dst; + u8 *dst_addr; + struct page **dst_pages; + u32 dst_num_pages; + pgprot_t dst_prot; + u32 mapped_src; + u8 *src_addr; + struct page **src_pages; + u32 src_num_pages; + pgprot_t src_prot; + struct vmw_diff_cpy *diff; +}; + +/** + * vmw_bo_cpu_blit_line - Blit part of a line from one bo to another. + * + * @d: Blit data as described above. + * @dst_offset: Destination copy start offset from start of bo. + * @src_offset: Source copy start offset from start of bo. + * @bytes_to_copy: Number of bytes to copy in this line. + */ +static int vmw_bo_cpu_blit_line(struct vmw_bo_blit_line_data *d, + u32 dst_offset, + u32 src_offset, + u32 bytes_to_copy) +{ + struct vmw_diff_cpy *diff = d->diff; + + while (bytes_to_copy) { + u32 copy_size = bytes_to_copy; + u32 dst_page = dst_offset >> PAGE_SHIFT; + u32 src_page = src_offset >> PAGE_SHIFT; + u32 dst_page_offset = dst_offset & ~PAGE_MASK; + u32 src_page_offset = src_offset & ~PAGE_MASK; + bool unmap_dst = d->dst_addr && dst_page != d->mapped_dst; + bool unmap_src = d->src_addr && (src_page != d->mapped_src || + unmap_dst); + + copy_size = min_t(u32, copy_size, PAGE_SIZE - dst_page_offset); + copy_size = min_t(u32, copy_size, PAGE_SIZE - src_page_offset); + + if (unmap_src) { + kunmap_atomic(d->src_addr); + d->src_addr = NULL; + } + + if (unmap_dst) { + kunmap_atomic(d->dst_addr); + d->dst_addr = NULL; + } + + if (!d->dst_addr) { + if (WARN_ON_ONCE(dst_page >= d->dst_num_pages)) + return -EINVAL; + + d->dst_addr = + kmap_atomic_prot(d->dst_pages[dst_page], + d->dst_prot); + if (!d->dst_addr) + return -ENOMEM; + + d->mapped_dst = dst_page; + } + + if (!d->src_addr) { + if (WARN_ON_ONCE(src_page >= d->src_num_pages)) + return -EINVAL; + + d->src_addr = + kmap_atomic_prot(d->src_pages[src_page], + d->src_prot); + if (!d->src_addr) + return -ENOMEM; + + d->mapped_src = src_page; + } + diff->do_cpy(diff, d->dst_addr + dst_page_offset, + d->src_addr + src_page_offset, copy_size); + + bytes_to_copy -= copy_size; + dst_offset += copy_size; + src_offset += copy_size; + } + + return 0; +} + +/** + * vmw_bo_cpu_blit - in-kernel cpu blit. + * + * @dst: Destination buffer object. + * @dst_offset: Destination offset of blit start in bytes. + * @dst_stride: Destination stride in bytes. + * @src: Source buffer object. + * @src_offset: Source offset of blit start in bytes. + * @src_stride: Source stride in bytes. + * @w: Width of blit. + * @h: Height of blit. + * @diff: The struct vmw_diff_cpy used to track the modified bounding box. + * return: Zero on success. Negative error value on failure. Will print out + * kernel warnings on caller bugs. + * + * Performs a CPU blit from one buffer object to another avoiding a full + * bo vmap which may exhaust- or fragment vmalloc space. + * On supported architectures (x86), we're using kmap_atomic which avoids + * cross-processor TLB- and cache flushes and may, on non-HIGHMEM systems + * reference already set-up mappings. + * + * Neither of the buffer objects may be placed in PCI memory + * (Fixed memory in TTM terminology) when using this function. + */ +int vmw_bo_cpu_blit(struct ttm_buffer_object *dst, + u32 dst_offset, u32 dst_stride, + struct ttm_buffer_object *src, + u32 src_offset, u32 src_stride, + u32 w, u32 h, + struct vmw_diff_cpy *diff) +{ + struct ttm_operation_ctx ctx = { + .interruptible = false, + .no_wait_gpu = false + }; + u32 j, initial_line = dst_offset / dst_stride; + struct vmw_bo_blit_line_data d; + int ret = 0; + + /* Buffer objects need to be either pinned or reserved: */ + if (!(dst->pin_count)) + dma_resv_assert_held(dst->base.resv); + if (!(src->pin_count)) + dma_resv_assert_held(src->base.resv); + + if (!ttm_tt_is_populated(dst->ttm)) { + ret = dst->bdev->funcs->ttm_tt_populate(dst->bdev, dst->ttm, &ctx); + if (ret) + return ret; + } + + if (!ttm_tt_is_populated(src->ttm)) { + ret = src->bdev->funcs->ttm_tt_populate(src->bdev, src->ttm, &ctx); + if (ret) + return ret; + } + + d.mapped_dst = 0; + d.mapped_src = 0; + d.dst_addr = NULL; + d.src_addr = NULL; + d.dst_pages = dst->ttm->pages; + d.src_pages = src->ttm->pages; + d.dst_num_pages = PFN_UP(dst->resource->size); + d.src_num_pages = PFN_UP(src->resource->size); + d.dst_prot = ttm_io_prot(dst, dst->resource, PAGE_KERNEL); + d.src_prot = ttm_io_prot(src, src->resource, PAGE_KERNEL); + d.diff = diff; + + for (j = 0; j < h; ++j) { + diff->line = j + initial_line; + diff->line_offset = dst_offset % dst_stride; + ret = vmw_bo_cpu_blit_line(&d, dst_offset, src_offset, w); + if (ret) + goto out; + + dst_offset += dst_stride; + src_offset += src_stride; + } +out: + if (d.src_addr) + kunmap_atomic(d.src_addr); + if (d.dst_addr) + kunmap_atomic(d.dst_addr); + + return ret; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c new file mode 100644 index 0000000000..2bfac3aad7 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c @@ -0,0 +1,838 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright © 2011-2023 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_bo.h" +#include "vmwgfx_drv.h" + + +#include <drm/ttm/ttm_placement.h> + +static void vmw_bo_release(struct vmw_bo *vbo) +{ + WARN_ON(vbo->tbo.base.funcs && + kref_read(&vbo->tbo.base.refcount) != 0); + vmw_bo_unmap(vbo); + drm_gem_object_release(&vbo->tbo.base); +} + +/** + * vmw_bo_free - vmw_bo destructor + * + * @bo: Pointer to the embedded struct ttm_buffer_object + */ +static void vmw_bo_free(struct ttm_buffer_object *bo) +{ + struct vmw_bo *vbo = to_vmw_bo(&bo->base); + + WARN_ON(vbo->dirty); + WARN_ON(!RB_EMPTY_ROOT(&vbo->res_tree)); + vmw_bo_release(vbo); + kfree(vbo); +} + +/** + * vmw_bo_pin_in_placement - Validate a buffer to placement. + * + * @dev_priv: Driver private. + * @buf: DMA buffer to move. + * @placement: The placement to pin it. + * @interruptible: Use interruptible wait. + * Return: Zero on success, Negative error code on failure. In particular + * -ERESTARTSYS if interrupted by a signal + */ +static int vmw_bo_pin_in_placement(struct vmw_private *dev_priv, + struct vmw_bo *buf, + struct ttm_placement *placement, + bool interruptible) +{ + struct ttm_operation_ctx ctx = {interruptible, false }; + struct ttm_buffer_object *bo = &buf->tbo; + int ret; + + vmw_execbuf_release_pinned_bo(dev_priv); + + ret = ttm_bo_reserve(bo, interruptible, false, NULL); + if (unlikely(ret != 0)) + goto err; + + ret = ttm_bo_validate(bo, placement, &ctx); + if (!ret) + vmw_bo_pin_reserved(buf, true); + + ttm_bo_unreserve(bo); +err: + return ret; +} + + +/** + * vmw_bo_pin_in_vram_or_gmr - Move a buffer to vram or gmr. + * + * This function takes the reservation_sem in write mode. + * Flushes and unpins the query bo to avoid failures. + * + * @dev_priv: Driver private. + * @buf: DMA buffer to move. + * @interruptible: Use interruptible wait. + * Return: Zero on success, Negative error code on failure. In particular + * -ERESTARTSYS if interrupted by a signal + */ +int vmw_bo_pin_in_vram_or_gmr(struct vmw_private *dev_priv, + struct vmw_bo *buf, + bool interruptible) +{ + struct ttm_operation_ctx ctx = {interruptible, false }; + struct ttm_buffer_object *bo = &buf->tbo; + int ret; + + vmw_execbuf_release_pinned_bo(dev_priv); + + ret = ttm_bo_reserve(bo, interruptible, false, NULL); + if (unlikely(ret != 0)) + goto err; + + vmw_bo_placement_set(buf, + VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM, + VMW_BO_DOMAIN_GMR); + ret = ttm_bo_validate(bo, &buf->placement, &ctx); + if (likely(ret == 0) || ret == -ERESTARTSYS) + goto out_unreserve; + + vmw_bo_placement_set(buf, + VMW_BO_DOMAIN_VRAM, + VMW_BO_DOMAIN_VRAM); + ret = ttm_bo_validate(bo, &buf->placement, &ctx); + +out_unreserve: + if (!ret) + vmw_bo_pin_reserved(buf, true); + + ttm_bo_unreserve(bo); +err: + return ret; +} + + +/** + * vmw_bo_pin_in_vram - Move a buffer to vram. + * + * This function takes the reservation_sem in write mode. + * Flushes and unpins the query bo to avoid failures. + * + * @dev_priv: Driver private. + * @buf: DMA buffer to move. + * @interruptible: Use interruptible wait. + * Return: Zero on success, Negative error code on failure. In particular + * -ERESTARTSYS if interrupted by a signal + */ +int vmw_bo_pin_in_vram(struct vmw_private *dev_priv, + struct vmw_bo *buf, + bool interruptible) +{ + return vmw_bo_pin_in_placement(dev_priv, buf, &vmw_vram_placement, + interruptible); +} + + +/** + * vmw_bo_pin_in_start_of_vram - Move a buffer to start of vram. + * + * This function takes the reservation_sem in write mode. + * Flushes and unpins the query bo to avoid failures. + * + * @dev_priv: Driver private. + * @buf: DMA buffer to pin. + * @interruptible: Use interruptible wait. + * Return: Zero on success, Negative error code on failure. In particular + * -ERESTARTSYS if interrupted by a signal + */ +int vmw_bo_pin_in_start_of_vram(struct vmw_private *dev_priv, + struct vmw_bo *buf, + bool interruptible) +{ + struct ttm_operation_ctx ctx = {interruptible, false }; + struct ttm_buffer_object *bo = &buf->tbo; + int ret = 0; + + vmw_execbuf_release_pinned_bo(dev_priv); + ret = ttm_bo_reserve(bo, interruptible, false, NULL); + if (unlikely(ret != 0)) + goto err_unlock; + + /* + * Is this buffer already in vram but not at the start of it? + * In that case, evict it first because TTM isn't good at handling + * that situation. + */ + if (bo->resource->mem_type == TTM_PL_VRAM && + bo->resource->start < PFN_UP(bo->resource->size) && + bo->resource->start > 0 && + buf->tbo.pin_count == 0) { + ctx.interruptible = false; + vmw_bo_placement_set(buf, + VMW_BO_DOMAIN_SYS, + VMW_BO_DOMAIN_SYS); + (void)ttm_bo_validate(bo, &buf->placement, &ctx); + } + + vmw_bo_placement_set(buf, + VMW_BO_DOMAIN_VRAM, + VMW_BO_DOMAIN_VRAM); + buf->places[0].lpfn = PFN_UP(bo->resource->size); + ret = ttm_bo_validate(bo, &buf->placement, &ctx); + + /* For some reason we didn't end up at the start of vram */ + WARN_ON(ret == 0 && bo->resource->start != 0); + if (!ret) + vmw_bo_pin_reserved(buf, true); + + ttm_bo_unreserve(bo); +err_unlock: + + return ret; +} + + +/** + * vmw_bo_unpin - Unpin the buffer given buffer, does not move the buffer. + * + * This function takes the reservation_sem in write mode. + * + * @dev_priv: Driver private. + * @buf: DMA buffer to unpin. + * @interruptible: Use interruptible wait. + * Return: Zero on success, Negative error code on failure. In particular + * -ERESTARTSYS if interrupted by a signal + */ +int vmw_bo_unpin(struct vmw_private *dev_priv, + struct vmw_bo *buf, + bool interruptible) +{ + struct ttm_buffer_object *bo = &buf->tbo; + int ret; + + ret = ttm_bo_reserve(bo, interruptible, false, NULL); + if (unlikely(ret != 0)) + goto err; + + vmw_bo_pin_reserved(buf, false); + + ttm_bo_unreserve(bo); + +err: + return ret; +} + +/** + * 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->resource->mem_type == TTM_PL_VRAM) { + ptr->gmrId = SVGA_GMR_FRAMEBUFFER; + ptr->offset = bo->resource->start << PAGE_SHIFT; + } else { + ptr->gmrId = bo->resource->start; + ptr->offset = 0; + } +} + + +/** + * vmw_bo_pin_reserved - Pin or unpin a buffer object without moving it. + * + * @vbo: The buffer object. Must be reserved. + * @pin: Whether to pin or unpin. + * + */ +void vmw_bo_pin_reserved(struct vmw_bo *vbo, bool pin) +{ + struct ttm_operation_ctx ctx = { false, true }; + struct ttm_place pl; + struct ttm_placement placement; + struct ttm_buffer_object *bo = &vbo->tbo; + uint32_t old_mem_type = bo->resource->mem_type; + int ret; + + dma_resv_assert_held(bo->base.resv); + + if (pin == !!bo->pin_count) + return; + + pl.fpfn = 0; + pl.lpfn = 0; + pl.mem_type = bo->resource->mem_type; + pl.flags = bo->resource->placement; + + memset(&placement, 0, sizeof(placement)); + placement.num_placement = 1; + placement.placement = &pl; + + ret = ttm_bo_validate(bo, &placement, &ctx); + + BUG_ON(ret != 0 || bo->resource->mem_type != old_mem_type); + + if (pin) + ttm_bo_pin(bo); + else + ttm_bo_unpin(bo); +} + +/** + * vmw_bo_map_and_cache - Map a buffer object and cache the map + * + * @vbo: The buffer object to map + * Return: A kernel virtual address or NULL if mapping failed. + * + * This function maps a buffer object into the kernel address space, or + * returns the virtual kernel address of an already existing map. The virtual + * address remains valid as long as the buffer object is pinned or reserved. + * The cached map is torn down on either + * 1) Buffer object move + * 2) Buffer object swapout + * 3) Buffer object destruction + * + */ +void *vmw_bo_map_and_cache(struct vmw_bo *vbo) +{ + struct ttm_buffer_object *bo = &vbo->tbo; + bool not_used; + void *virtual; + int ret; + + virtual = ttm_kmap_obj_virtual(&vbo->map, ¬_used); + if (virtual) + return virtual; + + ret = ttm_bo_kmap(bo, 0, PFN_UP(bo->base.size), &vbo->map); + if (ret) + DRM_ERROR("Buffer object map failed: %d.\n", ret); + + return ttm_kmap_obj_virtual(&vbo->map, ¬_used); +} + + +/** + * vmw_bo_unmap - Tear down a cached buffer object map. + * + * @vbo: The buffer object whose map we are tearing down. + * + * This function tears down a cached map set up using + * vmw_bo_map_and_cache(). + */ +void vmw_bo_unmap(struct vmw_bo *vbo) +{ + if (vbo->map.bo == NULL) + return; + + ttm_bo_kunmap(&vbo->map); + vbo->map.bo = NULL; +} + + +/** + * vmw_bo_init - Initialize a vmw buffer object + * + * @dev_priv: Pointer to the device private struct + * @vmw_bo: Buffer object to initialize + * @params: Parameters used to initialize the buffer object + * @destroy: The function used to delete the buffer object + * Returns: Zero on success, negative error code on error. + * + */ +static int vmw_bo_init(struct vmw_private *dev_priv, + struct vmw_bo *vmw_bo, + struct vmw_bo_params *params, + void (*destroy)(struct ttm_buffer_object *)) +{ + struct ttm_operation_ctx ctx = { + .interruptible = params->bo_type != ttm_bo_type_kernel, + .no_wait_gpu = false + }; + struct ttm_device *bdev = &dev_priv->bdev; + struct drm_device *vdev = &dev_priv->drm; + int ret; + + memset(vmw_bo, 0, sizeof(*vmw_bo)); + + BUILD_BUG_ON(TTM_MAX_BO_PRIORITY <= 3); + vmw_bo->tbo.priority = 3; + vmw_bo->res_tree = RB_ROOT; + + params->size = ALIGN(params->size, PAGE_SIZE); + drm_gem_private_object_init(vdev, &vmw_bo->tbo.base, params->size); + + vmw_bo_placement_set(vmw_bo, params->domain, params->busy_domain); + ret = ttm_bo_init_reserved(bdev, &vmw_bo->tbo, params->bo_type, + &vmw_bo->placement, 0, &ctx, NULL, + NULL, destroy); + if (unlikely(ret)) + return ret; + + if (params->pin) + ttm_bo_pin(&vmw_bo->tbo); + ttm_bo_unreserve(&vmw_bo->tbo); + + return 0; +} + +int vmw_bo_create(struct vmw_private *vmw, + struct vmw_bo_params *params, + struct vmw_bo **p_bo) +{ + int ret; + + *p_bo = kmalloc(sizeof(**p_bo), GFP_KERNEL); + if (unlikely(!*p_bo)) { + DRM_ERROR("Failed to allocate a buffer.\n"); + return -ENOMEM; + } + + /* + * vmw_bo_init will delete the *p_bo object if it fails + */ + ret = vmw_bo_init(vmw, *p_bo, params, vmw_bo_free); + if (unlikely(ret != 0)) + goto out_error; + + return ret; +out_error: + *p_bo = NULL; + return ret; +} + +/** + * vmw_user_bo_synccpu_grab - Grab a struct vmw_bo for cpu + * access, idling previous GPU operations on the buffer and optionally + * blocking it for further command submissions. + * + * @vmw_bo: Pointer to the buffer object being grabbed for CPU access + * @flags: Flags indicating how the grab should be performed. + * Return: Zero on success, Negative error code on error. In particular, + * -EBUSY will be returned if a dontblock operation is requested and the + * buffer object is busy, and -ERESTARTSYS will be returned if a wait is + * interrupted by a signal. + * + * A blocking grab will be automatically released when @tfile is closed. + */ +static int vmw_user_bo_synccpu_grab(struct vmw_bo *vmw_bo, + uint32_t flags) +{ + bool nonblock = !!(flags & drm_vmw_synccpu_dontblock); + struct ttm_buffer_object *bo = &vmw_bo->tbo; + int ret; + + if (flags & drm_vmw_synccpu_allow_cs) { + long lret; + + lret = dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_READ, + true, nonblock ? 0 : + MAX_SCHEDULE_TIMEOUT); + if (!lret) + return -EBUSY; + else if (lret < 0) + return lret; + return 0; + } + + ret = ttm_bo_reserve(bo, true, nonblock, NULL); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_bo_wait(bo, true, nonblock); + if (likely(ret == 0)) + atomic_inc(&vmw_bo->cpu_writers); + + ttm_bo_unreserve(bo); + if (unlikely(ret != 0)) + return ret; + + return ret; +} + +/** + * vmw_user_bo_synccpu_release - Release a previous grab for CPU access, + * and unblock command submission on the buffer if blocked. + * + * @filp: Identifying the caller. + * @handle: Handle identifying the buffer object. + * @flags: Flags indicating the type of release. + */ +static int vmw_user_bo_synccpu_release(struct drm_file *filp, + uint32_t handle, + uint32_t flags) +{ + struct vmw_bo *vmw_bo; + int ret = vmw_user_bo_lookup(filp, handle, &vmw_bo); + + if (!ret) { + if (!(flags & drm_vmw_synccpu_allow_cs)) { + atomic_dec(&vmw_bo->cpu_writers); + } + vmw_user_bo_unref(&vmw_bo); + } + + return ret; +} + + +/** + * vmw_user_bo_synccpu_ioctl - ioctl function implementing the synccpu + * functionality. + * + * @dev: Identifies the drm device. + * @data: Pointer to the ioctl argument. + * @file_priv: Identifies the caller. + * Return: Zero on success, negative error code on error. + * + * This function checks the ioctl arguments for validity and calls the + * relevant synccpu functions. + */ +int vmw_user_bo_synccpu_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_synccpu_arg *arg = + (struct drm_vmw_synccpu_arg *) data; + struct vmw_bo *vbo; + int ret; + + 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; + } + + switch (arg->op) { + case drm_vmw_synccpu_grab: + ret = vmw_user_bo_lookup(file_priv, arg->handle, &vbo); + if (unlikely(ret != 0)) + return ret; + + ret = vmw_user_bo_synccpu_grab(vbo, arg->flags); + vmw_user_bo_unref(&vbo); + if (unlikely(ret != 0)) { + if (ret == -ERESTARTSYS || ret == -EBUSY) + return -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_bo_synccpu_release(file_priv, + arg->handle, + 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; + } + + return 0; +} + +/** + * vmw_bo_unref_ioctl - Generic handle close ioctl. + * + * @dev: Identifies the drm device. + * @data: Pointer to the ioctl argument. + * @file_priv: Identifies the caller. + * Return: Zero on success, negative error code on error. + * + * This function checks the ioctl arguments for validity and closes a + * handle to a TTM base object, optionally freeing the object. + */ +int vmw_bo_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_unref_dmabuf_arg *arg = + (struct drm_vmw_unref_dmabuf_arg *)data; + + return drm_gem_handle_delete(file_priv, arg->handle); +} + + +/** + * vmw_user_bo_lookup - Look up a vmw user buffer object from a handle. + * + * @filp: The file the handle is registered with. + * @handle: The user buffer object handle + * @out: Pointer to a where a pointer to the embedded + * struct vmw_bo should be placed. + * Return: Zero on success, Negative error code on error. + * + * The vmw buffer object pointer will be refcounted (both ttm and gem) + */ +int vmw_user_bo_lookup(struct drm_file *filp, + u32 handle, + struct vmw_bo **out) +{ + struct drm_gem_object *gobj; + + gobj = drm_gem_object_lookup(filp, handle); + if (!gobj) { + DRM_ERROR("Invalid buffer object handle 0x%08lx.\n", + (unsigned long)handle); + return -ESRCH; + } + + *out = to_vmw_bo(gobj); + + return 0; +} + +/** + * vmw_bo_fence_single - 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_bo_fence_single(struct ttm_buffer_object *bo, + struct vmw_fence_obj *fence) +{ + struct ttm_device *bdev = bo->bdev; + struct vmw_private *dev_priv = vmw_priv_from_ttm(bdev); + int ret; + + if (fence == NULL) + vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); + else + dma_fence_get(&fence->base); + + ret = dma_resv_reserve_fences(bo->base.resv, 1); + if (!ret) + dma_resv_add_fence(bo->base.resv, &fence->base, + DMA_RESV_USAGE_KERNEL); + else + /* Last resort fallback when we are OOM */ + dma_fence_wait(&fence->base, false); + dma_fence_put(&fence->base); +} + + +/** + * 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 + * Return: Zero on success, negative error code on failure. + * + * This is a driver callback for the core drm create_dumb functionality. + * Note that this is very similar to the vmw_bo_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_bo *vbo; + int cpp = DIV_ROUND_UP(args->bpp, 8); + int ret; + + switch (cpp) { + case 1: /* DRM_FORMAT_C8 */ + case 2: /* DRM_FORMAT_RGB565 */ + case 4: /* DRM_FORMAT_XRGB8888 */ + break; + default: + /* + * Dumb buffers don't allow anything else. + * This is tested via IGT's dumb_buffers + */ + return -EINVAL; + } + + args->pitch = args->width * cpp; + args->size = ALIGN(args->pitch * args->height, PAGE_SIZE); + + ret = vmw_gem_object_create_with_handle(dev_priv, file_priv, + args->size, &args->handle, + &vbo); + /* drop reference from allocate - handle holds it now */ + drm_gem_object_put(&vbo->tbo.base); + return ret; +} + +/** + * vmw_bo_swap_notify - swapout notify callback. + * + * @bo: The buffer object to be swapped out. + */ +void vmw_bo_swap_notify(struct ttm_buffer_object *bo) +{ + /* Kill any cached kernel maps before swapout */ + vmw_bo_unmap(to_vmw_bo(&bo->base)); +} + + +/** + * vmw_bo_move_notify - TTM move_notify_callback + * + * @bo: The TTM buffer object about to move. + * @mem: The struct ttm_resource indicating to what memory + * region the move is taking place. + * + * Detaches cached maps and device bindings that require that the + * buffer doesn't move. + */ +void vmw_bo_move_notify(struct ttm_buffer_object *bo, + struct ttm_resource *mem) +{ + struct vmw_bo *vbo = to_vmw_bo(&bo->base); + + /* + * Kill any cached kernel maps before move to or from VRAM. + * With other types of moves, the underlying pages stay the same, + * and the map can be kept. + */ + if (mem->mem_type == TTM_PL_VRAM || bo->resource->mem_type == TTM_PL_VRAM) + vmw_bo_unmap(vbo); + + /* + * If we're moving a backup MOB out of MOB placement, then make sure we + * read back all resource content first, and unbind the MOB from + * the resource. + */ + if (mem->mem_type != VMW_PL_MOB && bo->resource->mem_type == VMW_PL_MOB) + vmw_resource_unbind_list(vbo); +} + +static u32 +set_placement_list(struct ttm_place *pl, u32 domain) +{ + u32 n = 0; + + /* + * The placements are ordered according to our preferences + */ + if (domain & VMW_BO_DOMAIN_MOB) { + pl[n].mem_type = VMW_PL_MOB; + pl[n].flags = 0; + pl[n].fpfn = 0; + pl[n].lpfn = 0; + n++; + } + if (domain & VMW_BO_DOMAIN_GMR) { + pl[n].mem_type = VMW_PL_GMR; + pl[n].flags = 0; + pl[n].fpfn = 0; + pl[n].lpfn = 0; + n++; + } + if (domain & VMW_BO_DOMAIN_VRAM) { + pl[n].mem_type = TTM_PL_VRAM; + pl[n].flags = 0; + pl[n].fpfn = 0; + pl[n].lpfn = 0; + n++; + } + if (domain & VMW_BO_DOMAIN_WAITABLE_SYS) { + pl[n].mem_type = VMW_PL_SYSTEM; + pl[n].flags = 0; + pl[n].fpfn = 0; + pl[n].lpfn = 0; + n++; + } + if (domain & VMW_BO_DOMAIN_SYS) { + pl[n].mem_type = TTM_PL_SYSTEM; + pl[n].flags = 0; + pl[n].fpfn = 0; + pl[n].lpfn = 0; + n++; + } + + WARN_ON(!n); + if (!n) { + pl[n].mem_type = TTM_PL_SYSTEM; + pl[n].flags = 0; + pl[n].fpfn = 0; + pl[n].lpfn = 0; + n++; + } + return n; +} + +void vmw_bo_placement_set(struct vmw_bo *bo, u32 domain, u32 busy_domain) +{ + struct ttm_device *bdev = bo->tbo.bdev; + struct vmw_private *vmw = vmw_priv_from_ttm(bdev); + struct ttm_placement *pl = &bo->placement; + bool mem_compatible = false; + u32 i; + + pl->placement = bo->places; + pl->num_placement = set_placement_list(bo->places, domain); + + if (drm_debug_enabled(DRM_UT_DRIVER) && bo->tbo.resource) { + for (i = 0; i < pl->num_placement; ++i) { + if (bo->tbo.resource->mem_type == TTM_PL_SYSTEM || + bo->tbo.resource->mem_type == pl->placement[i].mem_type) + mem_compatible = true; + } + if (!mem_compatible) + drm_warn(&vmw->drm, + "%s: Incompatible transition from " + "bo->base.resource->mem_type = %u to domain = %u\n", + __func__, bo->tbo.resource->mem_type, domain); + } + + pl->busy_placement = bo->busy_places; + pl->num_busy_placement = set_placement_list(bo->busy_places, busy_domain); +} + +void vmw_bo_placement_set_default_accelerated(struct vmw_bo *bo) +{ + struct ttm_device *bdev = bo->tbo.bdev; + struct vmw_private *vmw = vmw_priv_from_ttm(bdev); + u32 domain = VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM; + + if (vmw->has_mob) + domain = VMW_BO_DOMAIN_MOB; + + vmw_bo_placement_set(bo, domain, domain); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h new file mode 100644 index 0000000000..0d496dc9c6 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright 2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_BO_H +#define VMWGFX_BO_H + +#include "device_include/svga_reg.h" + +#include <drm/ttm/ttm_bo.h> +#include <drm/ttm/ttm_placement.h> + +#include <linux/rbtree_types.h> +#include <linux/types.h> + +struct vmw_bo_dirty; +struct vmw_fence_obj; +struct vmw_private; +struct vmw_resource; + +enum vmw_bo_domain { + VMW_BO_DOMAIN_SYS = BIT(0), + VMW_BO_DOMAIN_WAITABLE_SYS = BIT(1), + VMW_BO_DOMAIN_VRAM = BIT(2), + VMW_BO_DOMAIN_GMR = BIT(3), + VMW_BO_DOMAIN_MOB = BIT(4), +}; + +struct vmw_bo_params { + u32 domain; + u32 busy_domain; + enum ttm_bo_type bo_type; + size_t size; + bool pin; +}; + +/** + * struct vmw_bo - TTM buffer object with vmwgfx additions + * @tbo: The TTM buffer object + * @placement: The preferred placement for this buffer object + * @places: The chosen places for the preferred placement. + * @busy_places: Chosen busy places for the preferred placement + * @map: Kmap object for semi-persistent mappings + * @res_tree: RB tree of resources using this buffer object as a backing MOB + * @res_prios: Eviction priority counts for attached resources + * @cpu_writers: Number of synccpu write grabs. Protected by reservation when + * increased. May be decreased without reservation. + * @dx_query_ctx: DX context if this buffer object is used as a DX query MOB + * @dirty: structure for user-space dirty-tracking + */ +struct vmw_bo { + struct ttm_buffer_object tbo; + + struct ttm_placement placement; + struct ttm_place places[5]; + struct ttm_place busy_places[5]; + + /* Protected by reservation */ + struct ttm_bo_kmap_obj map; + + struct rb_root res_tree; + u32 res_prios[TTM_MAX_BO_PRIORITY]; + + atomic_t cpu_writers; + /* Not ref-counted. Protected by binding_mutex */ + struct vmw_resource *dx_query_ctx; + struct vmw_bo_dirty *dirty; +}; + +void vmw_bo_placement_set(struct vmw_bo *bo, u32 domain, u32 busy_domain); +void vmw_bo_placement_set_default_accelerated(struct vmw_bo *bo); + +int vmw_bo_create(struct vmw_private *dev_priv, + struct vmw_bo_params *params, + struct vmw_bo **p_bo); + +int vmw_bo_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +int vmw_bo_pin_in_vram(struct vmw_private *dev_priv, + struct vmw_bo *buf, + bool interruptible); +int vmw_bo_pin_in_vram_or_gmr(struct vmw_private *dev_priv, + struct vmw_bo *buf, + bool interruptible); +int vmw_bo_pin_in_start_of_vram(struct vmw_private *vmw_priv, + struct vmw_bo *bo, + bool interruptible); +void vmw_bo_pin_reserved(struct vmw_bo *bo, bool pin); +int vmw_bo_unpin(struct vmw_private *vmw_priv, + struct vmw_bo *bo, + bool interruptible); + +void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *buf, + SVGAGuestPtr *ptr); +int vmw_user_bo_synccpu_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +void vmw_bo_fence_single(struct ttm_buffer_object *bo, + struct vmw_fence_obj *fence); + +void *vmw_bo_map_and_cache(struct vmw_bo *vbo); +void vmw_bo_unmap(struct vmw_bo *vbo); + +void vmw_bo_move_notify(struct ttm_buffer_object *bo, + struct ttm_resource *mem); +void vmw_bo_swap_notify(struct ttm_buffer_object *bo); + +int vmw_user_bo_lookup(struct drm_file *filp, + u32 handle, + struct vmw_bo **out); +/** + * vmw_bo_adjust_prio - Adjust the buffer object eviction priority + * according to attached resources + * @vbo: The struct vmw_bo + */ +static inline void vmw_bo_prio_adjust(struct vmw_bo *vbo) +{ + int i = ARRAY_SIZE(vbo->res_prios); + + while (i--) { + if (vbo->res_prios[i]) { + vbo->tbo.priority = i; + return; + } + } + + vbo->tbo.priority = 3; +} + +/** + * vmw_bo_prio_add - Notify a buffer object of a newly attached resource + * eviction priority + * @vbo: The struct vmw_bo + * @prio: The resource priority + * + * After being notified, the code assigns the highest resource eviction priority + * to the backing buffer object (mob). + */ +static inline void vmw_bo_prio_add(struct vmw_bo *vbo, int prio) +{ + if (vbo->res_prios[prio]++ == 0) + vmw_bo_prio_adjust(vbo); +} + +/** + * vmw_bo_used_prio_del - Notify a buffer object of a resource with a certain + * priority being removed + * @vbo: The struct vmw_bo + * @prio: The resource priority + * + * After being notified, the code assigns the highest resource eviction priority + * to the backing buffer object (mob). + */ +static inline void vmw_bo_prio_del(struct vmw_bo *vbo, int prio) +{ + if (--vbo->res_prios[prio] == 0) + vmw_bo_prio_adjust(vbo); +} + +static inline void vmw_bo_unreference(struct vmw_bo **buf) +{ + struct vmw_bo *tmp_buf = *buf; + + *buf = NULL; + if (tmp_buf) + ttm_bo_put(&tmp_buf->tbo); +} + +static inline struct vmw_bo *vmw_bo_reference(struct vmw_bo *buf) +{ + ttm_bo_get(&buf->tbo); + return buf; +} + +static inline struct vmw_bo *vmw_user_bo_ref(struct vmw_bo *vbo) +{ + drm_gem_object_get(&vbo->tbo.base); + return vbo; +} + +static inline void vmw_user_bo_unref(struct vmw_bo **buf) +{ + struct vmw_bo *tmp_buf = *buf; + + *buf = NULL; + if (tmp_buf) + drm_gem_object_put(&tmp_buf->tbo.base); +} + +static inline struct vmw_bo *to_vmw_bo(struct drm_gem_object *gobj) +{ + return container_of((gobj), struct vmw_bo, tbo.base); +} + +#endif // VMWGFX_BO_H diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c new file mode 100644 index 0000000000..195ff8792e --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c @@ -0,0 +1,688 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_devcaps.h" + +#include <drm/ttm/ttm_placement.h> + +#include <linux/sched/signal.h> + +bool vmw_supports_3d(struct vmw_private *dev_priv) +{ + 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; + + result = vmw_devcap_get(dev_priv, SVGA3D_DEVCAP_3D); + + return (result != 0); + } + + if (!(dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO)) + return false; + + BUG_ON(vmw_is_svga_v3(dev_priv)); + + fifo_min = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MIN); + if (fifo_min <= SVGA_FIFO_3D_HWVERSION * sizeof(unsigned int)) + return false; + + hwversion = vmw_fifo_mem_read(dev_priv, + ((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_WS8_B1) + return false; + + /* Legacy Display Unit does not support surfaces */ + if (dev_priv->active_display_unit == vmw_du_legacy) + return false; + + return true; +} + +bool vmw_fifo_have_pitchlock(struct vmw_private *dev_priv) +{ + uint32_t caps; + + if (!(dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO)) + return false; + + caps = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_CAPABILITIES); + if (caps & SVGA_FIFO_CAP_PITCHLOCK) + return true; + + return false; +} + +struct vmw_fifo_state *vmw_fifo_create(struct vmw_private *dev_priv) +{ + struct vmw_fifo_state *fifo; + uint32_t max; + uint32_t min; + + if (!dev_priv->fifo_mem) + return NULL; + + fifo = kzalloc(sizeof(*fifo), GFP_KERNEL); + if (!fifo) + return ERR_PTR(-ENOMEM); + fifo->static_buffer_size = VMWGFX_FIFO_STATIC_SIZE; + fifo->static_buffer = vmalloc(fifo->static_buffer_size); + if (unlikely(fifo->static_buffer == NULL)) { + kfree(fifo); + return ERR_PTR(-ENOMEM); + } + + fifo->dynamic_buffer = NULL; + fifo->reserved_size = 0; + fifo->using_bounce_buffer = false; + + mutex_init(&fifo->fifo_mutex); + init_rwsem(&fifo->rwsem); + min = 4; + if (dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO) + min = vmw_read(dev_priv, SVGA_REG_MEM_REGS); + min <<= 2; + + if (min < PAGE_SIZE) + min = PAGE_SIZE; + + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_MIN, min); + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_MAX, dev_priv->fifo_mem_size); + wmb(); + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_NEXT_CMD, min); + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_STOP, min); + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_BUSY, 0); + mb(); + + vmw_write(dev_priv, SVGA_REG_CONFIG_DONE, 1); + + max = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MAX); + min = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MIN); + fifo->capabilities = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_CAPABILITIES); + + drm_info(&dev_priv->drm, + "Fifo max 0x%08x min 0x%08x cap 0x%08x\n", + (unsigned int) max, + (unsigned int) min, + (unsigned int) fifo->capabilities); + + if (unlikely(min >= max)) { + drm_warn(&dev_priv->drm, + "FIFO memory is not usable. Driver failed to initialize."); + return ERR_PTR(-ENXIO); + } + + return fifo; +} + +void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason) +{ + u32 *fifo_mem = dev_priv->fifo_mem; + if (fifo_mem && cmpxchg(fifo_mem + SVGA_FIFO_BUSY, 0, 1) == 0) + vmw_write(dev_priv, SVGA_REG_SYNC, reason); + +} + +void vmw_fifo_destroy(struct vmw_private *dev_priv) +{ + struct vmw_fifo_state *fifo = dev_priv->fifo; + + if (!fifo) + return; + + if (likely(fifo->static_buffer != NULL)) { + vfree(fifo->static_buffer); + fifo->static_buffer = NULL; + } + + if (likely(fifo->dynamic_buffer != NULL)) { + vfree(fifo->dynamic_buffer); + fifo->dynamic_buffer = NULL; + } + kfree(fifo); + dev_priv->fifo = NULL; +} + +static bool vmw_fifo_is_full(struct vmw_private *dev_priv, uint32_t bytes) +{ + uint32_t max = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MAX); + uint32_t next_cmd = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_NEXT_CMD); + uint32_t min = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MIN); + uint32_t stop = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_STOP); + + return ((max - next_cmd) + (stop - min) <= bytes); +} + +static int vmw_fifo_wait_noirq(struct vmw_private *dev_priv, + uint32_t bytes, bool interruptible, + unsigned long timeout) +{ + int ret = 0; + unsigned long end_jiffies = jiffies + timeout; + DEFINE_WAIT(__wait); + + DRM_INFO("Fifo wait noirq.\n"); + + for (;;) { + prepare_to_wait(&dev_priv->fifo_queue, &__wait, + (interruptible) ? + TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + if (!vmw_fifo_is_full(dev_priv, bytes)) + break; + if (time_after_eq(jiffies, end_jiffies)) { + ret = -EBUSY; + DRM_ERROR("SVGA device lockup.\n"); + break; + } + schedule_timeout(1); + if (interruptible && signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + finish_wait(&dev_priv->fifo_queue, &__wait); + wake_up_all(&dev_priv->fifo_queue); + DRM_INFO("Fifo noirq exit.\n"); + return ret; +} + +static int vmw_fifo_wait(struct vmw_private *dev_priv, + uint32_t bytes, bool interruptible, + unsigned long timeout) +{ + long ret = 1L; + + if (likely(!vmw_fifo_is_full(dev_priv, bytes))) + return 0; + + vmw_fifo_ping_host(dev_priv, SVGA_SYNC_FIFOFULL); + if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) + return vmw_fifo_wait_noirq(dev_priv, bytes, + interruptible, timeout); + + vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_FIFO_PROGRESS, + &dev_priv->fifo_queue_waiters); + + if (interruptible) + ret = wait_event_interruptible_timeout + (dev_priv->fifo_queue, + !vmw_fifo_is_full(dev_priv, bytes), timeout); + else + ret = wait_event_timeout + (dev_priv->fifo_queue, + !vmw_fifo_is_full(dev_priv, bytes), timeout); + + if (unlikely(ret == 0)) + ret = -EBUSY; + else if (likely(ret > 0)) + ret = 0; + + vmw_generic_waiter_remove(dev_priv, SVGA_IRQFLAG_FIFO_PROGRESS, + &dev_priv->fifo_queue_waiters); + + 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). + */ +static void *vmw_local_fifo_reserve(struct vmw_private *dev_priv, + uint32_t bytes) +{ + struct vmw_fifo_state *fifo_state = dev_priv->fifo; + u32 *fifo_mem = dev_priv->fifo_mem; + uint32_t max; + uint32_t min; + uint32_t next_cmd; + uint32_t reserveable = fifo_state->capabilities & SVGA_FIFO_CAP_RESERVE; + int ret; + + mutex_lock(&fifo_state->fifo_mutex); + max = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MAX); + min = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MIN); + next_cmd = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_NEXT_CMD); + + if (unlikely(bytes >= (max - min))) + goto out_err; + + BUG_ON(fifo_state->reserved_size != 0); + BUG_ON(fifo_state->dynamic_buffer != NULL); + + fifo_state->reserved_size = bytes; + + while (1) { + uint32_t stop = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_STOP); + bool need_bounce = false; + bool reserve_in_place = false; + + if (next_cmd >= stop) { + if (likely((next_cmd + bytes < max || + (next_cmd + bytes == max && stop > min)))) + reserve_in_place = true; + + else if (vmw_fifo_is_full(dev_priv, bytes)) { + ret = vmw_fifo_wait(dev_priv, bytes, + false, 3 * HZ); + if (unlikely(ret != 0)) + goto out_err; + } else + need_bounce = true; + + } else { + + if (likely((next_cmd + bytes < stop))) + reserve_in_place = true; + else { + ret = vmw_fifo_wait(dev_priv, bytes, + false, 3 * HZ); + if (unlikely(ret != 0)) + goto out_err; + } + } + + if (reserve_in_place) { + if (reserveable || bytes <= sizeof(uint32_t)) { + fifo_state->using_bounce_buffer = false; + + if (reserveable) + vmw_fifo_mem_write(dev_priv, + SVGA_FIFO_RESERVED, + bytes); + return (void __force *) (fifo_mem + + (next_cmd >> 2)); + } else { + need_bounce = true; + } + } + + if (need_bounce) { + fifo_state->using_bounce_buffer = true; + if (bytes < fifo_state->static_buffer_size) + return fifo_state->static_buffer; + else { + fifo_state->dynamic_buffer = vmalloc(bytes); + if (!fifo_state->dynamic_buffer) + goto out_err; + return fifo_state->dynamic_buffer; + } + } + } +out_err: + fifo_state->reserved_size = 0; + mutex_unlock(&fifo_state->fifo_mutex); + + return NULL; +} + +void *vmw_cmd_ctx_reserve(struct vmw_private *dev_priv, uint32_t bytes, + int ctx_id) +{ + void *ret; + + if (dev_priv->cman) + ret = vmw_cmdbuf_reserve(dev_priv->cman, bytes, + ctx_id, false, NULL); + else if (ctx_id == SVGA3D_INVALID_ID) + ret = vmw_local_fifo_reserve(dev_priv, bytes); + else { + WARN(1, "Command buffer has not been allocated.\n"); + ret = NULL; + } + if (IS_ERR_OR_NULL(ret)) + return NULL; + + return ret; +} + +static void vmw_fifo_res_copy(struct vmw_fifo_state *fifo_state, + struct vmw_private *vmw, + uint32_t next_cmd, + uint32_t max, uint32_t min, uint32_t bytes) +{ + u32 *fifo_mem = vmw->fifo_mem; + uint32_t chunk_size = max - next_cmd; + uint32_t rest; + uint32_t *buffer = (fifo_state->dynamic_buffer != NULL) ? + fifo_state->dynamic_buffer : fifo_state->static_buffer; + + if (bytes < chunk_size) + chunk_size = bytes; + + vmw_fifo_mem_write(vmw, SVGA_FIFO_RESERVED, bytes); + mb(); + memcpy(fifo_mem + (next_cmd >> 2), buffer, chunk_size); + rest = bytes - chunk_size; + if (rest) + memcpy(fifo_mem + (min >> 2), buffer + (chunk_size >> 2), rest); +} + +static void vmw_fifo_slow_copy(struct vmw_fifo_state *fifo_state, + struct vmw_private *vmw, + uint32_t next_cmd, + uint32_t max, uint32_t min, uint32_t bytes) +{ + uint32_t *buffer = (fifo_state->dynamic_buffer != NULL) ? + fifo_state->dynamic_buffer : fifo_state->static_buffer; + + while (bytes > 0) { + vmw_fifo_mem_write(vmw, (next_cmd >> 2), *buffer++); + next_cmd += sizeof(uint32_t); + if (unlikely(next_cmd == max)) + next_cmd = min; + mb(); + vmw_fifo_mem_write(vmw, SVGA_FIFO_NEXT_CMD, next_cmd); + mb(); + bytes -= sizeof(uint32_t); + } +} + +static void vmw_local_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes) +{ + struct vmw_fifo_state *fifo_state = dev_priv->fifo; + uint32_t next_cmd = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_NEXT_CMD); + uint32_t max = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MAX); + uint32_t min = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_MIN); + bool reserveable = fifo_state->capabilities & SVGA_FIFO_CAP_RESERVE; + + BUG_ON((bytes & 3) != 0); + BUG_ON(bytes > fifo_state->reserved_size); + + fifo_state->reserved_size = 0; + + if (fifo_state->using_bounce_buffer) { + if (reserveable) + vmw_fifo_res_copy(fifo_state, dev_priv, + next_cmd, max, min, bytes); + else + vmw_fifo_slow_copy(fifo_state, dev_priv, + next_cmd, max, min, bytes); + + if (fifo_state->dynamic_buffer) { + vfree(fifo_state->dynamic_buffer); + fifo_state->dynamic_buffer = NULL; + } + + } + + down_write(&fifo_state->rwsem); + if (fifo_state->using_bounce_buffer || reserveable) { + next_cmd += bytes; + if (next_cmd >= max) + next_cmd -= max - min; + mb(); + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_NEXT_CMD, next_cmd); + } + + if (reserveable) + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_RESERVED, 0); + mb(); + up_write(&fifo_state->rwsem); + vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); + mutex_unlock(&fifo_state->fifo_mutex); +} + +void vmw_cmd_commit(struct vmw_private *dev_priv, uint32_t bytes) +{ + if (dev_priv->cman) + vmw_cmdbuf_commit(dev_priv->cman, bytes, NULL, false); + else + vmw_local_fifo_commit(dev_priv, bytes); +} + + +/** + * vmw_cmd_commit_flush - Commit fifo space and flush any buffered commands. + * + * @dev_priv: Pointer to device private structure. + * @bytes: Number of bytes to commit. + */ +void vmw_cmd_commit_flush(struct vmw_private *dev_priv, uint32_t bytes) +{ + if (dev_priv->cman) + vmw_cmdbuf_commit(dev_priv->cman, bytes, NULL, true); + else + vmw_local_fifo_commit(dev_priv, bytes); +} + +/** + * vmw_cmd_flush - Flush any buffered commands and make sure command processing + * starts. + * + * @dev_priv: Pointer to device private structure. + * @interruptible: Whether to wait interruptible if function needs to sleep. + */ +int vmw_cmd_flush(struct vmw_private *dev_priv, bool interruptible) +{ + might_sleep(); + + if (dev_priv->cman) + return vmw_cmdbuf_cur_flush(dev_priv->cman, interruptible); + else + return 0; +} + +int vmw_cmd_send_fence(struct vmw_private *dev_priv, uint32_t *seqno) +{ + struct svga_fifo_cmd_fence *cmd_fence; + u32 *fm; + int ret = 0; + uint32_t bytes = sizeof(u32) + sizeof(*cmd_fence); + + fm = VMW_CMD_RESERVE(dev_priv, bytes); + if (unlikely(fm == NULL)) { + *seqno = atomic_read(&dev_priv->marker_seq); + ret = -ENOMEM; + (void)vmw_fallback_wait(dev_priv, false, true, *seqno, + false, 3*HZ); + goto out_err; + } + + do { + *seqno = atomic_add_return(1, &dev_priv->marker_seq); + } while (*seqno == 0); + + if (!vmw_has_fences(dev_priv)) { + + /* + * Don't request hardware to send a fence. The + * waiting code in vmwgfx_irq.c will emulate this. + */ + + vmw_cmd_commit(dev_priv, 0); + return 0; + } + + *fm++ = SVGA_CMD_FENCE; + cmd_fence = (struct svga_fifo_cmd_fence *) fm; + cmd_fence->fence = *seqno; + vmw_cmd_commit_flush(dev_priv, bytes); + vmw_update_seqno(dev_priv); + +out_err: + return ret; +} + +/** + * vmw_cmd_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_cmd_emit_dummy_query documentation. + */ +static int vmw_cmd_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->tbo; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdWaitForQuery body; + } *cmd; + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + 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->resource->mem_type == TTM_PL_VRAM) { + cmd->body.guestResult.gmrId = SVGA_GMR_FRAMEBUFFER; + cmd->body.guestResult.offset = bo->resource->start << PAGE_SHIFT; + } else { + cmd->body.guestResult.gmrId = bo->resource->start; + cmd->body.guestResult.offset = 0; + } + + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; +} + +/** + * vmw_cmd_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_cmd_emit_dummy_query documentation. + */ +static int vmw_cmd_emit_dummy_gb_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->tbo; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdWaitForGBQuery body; + } *cmd; + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + 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->resource->mem_type != VMW_PL_MOB); + cmd->body.mobid = bo->resource->start; + cmd->body.offset = 0; + + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; +} + + +/** + * vmw_cmd_emit_dummy_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_cmd_emit_dummy_query(struct vmw_private *dev_priv, + uint32_t cid) +{ + if (dev_priv->has_mob) + return vmw_cmd_emit_dummy_gb_query(dev_priv, cid); + + return vmw_cmd_emit_dummy_legacy_query(dev_priv, cid); +} + + +/** + * vmw_cmd_supported - returns true if the given device supports + * command queues. + * + * @vmw: The device private structure. + * + * Returns true if we can issue commands. + */ +bool vmw_cmd_supported(struct vmw_private *vmw) +{ + bool has_cmdbufs = + (vmw->capabilities & (SVGA_CAP_COMMAND_BUFFERS | + SVGA_CAP_CMD_BUFFERS_2)) != 0; + if (vmw_is_svga_v3(vmw)) + return (has_cmdbufs && + (vmw->capabilities & SVGA_CAP_GBOBJECTS) != 0); + /* + * We have FIFO cmd's + */ + return has_cmdbufs || vmw->fifo_mem != NULL; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c new file mode 100644 index 0000000000..94e8982f56 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c @@ -0,0 +1,1406 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2015-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_drv.h" + +#include <drm/ttm/ttm_bo.h> + +#include <linux/dmapool.h> +#include <linux/pci.h> + +/* + * Size of inline command buffers. Try to make sure that a page size is a + * multiple of the DMA pool allocation size. + */ +#define VMW_CMDBUF_INLINE_ALIGN 64 +#define VMW_CMDBUF_INLINE_SIZE \ + (1024 - ALIGN(sizeof(SVGACBHeader), VMW_CMDBUF_INLINE_ALIGN)) + +/** + * struct vmw_cmdbuf_context - Command buffer context queues + * + * @submitted: List of command buffers that have been submitted to the + * manager but not yet submitted to hardware. + * @hw_submitted: List of command buffers submitted to hardware. + * @preempted: List of preempted command buffers. + * @num_hw_submitted: Number of buffers currently being processed by hardware + * @block_submission: Identifies a block command submission. + */ +struct vmw_cmdbuf_context { + struct list_head submitted; + struct list_head hw_submitted; + struct list_head preempted; + unsigned num_hw_submitted; + bool block_submission; +}; + +/** + * struct vmw_cmdbuf_man - Command buffer manager + * + * @cur_mutex: Mutex protecting the command buffer used for incremental small + * kernel command submissions, @cur. + * @space_mutex: Mutex to protect against starvation when we allocate + * main pool buffer space. + * @error_mutex: Mutex to serialize the work queue error handling. + * Note this is not needed if the same workqueue handler + * can't race with itself... + * @work: A struct work_struct implementeing command buffer error handling. + * Immutable. + * @dev_priv: Pointer to the device private struct. Immutable. + * @ctx: Array of command buffer context queues. The queues and the context + * data is protected by @lock. + * @error: List of command buffers that have caused device errors. + * Protected by @lock. + * @mm: Range manager for the command buffer space. Manager allocations and + * frees are protected by @lock. + * @cmd_space: Buffer object for the command buffer space, unless we were + * able to make a contigous coherent DMA memory allocation, @handle. Immutable. + * @map: Pointer to command buffer space. May be a mapped buffer object or + * a contigous coherent DMA memory allocation. Immutable. + * @cur: Command buffer for small kernel command submissions. Protected by + * the @cur_mutex. + * @cur_pos: Space already used in @cur. Protected by @cur_mutex. + * @default_size: Default size for the @cur command buffer. Immutable. + * @max_hw_submitted: Max number of in-flight command buffers the device can + * handle. Immutable. + * @lock: Spinlock protecting command submission queues. + * @headers: Pool of DMA memory for device command buffer headers. + * Internal protection. + * @dheaders: Pool of DMA memory for device command buffer headers with trailing + * space for inline data. Internal protection. + * @alloc_queue: Wait queue for processes waiting to allocate command buffer + * space. + * @idle_queue: Wait queue for processes waiting for command buffer idle. + * @irq_on: Whether the process function has requested irq to be turned on. + * Protected by @lock. + * @using_mob: Whether the command buffer space is a MOB or a contigous DMA + * allocation. Immutable. + * @has_pool: Has a large pool of DMA memory which allows larger allocations. + * Typically this is false only during bootstrap. + * @handle: DMA address handle for the command buffer space if @using_mob is + * false. Immutable. + * @size: The size of the command buffer space. Immutable. + * @num_contexts: Number of contexts actually enabled. + */ +struct vmw_cmdbuf_man { + struct mutex cur_mutex; + struct mutex space_mutex; + struct mutex error_mutex; + struct work_struct work; + struct vmw_private *dev_priv; + struct vmw_cmdbuf_context ctx[SVGA_CB_CONTEXT_MAX]; + struct list_head error; + struct drm_mm mm; + struct vmw_bo *cmd_space; + u8 *map; + struct vmw_cmdbuf_header *cur; + size_t cur_pos; + size_t default_size; + unsigned max_hw_submitted; + spinlock_t lock; + struct dma_pool *headers; + struct dma_pool *dheaders; + wait_queue_head_t alloc_queue; + wait_queue_head_t idle_queue; + bool irq_on; + bool using_mob; + bool has_pool; + dma_addr_t handle; + size_t size; + u32 num_contexts; +}; + +/** + * struct vmw_cmdbuf_header - Command buffer metadata + * + * @man: The command buffer manager. + * @cb_header: Device command buffer header, allocated from a DMA pool. + * @cb_context: The device command buffer context. + * @list: List head for attaching to the manager lists. + * @node: The range manager node. + * @handle: The DMA address of @cb_header. Handed to the device on command + * buffer submission. + * @cmd: Pointer to the command buffer space of this buffer. + * @size: Size of the command buffer space of this buffer. + * @reserved: Reserved space of this buffer. + * @inline_space: Whether inline command buffer space is used. + */ +struct vmw_cmdbuf_header { + struct vmw_cmdbuf_man *man; + SVGACBHeader *cb_header; + SVGACBContext cb_context; + struct list_head list; + struct drm_mm_node node; + dma_addr_t handle; + u8 *cmd; + size_t size; + size_t reserved; + bool inline_space; +}; + +/** + * struct vmw_cmdbuf_dheader - Device command buffer header with inline + * command buffer space. + * + * @cb_header: Device command buffer header. + * @cmd: Inline command buffer space. + */ +struct vmw_cmdbuf_dheader { + SVGACBHeader cb_header; + u8 cmd[VMW_CMDBUF_INLINE_SIZE] __aligned(VMW_CMDBUF_INLINE_ALIGN); +}; + +/** + * struct vmw_cmdbuf_alloc_info - Command buffer space allocation metadata + * + * @page_size: Size of requested command buffer space in pages. + * @node: Pointer to the range manager node. + * @done: True if this allocation has succeeded. + */ +struct vmw_cmdbuf_alloc_info { + size_t page_size; + struct drm_mm_node *node; + bool done; +}; + +/* Loop over each context in the command buffer manager. */ +#define for_each_cmdbuf_ctx(_man, _i, _ctx) \ + for (_i = 0, _ctx = &(_man)->ctx[0]; (_i) < (_man)->num_contexts; \ + ++(_i), ++(_ctx)) + +static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, u32 context, + bool enable); +static int vmw_cmdbuf_preempt(struct vmw_cmdbuf_man *man, u32 context); + +/** + * vmw_cmdbuf_cur_lock - Helper to lock the cur_mutex. + * + * @man: The range manager. + * @interruptible: Whether to wait interruptible when locking. + */ +static int vmw_cmdbuf_cur_lock(struct vmw_cmdbuf_man *man, bool interruptible) +{ + if (interruptible) { + if (mutex_lock_interruptible(&man->cur_mutex)) + return -ERESTARTSYS; + } else { + mutex_lock(&man->cur_mutex); + } + + return 0; +} + +/** + * vmw_cmdbuf_cur_unlock - Helper to unlock the cur_mutex. + * + * @man: The range manager. + */ +static void vmw_cmdbuf_cur_unlock(struct vmw_cmdbuf_man *man) +{ + mutex_unlock(&man->cur_mutex); +} + +/** + * vmw_cmdbuf_header_inline_free - Free a struct vmw_cmdbuf_header that has + * been used for the device context with inline command buffers. + * Need not be called locked. + * + * @header: Pointer to the header to free. + */ +static void vmw_cmdbuf_header_inline_free(struct vmw_cmdbuf_header *header) +{ + struct vmw_cmdbuf_dheader *dheader; + + if (WARN_ON_ONCE(!header->inline_space)) + return; + + dheader = container_of(header->cb_header, struct vmw_cmdbuf_dheader, + cb_header); + dma_pool_free(header->man->dheaders, dheader, header->handle); + kfree(header); +} + +/** + * __vmw_cmdbuf_header_free - Free a struct vmw_cmdbuf_header and its + * associated structures. + * + * @header: Pointer to the header to free. + * + * For internal use. Must be called with man::lock held. + */ +static void __vmw_cmdbuf_header_free(struct vmw_cmdbuf_header *header) +{ + struct vmw_cmdbuf_man *man = header->man; + + lockdep_assert_held_once(&man->lock); + + if (header->inline_space) { + vmw_cmdbuf_header_inline_free(header); + return; + } + + drm_mm_remove_node(&header->node); + wake_up_all(&man->alloc_queue); + if (header->cb_header) + dma_pool_free(man->headers, header->cb_header, + header->handle); + kfree(header); +} + +/** + * vmw_cmdbuf_header_free - Free a struct vmw_cmdbuf_header and its + * associated structures. + * + * @header: Pointer to the header to free. + */ +void vmw_cmdbuf_header_free(struct vmw_cmdbuf_header *header) +{ + struct vmw_cmdbuf_man *man = header->man; + + /* Avoid locking if inline_space */ + if (header->inline_space) { + vmw_cmdbuf_header_inline_free(header); + return; + } + spin_lock(&man->lock); + __vmw_cmdbuf_header_free(header); + spin_unlock(&man->lock); +} + + +/** + * vmw_cmdbuf_header_submit: Submit a command buffer to hardware. + * + * @header: The header of the buffer to submit. + */ +static int vmw_cmdbuf_header_submit(struct vmw_cmdbuf_header *header) +{ + struct vmw_cmdbuf_man *man = header->man; + u32 val; + + val = upper_32_bits(header->handle); + vmw_write(man->dev_priv, SVGA_REG_COMMAND_HIGH, val); + + val = lower_32_bits(header->handle); + val |= header->cb_context & SVGA_CB_CONTEXT_MASK; + vmw_write(man->dev_priv, SVGA_REG_COMMAND_LOW, val); + + return header->cb_header->status; +} + +/** + * vmw_cmdbuf_ctx_init: Initialize a command buffer context. + * + * @ctx: The command buffer context to initialize + */ +static void vmw_cmdbuf_ctx_init(struct vmw_cmdbuf_context *ctx) +{ + INIT_LIST_HEAD(&ctx->hw_submitted); + INIT_LIST_HEAD(&ctx->submitted); + INIT_LIST_HEAD(&ctx->preempted); + ctx->num_hw_submitted = 0; +} + +/** + * vmw_cmdbuf_ctx_submit: Submit command buffers from a command buffer + * context. + * + * @man: The command buffer manager. + * @ctx: The command buffer context. + * + * Submits command buffers to hardware until there are no more command + * buffers to submit or the hardware can't handle more command buffers. + */ +static void vmw_cmdbuf_ctx_submit(struct vmw_cmdbuf_man *man, + struct vmw_cmdbuf_context *ctx) +{ + while (ctx->num_hw_submitted < man->max_hw_submitted && + !list_empty(&ctx->submitted) && + !ctx->block_submission) { + struct vmw_cmdbuf_header *entry; + SVGACBStatus status; + + entry = list_first_entry(&ctx->submitted, + struct vmw_cmdbuf_header, + list); + + status = vmw_cmdbuf_header_submit(entry); + + /* This should never happen */ + if (WARN_ON_ONCE(status == SVGA_CB_STATUS_QUEUE_FULL)) { + entry->cb_header->status = SVGA_CB_STATUS_NONE; + break; + } + + list_move_tail(&entry->list, &ctx->hw_submitted); + ctx->num_hw_submitted++; + } + +} + +/** + * vmw_cmdbuf_ctx_process - Process a command buffer context. + * + * @man: The command buffer manager. + * @ctx: The command buffer context. + * @notempty: Pass back count of non-empty command submitted lists. + * + * Submit command buffers to hardware if possible, and process finished + * buffers. Typically freeing them, but on preemption or error take + * appropriate action. Wake up waiters if appropriate. + */ +static void vmw_cmdbuf_ctx_process(struct vmw_cmdbuf_man *man, + struct vmw_cmdbuf_context *ctx, + int *notempty) +{ + struct vmw_cmdbuf_header *entry, *next; + + vmw_cmdbuf_ctx_submit(man, ctx); + + list_for_each_entry_safe(entry, next, &ctx->hw_submitted, list) { + SVGACBStatus status = entry->cb_header->status; + + if (status == SVGA_CB_STATUS_NONE) + break; + + list_del(&entry->list); + wake_up_all(&man->idle_queue); + ctx->num_hw_submitted--; + switch (status) { + case SVGA_CB_STATUS_COMPLETED: + __vmw_cmdbuf_header_free(entry); + break; + case SVGA_CB_STATUS_COMMAND_ERROR: + WARN_ONCE(true, "Command buffer error.\n"); + entry->cb_header->status = SVGA_CB_STATUS_NONE; + list_add_tail(&entry->list, &man->error); + schedule_work(&man->work); + break; + case SVGA_CB_STATUS_PREEMPTED: + entry->cb_header->status = SVGA_CB_STATUS_NONE; + list_add_tail(&entry->list, &ctx->preempted); + break; + case SVGA_CB_STATUS_CB_HEADER_ERROR: + WARN_ONCE(true, "Command buffer header error.\n"); + __vmw_cmdbuf_header_free(entry); + break; + default: + WARN_ONCE(true, "Undefined command buffer status.\n"); + __vmw_cmdbuf_header_free(entry); + break; + } + } + + vmw_cmdbuf_ctx_submit(man, ctx); + if (!list_empty(&ctx->submitted)) + (*notempty)++; +} + +/** + * vmw_cmdbuf_man_process - Process all command buffer contexts and + * switch on and off irqs as appropriate. + * + * @man: The command buffer manager. + * + * Calls vmw_cmdbuf_ctx_process() on all contexts. If any context has + * command buffers left that are not submitted to hardware, Make sure + * IRQ handling is turned on. Otherwise, make sure it's turned off. + */ +static void vmw_cmdbuf_man_process(struct vmw_cmdbuf_man *man) +{ + int notempty; + struct vmw_cmdbuf_context *ctx; + int i; + +retry: + notempty = 0; + for_each_cmdbuf_ctx(man, i, ctx) + vmw_cmdbuf_ctx_process(man, ctx, ¬empty); + + if (man->irq_on && !notempty) { + vmw_generic_waiter_remove(man->dev_priv, + SVGA_IRQFLAG_COMMAND_BUFFER, + &man->dev_priv->cmdbuf_waiters); + man->irq_on = false; + } else if (!man->irq_on && notempty) { + vmw_generic_waiter_add(man->dev_priv, + SVGA_IRQFLAG_COMMAND_BUFFER, + &man->dev_priv->cmdbuf_waiters); + man->irq_on = true; + + /* Rerun in case we just missed an irq. */ + goto retry; + } +} + +/** + * vmw_cmdbuf_ctx_add - Schedule a command buffer for submission on a + * command buffer context + * + * @man: The command buffer manager. + * @header: The header of the buffer to submit. + * @cb_context: The command buffer context to use. + * + * This function adds @header to the "submitted" queue of the command + * buffer context identified by @cb_context. It then calls the command buffer + * manager processing to potentially submit the buffer to hardware. + * @man->lock needs to be held when calling this function. + */ +static void vmw_cmdbuf_ctx_add(struct vmw_cmdbuf_man *man, + struct vmw_cmdbuf_header *header, + SVGACBContext cb_context) +{ + if (!(header->cb_header->flags & SVGA_CB_FLAG_DX_CONTEXT)) + header->cb_header->dxContext = 0; + header->cb_context = cb_context; + list_add_tail(&header->list, &man->ctx[cb_context].submitted); + + vmw_cmdbuf_man_process(man); +} + +/** + * vmw_cmdbuf_irqthread - The main part of the command buffer interrupt + * handler implemented as a threaded irq task. + * + * @man: Pointer to the command buffer manager. + * + * The bottom half of the interrupt handler simply calls into the + * command buffer processor to free finished buffers and submit any + * queued buffers to hardware. + */ +void vmw_cmdbuf_irqthread(struct vmw_cmdbuf_man *man) +{ + spin_lock(&man->lock); + vmw_cmdbuf_man_process(man); + spin_unlock(&man->lock); +} + +/** + * vmw_cmdbuf_work_func - The deferred work function that handles + * command buffer errors. + * + * @work: The work func closure argument. + * + * Restarting the command buffer context after an error requires process + * context, so it is deferred to this work function. + */ +static void vmw_cmdbuf_work_func(struct work_struct *work) +{ + struct vmw_cmdbuf_man *man = + container_of(work, struct vmw_cmdbuf_man, work); + struct vmw_cmdbuf_header *entry, *next; + uint32_t dummy = 0; + bool send_fence = false; + struct list_head restart_head[SVGA_CB_CONTEXT_MAX]; + int i; + struct vmw_cmdbuf_context *ctx; + bool global_block = false; + + for_each_cmdbuf_ctx(man, i, ctx) + INIT_LIST_HEAD(&restart_head[i]); + + mutex_lock(&man->error_mutex); + spin_lock(&man->lock); + list_for_each_entry_safe(entry, next, &man->error, list) { + SVGACBHeader *cb_hdr = entry->cb_header; + SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) + (entry->cmd + cb_hdr->errorOffset); + u32 error_cmd_size, new_start_offset; + const char *cmd_name; + + list_del_init(&entry->list); + global_block = true; + + if (!vmw_cmd_describe(header, &error_cmd_size, &cmd_name)) { + VMW_DEBUG_USER("Unknown command causing device error.\n"); + VMW_DEBUG_USER("Command buffer offset is %lu\n", + (unsigned long) cb_hdr->errorOffset); + __vmw_cmdbuf_header_free(entry); + send_fence = true; + continue; + } + + VMW_DEBUG_USER("Command \"%s\" causing device error.\n", + cmd_name); + VMW_DEBUG_USER("Command buffer offset is %lu\n", + (unsigned long) cb_hdr->errorOffset); + VMW_DEBUG_USER("Command size is %lu\n", + (unsigned long) error_cmd_size); + + new_start_offset = cb_hdr->errorOffset + error_cmd_size; + + if (new_start_offset >= cb_hdr->length) { + __vmw_cmdbuf_header_free(entry); + send_fence = true; + continue; + } + + if (man->using_mob) + cb_hdr->ptr.mob.mobOffset += new_start_offset; + else + cb_hdr->ptr.pa += (u64) new_start_offset; + + entry->cmd += new_start_offset; + cb_hdr->length -= new_start_offset; + cb_hdr->errorOffset = 0; + cb_hdr->offset = 0; + + list_add_tail(&entry->list, &restart_head[entry->cb_context]); + } + + for_each_cmdbuf_ctx(man, i, ctx) + man->ctx[i].block_submission = true; + + spin_unlock(&man->lock); + + /* Preempt all contexts */ + if (global_block && vmw_cmdbuf_preempt(man, 0)) + DRM_ERROR("Failed preempting command buffer contexts\n"); + + spin_lock(&man->lock); + for_each_cmdbuf_ctx(man, i, ctx) { + /* Move preempted command buffers to the preempted queue. */ + vmw_cmdbuf_ctx_process(man, ctx, &dummy); + + /* + * Add the preempted queue after the command buffer + * that caused an error. + */ + list_splice_init(&ctx->preempted, restart_head[i].prev); + + /* + * Finally add all command buffers first in the submitted + * queue, to rerun them. + */ + + ctx->block_submission = false; + list_splice_init(&restart_head[i], &ctx->submitted); + } + + vmw_cmdbuf_man_process(man); + spin_unlock(&man->lock); + + if (global_block && vmw_cmdbuf_startstop(man, 0, true)) + DRM_ERROR("Failed restarting command buffer contexts\n"); + + /* Send a new fence in case one was removed */ + if (send_fence) { + vmw_cmd_send_fence(man->dev_priv, &dummy); + wake_up_all(&man->idle_queue); + } + + mutex_unlock(&man->error_mutex); +} + +/** + * vmw_cmdbuf_man_idle - Check whether the command buffer manager is idle. + * + * @man: The command buffer manager. + * @check_preempted: Check also the preempted queue for pending command buffers. + * + */ +static bool vmw_cmdbuf_man_idle(struct vmw_cmdbuf_man *man, + bool check_preempted) +{ + struct vmw_cmdbuf_context *ctx; + bool idle = false; + int i; + + spin_lock(&man->lock); + vmw_cmdbuf_man_process(man); + for_each_cmdbuf_ctx(man, i, ctx) { + if (!list_empty(&ctx->submitted) || + !list_empty(&ctx->hw_submitted) || + (check_preempted && !list_empty(&ctx->preempted))) + goto out_unlock; + } + + idle = list_empty(&man->error); + +out_unlock: + spin_unlock(&man->lock); + + return idle; +} + +/** + * __vmw_cmdbuf_cur_flush - Flush the current command buffer for small kernel + * command submissions + * + * @man: The command buffer manager. + * + * Flushes the current command buffer without allocating a new one. A new one + * is automatically allocated when needed. Call with @man->cur_mutex held. + */ +static void __vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man) +{ + struct vmw_cmdbuf_header *cur = man->cur; + + lockdep_assert_held_once(&man->cur_mutex); + + if (!cur) + return; + + spin_lock(&man->lock); + if (man->cur_pos == 0) { + __vmw_cmdbuf_header_free(cur); + goto out_unlock; + } + + man->cur->cb_header->length = man->cur_pos; + vmw_cmdbuf_ctx_add(man, man->cur, SVGA_CB_CONTEXT_0); +out_unlock: + spin_unlock(&man->lock); + man->cur = NULL; + man->cur_pos = 0; +} + +/** + * vmw_cmdbuf_cur_flush - Flush the current command buffer for small kernel + * command submissions + * + * @man: The command buffer manager. + * @interruptible: Whether to sleep interruptible when sleeping. + * + * Flushes the current command buffer without allocating a new one. A new one + * is automatically allocated when needed. + */ +int vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man, + bool interruptible) +{ + int ret = vmw_cmdbuf_cur_lock(man, interruptible); + + if (ret) + return ret; + + __vmw_cmdbuf_cur_flush(man); + vmw_cmdbuf_cur_unlock(man); + + return 0; +} + +/** + * vmw_cmdbuf_idle - Wait for command buffer manager idle. + * + * @man: The command buffer manager. + * @interruptible: Sleep interruptible while waiting. + * @timeout: Time out after this many ticks. + * + * Wait until the command buffer manager has processed all command buffers, + * or until a timeout occurs. If a timeout occurs, the function will return + * -EBUSY. + */ +int vmw_cmdbuf_idle(struct vmw_cmdbuf_man *man, bool interruptible, + unsigned long timeout) +{ + int ret; + + ret = vmw_cmdbuf_cur_flush(man, interruptible); + vmw_generic_waiter_add(man->dev_priv, + SVGA_IRQFLAG_COMMAND_BUFFER, + &man->dev_priv->cmdbuf_waiters); + + if (interruptible) { + ret = wait_event_interruptible_timeout + (man->idle_queue, vmw_cmdbuf_man_idle(man, true), + timeout); + } else { + ret = wait_event_timeout + (man->idle_queue, vmw_cmdbuf_man_idle(man, true), + timeout); + } + vmw_generic_waiter_remove(man->dev_priv, + SVGA_IRQFLAG_COMMAND_BUFFER, + &man->dev_priv->cmdbuf_waiters); + if (ret == 0) { + if (!vmw_cmdbuf_man_idle(man, true)) + ret = -EBUSY; + else + ret = 0; + } + if (ret > 0) + ret = 0; + + return ret; +} + +/** + * vmw_cmdbuf_try_alloc - Try to allocate buffer space from the main pool. + * + * @man: The command buffer manager. + * @info: Allocation info. Will hold the size on entry and allocated mm node + * on successful return. + * + * Try to allocate buffer space from the main pool. Returns true if succeeded. + * If a fatal error was hit, the error code is returned in @info->ret. + */ +static bool vmw_cmdbuf_try_alloc(struct vmw_cmdbuf_man *man, + struct vmw_cmdbuf_alloc_info *info) +{ + int ret; + + if (info->done) + return true; + + memset(info->node, 0, sizeof(*info->node)); + spin_lock(&man->lock); + ret = drm_mm_insert_node(&man->mm, info->node, info->page_size); + if (ret) { + vmw_cmdbuf_man_process(man); + ret = drm_mm_insert_node(&man->mm, info->node, info->page_size); + } + + spin_unlock(&man->lock); + info->done = !ret; + + return info->done; +} + +/** + * vmw_cmdbuf_alloc_space - Allocate buffer space from the main pool. + * + * @man: The command buffer manager. + * @node: Pointer to pre-allocated range-manager node. + * @size: The size of the allocation. + * @interruptible: Whether to sleep interruptible while waiting for space. + * + * This function allocates buffer space from the main pool, and if there is + * no space available ATM, it turns on IRQ handling and sleeps waiting for it to + * become available. + */ +static int vmw_cmdbuf_alloc_space(struct vmw_cmdbuf_man *man, + struct drm_mm_node *node, + size_t size, + bool interruptible) +{ + struct vmw_cmdbuf_alloc_info info; + + info.page_size = PFN_UP(size); + info.node = node; + info.done = false; + + /* + * To prevent starvation of large requests, only one allocating call + * at a time waiting for space. + */ + if (interruptible) { + if (mutex_lock_interruptible(&man->space_mutex)) + return -ERESTARTSYS; + } else { + mutex_lock(&man->space_mutex); + } + + /* Try to allocate space without waiting. */ + if (vmw_cmdbuf_try_alloc(man, &info)) + goto out_unlock; + + vmw_generic_waiter_add(man->dev_priv, + SVGA_IRQFLAG_COMMAND_BUFFER, + &man->dev_priv->cmdbuf_waiters); + + if (interruptible) { + int ret; + + ret = wait_event_interruptible + (man->alloc_queue, vmw_cmdbuf_try_alloc(man, &info)); + if (ret) { + vmw_generic_waiter_remove + (man->dev_priv, SVGA_IRQFLAG_COMMAND_BUFFER, + &man->dev_priv->cmdbuf_waiters); + mutex_unlock(&man->space_mutex); + return ret; + } + } else { + wait_event(man->alloc_queue, vmw_cmdbuf_try_alloc(man, &info)); + } + vmw_generic_waiter_remove(man->dev_priv, + SVGA_IRQFLAG_COMMAND_BUFFER, + &man->dev_priv->cmdbuf_waiters); + +out_unlock: + mutex_unlock(&man->space_mutex); + + return 0; +} + +/** + * vmw_cmdbuf_space_pool - Set up a command buffer header with command buffer + * space from the main pool. + * + * @man: The command buffer manager. + * @header: Pointer to the header to set up. + * @size: The requested size of the buffer space. + * @interruptible: Whether to sleep interruptible while waiting for space. + */ +static int vmw_cmdbuf_space_pool(struct vmw_cmdbuf_man *man, + struct vmw_cmdbuf_header *header, + size_t size, + bool interruptible) +{ + SVGACBHeader *cb_hdr; + size_t offset; + int ret; + + if (!man->has_pool) + return -ENOMEM; + + ret = vmw_cmdbuf_alloc_space(man, &header->node, size, interruptible); + + if (ret) + return ret; + + header->cb_header = dma_pool_zalloc(man->headers, GFP_KERNEL, + &header->handle); + if (!header->cb_header) { + ret = -ENOMEM; + goto out_no_cb_header; + } + + header->size = header->node.size << PAGE_SHIFT; + cb_hdr = header->cb_header; + offset = header->node.start << PAGE_SHIFT; + header->cmd = man->map + offset; + if (man->using_mob) { + cb_hdr->flags = SVGA_CB_FLAG_MOB; + cb_hdr->ptr.mob.mobid = man->cmd_space->tbo.resource->start; + cb_hdr->ptr.mob.mobOffset = offset; + } else { + cb_hdr->ptr.pa = (u64)man->handle + (u64)offset; + } + + return 0; + +out_no_cb_header: + spin_lock(&man->lock); + drm_mm_remove_node(&header->node); + spin_unlock(&man->lock); + + return ret; +} + +/** + * vmw_cmdbuf_space_inline - Set up a command buffer header with + * inline command buffer space. + * + * @man: The command buffer manager. + * @header: Pointer to the header to set up. + * @size: The requested size of the buffer space. + */ +static int vmw_cmdbuf_space_inline(struct vmw_cmdbuf_man *man, + struct vmw_cmdbuf_header *header, + int size) +{ + struct vmw_cmdbuf_dheader *dheader; + SVGACBHeader *cb_hdr; + + if (WARN_ON_ONCE(size > VMW_CMDBUF_INLINE_SIZE)) + return -ENOMEM; + + dheader = dma_pool_zalloc(man->dheaders, GFP_KERNEL, + &header->handle); + if (!dheader) + return -ENOMEM; + + header->inline_space = true; + header->size = VMW_CMDBUF_INLINE_SIZE; + cb_hdr = &dheader->cb_header; + header->cb_header = cb_hdr; + header->cmd = dheader->cmd; + cb_hdr->status = SVGA_CB_STATUS_NONE; + cb_hdr->flags = SVGA_CB_FLAG_NONE; + cb_hdr->ptr.pa = (u64)header->handle + + (u64)offsetof(struct vmw_cmdbuf_dheader, cmd); + + return 0; +} + +/** + * vmw_cmdbuf_alloc - Allocate a command buffer header complete with + * command buffer space. + * + * @man: The command buffer manager. + * @size: The requested size of the buffer space. + * @interruptible: Whether to sleep interruptible while waiting for space. + * @p_header: points to a header pointer to populate on successful return. + * + * Returns a pointer to command buffer space if successful. Otherwise + * returns an error pointer. The header pointer returned in @p_header should + * be used for upcoming calls to vmw_cmdbuf_reserve() and vmw_cmdbuf_commit(). + */ +void *vmw_cmdbuf_alloc(struct vmw_cmdbuf_man *man, + size_t size, bool interruptible, + struct vmw_cmdbuf_header **p_header) +{ + struct vmw_cmdbuf_header *header; + int ret = 0; + + *p_header = NULL; + + header = kzalloc(sizeof(*header), GFP_KERNEL); + if (!header) + return ERR_PTR(-ENOMEM); + + if (size <= VMW_CMDBUF_INLINE_SIZE) + ret = vmw_cmdbuf_space_inline(man, header, size); + else + ret = vmw_cmdbuf_space_pool(man, header, size, interruptible); + + if (ret) { + kfree(header); + return ERR_PTR(ret); + } + + header->man = man; + INIT_LIST_HEAD(&header->list); + header->cb_header->status = SVGA_CB_STATUS_NONE; + *p_header = header; + + return header->cmd; +} + +/** + * vmw_cmdbuf_reserve_cur - Reserve space for commands in the current + * command buffer. + * + * @man: The command buffer manager. + * @size: The requested size of the commands. + * @ctx_id: The context id if any. Otherwise set to SVGA3D_REG_INVALID. + * @interruptible: Whether to sleep interruptible while waiting for space. + * + * Returns a pointer to command buffer space if successful. Otherwise + * returns an error pointer. + */ +static void *vmw_cmdbuf_reserve_cur(struct vmw_cmdbuf_man *man, + size_t size, + int ctx_id, + bool interruptible) +{ + struct vmw_cmdbuf_header *cur; + void *ret; + + if (vmw_cmdbuf_cur_lock(man, interruptible)) + return ERR_PTR(-ERESTARTSYS); + + cur = man->cur; + if (cur && (size + man->cur_pos > cur->size || + ((cur->cb_header->flags & SVGA_CB_FLAG_DX_CONTEXT) && + ctx_id != cur->cb_header->dxContext))) + __vmw_cmdbuf_cur_flush(man); + + if (!man->cur) { + ret = vmw_cmdbuf_alloc(man, + max_t(size_t, size, man->default_size), + interruptible, &man->cur); + if (IS_ERR(ret)) { + vmw_cmdbuf_cur_unlock(man); + return ret; + } + + cur = man->cur; + } + + if (ctx_id != SVGA3D_INVALID_ID) { + cur->cb_header->flags |= SVGA_CB_FLAG_DX_CONTEXT; + cur->cb_header->dxContext = ctx_id; + } + + cur->reserved = size; + + return (void *) (man->cur->cmd + man->cur_pos); +} + +/** + * vmw_cmdbuf_commit_cur - Commit commands in the current command buffer. + * + * @man: The command buffer manager. + * @size: The size of the commands actually written. + * @flush: Whether to flush the command buffer immediately. + */ +static void vmw_cmdbuf_commit_cur(struct vmw_cmdbuf_man *man, + size_t size, bool flush) +{ + struct vmw_cmdbuf_header *cur = man->cur; + + lockdep_assert_held_once(&man->cur_mutex); + + WARN_ON(size > cur->reserved); + man->cur_pos += size; + if (!size) + cur->cb_header->flags &= ~SVGA_CB_FLAG_DX_CONTEXT; + if (flush) + __vmw_cmdbuf_cur_flush(man); + vmw_cmdbuf_cur_unlock(man); +} + +/** + * vmw_cmdbuf_reserve - Reserve space for commands in a command buffer. + * + * @man: The command buffer manager. + * @size: The requested size of the commands. + * @ctx_id: The context id if any. Otherwise set to SVGA3D_REG_INVALID. + * @interruptible: Whether to sleep interruptible while waiting for space. + * @header: Header of the command buffer. NULL if the current command buffer + * should be used. + * + * Returns a pointer to command buffer space if successful. Otherwise + * returns an error pointer. + */ +void *vmw_cmdbuf_reserve(struct vmw_cmdbuf_man *man, size_t size, + int ctx_id, bool interruptible, + struct vmw_cmdbuf_header *header) +{ + if (!header) + return vmw_cmdbuf_reserve_cur(man, size, ctx_id, interruptible); + + if (size > header->size) + return ERR_PTR(-EINVAL); + + if (ctx_id != SVGA3D_INVALID_ID) { + header->cb_header->flags |= SVGA_CB_FLAG_DX_CONTEXT; + header->cb_header->dxContext = ctx_id; + } + + header->reserved = size; + return header->cmd; +} + +/** + * vmw_cmdbuf_commit - Commit commands in a command buffer. + * + * @man: The command buffer manager. + * @size: The size of the commands actually written. + * @header: Header of the command buffer. NULL if the current command buffer + * should be used. + * @flush: Whether to flush the command buffer immediately. + */ +void vmw_cmdbuf_commit(struct vmw_cmdbuf_man *man, size_t size, + struct vmw_cmdbuf_header *header, bool flush) +{ + if (!header) { + vmw_cmdbuf_commit_cur(man, size, flush); + return; + } + + (void) vmw_cmdbuf_cur_lock(man, false); + __vmw_cmdbuf_cur_flush(man); + WARN_ON(size > header->reserved); + man->cur = header; + man->cur_pos = size; + if (!size) + header->cb_header->flags &= ~SVGA_CB_FLAG_DX_CONTEXT; + if (flush) + __vmw_cmdbuf_cur_flush(man); + vmw_cmdbuf_cur_unlock(man); +} + + +/** + * vmw_cmdbuf_send_device_command - Send a command through the device context. + * + * @man: The command buffer manager. + * @command: Pointer to the command to send. + * @size: Size of the command. + * + * Synchronously sends a device context command. + */ +static int vmw_cmdbuf_send_device_command(struct vmw_cmdbuf_man *man, + const void *command, + size_t size) +{ + struct vmw_cmdbuf_header *header; + int status; + void *cmd = vmw_cmdbuf_alloc(man, size, false, &header); + + if (IS_ERR(cmd)) + return PTR_ERR(cmd); + + memcpy(cmd, command, size); + header->cb_header->length = size; + header->cb_context = SVGA_CB_CONTEXT_DEVICE; + spin_lock(&man->lock); + status = vmw_cmdbuf_header_submit(header); + spin_unlock(&man->lock); + vmw_cmdbuf_header_free(header); + + if (status != SVGA_CB_STATUS_COMPLETED) { + DRM_ERROR("Device context command failed with status %d\n", + status); + return -EINVAL; + } + + return 0; +} + +/** + * vmw_cmdbuf_preempt - Send a preempt command through the device + * context. + * + * @man: The command buffer manager. + * @context: Device context to pass command through. + * + * Synchronously sends a preempt command. + */ +static int vmw_cmdbuf_preempt(struct vmw_cmdbuf_man *man, u32 context) +{ + struct { + uint32 id; + SVGADCCmdPreempt body; + } __packed cmd; + + cmd.id = SVGA_DC_CMD_PREEMPT; + cmd.body.context = SVGA_CB_CONTEXT_0 + context; + cmd.body.ignoreIDZero = 0; + + return vmw_cmdbuf_send_device_command(man, &cmd, sizeof(cmd)); +} + + +/** + * vmw_cmdbuf_startstop - Send a start / stop command through the device + * context. + * + * @man: The command buffer manager. + * @context: Device context to start/stop. + * @enable: Whether to enable or disable the context. + * + * Synchronously sends a device start / stop context command. + */ +static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, u32 context, + bool enable) +{ + struct { + uint32 id; + SVGADCCmdStartStop body; + } __packed cmd; + + cmd.id = SVGA_DC_CMD_START_STOP_CONTEXT; + cmd.body.enable = (enable) ? 1 : 0; + cmd.body.context = SVGA_CB_CONTEXT_0 + context; + + return vmw_cmdbuf_send_device_command(man, &cmd, sizeof(cmd)); +} + +/** + * vmw_cmdbuf_set_pool_size - Set command buffer manager sizes + * + * @man: The command buffer manager. + * @size: The size of the main space pool. + * + * Set the size and allocate the main command buffer space pool. + * If successful, this enables large command submissions. + * Note that this function requires that rudimentary command + * submission is already available and that the MOB memory manager is alive. + * Returns 0 on success. Negative error code on failure. + */ +int vmw_cmdbuf_set_pool_size(struct vmw_cmdbuf_man *man, size_t size) +{ + struct vmw_private *dev_priv = man->dev_priv; + int ret; + + if (man->has_pool) + return -EINVAL; + + /* First, try to allocate a huge chunk of DMA memory */ + size = PAGE_ALIGN(size); + man->map = dma_alloc_coherent(dev_priv->drm.dev, size, + &man->handle, GFP_KERNEL); + if (man->map) { + man->using_mob = false; + } else { + struct vmw_bo_params bo_params = { + .domain = VMW_BO_DOMAIN_MOB, + .busy_domain = VMW_BO_DOMAIN_MOB, + .bo_type = ttm_bo_type_kernel, + .size = size, + .pin = true + }; + /* + * DMA memory failed. If we can have command buffers in a + * MOB, try to use that instead. Note that this will + * actually call into the already enabled manager, when + * binding the MOB. + */ + if (!(dev_priv->capabilities & SVGA_CAP_DX) || + !dev_priv->has_mob) + return -ENOMEM; + + ret = vmw_bo_create(dev_priv, &bo_params, &man->cmd_space); + if (ret) + return ret; + + man->map = vmw_bo_map_and_cache(man->cmd_space); + man->using_mob = man->map; + } + + man->size = size; + drm_mm_init(&man->mm, 0, size >> PAGE_SHIFT); + + man->has_pool = true; + + /* + * For now, set the default size to VMW_CMDBUF_INLINE_SIZE to + * prevent deadlocks from happening when vmw_cmdbuf_space_pool() + * needs to wait for space and we block on further command + * submissions to be able to free up space. + */ + man->default_size = VMW_CMDBUF_INLINE_SIZE; + drm_info(&dev_priv->drm, + "Using command buffers with %s pool.\n", + (man->using_mob) ? "MOB" : "DMA"); + + return 0; +} + +/** + * vmw_cmdbuf_man_create: Create a command buffer manager and enable it for + * inline command buffer submissions only. + * + * @dev_priv: Pointer to device private structure. + * + * Returns a pointer to a cummand buffer manager to success or error pointer + * on failure. The command buffer manager will be enabled for submissions of + * size VMW_CMDBUF_INLINE_SIZE only. + */ +struct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv) +{ + struct vmw_cmdbuf_man *man; + struct vmw_cmdbuf_context *ctx; + unsigned int i; + int ret; + + if (!(dev_priv->capabilities & SVGA_CAP_COMMAND_BUFFERS)) + return ERR_PTR(-ENOSYS); + + man = kzalloc(sizeof(*man), GFP_KERNEL); + if (!man) + return ERR_PTR(-ENOMEM); + + man->num_contexts = (dev_priv->capabilities & SVGA_CAP_HP_CMD_QUEUE) ? + 2 : 1; + man->headers = dma_pool_create("vmwgfx cmdbuf", + dev_priv->drm.dev, + sizeof(SVGACBHeader), + 64, PAGE_SIZE); + if (!man->headers) { + ret = -ENOMEM; + goto out_no_pool; + } + + man->dheaders = dma_pool_create("vmwgfx inline cmdbuf", + dev_priv->drm.dev, + sizeof(struct vmw_cmdbuf_dheader), + 64, PAGE_SIZE); + if (!man->dheaders) { + ret = -ENOMEM; + goto out_no_dpool; + } + + for_each_cmdbuf_ctx(man, i, ctx) + vmw_cmdbuf_ctx_init(ctx); + + INIT_LIST_HEAD(&man->error); + spin_lock_init(&man->lock); + mutex_init(&man->cur_mutex); + mutex_init(&man->space_mutex); + mutex_init(&man->error_mutex); + man->default_size = VMW_CMDBUF_INLINE_SIZE; + init_waitqueue_head(&man->alloc_queue); + init_waitqueue_head(&man->idle_queue); + man->dev_priv = dev_priv; + man->max_hw_submitted = SVGA_CB_MAX_QUEUED_PER_CONTEXT - 1; + INIT_WORK(&man->work, &vmw_cmdbuf_work_func); + vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_ERROR, + &dev_priv->error_waiters); + ret = vmw_cmdbuf_startstop(man, 0, true); + if (ret) { + DRM_ERROR("Failed starting command buffer contexts\n"); + vmw_cmdbuf_man_destroy(man); + return ERR_PTR(ret); + } + + return man; + +out_no_dpool: + dma_pool_destroy(man->headers); +out_no_pool: + kfree(man); + + return ERR_PTR(ret); +} + +/** + * vmw_cmdbuf_remove_pool - Take down the main buffer space pool. + * + * @man: Pointer to a command buffer manager. + * + * This function removes the main buffer space pool, and should be called + * before MOB memory management is removed. When this function has been called, + * only small command buffer submissions of size VMW_CMDBUF_INLINE_SIZE or + * less are allowed, and the default size of the command buffer for small kernel + * submissions is also set to this size. + */ +void vmw_cmdbuf_remove_pool(struct vmw_cmdbuf_man *man) +{ + if (!man->has_pool) + return; + + man->has_pool = false; + man->default_size = VMW_CMDBUF_INLINE_SIZE; + (void) vmw_cmdbuf_idle(man, false, 10*HZ); + if (man->using_mob) + vmw_bo_unreference(&man->cmd_space); + else + dma_free_coherent(man->dev_priv->drm.dev, + man->size, man->map, man->handle); +} + +/** + * vmw_cmdbuf_man_destroy - Take down a command buffer manager. + * + * @man: Pointer to a command buffer manager. + * + * This function idles and then destroys a command buffer manager. + */ +void vmw_cmdbuf_man_destroy(struct vmw_cmdbuf_man *man) +{ + WARN_ON_ONCE(man->has_pool); + (void) vmw_cmdbuf_idle(man, false, 10*HZ); + + if (vmw_cmdbuf_startstop(man, 0, false)) + DRM_ERROR("Failed stopping command buffer contexts.\n"); + + vmw_generic_waiter_remove(man->dev_priv, SVGA_IRQFLAG_ERROR, + &man->dev_priv->error_waiters); + (void) cancel_work_sync(&man->work); + dma_pool_destroy(man->dheaders); + dma_pool_destroy(man->headers); + mutex_destroy(&man->cur_mutex); + mutex_destroy(&man->space_mutex); + mutex_destroy(&man->error_mutex); + kfree(man); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c new file mode 100644 index 0000000000..47bc0b4110 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2014-2022 VMware, Inc., Palo Alto, CA., USA + * + * 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 <linux/hashtable.h> + +#define VMW_CMDBUF_RES_MAN_HT_ORDER 12 + +/** + * struct vmw_cmdbuf_res - Command buffer managed resource entry. + * + * @res: Refcounted pointer to a struct vmw_resource. + * @hash: Hash entry for the manager hash table. + * @head: List head used either by the staging list or the manager list + * of committed resources. + * @state: Staging state of this resource entry. + * @man: Pointer to a resource manager for this entry. + */ +struct vmw_cmdbuf_res { + struct vmw_resource *res; + struct vmwgfx_hash_item hash; + struct list_head head; + enum vmw_cmdbuf_res_state state; + struct vmw_cmdbuf_res_manager *man; +}; + +/** + * struct vmw_cmdbuf_res_manager - Command buffer resource manager. + * + * @resources: Hash table containing staged and committed command buffer + * resources + * @list: List of committed command buffer resources. + * @dev_priv: Pointer to a device private structure. + * + * @resources and @list are protected by the cmdbuf mutex for now. + */ +struct vmw_cmdbuf_res_manager { + DECLARE_HASHTABLE(resources, VMW_CMDBUF_RES_MAN_HT_ORDER); + struct list_head list; + struct vmw_private *dev_priv; +}; + + +/** + * vmw_cmdbuf_res_lookup - Look up a command buffer resource + * + * @man: Pointer to the command buffer resource manager + * @res_type: The resource type, that combined with the user key + * identifies the resource. + * @user_key: The user key. + * + * Returns a valid refcounted struct vmw_resource pointer on success, + * an error pointer on failure. + */ +struct vmw_resource * +vmw_cmdbuf_res_lookup(struct vmw_cmdbuf_res_manager *man, + enum vmw_cmdbuf_res_type res_type, + u32 user_key) +{ + struct vmwgfx_hash_item *hash; + unsigned long key = user_key | (res_type << 24); + + hash_for_each_possible_rcu(man->resources, hash, head, key) { + if (hash->key == key) + return hlist_entry(hash, struct vmw_cmdbuf_res, hash)->res; + } + return ERR_PTR(-EINVAL); +} + +/** + * vmw_cmdbuf_res_free - Free a command buffer resource. + * + * @man: Pointer to the command buffer resource manager + * @entry: Pointer to a struct vmw_cmdbuf_res. + * + * Frees a struct vmw_cmdbuf_res entry and drops its reference to the + * struct vmw_resource. + */ +static void vmw_cmdbuf_res_free(struct vmw_cmdbuf_res_manager *man, + struct vmw_cmdbuf_res *entry) +{ + list_del(&entry->head); + hash_del_rcu(&entry->hash.head); + vmw_resource_unreference(&entry->res); + kfree(entry); +} + +/** + * vmw_cmdbuf_res_commit - Commit a list of command buffer resource actions + * + * @list: Caller's list of command buffer resource actions. + * + * This function commits a list of command buffer resource + * additions or removals. + * It is typically called when the execbuf ioctl call triggering these + * actions has committed the fifo contents to the device. + */ +void vmw_cmdbuf_res_commit(struct list_head *list) +{ + struct vmw_cmdbuf_res *entry, *next; + + list_for_each_entry_safe(entry, next, list, head) { + list_del(&entry->head); + if (entry->res->func->commit_notify) + entry->res->func->commit_notify(entry->res, + entry->state); + switch (entry->state) { + case VMW_CMDBUF_RES_ADD: + entry->state = VMW_CMDBUF_RES_COMMITTED; + list_add_tail(&entry->head, &entry->man->list); + break; + case VMW_CMDBUF_RES_DEL: + vmw_resource_unreference(&entry->res); + kfree(entry); + break; + default: + BUG(); + break; + } + } +} + +/** + * vmw_cmdbuf_res_revert - Revert a list of command buffer resource actions + * + * @list: Caller's list of command buffer resource action + * + * This function reverts a list of command buffer resource + * 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_cmdbuf_res_revert(struct list_head *list) +{ + struct vmw_cmdbuf_res *entry, *next; + + list_for_each_entry_safe(entry, next, list, head) { + switch (entry->state) { + case VMW_CMDBUF_RES_ADD: + vmw_cmdbuf_res_free(entry->man, entry); + break; + case VMW_CMDBUF_RES_DEL: + hash_add_rcu(entry->man->resources, &entry->hash.head, + entry->hash.key); + list_move_tail(&entry->head, &entry->man->list); + entry->state = VMW_CMDBUF_RES_COMMITTED; + break; + default: + BUG(); + break; + } + } +} + +/** + * vmw_cmdbuf_res_add - Stage a command buffer managed resource for addition. + * + * @man: Pointer to the command buffer resource manager. + * @res_type: The resource type. + * @user_key: The user-space id of the resource. + * @res: Valid (refcount != 0) pointer to a struct vmw_resource. + * @list: The staging list. + * + * This function allocates a struct vmw_cmdbuf_res entry and adds the + * resource to the hash table of the manager identified by @man. The + * entry is then put on the staging list identified by @list. + */ +int vmw_cmdbuf_res_add(struct vmw_cmdbuf_res_manager *man, + enum vmw_cmdbuf_res_type res_type, + u32 user_key, + struct vmw_resource *res, + struct list_head *list) +{ + struct vmw_cmdbuf_res *cres; + + cres = kzalloc(sizeof(*cres), GFP_KERNEL); + if (unlikely(!cres)) + return -ENOMEM; + + cres->hash.key = user_key | (res_type << 24); + hash_add_rcu(man->resources, &cres->hash.head, cres->hash.key); + + cres->state = VMW_CMDBUF_RES_ADD; + cres->res = vmw_resource_reference(res); + cres->man = man; + list_add_tail(&cres->head, list); + + return 0; +} + +/** + * vmw_cmdbuf_res_remove - Stage a command buffer managed resource for removal. + * + * @man: Pointer to the command buffer resource manager. + * @res_type: The resource type. + * @user_key: The user-space id of the resource. + * @list: The staging list. + * @res_p: If the resource is in an already committed state, points to the + * struct vmw_resource on successful return. The pointer will be + * non ref-counted. + * + * This function looks up the struct vmw_cmdbuf_res entry from the manager + * hash table and, if it exists, removes it. Depending on its current staging + * state it then either removes the entry from the staging list or adds it + * to it with a staging state of removal. + */ +int vmw_cmdbuf_res_remove(struct vmw_cmdbuf_res_manager *man, + enum vmw_cmdbuf_res_type res_type, + u32 user_key, + struct list_head *list, + struct vmw_resource **res_p) +{ + struct vmw_cmdbuf_res *entry = NULL; + struct vmwgfx_hash_item *hash; + unsigned long key = user_key | (res_type << 24); + + hash_for_each_possible_rcu(man->resources, hash, head, key) { + if (hash->key == key) { + entry = hlist_entry(hash, struct vmw_cmdbuf_res, hash); + break; + } + } + if (unlikely(!entry)) + return -EINVAL; + + switch (entry->state) { + case VMW_CMDBUF_RES_ADD: + vmw_cmdbuf_res_free(man, entry); + *res_p = NULL; + break; + case VMW_CMDBUF_RES_COMMITTED: + hash_del_rcu(&entry->hash.head); + list_del(&entry->head); + entry->state = VMW_CMDBUF_RES_DEL; + list_add_tail(&entry->head, list); + *res_p = entry->res; + break; + default: + BUG(); + break; + } + + return 0; +} + +/** + * vmw_cmdbuf_res_man_create - Allocate a command buffer managed resource + * manager. + * + * @dev_priv: Pointer to a struct vmw_private + * + * Allocates and initializes a command buffer managed resource manager. Returns + * an error pointer on failure. + */ +struct vmw_cmdbuf_res_manager * +vmw_cmdbuf_res_man_create(struct vmw_private *dev_priv) +{ + struct vmw_cmdbuf_res_manager *man; + + man = kzalloc(sizeof(*man), GFP_KERNEL); + if (!man) + return ERR_PTR(-ENOMEM); + + man->dev_priv = dev_priv; + INIT_LIST_HEAD(&man->list); + hash_init(man->resources); + return man; +} + +/** + * vmw_cmdbuf_res_man_destroy - Destroy a command buffer managed resource + * manager. + * + * @man: Pointer to the manager to destroy. + * + * This function destroys a command buffer managed resource manager and + * unreferences / frees all command buffer managed resources and -entries + * associated with it. + */ +void vmw_cmdbuf_res_man_destroy(struct vmw_cmdbuf_res_manager *man) +{ + struct vmw_cmdbuf_res *entry, *next; + + list_for_each_entry_safe(entry, next, &man->list, head) + vmw_cmdbuf_res_free(man, entry); + + kfree(man); +} + diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c new file mode 100644 index 0000000000..ecc503e427 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c @@ -0,0 +1,899 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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 "vmwgfx_binding.h" +#include "vmwgfx_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" + +struct vmw_user_context { + struct ttm_base_object base; + struct vmw_resource res; + struct vmw_ctx_binding_state *cbs; + struct vmw_cmdbuf_res_manager *man; + struct vmw_resource *cotables[SVGA_COTABLE_MAX]; + spinlock_t cotable_lock; + struct vmw_bo *dx_query_mob; +}; + +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_dx_context_create(struct vmw_resource *res); +static int vmw_dx_context_bind(struct vmw_resource *res, + struct ttm_validate_buffer *val_buf); +static int vmw_dx_context_unbind(struct vmw_resource *res, + bool readback, + struct ttm_validate_buffer *val_buf); +static int vmw_dx_context_destroy(struct vmw_resource *res); + +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_guest_memory = false, + .may_evict = false, + .type_name = "legacy contexts", + .domain = VMW_BO_DOMAIN_SYS, + .busy_domain = VMW_BO_DOMAIN_SYS, + .create = NULL, + .destroy = NULL, + .bind = NULL, + .unbind = NULL +}; + +static const struct vmw_res_func vmw_gb_context_func = { + .res_type = vmw_res_context, + .needs_guest_memory = true, + .may_evict = true, + .prio = 3, + .dirty_prio = 3, + .type_name = "guest backed contexts", + .domain = VMW_BO_DOMAIN_MOB, + .busy_domain = VMW_BO_DOMAIN_MOB, + .create = vmw_gb_context_create, + .destroy = vmw_gb_context_destroy, + .bind = vmw_gb_context_bind, + .unbind = vmw_gb_context_unbind +}; + +static const struct vmw_res_func vmw_dx_context_func = { + .res_type = vmw_res_dx_context, + .needs_guest_memory = true, + .may_evict = true, + .prio = 3, + .dirty_prio = 3, + .type_name = "dx contexts", + .domain = VMW_BO_DOMAIN_MOB, + .busy_domain = VMW_BO_DOMAIN_MOB, + .create = vmw_dx_context_create, + .destroy = vmw_dx_context_destroy, + .bind = vmw_dx_context_bind, + .unbind = vmw_dx_context_unbind +}; + +/* + * Context management: + */ + +static void vmw_context_cotables_unref(struct vmw_private *dev_priv, + struct vmw_user_context *uctx) +{ + struct vmw_resource *res; + int i; + u32 cotable_max = has_sm5_context(dev_priv) ? + SVGA_COTABLE_MAX : SVGA_COTABLE_DX10_MAX; + + for (i = 0; i < cotable_max; ++i) { + spin_lock(&uctx->cotable_lock); + res = uctx->cotables[i]; + uctx->cotables[i] = NULL; + spin_unlock(&uctx->cotable_lock); + + if (res) + vmw_resource_unreference(&res); + } +} + +static void vmw_hw_context_destroy(struct vmw_resource *res) +{ + struct vmw_user_context *uctx = + container_of(res, struct vmw_user_context, res); + struct vmw_private *dev_priv = res->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDestroyContext body; + } *cmd; + + + if (res->func->destroy == vmw_gb_context_destroy || + res->func->destroy == vmw_dx_context_destroy) { + mutex_lock(&dev_priv->cmdbuf_mutex); + vmw_cmdbuf_res_man_destroy(uctx->man); + mutex_lock(&dev_priv->binding_mutex); + vmw_binding_state_kill(uctx->cbs); + (void) res->func->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); + vmw_context_cotables_unref(dev_priv, uctx); + return; + } + + vmw_execbuf_release_pinned_bo(dev_priv); + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + return; + + cmd->header.id = SVGA_3D_CMD_CONTEXT_DESTROY; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = res->id; + + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + vmw_fifo_resource_dec(dev_priv); +} + +static int vmw_gb_context_init(struct vmw_private *dev_priv, + bool dx, + struct vmw_resource *res, + void (*res_free)(struct vmw_resource *res)) +{ + int ret, i; + struct vmw_user_context *uctx = + container_of(res, struct vmw_user_context, res); + + res->guest_memory_size = (dx ? sizeof(SVGADXContextMobFormat) : + sizeof(SVGAGBContextData)); + ret = vmw_resource_init(dev_priv, res, true, + res_free, + dx ? &vmw_dx_context_func : + &vmw_gb_context_func); + if (unlikely(ret != 0)) + goto out_err; + + if (dev_priv->has_mob) { + uctx->man = vmw_cmdbuf_res_man_create(dev_priv); + if (IS_ERR(uctx->man)) { + ret = PTR_ERR(uctx->man); + uctx->man = NULL; + goto out_err; + } + } + + uctx->cbs = vmw_binding_state_alloc(dev_priv); + if (IS_ERR(uctx->cbs)) { + ret = PTR_ERR(uctx->cbs); + goto out_err; + } + + spin_lock_init(&uctx->cotable_lock); + + if (dx) { + u32 cotable_max = has_sm5_context(dev_priv) ? + SVGA_COTABLE_MAX : SVGA_COTABLE_DX10_MAX; + for (i = 0; i < cotable_max; ++i) { + uctx->cotables[i] = vmw_cotable_alloc(dev_priv, + &uctx->res, i); + if (IS_ERR(uctx->cotables[i])) { + ret = PTR_ERR(uctx->cotables[i]); + goto out_cotables; + } + } + } + + res->hw_destroy = vmw_hw_context_destroy; + return 0; + +out_cotables: + vmw_context_cotables_unref(dev_priv, uctx); +out_err: + if (res_free) + res_free(res); + else + kfree(res); + return ret; +} + +static int vmw_context_init(struct vmw_private *dev_priv, + struct vmw_resource *res, + void (*res_free)(struct vmw_resource *res), + bool dx) +{ + int ret; + + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDefineContext body; + } *cmd; + + if (dev_priv->has_mob) + return vmw_gb_context_init(dev_priv, dx, 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_HB_MAX_CONTEXT_IDS)) { + DRM_ERROR("Out of hw context ids.\n"); + vmw_resource_unreference(&res); + return -ENOMEM; + } + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) { + vmw_resource_unreference(&res); + return -ENOMEM; + } + + cmd->header.id = SVGA_3D_CMD_CONTEXT_DEFINE; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = res->id; + + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + vmw_fifo_resource_inc(dev_priv); + res->hw_destroy = vmw_hw_context_destroy; + return 0; + +out_early: + if (res_free == NULL) + kfree(res); + else + res_free(res); + return ret; +} + + +/* + * GB context. + */ + +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_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) { + 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_cmd_commit(dev_priv, sizeof(*cmd)); + vmw_fifo_resource_inc(dev_priv); + + 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->resource->mem_type != VMW_PL_MOB); + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + 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->resource->start; + cmd->body.validContents = res->guest_memory_dirty; + res->guest_memory_dirty = false; + vmw_cmd_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->resource->mem_type != VMW_PL_MOB); + + mutex_lock(&dev_priv->binding_mutex); + vmw_binding_state_scrub(uctx->cbs); + + submit_size = sizeof(*cmd2) + (readback ? sizeof(*cmd1) : 0); + + cmd = VMW_CMD_RESERVE(dev_priv, submit_size); + if (unlikely(cmd == NULL)) { + 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_cmd_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_bo_fence_single(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_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DESTROY_GB_CONTEXT; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = res->id; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + if (dev_priv->query_cid == res->id) + dev_priv->query_cid_valid = false; + vmw_resource_release_id(res); + vmw_fifo_resource_dec(dev_priv); + + return 0; +} + +/* + * DX context. + */ + +static int vmw_dx_context_create(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + int ret; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXDefineContext 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_DXCONTEXT)) { + ret = -EBUSY; + goto out_no_fifo; + } + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) { + ret = -ENOMEM; + goto out_no_fifo; + } + + cmd->header.id = SVGA_3D_CMD_DX_DEFINE_CONTEXT; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = res->id; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + vmw_fifo_resource_inc(dev_priv); + + return 0; + +out_no_fifo: + vmw_resource_release_id(res); +out_no_id: + return ret; +} + +static int vmw_dx_context_bind(struct vmw_resource *res, + struct ttm_validate_buffer *val_buf) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXBindContext body; + } *cmd; + struct ttm_buffer_object *bo = val_buf->bo; + + BUG_ON(bo->resource->mem_type != VMW_PL_MOB); + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_BIND_CONTEXT; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = res->id; + cmd->body.mobid = bo->resource->start; + cmd->body.validContents = res->guest_memory_dirty; + res->guest_memory_dirty = false; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + + return 0; +} + +/** + * vmw_dx_context_scrub_cotables - Scrub all bindings and + * cotables from a context + * + * @ctx: Pointer to the context resource + * @readback: Whether to save the otable contents on scrubbing. + * + * COtables must be unbound before their context, but unbinding requires + * the backup buffer being reserved, whereas scrubbing does not. + * This function scrubs all cotables of a context, potentially reading back + * the contents into their backup buffers. However, scrubbing cotables + * also makes the device context invalid, so scrub all bindings first so + * that doesn't have to be done later with an invalid context. + */ +void vmw_dx_context_scrub_cotables(struct vmw_resource *ctx, + bool readback) +{ + struct vmw_user_context *uctx = + container_of(ctx, struct vmw_user_context, res); + u32 cotable_max = has_sm5_context(ctx->dev_priv) ? + SVGA_COTABLE_MAX : SVGA_COTABLE_DX10_MAX; + int i; + + vmw_binding_state_scrub(uctx->cbs); + for (i = 0; i < cotable_max; ++i) { + struct vmw_resource *res; + + /* Avoid racing with ongoing cotable destruction. */ + spin_lock(&uctx->cotable_lock); + res = uctx->cotables[vmw_cotable_scrub_order[i]]; + if (res) + res = vmw_resource_reference_unless_doomed(res); + spin_unlock(&uctx->cotable_lock); + if (!res) + continue; + + WARN_ON(vmw_cotable_scrub(res, readback)); + vmw_resource_unreference(&res); + } +} + +static int vmw_dx_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; + SVGA3dCmdDXReadbackContext body; + } *cmd1; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXBindContext body; + } *cmd2; + uint32_t submit_size; + uint8_t *cmd; + + + BUG_ON(bo->resource->mem_type != VMW_PL_MOB); + + mutex_lock(&dev_priv->binding_mutex); + vmw_dx_context_scrub_cotables(res, readback); + + if (uctx->dx_query_mob && uctx->dx_query_mob->dx_query_ctx && + readback) { + WARN_ON(uctx->dx_query_mob->dx_query_ctx != res); + if (vmw_query_readback_all(uctx->dx_query_mob)) + DRM_ERROR("Failed to read back query states\n"); + } + + submit_size = sizeof(*cmd2) + (readback ? sizeof(*cmd1) : 0); + + cmd = VMW_CMD_RESERVE(dev_priv, submit_size); + if (unlikely(cmd == NULL)) { + mutex_unlock(&dev_priv->binding_mutex); + return -ENOMEM; + } + + cmd2 = (void *) cmd; + if (readback) { + cmd1 = (void *) cmd; + cmd1->header.id = SVGA_3D_CMD_DX_READBACK_CONTEXT; + cmd1->header.size = sizeof(cmd1->body); + cmd1->body.cid = res->id; + cmd2 = (void *) (&cmd1[1]); + } + cmd2->header.id = SVGA_3D_CMD_DX_BIND_CONTEXT; + cmd2->header.size = sizeof(cmd2->body); + cmd2->body.cid = res->id; + cmd2->body.mobid = SVGA3D_INVALID_ID; + + vmw_cmd_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_bo_fence_single(bo, fence); + + if (likely(fence != NULL)) + vmw_fence_obj_unreference(&fence); + + return 0; +} + +static int vmw_dx_context_destroy(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXDestroyContext body; + } *cmd; + + if (likely(res->id == -1)) + return 0; + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_DESTROY_CONTEXT; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = res->id; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + if (dev_priv->query_cid == res->id) + dev_priv->query_cid_valid = false; + vmw_resource_release_id(res); + vmw_fifo_resource_dec(dev_priv); + + 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); + + if (ctx->cbs) + vmw_binding_state_free(ctx->cbs); + + (void) vmw_context_bind_dx_query(res, NULL); + + ttm_base_object_kfree(ctx, base); +} + +/* + * 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); +} + +static int vmw_context_define(struct drm_device *dev, void *data, + struct drm_file *file_priv, bool dx) +{ + 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; + + if (!has_sm4_context(dev_priv) && dx) { + VMW_DEBUG_USER("DX contexts not supported by device.\n"); + return -EINVAL; + } + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (unlikely(!ctx)) { + ret = -ENOMEM; + goto out_ret; + } + + 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, dx); + if (unlikely(ret != 0)) + goto out_ret; + + tmp = vmw_resource_reference(&ctx->res); + ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT, + &vmw_user_context_base_release); + + if (unlikely(ret != 0)) { + vmw_resource_unreference(&tmp); + goto out_err; + } + + arg->cid = ctx->base.handle; +out_err: + vmw_resource_unreference(&res); +out_ret: + return ret; +} + +int vmw_context_define_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return vmw_context_define(dev, data, file_priv, false); +} + +int vmw_extended_context_define_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + union drm_vmw_extended_context_arg *arg = (typeof(arg)) data; + struct drm_vmw_context_arg *rep = &arg->rep; + + switch (arg->req) { + case drm_vmw_context_legacy: + return vmw_context_define(dev, rep, file_priv, false); + case drm_vmw_context_dx: + return vmw_context_define(dev, rep, file_priv, true); + default: + break; + } + return -EINVAL; +} + +/** + * 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) +{ + struct vmw_user_context *uctx = + container_of(ctx, struct vmw_user_context, res); + + return vmw_binding_state_list(uctx->cbs); +} + +struct vmw_cmdbuf_res_manager *vmw_context_res_man(struct vmw_resource *ctx) +{ + return container_of(ctx, struct vmw_user_context, res)->man; +} + +struct vmw_resource *vmw_context_cotable(struct vmw_resource *ctx, + SVGACOTableType cotable_type) +{ + u32 cotable_max = has_sm5_context(ctx->dev_priv) ? + SVGA_COTABLE_MAX : SVGA_COTABLE_DX10_MAX; + + if (cotable_type >= cotable_max) + return ERR_PTR(-EINVAL); + + return container_of(ctx, struct vmw_user_context, res)-> + cotables[cotable_type]; +} + +/** + * vmw_context_binding_state - + * Return a pointer to a context binding state structure + * + * @ctx: The context resource + * + * Returns the current state of bindings of the given context. Note that + * this state becomes stale as soon as the dev_priv::binding_mutex is unlocked. + */ +struct vmw_ctx_binding_state * +vmw_context_binding_state(struct vmw_resource *ctx) +{ + return container_of(ctx, struct vmw_user_context, res)->cbs; +} + +/** + * vmw_context_bind_dx_query - + * Sets query MOB for the context. If @mob is NULL, then this function will + * remove the association between the MOB and the context. This function + * assumes the binding_mutex is held. + * + * @ctx_res: The context resource + * @mob: a reference to the query MOB + * + * Returns -EINVAL if a MOB has already been set and does not match the one + * specified in the parameter. 0 otherwise. + */ +int vmw_context_bind_dx_query(struct vmw_resource *ctx_res, + struct vmw_bo *mob) +{ + struct vmw_user_context *uctx = + container_of(ctx_res, struct vmw_user_context, res); + + if (mob == NULL) { + if (uctx->dx_query_mob) { + uctx->dx_query_mob->dx_query_ctx = NULL; + vmw_bo_unreference(&uctx->dx_query_mob); + uctx->dx_query_mob = NULL; + } + + return 0; + } + + /* Can only have one MOB per context for queries */ + if (uctx->dx_query_mob && uctx->dx_query_mob != mob) + return -EINVAL; + + mob->dx_query_ctx = ctx_res; + + if (!uctx->dx_query_mob) + uctx->dx_query_mob = vmw_bo_reference(mob); + + return 0; +} + +/** + * vmw_context_get_dx_query_mob - Returns non-counted reference to DX query mob + * + * @ctx_res: The context resource + */ +struct vmw_bo * +vmw_context_get_dx_query_mob(struct vmw_resource *ctx_res) +{ + struct vmw_user_context *uctx = + container_of(ctx_res, struct vmw_user_context, res); + + return uctx->dx_query_mob; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c new file mode 100644 index 0000000000..a7c0769226 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c @@ -0,0 +1,681 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2014-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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. + * + **************************************************************************/ +/* + * Treat context OTables as resources to make use of the resource + * backing MOB eviction mechanism, that is used to read back the COTable + * whenever the backing MOB is evicted. + */ + +#include "vmwgfx_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_mksstat.h" +#include "vmwgfx_resource_priv.h" +#include "vmwgfx_so.h" + +#include <drm/ttm/ttm_placement.h> + +/** + * struct vmw_cotable - Context Object Table resource + * + * @res: struct vmw_resource we are deriving from. + * @ctx: non-refcounted pointer to the owning context. + * @size_read_back: Size of data read back during eviction. + * @seen_entries: Seen entries in command stream for this cotable. + * @type: The cotable type. + * @scrubbed: Whether the cotable has been scrubbed. + * @resource_list: List of resources in the cotable. + */ +struct vmw_cotable { + struct vmw_resource res; + struct vmw_resource *ctx; + size_t size_read_back; + int seen_entries; + u32 type; + bool scrubbed; + struct list_head resource_list; +}; + +/** + * struct vmw_cotable_info - Static info about cotable types + * + * @min_initial_entries: Min number of initial intries at cotable allocation + * for this cotable type. + * @size: Size of each entry. + * @unbind_func: Unbind call-back function. + */ +struct vmw_cotable_info { + u32 min_initial_entries; + u32 size; + void (*unbind_func)(struct vmw_private *, struct list_head *, + bool); +}; + + +/* + * Getting the initial size right is difficult because it all depends + * on what the userspace is doing. The sizes will be aligned up to + * a PAGE_SIZE so we just want to make sure that for majority of apps + * the initial number of entries doesn't require an immediate resize. + * For all cotables except SVGACOTableDXElementLayoutEntry and + * SVGACOTableDXBlendStateEntry the initial number of entries fits + * within the PAGE_SIZE. For SVGACOTableDXElementLayoutEntry and + * SVGACOTableDXBlendStateEntry we want to reserve two pages, + * because that's what all apps will require initially. + */ +static const struct vmw_cotable_info co_info[] = { + {1, sizeof(SVGACOTableDXRTViewEntry), &vmw_view_cotable_list_destroy}, + {1, sizeof(SVGACOTableDXDSViewEntry), &vmw_view_cotable_list_destroy}, + {1, sizeof(SVGACOTableDXSRViewEntry), &vmw_view_cotable_list_destroy}, + {PAGE_SIZE/sizeof(SVGACOTableDXElementLayoutEntry) + 1, sizeof(SVGACOTableDXElementLayoutEntry), NULL}, + {PAGE_SIZE/sizeof(SVGACOTableDXBlendStateEntry) + 1, sizeof(SVGACOTableDXBlendStateEntry), NULL}, + {1, sizeof(SVGACOTableDXDepthStencilEntry), NULL}, + {1, sizeof(SVGACOTableDXRasterizerStateEntry), NULL}, + {1, sizeof(SVGACOTableDXSamplerEntry), NULL}, + {1, sizeof(SVGACOTableDXStreamOutputEntry), &vmw_dx_streamoutput_cotable_list_scrub}, + {1, sizeof(SVGACOTableDXQueryEntry), NULL}, + {1, sizeof(SVGACOTableDXShaderEntry), &vmw_dx_shader_cotable_list_scrub}, + {1, sizeof(SVGACOTableDXUAViewEntry), &vmw_view_cotable_list_destroy} +}; + +/* + * Cotables with bindings that we remove must be scrubbed first, + * otherwise, the device will swap in an invalid context when we remove + * bindings before scrubbing a cotable... + */ +const SVGACOTableType vmw_cotable_scrub_order[] = { + SVGA_COTABLE_RTVIEW, + SVGA_COTABLE_DSVIEW, + SVGA_COTABLE_SRVIEW, + SVGA_COTABLE_DXSHADER, + SVGA_COTABLE_ELEMENTLAYOUT, + SVGA_COTABLE_BLENDSTATE, + SVGA_COTABLE_DEPTHSTENCIL, + SVGA_COTABLE_RASTERIZERSTATE, + SVGA_COTABLE_SAMPLER, + SVGA_COTABLE_STREAMOUTPUT, + SVGA_COTABLE_DXQUERY, + SVGA_COTABLE_UAVIEW, +}; + +static int vmw_cotable_bind(struct vmw_resource *res, + struct ttm_validate_buffer *val_buf); +static int vmw_cotable_unbind(struct vmw_resource *res, + bool readback, + struct ttm_validate_buffer *val_buf); +static int vmw_cotable_create(struct vmw_resource *res); +static int vmw_cotable_destroy(struct vmw_resource *res); + +static const struct vmw_res_func vmw_cotable_func = { + .res_type = vmw_res_cotable, + .needs_guest_memory = true, + .may_evict = true, + .prio = 3, + .dirty_prio = 3, + .type_name = "context guest backed object tables", + .domain = VMW_BO_DOMAIN_MOB, + .busy_domain = VMW_BO_DOMAIN_MOB, + .create = vmw_cotable_create, + .destroy = vmw_cotable_destroy, + .bind = vmw_cotable_bind, + .unbind = vmw_cotable_unbind, +}; + +/** + * vmw_cotable - Convert a struct vmw_resource pointer to a struct + * vmw_cotable pointer + * + * @res: Pointer to the resource. + */ +static struct vmw_cotable *vmw_cotable(struct vmw_resource *res) +{ + return container_of(res, struct vmw_cotable, res); +} + +/** + * vmw_cotable_destroy - Cotable resource destroy callback + * + * @res: Pointer to the cotable resource. + * + * There is no device cotable destroy command, so this function only + * makes sure that the resource id is set to invalid. + */ +static int vmw_cotable_destroy(struct vmw_resource *res) +{ + res->id = -1; + return 0; +} + +/** + * vmw_cotable_unscrub - Undo a cotable unscrub operation + * + * @res: Pointer to the cotable resource + * + * This function issues commands to (re)bind the cotable to + * its backing mob, which needs to be validated and reserved at this point. + * This is identical to bind() except the function interface looks different. + */ +static int vmw_cotable_unscrub(struct vmw_resource *res) +{ + struct vmw_cotable *vcotbl = vmw_cotable(res); + struct vmw_private *dev_priv = res->dev_priv; + struct ttm_buffer_object *bo = &res->guest_memory_bo->tbo; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetCOTable body; + } *cmd; + + WARN_ON_ONCE(bo->resource->mem_type != VMW_PL_MOB); + dma_resv_assert_held(bo->base.resv); + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (!cmd) + return -ENOMEM; + + WARN_ON(vcotbl->ctx->id == SVGA3D_INVALID_ID); + WARN_ON(bo->resource->mem_type != VMW_PL_MOB); + cmd->header.id = SVGA_3D_CMD_DX_SET_COTABLE; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = vcotbl->ctx->id; + cmd->body.type = vcotbl->type; + cmd->body.mobid = bo->resource->start; + cmd->body.validSizeInBytes = vcotbl->size_read_back; + + vmw_cmd_commit_flush(dev_priv, sizeof(*cmd)); + vcotbl->scrubbed = false; + + return 0; +} + +/** + * vmw_cotable_bind - Undo a cotable unscrub operation + * + * @res: Pointer to the cotable resource + * @val_buf: Pointer to a struct ttm_validate_buffer prepared by the caller + * for convenience / fencing. + * + * This function issues commands to (re)bind the cotable to + * its backing mob, which needs to be validated and reserved at this point. + */ +static int vmw_cotable_bind(struct vmw_resource *res, + struct ttm_validate_buffer *val_buf) +{ + /* + * The create() callback may have changed @res->backup without + * the caller noticing, and with val_buf->bo still pointing to + * the old backup buffer. Although hackish, and not used currently, + * take the opportunity to correct the value here so that it's not + * misused in the future. + */ + val_buf->bo = &res->guest_memory_bo->tbo; + + return vmw_cotable_unscrub(res); +} + +/** + * vmw_cotable_scrub - Scrub the cotable from the device. + * + * @res: Pointer to the cotable resource. + * @readback: Whether initiate a readback of the cotable data to the backup + * buffer. + * + * In some situations (context swapouts) it might be desirable to make the + * device forget about the cotable without performing a full unbind. A full + * unbind requires reserved backup buffers and it might not be possible to + * reserve them due to locking order violation issues. The vmw_cotable_scrub + * function implements a partial unbind() without that requirement but with the + * following restrictions. + * 1) Before the cotable is again used by the GPU, vmw_cotable_unscrub() must + * be called. + * 2) Before the cotable backing buffer is used by the CPU, or during the + * resource destruction, vmw_cotable_unbind() must be called. + */ +int vmw_cotable_scrub(struct vmw_resource *res, bool readback) +{ + struct vmw_cotable *vcotbl = vmw_cotable(res); + struct vmw_private *dev_priv = res->dev_priv; + size_t submit_size; + + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXReadbackCOTable body; + } *cmd0; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetCOTable body; + } *cmd1; + + if (vcotbl->scrubbed) + return 0; + + if (co_info[vcotbl->type].unbind_func) + co_info[vcotbl->type].unbind_func(dev_priv, + &vcotbl->resource_list, + readback); + submit_size = sizeof(*cmd1); + if (readback) + submit_size += sizeof(*cmd0); + + cmd1 = VMW_CMD_RESERVE(dev_priv, submit_size); + if (!cmd1) + return -ENOMEM; + + vcotbl->size_read_back = 0; + if (readback) { + cmd0 = (void *) cmd1; + cmd0->header.id = SVGA_3D_CMD_DX_READBACK_COTABLE; + cmd0->header.size = sizeof(cmd0->body); + cmd0->body.cid = vcotbl->ctx->id; + cmd0->body.type = vcotbl->type; + cmd1 = (void *) &cmd0[1]; + vcotbl->size_read_back = res->guest_memory_size; + } + cmd1->header.id = SVGA_3D_CMD_DX_SET_COTABLE; + cmd1->header.size = sizeof(cmd1->body); + cmd1->body.cid = vcotbl->ctx->id; + cmd1->body.type = vcotbl->type; + cmd1->body.mobid = SVGA3D_INVALID_ID; + cmd1->body.validSizeInBytes = 0; + vmw_cmd_commit_flush(dev_priv, submit_size); + vcotbl->scrubbed = true; + + /* Trigger a create() on next validate. */ + res->id = -1; + + return 0; +} + +/** + * vmw_cotable_unbind - Cotable resource unbind callback + * + * @res: Pointer to the cotable resource. + * @readback: Whether to read back cotable data to the backup buffer. + * @val_buf: Pointer to a struct ttm_validate_buffer prepared by the caller + * for convenience / fencing. + * + * Unbinds the cotable from the device and fences the backup buffer. + */ +static int vmw_cotable_unbind(struct vmw_resource *res, + bool readback, + struct ttm_validate_buffer *val_buf) +{ + struct vmw_cotable *vcotbl = vmw_cotable(res); + struct vmw_private *dev_priv = res->dev_priv; + struct ttm_buffer_object *bo = val_buf->bo; + struct vmw_fence_obj *fence; + + if (!vmw_resource_mob_attached(res)) + return 0; + + WARN_ON_ONCE(bo->resource->mem_type != VMW_PL_MOB); + dma_resv_assert_held(bo->base.resv); + + mutex_lock(&dev_priv->binding_mutex); + if (!vcotbl->scrubbed) + vmw_dx_context_scrub_cotables(vcotbl->ctx, readback); + mutex_unlock(&dev_priv->binding_mutex); + (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); + vmw_bo_fence_single(bo, fence); + if (likely(fence != NULL)) + vmw_fence_obj_unreference(&fence); + + return 0; +} + +/** + * vmw_cotable_readback - Read back a cotable without unbinding. + * + * @res: The cotable resource. + * + * Reads back a cotable to its backing mob without scrubbing the MOB from + * the cotable. The MOB is fenced for subsequent CPU access. + */ +static int vmw_cotable_readback(struct vmw_resource *res) +{ + struct vmw_cotable *vcotbl = vmw_cotable(res); + struct vmw_private *dev_priv = res->dev_priv; + + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXReadbackCOTable body; + } *cmd; + struct vmw_fence_obj *fence; + + if (!vcotbl->scrubbed) { + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (!cmd) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_READBACK_COTABLE; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = vcotbl->ctx->id; + cmd->body.type = vcotbl->type; + vcotbl->size_read_back = res->guest_memory_size; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + } + + (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); + vmw_bo_fence_single(&res->guest_memory_bo->tbo, fence); + vmw_fence_obj_unreference(&fence); + + return 0; +} + +/** + * vmw_cotable_resize - Resize a cotable. + * + * @res: The cotable resource. + * @new_size: The new size. + * + * Resizes a cotable and binds the new backup buffer. + * On failure the cotable is left intact. + * Important! This function may not fail once the MOB switch has been + * committed to hardware. That would put the device context in an + * invalid state which we can't currently recover from. + */ +static int vmw_cotable_resize(struct vmw_resource *res, size_t new_size) +{ + struct ttm_operation_ctx ctx = { false, false }; + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_cotable *vcotbl = vmw_cotable(res); + struct vmw_bo *buf, *old_buf = res->guest_memory_bo; + struct ttm_buffer_object *bo, *old_bo = &res->guest_memory_bo->tbo; + size_t old_size = res->guest_memory_size; + size_t old_size_read_back = vcotbl->size_read_back; + size_t cur_size_read_back; + struct ttm_bo_kmap_obj old_map, new_map; + int ret; + size_t i; + struct vmw_bo_params bo_params = { + .domain = VMW_BO_DOMAIN_MOB, + .busy_domain = VMW_BO_DOMAIN_MOB, + .bo_type = ttm_bo_type_device, + .size = new_size, + .pin = true + }; + + MKS_STAT_TIME_DECL(MKSSTAT_KERN_COTABLE_RESIZE); + MKS_STAT_TIME_PUSH(MKSSTAT_KERN_COTABLE_RESIZE); + + ret = vmw_cotable_readback(res); + if (ret) + goto out_done; + + cur_size_read_back = vcotbl->size_read_back; + vcotbl->size_read_back = old_size_read_back; + + /* + * While device is processing, Allocate and reserve a buffer object + * for the new COTable. Initially pin the buffer object to make sure + * we can use tryreserve without failure. + */ + ret = vmw_gem_object_create(dev_priv, &bo_params, &buf); + if (ret) { + DRM_ERROR("Failed initializing new cotable MOB.\n"); + goto out_done; + } + + bo = &buf->tbo; + WARN_ON_ONCE(ttm_bo_reserve(bo, false, true, NULL)); + + ret = ttm_bo_wait(old_bo, false, false); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed waiting for cotable unbind.\n"); + goto out_wait; + } + + /* + * Do a page by page copy of COTables. This eliminates slow vmap()s. + * This should really be a TTM utility. + */ + for (i = 0; i < PFN_UP(old_bo->resource->size); ++i) { + bool dummy; + + ret = ttm_bo_kmap(old_bo, i, 1, &old_map); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed mapping old COTable on resize.\n"); + goto out_wait; + } + ret = ttm_bo_kmap(bo, i, 1, &new_map); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed mapping new COTable on resize.\n"); + goto out_map_new; + } + memcpy(ttm_kmap_obj_virtual(&new_map, &dummy), + ttm_kmap_obj_virtual(&old_map, &dummy), + PAGE_SIZE); + ttm_bo_kunmap(&new_map); + ttm_bo_kunmap(&old_map); + } + + /* Unpin new buffer, and switch backup buffers. */ + vmw_bo_placement_set(buf, + VMW_BO_DOMAIN_MOB, + VMW_BO_DOMAIN_MOB); + ret = ttm_bo_validate(bo, &buf->placement, &ctx); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed validating new COTable backup buffer.\n"); + goto out_wait; + } + + vmw_resource_mob_detach(res); + res->guest_memory_bo = buf; + res->guest_memory_size = new_size; + vcotbl->size_read_back = cur_size_read_back; + + /* + * Now tell the device to switch. If this fails, then we need to + * revert the full resize. + */ + ret = vmw_cotable_unscrub(res); + if (ret) { + DRM_ERROR("Failed switching COTable backup buffer.\n"); + res->guest_memory_bo = old_buf; + res->guest_memory_size = old_size; + vcotbl->size_read_back = old_size_read_back; + vmw_resource_mob_attach(res); + goto out_wait; + } + + vmw_resource_mob_attach(res); + /* Let go of the old mob. */ + vmw_user_bo_unref(&old_buf); + res->id = vcotbl->type; + + ret = dma_resv_reserve_fences(bo->base.resv, 1); + if (unlikely(ret)) + goto out_wait; + + /* Release the pin acquired in vmw_bo_create */ + ttm_bo_unpin(bo); + + MKS_STAT_TIME_POP(MKSSTAT_KERN_COTABLE_RESIZE); + + return 0; + +out_map_new: + ttm_bo_kunmap(&old_map); +out_wait: + ttm_bo_unpin(bo); + ttm_bo_unreserve(bo); + vmw_user_bo_unref(&buf); + +out_done: + MKS_STAT_TIME_POP(MKSSTAT_KERN_COTABLE_RESIZE); + + return ret; +} + +/** + * vmw_cotable_create - Cotable resource create callback + * + * @res: Pointer to a cotable resource. + * + * There is no separate create command for cotables, so this callback, which + * is called before bind() in the validation sequence is instead used for two + * things. + * 1) Unscrub the cotable if it is scrubbed and still attached to a backup + * buffer. + * 2) Resize the cotable if needed. + */ +static int vmw_cotable_create(struct vmw_resource *res) +{ + struct vmw_cotable *vcotbl = vmw_cotable(res); + size_t new_size = res->guest_memory_size; + size_t needed_size; + int ret; + + /* Check whether we need to resize the cotable */ + needed_size = (vcotbl->seen_entries + 1) * co_info[vcotbl->type].size; + while (needed_size > new_size) + new_size *= 2; + + if (likely(new_size <= res->guest_memory_size)) { + if (vcotbl->scrubbed && vmw_resource_mob_attached(res)) { + ret = vmw_cotable_unscrub(res); + if (ret) + return ret; + } + res->id = vcotbl->type; + return 0; + } + + return vmw_cotable_resize(res, new_size); +} + +/** + * vmw_hw_cotable_destroy - Cotable hw_destroy callback + * + * @res: Pointer to a cotable resource. + * + * The final (part of resource destruction) destroy callback. + */ +static void vmw_hw_cotable_destroy(struct vmw_resource *res) +{ + (void) vmw_cotable_destroy(res); +} + +/** + * vmw_cotable_free - Cotable resource destructor + * + * @res: Pointer to a cotable resource. + */ +static void vmw_cotable_free(struct vmw_resource *res) +{ + kfree(res); +} + +/** + * vmw_cotable_alloc - Create a cotable resource + * + * @dev_priv: Pointer to a device private struct. + * @ctx: Pointer to the context resource. + * The cotable resource will not add a refcount. + * @type: The cotable type. + */ +struct vmw_resource *vmw_cotable_alloc(struct vmw_private *dev_priv, + struct vmw_resource *ctx, + u32 type) +{ + struct vmw_cotable *vcotbl; + int ret; + u32 num_entries; + + vcotbl = kzalloc(sizeof(*vcotbl), GFP_KERNEL); + if (unlikely(!vcotbl)) { + ret = -ENOMEM; + goto out_no_alloc; + } + + ret = vmw_resource_init(dev_priv, &vcotbl->res, true, + vmw_cotable_free, &vmw_cotable_func); + if (unlikely(ret != 0)) + goto out_no_init; + + INIT_LIST_HEAD(&vcotbl->resource_list); + vcotbl->res.id = type; + vcotbl->res.guest_memory_size = PAGE_SIZE; + num_entries = PAGE_SIZE / co_info[type].size; + if (num_entries < co_info[type].min_initial_entries) { + vcotbl->res.guest_memory_size = co_info[type].min_initial_entries * + co_info[type].size; + vcotbl->res.guest_memory_size = PFN_ALIGN(vcotbl->res.guest_memory_size); + } + + vcotbl->scrubbed = true; + vcotbl->seen_entries = -1; + vcotbl->type = type; + vcotbl->ctx = ctx; + + vcotbl->res.hw_destroy = vmw_hw_cotable_destroy; + + return &vcotbl->res; + +out_no_init: + kfree(vcotbl); +out_no_alloc: + return ERR_PTR(ret); +} + +/** + * vmw_cotable_notify - Notify the cotable about an item creation + * + * @res: Pointer to a cotable resource. + * @id: Item id. + */ +int vmw_cotable_notify(struct vmw_resource *res, int id) +{ + struct vmw_cotable *vcotbl = vmw_cotable(res); + + if (id < 0 || id >= SVGA_COTABLE_MAX_IDS) { + DRM_ERROR("Illegal COTable id. Type is %u. Id is %d\n", + (unsigned) vcotbl->type, id); + return -EINVAL; + } + + if (vcotbl->seen_entries < id) { + /* Trigger a call to create() on next validate */ + res->id = -1; + vcotbl->seen_entries = id; + } + + return 0; +} + +/** + * vmw_cotable_add_resource - add a view to the cotable's list of active views. + * + * @res: pointer struct vmw_resource representing the cotable. + * @head: pointer to the struct list_head member of the resource, dedicated + * to the cotable active resource list. + */ +void vmw_cotable_add_resource(struct vmw_resource *res, struct list_head *head) +{ + struct vmw_cotable *vcotbl = + container_of(res, struct vmw_cotable, res); + + list_add_tail(head, &vcotbl->resource_list); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.c b/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.c new file mode 100644 index 0000000000..829df395c2 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.c @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright 2021 VMware, Inc., Palo Alto, CA., USA + * + * 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_devcaps.h" + +#include "vmwgfx_drv.h" + + +struct svga_3d_compat_cap { + SVGA3dFifoCapsRecordHeader header; + SVGA3dFifoCapPair pairs[SVGA3D_DEVCAP_MAX]; +}; + + +static u32 vmw_mask_legacy_multisample(unsigned int cap, u32 fmt_value) +{ + /* + * A version of user-space exists which use MULTISAMPLE_MASKABLESAMPLES + * to check the sample count supported by virtual device. Since there + * never was support for multisample count for backing MOB return 0. + * + * MULTISAMPLE_MASKABLESAMPLES devcap is marked as deprecated by virtual + * device. + */ + if (cap == SVGA3D_DEVCAP_DEAD5) + return 0; + + return fmt_value; +} + +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(SVGA3dFifoCapPair); + + if (max_size > SVGA3D_DEVCAP_MAX) + max_size = SVGA3D_DEVCAP_MAX; + + compat_cap->header.length = + (pair_offset + max_size * sizeof(SVGA3dFifoCapPair)) / sizeof(u32); + compat_cap->header.type = SVGA3D_FIFO_CAPS_RECORD_DEVCAPS; + + for (i = 0; i < max_size; ++i) { + compat_cap->pairs[i][0] = i; + compat_cap->pairs[i][1] = vmw_mask_legacy_multisample + (i, dev_priv->devcaps[i]); + } + + return 0; +} + +int vmw_devcaps_create(struct vmw_private *vmw) +{ + bool gb_objects = !!(vmw->capabilities & SVGA_CAP_GBOBJECTS); + uint32_t i; + + if (gb_objects) { + vmw->devcaps = vzalloc(sizeof(uint32_t) * SVGA3D_DEVCAP_MAX); + if (!vmw->devcaps) + return -ENOMEM; + for (i = 0; i < SVGA3D_DEVCAP_MAX; ++i) { + vmw_write(vmw, SVGA_REG_DEV_CAP, i); + vmw->devcaps[i] = vmw_read(vmw, SVGA_REG_DEV_CAP); + } + } + return 0; +} + +void vmw_devcaps_destroy(struct vmw_private *vmw) +{ + vfree(vmw->devcaps); + vmw->devcaps = NULL; +} + + +uint32 vmw_devcaps_size(const struct vmw_private *vmw, + bool gb_aware) +{ + bool gb_objects = !!(vmw->capabilities & SVGA_CAP_GBOBJECTS); + if (gb_objects && gb_aware) + return SVGA3D_DEVCAP_MAX * sizeof(uint32_t); + else if (gb_objects) + return sizeof(struct svga_3d_compat_cap) + + sizeof(uint32_t); + else if (vmw->fifo_mem != NULL) + return (SVGA_FIFO_3D_CAPS_LAST - SVGA_FIFO_3D_CAPS + 1) * + sizeof(uint32_t); + else + return 0; +} + +int vmw_devcaps_copy(struct vmw_private *vmw, bool gb_aware, + void *dst, uint32_t dst_size) +{ + int ret; + bool gb_objects = !!(vmw->capabilities & SVGA_CAP_GBOBJECTS); + if (gb_objects && gb_aware) { + memcpy(dst, vmw->devcaps, dst_size); + } else if (gb_objects) { + ret = vmw_fill_compat_cap(vmw, dst, dst_size); + if (unlikely(ret != 0)) + return ret; + } else if (vmw->fifo_mem) { + u32 *fifo_mem = vmw->fifo_mem; + memcpy(dst, &fifo_mem[SVGA_FIFO_3D_CAPS], dst_size); + } else + return -EINVAL; + return 0; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.h b/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.h new file mode 100644 index 0000000000..f70e923ac3 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright 2021 VMware, Inc., Palo Alto, CA., USA + * + * 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_DEVCAPS_H_ +#define _VMWGFX_DEVCAPS_H_ + +#include "vmwgfx_drv.h" + +#include "device_include/svga_reg.h" + +int vmw_devcaps_create(struct vmw_private *vmw); +void vmw_devcaps_destroy(struct vmw_private *vmw); +uint32_t vmw_devcaps_size(const struct vmw_private *vmw, bool gb_aware); +int vmw_devcaps_copy(struct vmw_private *vmw, bool gb_aware, + void *dst, uint32_t dst_size); + +static inline uint32_t vmw_devcap_get(struct vmw_private *vmw, + uint32_t devcap) +{ + bool gb_objects = !!(vmw->capabilities & SVGA_CAP_GBOBJECTS); + if (gb_objects) + return vmw->devcaps[devcap]; + return 0; +} + +#endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c new file mode 100644 index 0000000000..d3e308fdfd --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -0,0 +1,1699 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_binding.h" +#include "vmwgfx_devcaps.h" +#include "vmwgfx_mksstat.h" +#include "ttm_object.h" + +#include <drm/drm_aperture.h> +#include <drm/drm_drv.h> +#include <drm/drm_fbdev_generic.h> +#include <drm/drm_gem_ttm_helper.h> +#include <drm/drm_ioctl.h> +#include <drm/drm_module.h> +#include <drm/drm_sysfs.h> +#include <drm/ttm/ttm_range_manager.h> +#include <drm/ttm/ttm_placement.h> +#include <generated/utsrelease.h> + +#ifdef CONFIG_X86 +#include <asm/hypervisor.h> +#endif +#include <linux/cc_platform.h> +#include <linux/dma-mapping.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/version.h> + +#define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices" + +/* + * Fully encoded drm commands. Might move to vmw_drm.h + */ + +#define DRM_IOCTL_VMW_GET_PARAM \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GET_PARAM, \ + struct drm_vmw_getparam_arg) +#define DRM_IOCTL_VMW_ALLOC_DMABUF \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_ALLOC_DMABUF, \ + union drm_vmw_alloc_dmabuf_arg) +#define DRM_IOCTL_VMW_UNREF_DMABUF \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_DMABUF, \ + struct drm_vmw_unref_dmabuf_arg) +#define DRM_IOCTL_VMW_CURSOR_BYPASS \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_CURSOR_BYPASS, \ + struct drm_vmw_cursor_bypass_arg) + +#define DRM_IOCTL_VMW_CONTROL_STREAM \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_CONTROL_STREAM, \ + struct drm_vmw_control_stream_arg) +#define DRM_IOCTL_VMW_CLAIM_STREAM \ + DRM_IOR(DRM_COMMAND_BASE + DRM_VMW_CLAIM_STREAM, \ + struct drm_vmw_stream_arg) +#define DRM_IOCTL_VMW_UNREF_STREAM \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_STREAM, \ + struct drm_vmw_stream_arg) + +#define DRM_IOCTL_VMW_CREATE_CONTEXT \ + DRM_IOR(DRM_COMMAND_BASE + DRM_VMW_CREATE_CONTEXT, \ + struct drm_vmw_context_arg) +#define DRM_IOCTL_VMW_UNREF_CONTEXT \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_CONTEXT, \ + struct drm_vmw_context_arg) +#define DRM_IOCTL_VMW_CREATE_SURFACE \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_CREATE_SURFACE, \ + union drm_vmw_surface_create_arg) +#define DRM_IOCTL_VMW_UNREF_SURFACE \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_SURFACE, \ + struct drm_vmw_surface_arg) +#define DRM_IOCTL_VMW_REF_SURFACE \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_REF_SURFACE, \ + union drm_vmw_surface_reference_arg) +#define DRM_IOCTL_VMW_EXECBUF \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_EXECBUF, \ + struct drm_vmw_execbuf_arg) +#define DRM_IOCTL_VMW_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_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) +#define DRM_IOCTL_VMW_CREATE_EXTENDED_CONTEXT \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_CREATE_EXTENDED_CONTEXT, \ + struct drm_vmw_context_arg) +#define DRM_IOCTL_VMW_GB_SURFACE_CREATE_EXT \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GB_SURFACE_CREATE_EXT, \ + union drm_vmw_gb_surface_create_ext_arg) +#define DRM_IOCTL_VMW_GB_SURFACE_REF_EXT \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GB_SURFACE_REF_EXT, \ + union drm_vmw_gb_surface_reference_ext_arg) +#define DRM_IOCTL_VMW_MSG \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_MSG, \ + struct drm_vmw_msg_arg) +#define DRM_IOCTL_VMW_MKSSTAT_RESET \ + DRM_IO(DRM_COMMAND_BASE + DRM_VMW_MKSSTAT_RESET) +#define DRM_IOCTL_VMW_MKSSTAT_ADD \ + DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_MKSSTAT_ADD, \ + struct drm_vmw_mksstat_add_arg) +#define DRM_IOCTL_VMW_MKSSTAT_REMOVE \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_MKSSTAT_REMOVE, \ + struct drm_vmw_mksstat_remove_arg) + +/* + * Ioctl definitions. + */ + +static const struct drm_ioctl_desc vmw_ioctls[] = { + DRM_IOCTL_DEF_DRV(VMW_GET_PARAM, vmw_getparam_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_ALLOC_DMABUF, vmw_gem_object_create_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_UNREF_DMABUF, vmw_bo_unref_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_CURSOR_BYPASS, + vmw_kms_cursor_bypass_ioctl, + DRM_MASTER), + + DRM_IOCTL_DEF_DRV(VMW_CONTROL_STREAM, vmw_overlay_ioctl, + DRM_MASTER), + DRM_IOCTL_DEF_DRV(VMW_CLAIM_STREAM, vmw_stream_claim_ioctl, + DRM_MASTER), + DRM_IOCTL_DEF_DRV(VMW_UNREF_STREAM, vmw_stream_unref_ioctl, + DRM_MASTER), + + DRM_IOCTL_DEF_DRV(VMW_CREATE_CONTEXT, vmw_context_define_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_UNREF_CONTEXT, vmw_context_destroy_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_CREATE_SURFACE, vmw_surface_define_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_UNREF_SURFACE, vmw_surface_destroy_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_REF_SURFACE, vmw_surface_reference_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_EXECBUF, vmw_execbuf_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_FENCE_WAIT, vmw_fence_obj_wait_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_FENCE_SIGNALED, + vmw_fence_obj_signaled_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_FENCE_UNREF, vmw_fence_obj_unref_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_FENCE_EVENT, vmw_fence_event_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_GET_3D_CAP, vmw_get_cap_3d_ioctl, + DRM_RENDER_ALLOW), + + /* these allow direct access to the framebuffers mark as master only */ + DRM_IOCTL_DEF_DRV(VMW_PRESENT, vmw_present_ioctl, + DRM_MASTER | DRM_AUTH), + DRM_IOCTL_DEF_DRV(VMW_PRESENT_READBACK, + vmw_present_readback_ioctl, + DRM_MASTER | DRM_AUTH), + /* + * The permissions of the below ioctl are overridden in + * vmw_generic_ioctl(). We require either + * DRM_MASTER or capable(CAP_SYS_ADMIN). + */ + DRM_IOCTL_DEF_DRV(VMW_UPDATE_LAYOUT, + vmw_kms_update_layout_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_CREATE_SHADER, + vmw_shader_define_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_UNREF_SHADER, + vmw_shader_destroy_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_GB_SURFACE_CREATE, + vmw_gb_surface_define_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_GB_SURFACE_REF, + vmw_gb_surface_reference_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_SYNCCPU, + vmw_user_bo_synccpu_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_CREATE_EXTENDED_CONTEXT, + vmw_extended_context_define_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_GB_SURFACE_CREATE_EXT, + vmw_gb_surface_define_ext_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_GB_SURFACE_REF_EXT, + vmw_gb_surface_reference_ext_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_MSG, + vmw_msg_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_MKSSTAT_RESET, + vmw_mksstat_reset_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_MKSSTAT_ADD, + vmw_mksstat_add_ioctl, + DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VMW_MKSSTAT_REMOVE, + vmw_mksstat_remove_ioctl, + DRM_RENDER_ALLOW), +}; + +static const struct pci_device_id vmw_pci_id_list[] = { + { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, VMWGFX_PCI_ID_SVGA2) }, + { PCI_DEVICE(PCI_VENDOR_ID_VMWARE, VMWGFX_PCI_ID_SVGA3) }, + { } +}; +MODULE_DEVICE_TABLE(pci, vmw_pci_id_list); + +static int vmw_restrict_iommu; +static int vmw_force_coherent; +static int vmw_restrict_dma_mask; +static int vmw_assume_16bpp; + +static int vmw_probe(struct pci_dev *, const struct pci_device_id *); +static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, + void *ptr); + +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); +MODULE_PARM_DESC(assume_16bpp, "Assume 16-bpp when filtering modes"); +module_param_named(assume_16bpp, vmw_assume_16bpp, int, 0600); + + +struct bitmap_name { + uint32 value; + const char *name; +}; + +static const struct bitmap_name cap1_names[] = { + { SVGA_CAP_RECT_COPY, "rect copy" }, + { SVGA_CAP_CURSOR, "cursor" }, + { SVGA_CAP_CURSOR_BYPASS, "cursor bypass" }, + { SVGA_CAP_CURSOR_BYPASS_2, "cursor bypass 2" }, + { SVGA_CAP_8BIT_EMULATION, "8bit emulation" }, + { SVGA_CAP_ALPHA_CURSOR, "alpha cursor" }, + { SVGA_CAP_3D, "3D" }, + { SVGA_CAP_EXTENDED_FIFO, "extended fifo" }, + { SVGA_CAP_MULTIMON, "multimon" }, + { SVGA_CAP_PITCHLOCK, "pitchlock" }, + { SVGA_CAP_IRQMASK, "irq mask" }, + { SVGA_CAP_DISPLAY_TOPOLOGY, "display topology" }, + { SVGA_CAP_GMR, "gmr" }, + { SVGA_CAP_TRACES, "traces" }, + { SVGA_CAP_GMR2, "gmr2" }, + { SVGA_CAP_SCREEN_OBJECT_2, "screen object 2" }, + { SVGA_CAP_COMMAND_BUFFERS, "command buffers" }, + { SVGA_CAP_CMD_BUFFERS_2, "command buffers 2" }, + { SVGA_CAP_GBOBJECTS, "gbobject" }, + { SVGA_CAP_DX, "dx" }, + { SVGA_CAP_HP_CMD_QUEUE, "hp cmd queue" }, + { SVGA_CAP_NO_BB_RESTRICTION, "no bb restriction" }, + { SVGA_CAP_CAP2_REGISTER, "cap2 register" }, +}; + + +static const struct bitmap_name cap2_names[] = { + { SVGA_CAP2_GROW_OTABLE, "grow otable" }, + { SVGA_CAP2_INTRA_SURFACE_COPY, "intra surface copy" }, + { SVGA_CAP2_DX2, "dx2" }, + { SVGA_CAP2_GB_MEMSIZE_2, "gb memsize 2" }, + { SVGA_CAP2_SCREENDMA_REG, "screendma reg" }, + { SVGA_CAP2_OTABLE_PTDEPTH_2, "otable ptdepth2" }, + { SVGA_CAP2_NON_MS_TO_MS_STRETCHBLT, "non ms to ms stretchblt" }, + { SVGA_CAP2_CURSOR_MOB, "cursor mob" }, + { SVGA_CAP2_MSHINT, "mshint" }, + { SVGA_CAP2_CB_MAX_SIZE_4MB, "cb max size 4mb" }, + { SVGA_CAP2_DX3, "dx3" }, + { SVGA_CAP2_FRAME_TYPE, "frame type" }, + { SVGA_CAP2_COTABLE_COPY, "cotable copy" }, + { SVGA_CAP2_TRACE_FULL_FB, "trace full fb" }, + { SVGA_CAP2_EXTRA_REGS, "extra regs" }, + { SVGA_CAP2_LO_STAGING, "lo staging" }, +}; + +static void vmw_print_bitmap(struct drm_device *drm, + const char *prefix, uint32_t bitmap, + const struct bitmap_name *bnames, + uint32_t num_names) +{ + char buf[512]; + uint32_t i; + uint32_t offset = 0; + for (i = 0; i < num_names; ++i) { + if ((bitmap & bnames[i].value) != 0) { + offset += snprintf(buf + offset, + ARRAY_SIZE(buf) - offset, + "%s, ", bnames[i].name); + bitmap &= ~bnames[i].value; + } + } + + drm_info(drm, "%s: %s\n", prefix, buf); + if (bitmap != 0) + drm_dbg(drm, "%s: unknown enums: %x\n", prefix, bitmap); +} + + +static void vmw_print_sm_type(struct vmw_private *dev_priv) +{ + static const char *names[] = { + [VMW_SM_LEGACY] = "Legacy", + [VMW_SM_4] = "SM4", + [VMW_SM_4_1] = "SM4_1", + [VMW_SM_5] = "SM_5", + [VMW_SM_5_1X] = "SM_5_1X", + [VMW_SM_MAX] = "Invalid" + }; + BUILD_BUG_ON(ARRAY_SIZE(names) != (VMW_SM_MAX + 1)); + drm_info(&dev_priv->drm, "Available shader model: %s.\n", + names[dev_priv->sm_type]); +} + +/** + * 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 vmw_bo *vbo; + struct ttm_bo_kmap_obj map; + volatile SVGA3dQueryResult *result; + bool dummy; + struct vmw_bo_params bo_params = { + .domain = VMW_BO_DOMAIN_SYS, + .busy_domain = VMW_BO_DOMAIN_SYS, + .bo_type = ttm_bo_type_kernel, + .size = PAGE_SIZE, + .pin = true + }; + + /* + * Create the vbo as pinned, so that a tryreserve will + * immediately succeed. This is because we're the only + * user of the bo currently. + */ + ret = vmw_bo_create(dev_priv, &bo_params, &vbo); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_bo_reserve(&vbo->tbo, false, true, NULL); + BUG_ON(ret != 0); + vmw_bo_pin_reserved(vbo, true); + + ret = ttm_bo_kmap(&vbo->tbo, 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_reserved(vbo, false); + ttm_bo_unreserve(&vbo->tbo); + + if (unlikely(ret != 0)) { + DRM_ERROR("Dummy query buffer map failed.\n"); + vmw_bo_unreference(&vbo); + } else + dev_priv->dummy_query_bo = vbo; + + return ret; +} + +static int vmw_device_init(struct vmw_private *dev_priv) +{ + bool uses_fb_traces = false; + + dev_priv->enable_state = vmw_read(dev_priv, SVGA_REG_ENABLE); + dev_priv->config_done_state = vmw_read(dev_priv, SVGA_REG_CONFIG_DONE); + dev_priv->traces_state = vmw_read(dev_priv, SVGA_REG_TRACES); + + vmw_write(dev_priv, SVGA_REG_ENABLE, SVGA_REG_ENABLE_ENABLE | + SVGA_REG_ENABLE_HIDE); + + uses_fb_traces = !vmw_cmd_supported(dev_priv) && + (dev_priv->capabilities & SVGA_CAP_TRACES) != 0; + + vmw_write(dev_priv, SVGA_REG_TRACES, uses_fb_traces); + dev_priv->fifo = vmw_fifo_create(dev_priv); + if (IS_ERR(dev_priv->fifo)) { + int err = PTR_ERR(dev_priv->fifo); + dev_priv->fifo = NULL; + return err; + } else if (!dev_priv->fifo) { + vmw_write(dev_priv, SVGA_REG_CONFIG_DONE, 1); + } + + dev_priv->last_read_seqno = vmw_fence_read(dev_priv); + atomic_set(&dev_priv->marker_seq, dev_priv->last_read_seqno); + return 0; +} + +static void vmw_device_fini(struct vmw_private *vmw) +{ + /* + * Legacy sync + */ + vmw_write(vmw, SVGA_REG_SYNC, SVGA_SYNC_GENERIC); + while (vmw_read(vmw, SVGA_REG_BUSY) != 0) + ; + + vmw->last_read_seqno = vmw_fence_read(vmw); + + vmw_write(vmw, SVGA_REG_CONFIG_DONE, + vmw->config_done_state); + vmw_write(vmw, SVGA_REG_ENABLE, + vmw->enable_state); + vmw_write(vmw, SVGA_REG_TRACES, + vmw->traces_state); + + vmw_fifo_destroy(vmw); +} + +/** + * vmw_request_device_late - Perform late device setup + * + * @dev_priv: Pointer to device private. + * + * This function performs setup of otables and enables large command + * buffer submission. These tasks are split out to a separate function + * because it reverts vmw_release_device_early and is intended to be used + * by an error path in the hibernation code. + */ +static int vmw_request_device_late(struct vmw_private *dev_priv) +{ + int ret; + + if (dev_priv->has_mob) { + ret = vmw_otables_setup(dev_priv); + if (unlikely(ret != 0)) { + DRM_ERROR("Unable to initialize " + "guest Memory OBjects.\n"); + return ret; + } + } + + if (dev_priv->cman) { + ret = vmw_cmdbuf_set_pool_size(dev_priv->cman, 256*4096); + if (ret) { + struct vmw_cmdbuf_man *man = dev_priv->cman; + + dev_priv->cman = NULL; + vmw_cmdbuf_man_destroy(man); + } + } + + return 0; +} + +static int vmw_request_device(struct vmw_private *dev_priv) +{ + int ret; + + ret = vmw_device_init(dev_priv); + if (unlikely(ret != 0)) { + DRM_ERROR("Unable to initialize the device.\n"); + return ret; + } + vmw_fence_fifo_up(dev_priv->fman); + dev_priv->cman = vmw_cmdbuf_man_create(dev_priv); + if (IS_ERR(dev_priv->cman)) { + dev_priv->cman = NULL; + dev_priv->sm_type = VMW_SM_LEGACY; + } + + ret = vmw_request_device_late(dev_priv); + if (ret) + 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->cman) + vmw_cmdbuf_remove_pool(dev_priv->cman); + if (dev_priv->has_mob) { + struct ttm_resource_manager *man; + + man = ttm_manager_type(&dev_priv->bdev, VMW_PL_MOB); + ttm_resource_manager_evict_all(&dev_priv->bdev, man); + vmw_otables_takedown(dev_priv); + } + if (dev_priv->cman) + vmw_cmdbuf_man_destroy(dev_priv->cman); +out_no_mob: + vmw_fence_fifo_down(dev_priv->fman); + vmw_device_fini(dev_priv); + return ret; +} + +/** + * vmw_release_device_early - Early part of fifo takedown. + * + * @dev_priv: Pointer to device private struct. + * + * This is the first part of command submission takedown, to be called before + * buffer management is taken down. + */ +static void vmw_release_device_early(struct vmw_private *dev_priv) +{ + /* + * Previous destructions should've released + * the pinned bo. + */ + + BUG_ON(dev_priv->pinned_bo != NULL); + + vmw_bo_unreference(&dev_priv->dummy_query_bo); + if (dev_priv->cman) + vmw_cmdbuf_remove_pool(dev_priv->cman); + + if (dev_priv->has_mob) { + struct ttm_resource_manager *man; + + man = ttm_manager_type(&dev_priv->bdev, VMW_PL_MOB); + ttm_resource_manager_evict_all(&dev_priv->bdev, man); + vmw_otables_takedown(dev_priv); + } +} + +/** + * vmw_release_device_late - Late part of fifo takedown. + * + * @dev_priv: Pointer to device private struct. + * + * This is the last part of the command submission takedown, to be called when + * command submission is no longer needed. It may wait on pending fences. + */ +static void vmw_release_device_late(struct vmw_private *dev_priv) +{ + vmw_fence_fifo_down(dev_priv->fman); + if (dev_priv->cman) + vmw_cmdbuf_man_destroy(dev_priv->cman); + + vmw_device_fini(dev_priv); +} + +/* + * 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, VMWGFX_MIN_INITIAL_WIDTH); + height = max_t(uint32_t, height, VMWGFX_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 = VMWGFX_MIN_INITIAL_WIDTH; + height = VMWGFX_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 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_alloc_coherent] = "Using coherent TTM pages.", + [vmw_dma_map_populate] = "Caching DMA mappings.", + [vmw_dma_map_bind] = "Giving up DMA mappings early."}; + + /* TTM currently doesn't fully support SEV encryption. */ + if (cc_platform_has(CC_ATTR_MEM_ENCRYPT)) + return -EINVAL; + + if (vmw_force_coherent) + dev_priv->map_mode = vmw_dma_alloc_coherent; + else if (vmw_restrict_iommu) + dev_priv->map_mode = vmw_dma_map_bind; + else + dev_priv->map_mode = vmw_dma_map_populate; + + drm_info(&dev_priv->drm, + "DMA map mode: %s\n", names[dev_priv->map_mode]); + return 0; +} + +/** + * vmw_dma_masks - set required page- and dma masks + * + * @dev_priv: Pointer to struct drm-device + * + * With 32-bit we can only handle 32 bit PFNs. Optionally set that + * restriction also for 64-bit systems. + */ +static int vmw_dma_masks(struct vmw_private *dev_priv) +{ + struct drm_device *dev = &dev_priv->drm; + int ret = 0; + + ret = dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(64)); + if (sizeof(unsigned long) == 4 || vmw_restrict_dma_mask) { + drm_info(&dev_priv->drm, + "Restricting DMA addresses to 44 bits.\n"); + return dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(44)); + } + + return ret; +} + +static int vmw_vram_manager_init(struct vmw_private *dev_priv) +{ + int ret; + ret = ttm_range_man_init(&dev_priv->bdev, TTM_PL_VRAM, false, + dev_priv->vram_size >> PAGE_SHIFT); + ttm_resource_manager_set_used(ttm_manager_type(&dev_priv->bdev, TTM_PL_VRAM), false); + return ret; +} + +static void vmw_vram_manager_fini(struct vmw_private *dev_priv) +{ + ttm_range_man_fini(&dev_priv->bdev, TTM_PL_VRAM); +} + +static int vmw_setup_pci_resources(struct vmw_private *dev, + u32 pci_id) +{ + resource_size_t rmmio_start; + resource_size_t rmmio_size; + resource_size_t fifo_start; + resource_size_t fifo_size; + int ret; + struct pci_dev *pdev = to_pci_dev(dev->drm.dev); + + pci_set_master(pdev); + + ret = pci_request_regions(pdev, "vmwgfx probe"); + if (ret) + return ret; + + dev->pci_id = pci_id; + if (pci_id == VMWGFX_PCI_ID_SVGA3) { + rmmio_start = pci_resource_start(pdev, 0); + rmmio_size = pci_resource_len(pdev, 0); + dev->vram_start = pci_resource_start(pdev, 2); + dev->vram_size = pci_resource_len(pdev, 2); + + drm_info(&dev->drm, + "Register MMIO at 0x%pa size is %llu kiB\n", + &rmmio_start, (uint64_t)rmmio_size / 1024); + dev->rmmio = devm_ioremap(dev->drm.dev, + rmmio_start, + rmmio_size); + if (!dev->rmmio) { + drm_err(&dev->drm, + "Failed mapping registers mmio memory.\n"); + pci_release_regions(pdev); + return -ENOMEM; + } + } else if (pci_id == VMWGFX_PCI_ID_SVGA2) { + dev->io_start = pci_resource_start(pdev, 0); + dev->vram_start = pci_resource_start(pdev, 1); + dev->vram_size = pci_resource_len(pdev, 1); + fifo_start = pci_resource_start(pdev, 2); + fifo_size = pci_resource_len(pdev, 2); + + drm_info(&dev->drm, + "FIFO at %pa size is %llu kiB\n", + &fifo_start, (uint64_t)fifo_size / 1024); + dev->fifo_mem = devm_memremap(dev->drm.dev, + fifo_start, + fifo_size, + MEMREMAP_WB); + + if (IS_ERR(dev->fifo_mem)) { + drm_err(&dev->drm, + "Failed mapping FIFO memory.\n"); + pci_release_regions(pdev); + return PTR_ERR(dev->fifo_mem); + } + } else { + pci_release_regions(pdev); + return -EINVAL; + } + + /* + * This is approximate size of the vram, the exact size will only + * be known after we read SVGA_REG_VRAM_SIZE. The PCI resource + * size will be equal to or bigger than the size reported by + * SVGA_REG_VRAM_SIZE. + */ + drm_info(&dev->drm, + "VRAM at %pa size is %llu kiB\n", + &dev->vram_start, (uint64_t)dev->vram_size / 1024); + + return 0; +} + +static int vmw_detect_version(struct vmw_private *dev) +{ + uint32_t svga_id; + + vmw_write(dev, SVGA_REG_ID, vmw_is_svga_v3(dev) ? + SVGA_ID_3 : SVGA_ID_2); + svga_id = vmw_read(dev, SVGA_REG_ID); + if (svga_id != SVGA_ID_2 && svga_id != SVGA_ID_3) { + drm_err(&dev->drm, + "Unsupported SVGA ID 0x%x on chipset 0x%x\n", + svga_id, dev->pci_id); + return -ENOSYS; + } + BUG_ON(vmw_is_svga_v3(dev) && (svga_id != SVGA_ID_3)); + drm_info(&dev->drm, + "Running on SVGA version %d.\n", (svga_id & 0xff)); + return 0; +} + +static void vmw_write_driver_id(struct vmw_private *dev) +{ + if ((dev->capabilities2 & SVGA_CAP2_DX2) != 0) { + vmw_write(dev, SVGA_REG_GUEST_DRIVER_ID, + SVGA_REG_GUEST_DRIVER_ID_LINUX); + + vmw_write(dev, SVGA_REG_GUEST_DRIVER_VERSION1, + LINUX_VERSION_MAJOR << 24 | + LINUX_VERSION_PATCHLEVEL << 16 | + LINUX_VERSION_SUBLEVEL); + vmw_write(dev, SVGA_REG_GUEST_DRIVER_VERSION2, + VMWGFX_DRIVER_MAJOR << 24 | + VMWGFX_DRIVER_MINOR << 16 | + VMWGFX_DRIVER_PATCHLEVEL); + vmw_write(dev, SVGA_REG_GUEST_DRIVER_VERSION3, 0); + + vmw_write(dev, SVGA_REG_GUEST_DRIVER_ID, + SVGA_REG_GUEST_DRIVER_ID_SUBMIT); + } +} + +static void vmw_sw_context_init(struct vmw_private *dev_priv) +{ + struct vmw_sw_context *sw_context = &dev_priv->ctx; + + hash_init(sw_context->res_ht); +} + +static void vmw_sw_context_fini(struct vmw_private *dev_priv) +{ + struct vmw_sw_context *sw_context = &dev_priv->ctx; + + vfree(sw_context->cmd_bounce); + if (sw_context->staged_bindings) + vmw_binding_state_free(sw_context->staged_bindings); +} + +static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id) +{ + int ret; + enum vmw_res_type i; + bool refuse_dma = false; + struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); + + dev_priv->drm.dev_private = dev_priv; + + vmw_sw_context_init(dev_priv); + + mutex_init(&dev_priv->cmdbuf_mutex); + mutex_init(&dev_priv->binding_mutex); + spin_lock_init(&dev_priv->resource_lock); + spin_lock_init(&dev_priv->hw_lock); + spin_lock_init(&dev_priv->waiter_lock); + spin_lock_init(&dev_priv->cursor_lock); + + ret = vmw_setup_pci_resources(dev_priv, pci_id); + if (ret) + return ret; + ret = vmw_detect_version(dev_priv); + if (ret) + goto out_no_pci_or_version; + + + for (i = vmw_res_context; i < vmw_res_max; ++i) { + idr_init_base(&dev_priv->res_idr[i], 1); + INIT_LIST_HEAD(&dev_priv->res_lru[i]); + } + + init_waitqueue_head(&dev_priv->fence_queue); + init_waitqueue_head(&dev_priv->fifo_queue); + dev_priv->fence_queue_waiters = 0; + dev_priv->fifo_queue_waiters = 0; + + dev_priv->used_memory_size = 0; + + dev_priv->assume_16bpp = !!vmw_assume_16bpp; + + dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES); + vmw_print_bitmap(&dev_priv->drm, "Capabilities", + dev_priv->capabilities, + cap1_names, ARRAY_SIZE(cap1_names)); + if (dev_priv->capabilities & SVGA_CAP_CAP2_REGISTER) { + dev_priv->capabilities2 = vmw_read(dev_priv, SVGA_REG_CAP2); + vmw_print_bitmap(&dev_priv->drm, "Capabilities2", + dev_priv->capabilities2, + cap2_names, ARRAY_SIZE(cap2_names)); + } + + if (!vmwgfx_supported(dev_priv)) { + vmw_disable_backdoor(); + drm_err_once(&dev_priv->drm, + "vmwgfx seems to be running on an unsupported hypervisor."); + drm_err_once(&dev_priv->drm, + "This configuration is likely broken."); + drm_err_once(&dev_priv->drm, + "Please switch to a supported graphics device to avoid problems."); + } + + ret = vmw_dma_select_mode(dev_priv); + if (unlikely(ret != 0)) { + drm_info(&dev_priv->drm, + "Restricting capabilities since DMA not available.\n"); + refuse_dma = true; + if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS) + drm_info(&dev_priv->drm, + "Disabling 3D acceleration.\n"); + } + + dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE); + dev_priv->fifo_mem_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE); + dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH); + dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT); + + 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; + + if (dev_priv->capabilities2 & SVGA_CAP2_GB_MEMSIZE_2) + mem_size = vmw_read(dev_priv, + SVGA_REG_GBOBJECT_MEM_SIZE_KB); + else + mem_size = + vmw_read(dev_priv, + SVGA_REG_SUGGESTED_GBOBJECT_MEM_SIZE_KB); + + /* + * Workaround for low memory 2D VMs to compensate for the + * allocation taken by fbdev + */ + if (!(dev_priv->capabilities & SVGA_CAP_3D)) + mem_size *= 3; + + dev_priv->max_mob_pages = mem_size * 1024 / PAGE_SIZE; + dev_priv->max_primary_mem = + vmw_read(dev_priv, SVGA_REG_MAX_PRIMARY_MEM); + dev_priv->max_mob_size = + vmw_read(dev_priv, SVGA_REG_MOB_MAX_SIZE); + dev_priv->stdu_max_width = + vmw_read(dev_priv, SVGA_REG_SCREENTARGET_MAX_WIDTH); + dev_priv->stdu_max_height = + vmw_read(dev_priv, SVGA_REG_SCREENTARGET_MAX_HEIGHT); + + vmw_write(dev_priv, SVGA_REG_DEV_CAP, + SVGA3D_DEVCAP_MAX_TEXTURE_WIDTH); + dev_priv->texture_max_width = vmw_read(dev_priv, + SVGA_REG_DEV_CAP); + vmw_write(dev_priv, SVGA_REG_DEV_CAP, + SVGA3D_DEVCAP_MAX_TEXTURE_HEIGHT); + dev_priv->texture_max_height = vmw_read(dev_priv, + SVGA_REG_DEV_CAP); + } else { + dev_priv->texture_max_width = 8192; + dev_priv->texture_max_height = 8192; + dev_priv->max_primary_mem = dev_priv->vram_size; + } + drm_info(&dev_priv->drm, + "Legacy memory limits: VRAM = %llu kB, FIFO = %llu kB, surface = %u kB\n", + (u64)dev_priv->vram_size / 1024, + (u64)dev_priv->fifo_mem_size / 1024, + dev_priv->memory_size / 1024); + + drm_info(&dev_priv->drm, + "MOB limits: max mob size = %u kB, max mob pages = %u\n", + dev_priv->max_mob_size / 1024, dev_priv->max_mob_pages); + + ret = vmw_dma_masks(dev_priv); + if (unlikely(ret != 0)) + goto out_err0; + + dma_set_max_seg_size(dev_priv->drm.dev, U32_MAX); + + if (dev_priv->capabilities & SVGA_CAP_GMR2) { + drm_info(&dev_priv->drm, + "Max GMR ids is %u\n", + (unsigned)dev_priv->max_gmr_ids); + drm_info(&dev_priv->drm, + "Max number of GMR pages is %u\n", + (unsigned)dev_priv->max_gmr_pages); + } + drm_info(&dev_priv->drm, + "Maximum display memory size is %llu kiB\n", + (uint64_t)dev_priv->max_primary_mem / 1024); + + /* Need mmio memory to check for fifo pitchlock cap. */ + if (!(dev_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY) && + !(dev_priv->capabilities & SVGA_CAP_PITCHLOCK) && + !vmw_fifo_have_pitchlock(dev_priv)) { + ret = -ENOSYS; + DRM_ERROR("Hardware has no pitchlock\n"); + goto out_err0; + } + + dev_priv->tdev = ttm_object_device_init(&vmw_prime_dmabuf_ops); + + if (unlikely(dev_priv->tdev == NULL)) { + drm_err(&dev_priv->drm, + "Unable to initialize TTM object management.\n"); + ret = -ENOMEM; + goto out_err0; + } + + if (dev_priv->capabilities & SVGA_CAP_IRQMASK) { + ret = vmw_irq_install(dev_priv); + if (ret != 0) { + drm_err(&dev_priv->drm, + "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; + } + + ret = ttm_device_init(&dev_priv->bdev, &vmw_bo_driver, + dev_priv->drm.dev, + dev_priv->drm.anon_inode->i_mapping, + dev_priv->drm.vma_offset_manager, + dev_priv->map_mode == vmw_dma_alloc_coherent, + false); + if (unlikely(ret != 0)) { + drm_err(&dev_priv->drm, + "Failed initializing TTM buffer object driver.\n"); + goto out_no_bdev; + } + + /* + * Enable VRAM, but initially don't use it until SVGA is enabled and + * unhidden. + */ + + ret = vmw_vram_manager_init(dev_priv); + if (unlikely(ret != 0)) { + drm_err(&dev_priv->drm, + "Failed initializing memory manager for VRAM.\n"); + goto out_no_vram; + } + + ret = vmw_devcaps_create(dev_priv); + if (unlikely(ret != 0)) { + drm_err(&dev_priv->drm, + "Failed initializing device caps.\n"); + goto out_no_vram; + } + + /* + * "Guest Memory Regions" is an aperture like feature with + * one slot per bo. There is an upper limit of the number of + * slots as well as the bo size. + */ + dev_priv->has_gmr = true; + /* TODO: This is most likely not correct */ + if (((dev_priv->capabilities & (SVGA_CAP_GMR | SVGA_CAP_GMR2)) == 0) || + refuse_dma || + vmw_gmrid_man_init(dev_priv, VMW_PL_GMR) != 0) { + drm_info(&dev_priv->drm, + "No GMR memory available. " + "Graphics memory resources are very limited.\n"); + dev_priv->has_gmr = false; + } + + if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS && !refuse_dma) { + dev_priv->has_mob = true; + + if (vmw_gmrid_man_init(dev_priv, VMW_PL_MOB) != 0) { + drm_info(&dev_priv->drm, + "No MOB memory available. " + "3D will be disabled.\n"); + dev_priv->has_mob = false; + } + if (vmw_sys_man_init(dev_priv) != 0) { + drm_info(&dev_priv->drm, + "No MOB page table memory available. " + "3D will be disabled.\n"); + dev_priv->has_mob = false; + } + } + + if (dev_priv->has_mob && (dev_priv->capabilities & SVGA_CAP_DX)) { + if (vmw_devcap_get(dev_priv, SVGA3D_DEVCAP_DXCONTEXT)) + dev_priv->sm_type = VMW_SM_4; + } + + /* SVGA_CAP2_DX2 (DefineGBSurface_v3) is needed for SM4_1 support */ + if (has_sm4_context(dev_priv) && + (dev_priv->capabilities2 & SVGA_CAP2_DX2)) { + if (vmw_devcap_get(dev_priv, SVGA3D_DEVCAP_SM41)) + dev_priv->sm_type = VMW_SM_4_1; + if (has_sm4_1_context(dev_priv) && + (dev_priv->capabilities2 & SVGA_CAP2_DX3)) { + if (vmw_devcap_get(dev_priv, SVGA3D_DEVCAP_SM5)) { + dev_priv->sm_type = VMW_SM_5; + if (vmw_devcap_get(dev_priv, SVGA3D_DEVCAP_GL43)) + dev_priv->sm_type = VMW_SM_5_1X; + } + } + } + + ret = vmw_kms_init(dev_priv); + if (unlikely(ret != 0)) + goto out_no_kms; + vmw_overlay_init(dev_priv); + + ret = vmw_request_device(dev_priv); + if (ret) + goto out_no_fifo; + + vmw_print_sm_type(dev_priv); + vmw_host_printf("vmwgfx: Module Version: %d.%d.%d (kernel: %s)", + VMWGFX_DRIVER_MAJOR, VMWGFX_DRIVER_MINOR, + VMWGFX_DRIVER_PATCHLEVEL, UTS_RELEASE); + vmw_write_driver_id(dev_priv); + + dev_priv->pm_nb.notifier_call = vmwgfx_pm_notifier; + register_pm_notifier(&dev_priv->pm_nb); + + return 0; + +out_no_fifo: + vmw_overlay_close(dev_priv); + vmw_kms_close(dev_priv); +out_no_kms: + if (dev_priv->has_mob) { + vmw_gmrid_man_fini(dev_priv, VMW_PL_MOB); + vmw_sys_man_fini(dev_priv); + } + if (dev_priv->has_gmr) + vmw_gmrid_man_fini(dev_priv, VMW_PL_GMR); + vmw_devcaps_destroy(dev_priv); + vmw_vram_manager_fini(dev_priv); +out_no_vram: + ttm_device_fini(&dev_priv->bdev); +out_no_bdev: + vmw_fence_manager_takedown(dev_priv->fman); +out_no_fman: + if (dev_priv->capabilities & SVGA_CAP_IRQMASK) + vmw_irq_uninstall(&dev_priv->drm); +out_no_irq: + ttm_object_device_release(&dev_priv->tdev); +out_err0: + for (i = vmw_res_context; i < vmw_res_max; ++i) + idr_destroy(&dev_priv->res_idr[i]); + + if (dev_priv->ctx.staged_bindings) + vmw_binding_state_free(dev_priv->ctx.staged_bindings); +out_no_pci_or_version: + pci_release_regions(pdev); + return ret; +} + +static void vmw_driver_unload(struct drm_device *dev) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct pci_dev *pdev = to_pci_dev(dev->dev); + enum vmw_res_type i; + + unregister_pm_notifier(&dev_priv->pm_nb); + + vmw_sw_context_fini(dev_priv); + vmw_fifo_resource_dec(dev_priv); + + vmw_svga_disable(dev_priv); + + vmw_kms_close(dev_priv); + vmw_overlay_close(dev_priv); + + if (dev_priv->has_gmr) + vmw_gmrid_man_fini(dev_priv, VMW_PL_GMR); + + vmw_release_device_early(dev_priv); + if (dev_priv->has_mob) { + vmw_gmrid_man_fini(dev_priv, VMW_PL_MOB); + vmw_sys_man_fini(dev_priv); + } + vmw_devcaps_destroy(dev_priv); + vmw_vram_manager_fini(dev_priv); + ttm_device_fini(&dev_priv->bdev); + vmw_release_device_late(dev_priv); + vmw_fence_manager_takedown(dev_priv->fman); + if (dev_priv->capabilities & SVGA_CAP_IRQMASK) + vmw_irq_uninstall(&dev_priv->drm); + + ttm_object_device_release(&dev_priv->tdev); + + for (i = vmw_res_context; i < vmw_res_max; ++i) + idr_destroy(&dev_priv->res_idr[i]); + + vmw_mksstat_remove_all(dev_priv); + + pci_release_regions(pdev); +} + +static void vmw_postclose(struct drm_device *dev, + struct drm_file *file_priv) +{ + struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); + + ttm_object_file_release(&vmw_fp->tfile); + kfree(vmw_fp); +} + +static int vmw_driver_open(struct drm_device *dev, struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_fpriv *vmw_fp; + int ret = -ENOMEM; + + vmw_fp = kzalloc(sizeof(*vmw_fp), GFP_KERNEL); + if (unlikely(!vmw_fp)) + return ret; + + vmw_fp->tfile = ttm_object_file_init(dev_priv->tdev); + if (unlikely(vmw_fp->tfile == NULL)) + goto out_no_tfile; + + file_priv->driver_priv = vmw_fp; + + return 0; + +out_no_tfile: + kfree(vmw_fp); + return ret; +} + +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); + unsigned int flags; + + /* + * Do extra checking on driver private ioctls. + */ + + if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END) + && (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) { + const struct drm_ioctl_desc *ioctl = + &vmw_ioctls[nr - DRM_COMMAND_BASE]; + + if (nr == DRM_COMMAND_BASE + DRM_VMW_EXECBUF) { + return ioctl_func(filp, cmd, arg); + } else if (nr == DRM_COMMAND_BASE + DRM_VMW_UPDATE_LAYOUT) { + if (!drm_is_current_master(file_priv) && + !capable(CAP_SYS_ADMIN)) + return -EACCES; + } + + if (unlikely(ioctl->cmd != cmd)) + goto out_io_encoding; + + flags = ioctl->flags; + } else if (!drm_ioctl_flags(nr, &flags)) + return -EINVAL; + + return ioctl_func(filp, cmd, arg); + +out_io_encoding: + DRM_ERROR("Invalid command format, ioctl %d\n", + nr - DRM_COMMAND_BASE); + + return -EINVAL; +} + +static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + return vmw_generic_ioctl(filp, cmd, arg, &drm_ioctl); +} + +#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_master_set(struct drm_device *dev, + struct drm_file *file_priv, + bool from_open) +{ + /* + * Inform a new master that the layout may have changed while + * it was gone. + */ + if (!from_open) + drm_sysfs_hotplug_event(dev); +} + +static void vmw_master_drop(struct drm_device *dev, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + + vmw_kms_legacy_hotspot_clear(dev_priv); +} + +bool vmwgfx_supported(struct vmw_private *vmw) +{ +#if defined(CONFIG_X86) + return hypervisor_is_type(X86_HYPER_VMWARE); +#elif defined(CONFIG_ARM64) + /* + * On aarch64 only svga3 is supported + */ + return vmw->pci_id == VMWGFX_PCI_ID_SVGA3; +#else + drm_warn_once(&vmw->drm, + "vmwgfx is running on an unknown architecture."); + return false; +#endif +} + +/** + * __vmw_svga_enable - Enable SVGA mode, FIFO and use of VRAM. + * + * @dev_priv: Pointer to device private struct. + * Needs the reservation sem to be held in non-exclusive mode. + */ +static void __vmw_svga_enable(struct vmw_private *dev_priv) +{ + struct ttm_resource_manager *man = ttm_manager_type(&dev_priv->bdev, TTM_PL_VRAM); + + if (!ttm_resource_manager_used(man)) { + vmw_write(dev_priv, SVGA_REG_ENABLE, SVGA_REG_ENABLE_ENABLE); + ttm_resource_manager_set_used(man, true); + } +} + +/** + * vmw_svga_enable - Enable SVGA mode, FIFO and use of VRAM. + * + * @dev_priv: Pointer to device private struct. + */ +void vmw_svga_enable(struct vmw_private *dev_priv) +{ + __vmw_svga_enable(dev_priv); +} + +/** + * __vmw_svga_disable - Disable SVGA mode and use of VRAM. + * + * @dev_priv: Pointer to device private struct. + * Needs the reservation sem to be held in exclusive mode. + * Will not empty VRAM. VRAM must be emptied by caller. + */ +static void __vmw_svga_disable(struct vmw_private *dev_priv) +{ + struct ttm_resource_manager *man = ttm_manager_type(&dev_priv->bdev, TTM_PL_VRAM); + + if (ttm_resource_manager_used(man)) { + ttm_resource_manager_set_used(man, false); + vmw_write(dev_priv, SVGA_REG_ENABLE, + SVGA_REG_ENABLE_HIDE | + SVGA_REG_ENABLE_ENABLE); + } +} + +/** + * vmw_svga_disable - Disable SVGA_MODE, and use of VRAM. Keep the fifo + * running. + * + * @dev_priv: Pointer to device private struct. + * Will empty VRAM. + */ +void vmw_svga_disable(struct vmw_private *dev_priv) +{ + struct ttm_resource_manager *man = ttm_manager_type(&dev_priv->bdev, TTM_PL_VRAM); + /* + * Disabling SVGA will turn off device modesetting capabilities, so + * notify KMS about that so that it doesn't cache atomic state that + * isn't valid anymore, for example crtcs turned on. + * Strictly we'd want to do this under the SVGA lock (or an SVGA mutex), + * but vmw_kms_lost_device() takes the reservation sem and thus we'll + * end up with lock order reversal. Thus, a master may actually perform + * a new modeset just after we call vmw_kms_lost_device() and race with + * vmw_svga_disable(), but that should at worst cause atomic KMS state + * to be inconsistent with the device, causing modesetting problems. + * + */ + vmw_kms_lost_device(&dev_priv->drm); + if (ttm_resource_manager_used(man)) { + if (ttm_resource_manager_evict_all(&dev_priv->bdev, man)) + DRM_ERROR("Failed evicting VRAM buffers.\n"); + ttm_resource_manager_set_used(man, false); + vmw_write(dev_priv, SVGA_REG_ENABLE, + SVGA_REG_ENABLE_HIDE | + SVGA_REG_ENABLE_ENABLE); + } +} + +static void vmw_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_dev_unregister(dev); + vmw_driver_unload(dev); +} + +static void vmw_debugfs_resource_managers_init(struct vmw_private *vmw) +{ + struct drm_minor *minor = vmw->drm.primary; + struct dentry *root = minor->debugfs_root; + + ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, TTM_PL_SYSTEM), + root, "system_ttm"); + ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, TTM_PL_VRAM), + root, "vram_ttm"); + ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, VMW_PL_GMR), + root, "gmr_ttm"); + ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, VMW_PL_MOB), + root, "mob_ttm"); + ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, VMW_PL_SYSTEM), + root, "system_mob_ttm"); +} + +static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, + void *ptr) +{ + struct vmw_private *dev_priv = + container_of(nb, struct vmw_private, pm_nb); + + switch (val) { + case PM_HIBERNATION_PREPARE: + /* + * Take the reservation sem in write mode, which will make sure + * there are no other processes holding a buffer object + * reservation, meaning we should be able to evict all buffer + * objects if needed. + * Once user-space processes have been frozen, we can release + * the lock again. + */ + dev_priv->suspend_locked = true; + break; + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + if (READ_ONCE(dev_priv->suspend_locked)) { + dev_priv->suspend_locked = false; + } + break; + default: + break; + } + return 0; +} + +static int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + + if (dev_priv->refuse_hibernation) + return -EBUSY; + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + return 0; +} + +static int vmw_pci_resume(struct pci_dev *pdev) +{ + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + return pci_enable_device(pdev); +} + +static int vmw_pm_suspend(struct device *kdev) +{ + struct pci_dev *pdev = to_pci_dev(kdev); + struct pm_message dummy; + + dummy.event = 0; + + return vmw_pci_suspend(pdev, dummy); +} + +static int vmw_pm_resume(struct device *kdev) +{ + struct pci_dev *pdev = to_pci_dev(kdev); + + return vmw_pci_resume(pdev); +} + +static int vmw_pm_freeze(struct device *kdev) +{ + struct pci_dev *pdev = to_pci_dev(kdev); + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + struct ttm_operation_ctx ctx = { + .interruptible = false, + .no_wait_gpu = false + }; + int ret; + + /* + * No user-space processes should be running now. + */ + ret = vmw_kms_suspend(&dev_priv->drm); + if (ret) { + DRM_ERROR("Failed to freeze modesetting.\n"); + return ret; + } + + vmw_execbuf_release_pinned_bo(dev_priv); + vmw_resource_evict_all(dev_priv); + vmw_release_device_early(dev_priv); + while (ttm_device_swapout(&dev_priv->bdev, &ctx, GFP_KERNEL) > 0); + vmw_fifo_resource_dec(dev_priv); + if (atomic_read(&dev_priv->num_fifo_resources) != 0) { + DRM_ERROR("Can't hibernate while 3D resources are active.\n"); + vmw_fifo_resource_inc(dev_priv); + WARN_ON(vmw_request_device_late(dev_priv)); + dev_priv->suspend_locked = false; + if (dev_priv->suspend_state) + vmw_kms_resume(dev); + return -EBUSY; + } + + vmw_fence_fifo_down(dev_priv->fman); + __vmw_svga_disable(dev_priv); + + vmw_release_device_late(dev_priv); + return 0; +} + +static int vmw_pm_restore(struct device *kdev) +{ + struct pci_dev *pdev = to_pci_dev(kdev); + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + int ret; + + vmw_detect_version(dev_priv); + + vmw_fifo_resource_inc(dev_priv); + + ret = vmw_request_device(dev_priv); + if (ret) + return ret; + + __vmw_svga_enable(dev_priv); + + vmw_fence_fifo_up(dev_priv->fman); + dev_priv->suspend_locked = false; + if (dev_priv->suspend_state) + vmw_kms_resume(&dev_priv->drm); + + return 0; +} + +static const struct dev_pm_ops vmw_pm_ops = { + .freeze = vmw_pm_freeze, + .thaw = vmw_pm_restore, + .restore = vmw_pm_restore, + .suspend = vmw_pm_suspend, + .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 = drm_gem_mmap, + .poll = drm_poll, + .read = drm_read, +#if defined(CONFIG_COMPAT) + .compat_ioctl = vmw_compat_ioctl, +#endif + .llseek = noop_llseek, +}; + +static const struct drm_driver driver = { + .driver_features = + DRIVER_MODESET | DRIVER_RENDER | DRIVER_ATOMIC | DRIVER_GEM | DRIVER_CURSOR_HOTSPOT, + .ioctls = vmw_ioctls, + .num_ioctls = ARRAY_SIZE(vmw_ioctls), + .master_set = vmw_master_set, + .master_drop = vmw_master_drop, + .open = vmw_driver_open, + .postclose = vmw_postclose, + + .dumb_create = vmw_dumb_create, + .dumb_map_offset = drm_gem_ttm_dumb_map_offset, + + .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, + .major = VMWGFX_DRIVER_MAJOR, + .minor = VMWGFX_DRIVER_MINOR, + .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) +{ + struct vmw_private *vmw; + int ret; + + ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &driver); + if (ret) + goto out_error; + + ret = pcim_enable_device(pdev); + if (ret) + goto out_error; + + vmw = devm_drm_dev_alloc(&pdev->dev, &driver, + struct vmw_private, drm); + if (IS_ERR(vmw)) { + ret = PTR_ERR(vmw); + goto out_error; + } + + pci_set_drvdata(pdev, &vmw->drm); + + ret = vmw_driver_load(vmw, ent->device); + if (ret) + goto out_error; + + ret = drm_dev_register(&vmw->drm, 0); + if (ret) + goto out_unload; + + vmw_fifo_resource_inc(vmw); + vmw_svga_enable(vmw); + drm_fbdev_generic_setup(&vmw->drm, 0); + + vmw_debugfs_gem_init(vmw); + vmw_debugfs_resource_managers_init(vmw); + + return 0; +out_unload: + vmw_driver_unload(&vmw->drm); +out_error: + return ret; +} + +drm_module_pci_driver(vmw_pci_driver); + +MODULE_AUTHOR("VMware Inc. and others"); +MODULE_DESCRIPTION("Standalone drm driver for the VMware SVGA device"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_VERSION(__stringify(VMWGFX_DRIVER_MAJOR) "." + __stringify(VMWGFX_DRIVER_MINOR) "." + __stringify(VMWGFX_DRIVER_PATCHLEVEL) "." + "0"); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h new file mode 100644 index 0000000000..3cd5090ded --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -0,0 +1,1532 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef _VMWGFX_DRV_H_ +#define _VMWGFX_DRV_H_ + +#include <linux/suspend.h> +#include <linux/sync_file.h> +#include <linux/hashtable.h> + +#include <drm/drm_auth.h> +#include <drm/drm_device.h> +#include <drm/drm_file.h> +#include <drm/drm_rect.h> + +#include <drm/ttm/ttm_execbuf_util.h> +#include <drm/ttm/ttm_tt.h> +#include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_bo.h> + +#include "ttm_object.h" + +#include "vmwgfx_fence.h" +#include "vmwgfx_reg.h" +#include "vmwgfx_validation.h" + +/* + * FIXME: vmwgfx_drm.h needs to be last due to dependencies. + * uapi headers should not depend on header files outside uapi/. + */ +#include <drm/vmwgfx_drm.h> + + +#define VMWGFX_DRIVER_NAME "vmwgfx" +#define VMWGFX_DRIVER_DATE "20211206" +#define VMWGFX_DRIVER_MAJOR 2 +#define VMWGFX_DRIVER_MINOR 20 +#define VMWGFX_DRIVER_PATCHLEVEL 0 +#define VMWGFX_FIFO_STATIC_SIZE (1024*1024) +#define VMWGFX_MAX_DISPLAYS 16 +#define VMWGFX_CMD_BOUNCE_INIT_SIZE 32768 + +#define VMWGFX_MIN_INITIAL_WIDTH 1280 +#define VMWGFX_MIN_INITIAL_HEIGHT 800 + +#define VMWGFX_PCI_ID_SVGA2 0x0405 +#define VMWGFX_PCI_ID_SVGA3 0x0406 + +/* + * This has to match get_count_order(SVGA_IRQFLAG_MAX) + */ +#define VMWGFX_MAX_NUM_IRQS 6 + +/* + * 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_DXCONTEXT 256 +#define VMWGFX_NUM_DXQUERY 512 +#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_PRIV + 0) +#define VMW_PL_MOB (TTM_PL_PRIV + 1) +#define VMW_PL_SYSTEM (TTM_PL_PRIV + 2) + +#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 +#define VMW_RES_HT_ORDER 12 + +#define VMW_CURSOR_SNOOP_FORMAT SVGA3D_A8R8G8B8 +#define VMW_CURSOR_SNOOP_WIDTH 64 +#define VMW_CURSOR_SNOOP_HEIGHT 64 + +#define MKSSTAT_CAPACITY_LOG2 5U +#define MKSSTAT_CAPACITY (1U << MKSSTAT_CAPACITY_LOG2) + +struct vmw_fpriv { + struct ttm_object_file *tfile; + bool gb_aware; /* user-space is guest-backed aware */ +}; + +struct vmwgfx_hash_item { + struct hlist_node head; + unsigned long key; +}; + + +/** + * 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 vmwgfx_hash_item hash; + bool validate_as_mob; +}; + +struct vmw_res_func; + + +/** + * struct vmw-resource - base class for hardware resources + * + * @kref: For refcounting. + * @dev_priv: Pointer to the device private for this resource. Immutable. + * @id: Device id. Protected by @dev_priv::resource_lock. + * @guest_memory_size: Guest memory buffer size. Immutable. + * @res_dirty: Resource contains data not yet in the guest memory buffer. + * Protected by resource reserved. + * @guest_memory_dirty: Guest memory buffer contains data not yet in the HW + * resource. Protected by resource reserved. + * @coherent: Emulate coherency by tracking vm accesses. + * @guest_memory_bo: The guest memory buffer if any. Protected by resource + * reserved. + * @guest_memory_offset: Offset into the guest memory buffer if any. Protected + * by resource reserved. Note that only a few resource types can have a + * @guest_memory_offset different from zero. + * @pin_count: The pin count for this resource. A pinned resource has a + * pin-count greater than zero. It is not on the resource LRU lists and its + * guest memory buffer is pinned. Hence it can't be evicted. + * @func: Method vtable for this resource. Immutable. + * @mob_node; Node for the MOB guest memory rbtree. Protected by + * @guest_memory_bo reserved. + * @lru_head: List head for the LRU list. Protected by @dev_priv::resource_lock. + * @binding_head: List head for the context binding list. Protected by + * the @dev_priv::binding_mutex + * @res_free: The resource destructor. + * @hw_destroy: Callback to destroy the resource on the device, as part of + * resource destruction. + */ +struct vmw_bo; +struct vmw_bo; +struct vmw_resource_dirty; +struct vmw_resource { + struct kref kref; + struct vmw_private *dev_priv; + int id; + u32 used_prio; + unsigned long guest_memory_size; + u32 res_dirty : 1; + u32 guest_memory_dirty : 1; + u32 coherent : 1; + struct vmw_bo *guest_memory_bo; + unsigned long guest_memory_offset; + unsigned long pin_count; + const struct vmw_res_func *func; + struct rb_node mob_node; + struct list_head lru_head; + struct list_head binding_head; + struct vmw_resource_dirty *dirty; + void (*res_free) (struct vmw_resource *res); + void (*hw_destroy) (struct vmw_resource *res); +}; + + +/* + * Resources that are managed using ioctls. + */ +enum vmw_res_type { + vmw_res_context, + vmw_res_surface, + vmw_res_stream, + vmw_res_shader, + vmw_res_dx_context, + vmw_res_cotable, + vmw_res_view, + vmw_res_streamoutput, + vmw_res_max +}; + +/* + * Resources that are managed using command streams. + */ +enum vmw_cmdbuf_res_type { + vmw_cmdbuf_res_shader, + vmw_cmdbuf_res_view, + vmw_cmdbuf_res_streamoutput +}; + +struct vmw_cmdbuf_res_manager; + +struct vmw_cursor_snooper { + size_t age; + uint32_t *image; +}; + +struct vmw_framebuffer; +struct vmw_surface_offset; + +/** + * struct vmw_surface_metadata - Metadata describing a surface. + * + * @flags: Device flags. + * @format: Surface SVGA3D_x format. + * @mip_levels: Mip level for each face. For GB first index is used only. + * @multisample_count: Sample count. + * @multisample_pattern: Sample patterns. + * @quality_level: Quality level. + * @autogen_filter: Filter for automatically generated mipmaps. + * @array_size: Number of array elements for a 1D/2D texture. For cubemap + texture number of faces * array_size. This should be 0 for pre + SM4 device. + * @buffer_byte_stride: Buffer byte stride. + * @num_sizes: Size of @sizes. For GB surface this should always be 1. + * @base_size: Surface dimension. + * @sizes: Array representing mip sizes. Legacy only. + * @scanout: Whether this surface will be used for scanout. + * + * This tracks metadata for both legacy and guest backed surface. + */ +struct vmw_surface_metadata { + u64 flags; + u32 format; + u32 mip_levels[DRM_VMW_MAX_SURFACE_FACES]; + u32 multisample_count; + u32 multisample_pattern; + u32 quality_level; + u32 autogen_filter; + u32 array_size; + u32 num_sizes; + u32 buffer_byte_stride; + struct drm_vmw_size base_size; + struct drm_vmw_size *sizes; + bool scanout; +}; + +/** + * struct vmw_surface: Resource structure for a surface. + * + * @res: The base resource for this surface. + * @metadata: Metadata for this surface resource. + * @snooper: Cursor data. Legacy surface only. + * @offsets: Legacy surface only. + * @view_list: List of views bound to this surface. + */ +struct vmw_surface { + struct vmw_resource res; + struct vmw_surface_metadata metadata; + struct vmw_cursor_snooper snooper; + struct vmw_surface_offset *offsets; + struct list_head view_list; +}; + +struct vmw_fifo_state { + unsigned long reserved_size; + u32 *dynamic_buffer; + u32 *static_buffer; + unsigned long static_buffer_size; + bool using_bounce_buffer; + uint32_t capabilities; + struct mutex fifo_mutex; + struct rw_semaphore rwsem; +}; + +/** + * struct vmw_res_cache_entry - resource information cache entry + * @handle: User-space handle of a resource. + * @res: Non-ref-counted pointer to the resource. + * @valid_handle: Whether the @handle member is valid. + * @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. + * + * Used to avoid frequent repeated user-space handle lookups of the + * same resource. + */ +struct vmw_res_cache_entry { + uint32_t handle; + struct vmw_resource *res; + void *private; + unsigned short valid_handle; + unsigned short valid; +}; + +/** + * enum vmw_dma_map_mode - indicate how to perform TTM page dma mappings. + */ +enum vmw_dma_map_mode { + 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 contiguous 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_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_dma_page_iter iter; + unsigned long i; + unsigned long num_pages; + bool (*next)(struct vmw_piter *); + dma_addr_t (*dma_address)(struct vmw_piter *); +}; + + +struct vmw_ttm_tt { + struct ttm_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; + bool mapped; + bool bound; +}; + +/* + * enum vmw_display_unit_type - Describes the display unit + */ +enum vmw_display_unit_type { + vmw_du_invalid = 0, + vmw_du_legacy, + vmw_du_screen_object, + vmw_du_screen_target, + vmw_du_max +}; + +struct vmw_validation_context; +struct vmw_ctx_validation_info; + +/** + * struct vmw_sw_context - Command submission context + * @res_ht: Pointer hash table used to find validation duplicates + * @kernel: Whether the command buffer originates from kernel code rather + * than from user-space + * @fp: If @kernel is false, points to the file of the client. Otherwise + * NULL + * @cmd_bounce: Command bounce buffer used for command validation before + * copying to fifo space + * @cmd_bounce_size: Current command bounce buffer size + * @cur_query_bo: Current buffer object used as query result buffer + * @bo_relocations: List of buffer object relocations + * @res_relocations: List of resource relocations + * @buf_start: Pointer to start of memory where command validation takes + * place + * @res_cache: Cache of recently looked up resources + * @last_query_ctx: Last context that submitted a query + * @needs_post_query_barrier: Whether a query barrier is needed after + * command submission + * @staged_bindings: Cached per-context binding tracker + * @staged_bindings_inuse: Whether the cached per-context binding tracker + * is in use + * @staged_cmd_res: List of staged command buffer managed resources in this + * command buffer + * @ctx_list: List of context resources referenced in this command buffer + * @dx_ctx_node: Validation metadata of the current DX context + * @dx_query_mob: The MOB used for DX queries + * @dx_query_ctx: The DX context used for the last DX query + * @man: Pointer to the command buffer managed resource manager + * @ctx: The validation context + */ +struct vmw_sw_context{ + DECLARE_HASHTABLE(res_ht, VMW_RES_HT_ORDER); + bool kernel; + struct vmw_fpriv *fp; + struct drm_file *filp; + uint32_t *cmd_bounce; + uint32_t cmd_bounce_size; + struct vmw_bo *cur_query_bo; + struct list_head bo_relocations; + 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_ctx_binding_state *staged_bindings; + bool staged_bindings_inuse; + struct list_head staged_cmd_res; + struct list_head ctx_list; + struct vmw_ctx_validation_info *dx_ctx_node; + struct vmw_bo *dx_query_mob; + struct vmw_resource *dx_query_ctx; + struct vmw_cmdbuf_res_manager *man; + struct vmw_validation_context *ctx; +}; + +struct vmw_legacy_display; +struct vmw_overlay; + +struct vmw_vga_topology_state { + uint32_t width; + uint32_t height; + uint32_t primary; + uint32_t pos_x; + uint32_t pos_y; +}; + + +/* + * 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; + bool enabled; +}; + +struct vmw_otable_batch { + unsigned num_otables; + struct vmw_otable *otables; + struct vmw_resource *context; + struct vmw_bo *otable_bo; +}; + +enum { + VMW_IRQTHREAD_FENCE, + VMW_IRQTHREAD_CMDBUF, + VMW_IRQTHREAD_MAX +}; + +/** + * enum vmw_sm_type - Graphics context capability supported by device. + * @VMW_SM_LEGACY: Pre DX context. + * @VMW_SM_4: Context support upto SM4. + * @VMW_SM_4_1: Context support upto SM4_1. + * @VMW_SM_5: Context support up to SM5. + * @VMW_SM_5_1X: Adds support for sm5_1 and gl43 extensions. + * @VMW_SM_MAX: Should be the last. + */ +enum vmw_sm_type { + VMW_SM_LEGACY = 0, + VMW_SM_4, + VMW_SM_4_1, + VMW_SM_5, + VMW_SM_5_1X, + VMW_SM_MAX +}; + +struct vmw_private { + struct drm_device drm; + struct ttm_device bdev; + + struct drm_vma_offset_manager vma_manager; + u32 pci_id; + resource_size_t io_start; + resource_size_t vram_start; + resource_size_t vram_size; + resource_size_t max_primary_mem; + u32 __iomem *rmmio; + u32 *fifo_mem; + resource_size_t fifo_mem_size; + uint32_t fb_max_width; + uint32_t fb_max_height; + uint32_t texture_max_width; + uint32_t texture_max_height; + uint32_t stdu_max_width; + uint32_t stdu_max_height; + uint32_t initial_width; + uint32_t initial_height; + uint32_t capabilities; + uint32_t capabilities2; + 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; + spinlock_t hw_lock; + bool assume_16bpp; + u32 irqs[VMWGFX_MAX_NUM_IRQS]; + u32 num_irq_vectors; + + enum vmw_sm_type sm_type; + + /* + * Framebuffer info. + */ + + enum vmw_display_unit_type active_display_unit; + struct vmw_legacy_display *ldu_priv; + struct vmw_overlay *overlay_priv; + struct drm_property *hotplug_mode_update_property; + struct drm_property *implicit_placement_property; + spinlock_t cursor_lock; + struct drm_atomic_state *suspend_state; + + /* + * Context and surface management. + */ + + spinlock_t resource_lock; + struct idr res_idr[vmw_res_max]; + + /* + * A resource manager for kernel-only surfaces and + * contexts. + */ + + struct ttm_object_device *tdev; + + /* + * Fencing and IRQs. + */ + + atomic_t marker_seq; + wait_queue_head_t fence_queue; + wait_queue_head_t fifo_queue; + spinlock_t waiter_lock; + int fence_queue_waiters; /* Protected by waiter_lock */ + int goal_queue_waiters; /* Protected by waiter_lock */ + int cmdbuf_waiters; /* Protected by waiter_lock */ + int error_waiters; /* Protected by waiter_lock */ + int fifo_queue_waiters; /* Protected by waiter_lock */ + uint32_t last_read_seqno; + struct vmw_fence_manager *fman; + uint32_t irq_mask; /* Updates protected by waiter_lock */ + + /* + * Device state + */ + + uint32_t traces_state; + uint32_t enable_state; + uint32_t config_done_state; + + /** + * Execbuf + */ + /** + * Protected by the cmdbuf mutex. + */ + + struct vmw_sw_context ctx; + struct mutex cmdbuf_mutex; + struct mutex binding_mutex; + + /** + * PM management. + */ + struct notifier_block pm_nb; + bool refuse_hibernation; + bool suspend_locked; + + atomic_t num_fifo_resources; + + /* + * Query processing. These members + * are protected by the cmdbuf mutex. + */ + + struct vmw_bo *dummy_query_bo; + struct vmw_bo *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 vmw_otable_batch otable_batch; + + struct vmw_fifo_state *fifo; + struct vmw_cmdbuf_man *cman; + DECLARE_BITMAP(irqthread_pending, VMW_IRQTHREAD_MAX); + + uint32 *devcaps; + + /* + * mksGuestStat instance-descriptor and pid arrays + */ + struct page *mksstat_user_pages[MKSSTAT_CAPACITY]; + atomic_t mksstat_user_pids[MKSSTAT_CAPACITY]; + +#if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS) + struct page *mksstat_kern_pages[MKSSTAT_CAPACITY]; + u8 mksstat_kern_top_timer[MKSSTAT_CAPACITY]; + atomic_t mksstat_kern_pids[MKSSTAT_CAPACITY]; +#endif +}; + +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; +} + +static inline struct vmw_private *vmw_priv_from_ttm(struct ttm_device *bdev) +{ + return container_of(bdev, struct vmw_private, bdev); +} + +static inline struct vmw_fpriv *vmw_fpriv(struct drm_file *file_priv) +{ + return (struct vmw_fpriv *)file_priv->driver_priv; +} + +/* + * SVGA v3 has mmio register access and lacks fifo cmds + */ +static inline bool vmw_is_svga_v3(const struct vmw_private *dev) +{ + return dev->pci_id == VMWGFX_PCI_ID_SVGA3; +} + +/* + * The locking here is fine-grained, so that it is performed once + * for every read- and write operation. This is of course costly, but we + * don't perform much register access in the timing critical paths anyway. + * Instead we have the extra benefit of being sure that we don't forget + * the hw lock around register accesses. + */ +static inline void vmw_write(struct vmw_private *dev_priv, + unsigned int offset, uint32_t value) +{ + if (vmw_is_svga_v3(dev_priv)) { + iowrite32(value, dev_priv->rmmio + offset); + } else { + spin_lock(&dev_priv->hw_lock); + outl(offset, dev_priv->io_start + SVGA_INDEX_PORT); + outl(value, dev_priv->io_start + SVGA_VALUE_PORT); + spin_unlock(&dev_priv->hw_lock); + } +} + +static inline uint32_t vmw_read(struct vmw_private *dev_priv, + unsigned int offset) +{ + u32 val; + + if (vmw_is_svga_v3(dev_priv)) { + val = ioread32(dev_priv->rmmio + offset); + } else { + spin_lock(&dev_priv->hw_lock); + outl(offset, dev_priv->io_start + SVGA_INDEX_PORT); + val = inl(dev_priv->io_start + SVGA_VALUE_PORT); + spin_unlock(&dev_priv->hw_lock); + } + + return val; +} + +/** + * has_sm4_context - Does the device support SM4 context. + * @dev_priv: Device private. + * + * Return: Bool value if device support SM4 context or not. + */ +static inline bool has_sm4_context(const struct vmw_private *dev_priv) +{ + return (dev_priv->sm_type >= VMW_SM_4); +} + +/** + * has_sm4_1_context - Does the device support SM4_1 context. + * @dev_priv: Device private. + * + * Return: Bool value if device support SM4_1 context or not. + */ +static inline bool has_sm4_1_context(const struct vmw_private *dev_priv) +{ + return (dev_priv->sm_type >= VMW_SM_4_1); +} + +/** + * has_sm5_context - Does the device support SM5 context. + * @dev_priv: Device private. + * + * Return: Bool value if device support SM5 context or not. + */ +static inline bool has_sm5_context(const struct vmw_private *dev_priv) +{ + return (dev_priv->sm_type >= VMW_SM_5); +} + +/** + * has_gl43_context - Does the device support GL43 context. + * @dev_priv: Device private. + * + * Return: Bool value if device support SM5 context or not. + */ +static inline bool has_gl43_context(const struct vmw_private *dev_priv) +{ + return (dev_priv->sm_type >= VMW_SM_5_1X); +} + + +static inline u32 vmw_max_num_uavs(struct vmw_private *dev_priv) +{ + return (has_gl43_context(dev_priv) ? + SVGA3D_DX11_1_MAX_UAVIEWS : SVGA3D_MAX_UAVIEWS); +} + +extern void vmw_svga_enable(struct vmw_private *dev_priv); +extern void vmw_svga_disable(struct vmw_private *dev_priv); +bool vmwgfx_supported(struct vmw_private *vmw); + + +/** + * GMR utilities - vmwgfx_gmr.c + */ + +extern int vmw_gmr_bind(struct vmw_private *dev_priv, + 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); + +/** + * Resource utilities - vmwgfx_resource.c + */ +struct vmw_user_resource_conv; + +extern void vmw_resource_unreference(struct vmw_resource **p_res); +extern struct vmw_resource *vmw_resource_reference(struct vmw_resource *res); +extern struct vmw_resource * +vmw_resource_reference_unless_doomed(struct vmw_resource *res); +extern int vmw_resource_validate(struct vmw_resource *res, bool intr, + bool dirtying); +extern int vmw_resource_reserve(struct vmw_resource *res, bool interruptible, + 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 drm_file *filp, + uint32_t handle, + struct vmw_surface **out_surf, + struct vmw_bo **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 int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int vmw_user_stream_lookup(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t *inout_id, + struct vmw_resource **out); +extern void vmw_resource_unreserve(struct vmw_resource *res, + bool dirty_set, + bool dirty, + bool switch_guest_memory, + struct vmw_bo *new_guest_memory, + unsigned long new_guest_memory_offset); +extern void vmw_query_move_notify(struct ttm_buffer_object *bo, + struct ttm_resource *old_mem, + struct ttm_resource *new_mem); +int vmw_query_readback_all(struct vmw_bo *dx_query_mob); +void vmw_resource_evict_all(struct vmw_private *dev_priv); +void vmw_resource_unbind_list(struct vmw_bo *vbo); +void vmw_resource_mob_attach(struct vmw_resource *res); +void vmw_resource_mob_detach(struct vmw_resource *res); +void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start, + pgoff_t end); +int vmw_resources_clean(struct vmw_bo *vbo, pgoff_t start, + pgoff_t end, pgoff_t *num_prefault); + +/** + * vmw_resource_mob_attached - Whether a resource currently has a mob attached + * @res: The resource + * + * Return: true if the resource has a mob attached, false otherwise. + */ +static inline bool vmw_resource_mob_attached(const struct vmw_resource *res) +{ + return !RB_EMPTY_NODE(&res->mob_node); +} + +/** + * GEM related functionality - vmwgfx_gem.c + */ +struct vmw_bo_params; +int vmw_gem_object_create(struct vmw_private *vmw, + struct vmw_bo_params *params, + struct vmw_bo **p_vbo); +extern int vmw_gem_object_create_with_handle(struct vmw_private *dev_priv, + struct drm_file *filp, + uint32_t size, + uint32_t *handle, + struct vmw_bo **p_vbo); +extern int vmw_gem_object_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); +extern void vmw_debugfs_gem_init(struct vmw_private *vdev); + +/** + * Misc Ioctl functionality - vmwgfx_ioctl.c + */ + +extern int vmw_getparam_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int vmw_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); + +/** + * Fifo utilities - vmwgfx_fifo.c + */ + +extern struct vmw_fifo_state *vmw_fifo_create(struct vmw_private *dev_priv); +extern void vmw_fifo_destroy(struct vmw_private *dev_priv); +extern bool vmw_cmd_supported(struct vmw_private *vmw); +extern void * +vmw_cmd_ctx_reserve(struct vmw_private *dev_priv, uint32_t bytes, int ctx_id); +extern void vmw_cmd_commit(struct vmw_private *dev_priv, uint32_t bytes); +extern void vmw_cmd_commit_flush(struct vmw_private *dev_priv, uint32_t bytes); +extern int vmw_cmd_send_fence(struct vmw_private *dev_priv, uint32_t *seqno); +extern bool vmw_supports_3d(struct vmw_private *dev_priv); +extern void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason); +extern bool vmw_fifo_have_pitchlock(struct vmw_private *dev_priv); +extern int vmw_cmd_emit_dummy_query(struct vmw_private *dev_priv, + uint32_t cid); +extern int vmw_cmd_flush(struct vmw_private *dev_priv, + bool interruptible); + +#define VMW_CMD_CTX_RESERVE(__priv, __bytes, __ctx_id) \ +({ \ + vmw_cmd_ctx_reserve(__priv, __bytes, __ctx_id) ? : ({ \ + DRM_ERROR("FIFO reserve failed at %s for %u bytes\n", \ + __func__, (unsigned int) __bytes); \ + NULL; \ + }); \ +}) + +#define VMW_CMD_RESERVE(__priv, __bytes) \ + VMW_CMD_CTX_RESERVE(__priv, __bytes, SVGA3D_INVALID_ID) + + +/** + * vmw_fifo_caps - Returns the capabilities of the FIFO command + * queue or 0 if fifo memory isn't present. + * @dev_priv: The device private context + */ +static inline uint32_t vmw_fifo_caps(const struct vmw_private *dev_priv) +{ + if (!dev_priv->fifo_mem || !dev_priv->fifo) + return 0; + return dev_priv->fifo->capabilities; +} + + +/** + * vmw_is_cursor_bypass3_enabled - Returns TRUE iff Cursor Bypass 3 + * is enabled in the FIFO. + * @dev_priv: The device private context + */ +static inline bool +vmw_is_cursor_bypass3_enabled(const struct vmw_private *dev_priv) +{ + return (vmw_fifo_caps(dev_priv) & SVGA_FIFO_CAP_CURSOR_BYPASS_3) != 0; +} + +/** + * TTM buffer object driver - vmwgfx_ttm_buffer.c + */ + +extern const size_t vmw_tt_size; +extern struct ttm_placement vmw_vram_placement; +extern struct ttm_placement vmw_vram_gmr_placement; +extern struct ttm_placement vmw_sys_placement; +extern struct ttm_device_funcs vmw_bo_driver; +extern const struct vmw_sg_table * +vmw_bo_sg_table(struct ttm_buffer_object *bo); +int vmw_bo_create_and_populate(struct vmw_private *dev_priv, + size_t bo_size, + u32 domain, + struct vmw_bo **bo_p); + +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->pages[viter->i]; +} + +/** + * Command submission - vmwgfx_execbuf.c + */ + +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, + uint32_t dx_context_handle, + struct drm_vmw_fence_rep __user + *user_fence_rep, + struct vmw_fence_obj **out_fence, + uint32_t flags); +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 int 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, + int32_t out_fence_fd); +bool vmw_cmd_describe(const void *buf, u32 *size, char const **cmd); + +/** + * IRQs and wating - vmwgfx_irq.c + */ + +extern int vmw_irq_install(struct vmw_private *dev_priv); +extern void vmw_irq_uninstall(struct drm_device *dev); +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 seqno, + bool interruptible, + unsigned long timeout); +extern void vmw_update_seqno(struct vmw_private *dev_priv); +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); +extern void vmw_generic_waiter_add(struct vmw_private *dev_priv, u32 flag, + int *waiter_count); +extern void vmw_generic_waiter_remove(struct vmw_private *dev_priv, + u32 flag, int *waiter_count); + +/** + * Kernel modesetting - vmwgfx_kms.c + */ + +int vmw_kms_init(struct vmw_private *dev_priv); +int vmw_kms_close(struct vmw_private *dev_priv); +int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv); +void vmw_kms_cursor_snoop(struct vmw_surface *srf, + struct ttm_object_file *tfile, + struct ttm_buffer_object *bo, + SVGA3dCmdHeader *header); +int vmw_kms_write_svga(struct vmw_private *vmw_priv, + unsigned width, unsigned height, unsigned pitch, + unsigned bpp, unsigned depth); +bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, + uint32_t pitch, + uint32_t height); +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_update_layout_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +void vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv); +int vmw_kms_suspend(struct drm_device *dev); +int vmw_kms_resume(struct drm_device *dev); +void vmw_kms_lost_device(struct drm_device *dev); + +int vmw_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +extern int vmw_resource_pin(struct vmw_resource *res, bool interruptible); +extern void vmw_resource_unpin(struct vmw_resource *res); +extern enum vmw_res_type vmw_res_type(const struct vmw_resource *res); + +/** + * Overlay control - vmwgfx_overlay.c + */ + +int vmw_overlay_init(struct vmw_private *dev_priv); +int vmw_overlay_close(struct vmw_private *dev_priv); +int vmw_overlay_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int vmw_overlay_resume_all(struct vmw_private *dev_priv); +int vmw_overlay_pause_all(struct vmw_private *dev_priv); +int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out); +int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id); +int vmw_overlay_num_overlays(struct vmw_private *dev_priv); +int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv); + +/** + * GMR Id manager + */ + +int vmw_gmrid_man_init(struct vmw_private *dev_priv, int type); +void vmw_gmrid_man_fini(struct vmw_private *dev_priv, int type); + +/** + * System memory manager + */ +int vmw_sys_man_init(struct vmw_private *dev_priv); +void vmw_sys_man_fini(struct vmw_private *dev_priv); + +/** + * 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 int vmw_context_define_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int vmw_extended_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 struct list_head *vmw_context_binding_list(struct vmw_resource *ctx); +extern struct vmw_cmdbuf_res_manager * +vmw_context_res_man(struct vmw_resource *ctx); +extern struct vmw_resource *vmw_context_cotable(struct vmw_resource *ctx, + SVGACOTableType cotable_type); +struct vmw_ctx_binding_state; +extern struct vmw_ctx_binding_state * +vmw_context_binding_state(struct vmw_resource *ctx); +extern void vmw_dx_context_scrub_cotables(struct vmw_resource *ctx, + bool readback); +extern int vmw_context_bind_dx_query(struct vmw_resource *ctx_res, + struct vmw_bo *mob); +extern struct vmw_bo * +vmw_context_get_dx_query_mob(struct vmw_resource *ctx_res); + + +/* + * Surface management - vmwgfx_surface.c + */ + +extern const struct vmw_user_resource_conv *user_surface_converter; + +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_gb_surface_define_ext_ioctl(struct drm_device *dev, + void *data, + struct drm_file *file_priv); +extern int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, + void *data, + struct drm_file *file_priv); + +int vmw_gb_surface_define(struct vmw_private *dev_priv, + const struct vmw_surface_metadata *req, + struct vmw_surface **srf_out); + +/* + * 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_add(struct vmw_private *dev_priv, + struct vmw_cmdbuf_res_manager *man, + u32 user_key, const void *bytecode, + SVGA3dShaderType shader_type, + size_t size, + struct list_head *list); +extern int vmw_shader_remove(struct vmw_cmdbuf_res_manager *man, + u32 user_key, SVGA3dShaderType shader_type, + struct list_head *list); +extern int vmw_dx_shader_add(struct vmw_cmdbuf_res_manager *man, + struct vmw_resource *ctx, + u32 user_key, + SVGA3dShaderType shader_type, + struct list_head *list); +extern void vmw_dx_shader_cotable_list_scrub(struct vmw_private *dev_priv, + struct list_head *list, + bool readback); + +extern struct vmw_resource * +vmw_shader_lookup(struct vmw_cmdbuf_res_manager *man, + u32 user_key, SVGA3dShaderType shader_type); + +/* + * Streamoutput management + */ +struct vmw_resource * +vmw_dx_streamoutput_lookup(struct vmw_cmdbuf_res_manager *man, + u32 user_key); +int vmw_dx_streamoutput_add(struct vmw_cmdbuf_res_manager *man, + struct vmw_resource *ctx, + SVGA3dStreamOutputId user_key, + struct list_head *list); +void vmw_dx_streamoutput_set_size(struct vmw_resource *res, u32 size); +int vmw_dx_streamoutput_remove(struct vmw_cmdbuf_res_manager *man, + SVGA3dStreamOutputId user_key, + struct list_head *list); +void vmw_dx_streamoutput_cotable_list_scrub(struct vmw_private *dev_priv, + struct list_head *list, + bool readback); + +/* + * Command buffer managed resources - vmwgfx_cmdbuf_res.c + */ + +extern struct vmw_cmdbuf_res_manager * +vmw_cmdbuf_res_man_create(struct vmw_private *dev_priv); +extern void vmw_cmdbuf_res_man_destroy(struct vmw_cmdbuf_res_manager *man); +extern struct vmw_resource * +vmw_cmdbuf_res_lookup(struct vmw_cmdbuf_res_manager *man, + enum vmw_cmdbuf_res_type res_type, + u32 user_key); +extern void vmw_cmdbuf_res_revert(struct list_head *list); +extern void vmw_cmdbuf_res_commit(struct list_head *list); +extern int vmw_cmdbuf_res_add(struct vmw_cmdbuf_res_manager *man, + enum vmw_cmdbuf_res_type res_type, + u32 user_key, + struct vmw_resource *res, + struct list_head *list); +extern int vmw_cmdbuf_res_remove(struct vmw_cmdbuf_res_manager *man, + enum vmw_cmdbuf_res_type res_type, + u32 user_key, + struct list_head *list, + struct vmw_resource **res); + +/* + * COTable management - vmwgfx_cotable.c + */ +extern const SVGACOTableType vmw_cotable_scrub_order[]; +extern struct vmw_resource *vmw_cotable_alloc(struct vmw_private *dev_priv, + struct vmw_resource *ctx, + u32 type); +extern int vmw_cotable_notify(struct vmw_resource *res, int id); +extern int vmw_cotable_scrub(struct vmw_resource *res, bool readback); +extern void vmw_cotable_add_resource(struct vmw_resource *ctx, + struct list_head *head); + +/* + * Command buffer managerment vmwgfx_cmdbuf.c + */ +struct vmw_cmdbuf_man; +struct vmw_cmdbuf_header; + +extern struct vmw_cmdbuf_man * +vmw_cmdbuf_man_create(struct vmw_private *dev_priv); +extern int vmw_cmdbuf_set_pool_size(struct vmw_cmdbuf_man *man, size_t size); +extern void vmw_cmdbuf_remove_pool(struct vmw_cmdbuf_man *man); +extern void vmw_cmdbuf_man_destroy(struct vmw_cmdbuf_man *man); +extern int vmw_cmdbuf_idle(struct vmw_cmdbuf_man *man, bool interruptible, + unsigned long timeout); +extern void *vmw_cmdbuf_reserve(struct vmw_cmdbuf_man *man, size_t size, + int ctx_id, bool interruptible, + struct vmw_cmdbuf_header *header); +extern void vmw_cmdbuf_commit(struct vmw_cmdbuf_man *man, size_t size, + struct vmw_cmdbuf_header *header, + bool flush); +extern void *vmw_cmdbuf_alloc(struct vmw_cmdbuf_man *man, + size_t size, bool interruptible, + struct vmw_cmdbuf_header **p_header); +extern void vmw_cmdbuf_header_free(struct vmw_cmdbuf_header *header); +extern int vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man, + bool interruptible); +extern void vmw_cmdbuf_irqthread(struct vmw_cmdbuf_man *man); + +/* CPU blit utilities - vmwgfx_blit.c */ + +/** + * struct vmw_diff_cpy - CPU blit information structure + * + * @rect: The output bounding box rectangle. + * @line: The current line of the blit. + * @line_offset: Offset of the current line segment. + * @cpp: Bytes per pixel (granularity information). + * @memcpy: Which memcpy function to use. + */ +struct vmw_diff_cpy { + struct drm_rect rect; + size_t line; + size_t line_offset; + int cpp; + void (*do_cpy)(struct vmw_diff_cpy *diff, u8 *dest, const u8 *src, + size_t n); +}; + +#define VMW_CPU_BLIT_INITIALIZER { \ + .do_cpy = vmw_memcpy, \ +} + +#define VMW_CPU_BLIT_DIFF_INITIALIZER(_cpp) { \ + .line = 0, \ + .line_offset = 0, \ + .rect = { .x1 = INT_MAX/2, \ + .y1 = INT_MAX/2, \ + .x2 = INT_MIN/2, \ + .y2 = INT_MIN/2 \ + }, \ + .cpp = _cpp, \ + .do_cpy = vmw_diff_memcpy, \ +} + +void vmw_diff_memcpy(struct vmw_diff_cpy *diff, u8 *dest, const u8 *src, + size_t n); + +void vmw_memcpy(struct vmw_diff_cpy *diff, u8 *dest, const u8 *src, size_t n); + +int vmw_bo_cpu_blit(struct ttm_buffer_object *dst, + u32 dst_offset, u32 dst_stride, + struct ttm_buffer_object *src, + u32 src_offset, u32 src_stride, + u32 w, u32 h, + struct vmw_diff_cpy *diff); + +/* Host messaging -vmwgfx_msg.c: */ +void vmw_disable_backdoor(void); +int vmw_host_get_guestinfo(const char *guest_info_param, + char *buffer, size_t *length); +__printf(1, 2) int vmw_host_printf(const char *fmt, ...); +int vmw_msg_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +/* Host mksGuestStats -vmwgfx_msg.c: */ +int vmw_mksstat_get_kern_slot(pid_t pid, struct vmw_private *dev_priv); + +int vmw_mksstat_reset_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int vmw_mksstat_add_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int vmw_mksstat_remove_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int vmw_mksstat_remove_all(struct vmw_private *dev_priv); + +/* VMW logging */ + +/** + * VMW_DEBUG_USER - Debug output for user-space debugging. + * + * @fmt: printf() like format string. + * + * This macro is for logging user-space error and debugging messages for e.g. + * command buffer execution errors due to malformed commands, invalid context, + * etc. + */ +#define VMW_DEBUG_USER(fmt, ...) \ + DRM_DEBUG_DRIVER(fmt, ##__VA_ARGS__) + +/* Resource dirtying - vmwgfx_page_dirty.c */ +void vmw_bo_dirty_scan(struct vmw_bo *vbo); +int vmw_bo_dirty_add(struct vmw_bo *vbo); +void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res); +void vmw_bo_dirty_clear_res(struct vmw_resource *res); +void vmw_bo_dirty_release(struct vmw_bo *vbo); +void vmw_bo_dirty_unmap(struct vmw_bo *vbo, + pgoff_t start, pgoff_t end); +vm_fault_t vmw_bo_vm_fault(struct vm_fault *vmf); +vm_fault_t vmw_bo_vm_mkwrite(struct vm_fault *vmf); + + +/** + * VMW_DEBUG_KMS - Debug output for kernel mode-setting + * + * This macro is for debugging vmwgfx mode-setting code. + */ +#define VMW_DEBUG_KMS(fmt, ...) \ + DRM_DEBUG_DRIVER(fmt, ##__VA_ARGS__) + +/** + * Inline helper functions + */ + +static inline void vmw_surface_unreference(struct vmw_surface **srf) +{ + struct vmw_surface *tmp_srf = *srf; + struct vmw_resource *res = &tmp_srf->res; + *srf = NULL; + + vmw_resource_unreference(&res); +} + +static inline struct vmw_surface *vmw_surface_reference(struct vmw_surface *srf) +{ + (void) vmw_resource_reference(&srf->res); + return srf; +} + +static inline void vmw_fifo_resource_inc(struct vmw_private *dev_priv) +{ + atomic_inc(&dev_priv->num_fifo_resources); +} + +static inline void vmw_fifo_resource_dec(struct vmw_private *dev_priv) +{ + atomic_dec(&dev_priv->num_fifo_resources); +} + +/** + * vmw_fifo_mem_read - Perform a MMIO read from the fifo memory + * + * @fifo_reg: The fifo register to read from + * + * This function is intended to be equivalent to ioread32() on + * memremap'd memory, but without byteswapping. + */ +static inline u32 vmw_fifo_mem_read(struct vmw_private *vmw, uint32 fifo_reg) +{ + BUG_ON(vmw_is_svga_v3(vmw)); + return READ_ONCE(*(vmw->fifo_mem + fifo_reg)); +} + +/** + * vmw_fifo_mem_write - Perform a MMIO write to volatile memory + * + * @addr: The fifo register to write to + * + * This function is intended to be equivalent to iowrite32 on + * memremap'd memory, but without byteswapping. + */ +static inline void vmw_fifo_mem_write(struct vmw_private *vmw, u32 fifo_reg, + u32 value) +{ + BUG_ON(vmw_is_svga_v3(vmw)); + WRITE_ONCE(*(vmw->fifo_mem + fifo_reg), value); +} + +static inline u32 vmw_fence_read(struct vmw_private *dev_priv) +{ + u32 fence; + if (vmw_is_svga_v3(dev_priv)) + fence = vmw_read(dev_priv, SVGA_REG_FENCE); + else + fence = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_FENCE); + return fence; +} + +static inline void vmw_fence_write(struct vmw_private *dev_priv, + u32 fence) +{ + BUG_ON(vmw_is_svga_v3(dev_priv)); + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_FENCE, fence); +} + +static inline u32 vmw_irq_status_read(struct vmw_private *vmw) +{ + u32 status; + if (vmw_is_svga_v3(vmw)) + status = vmw_read(vmw, SVGA_REG_IRQ_STATUS); + else + status = inl(vmw->io_start + SVGA_IRQSTATUS_PORT); + return status; +} + +static inline void vmw_irq_status_write(struct vmw_private *vmw, + uint32 status) +{ + if (vmw_is_svga_v3(vmw)) + vmw_write(vmw, SVGA_REG_IRQ_STATUS, status); + else + outl(status, vmw->io_start + SVGA_IRQSTATUS_PORT); +} + +static inline bool vmw_has_fences(struct vmw_private *vmw) +{ + if ((vmw->capabilities & (SVGA_CAP_COMMAND_BUFFERS | + SVGA_CAP_CMD_BUFFERS_2)) != 0) + return true; + return (vmw_fifo_caps(vmw) & SVGA_FIFO_CAP_FENCE) != 0; +} + +static inline bool vmw_shadertype_is_valid(enum vmw_sm_type shader_model, + u32 shader_type) +{ + SVGA3dShaderType max_allowed = SVGA3D_SHADERTYPE_PREDX_MAX; + + if (shader_model >= VMW_SM_5) + max_allowed = SVGA3D_SHADERTYPE_MAX; + else if (shader_model >= VMW_SM_4) + max_allowed = SVGA3D_SHADERTYPE_DX10_MAX; + return shader_type >= SVGA3D_SHADERTYPE_MIN && shader_type < max_allowed; +} + +#endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c new file mode 100644 index 0000000000..36987ef3fc --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -0,0 +1,4513 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009 - 2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_binding.h" +#include "vmwgfx_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_mksstat.h" +#include "vmwgfx_so.h" + +#include <drm/ttm/ttm_bo.h> +#include <drm/ttm/ttm_placement.h> + +#include <linux/sync_file.h> +#include <linux/hashtable.h> + +/* + * Helper macro to get dx_ctx_node if available otherwise print an error + * message. This is for use in command verifier function where if dx_ctx_node + * is not set then command is invalid. + */ +#define VMW_GET_CTX_NODE(__sw_context) \ +({ \ + __sw_context->dx_ctx_node ? __sw_context->dx_ctx_node : ({ \ + VMW_DEBUG_USER("SM context is not set at %s\n", __func__); \ + __sw_context->dx_ctx_node; \ + }); \ +}) + +#define VMW_DECLARE_CMD_VAR(__var, __type) \ + struct { \ + SVGA3dCmdHeader header; \ + __type body; \ + } __var + +/** + * struct vmw_relocation - Buffer object relocation + * + * @head: List head for the command submission context's relocation list + * @vbo: Non ref-counted pointer to buffer object + * @mob_loc: Pointer to location for mob id to be modified + * @location: Pointer to location for guest pointer to be modified + */ +struct vmw_relocation { + struct list_head head; + struct vmw_bo *vbo; + union { + SVGAMobId *mob_loc; + SVGAGuestPtr *location; + }; +}; + +/** + * enum vmw_resource_relocation_type - Relocation type for resources + * + * @vmw_res_rel_normal: Traditional relocation. The resource id in the + * command stream is replaced with the actual id after validation. + * @vmw_res_rel_nop: NOP relocation. The command is unconditionally replaced + * with a NOP. + * @vmw_res_rel_cond_nop: Conditional NOP relocation. If the resource id after + * validation is -1, the command is replaced with a NOP. Otherwise no action. + * @vmw_res_rel_max: Last value in the enum - used for error checking +*/ +enum vmw_resource_relocation_type { + vmw_res_rel_normal, + vmw_res_rel_nop, + vmw_res_rel_cond_nop, + vmw_res_rel_max +}; + +/** + * 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 single byte entries into the command buffer where the id + * that needs fixup is located. + * @rel_type: Type of relocation. + */ +struct vmw_resource_relocation { + struct list_head head; + const struct vmw_resource *res; + u32 offset:29; + enum vmw_resource_relocation_type rel_type:3; +}; + +/** + * struct vmw_ctx_validation_info - Extra validation metadata for contexts + * + * @head: List head of context list + * @ctx: The context resource + * @cur: The context's persistent binding state + * @staged: The binding state changes of this command buffer + */ +struct vmw_ctx_validation_info { + struct list_head head; + struct vmw_resource *ctx; + struct vmw_ctx_binding_state *cur; + struct vmw_ctx_binding_state *staged; +}; + +/** + * struct vmw_cmd_entry - Describe a command for the verifier + * + * @func: Call-back to handle the command. + * @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. + * @cmd_name: Name of the command. + */ +struct vmw_cmd_entry { + int (*func) (struct vmw_private *, struct vmw_sw_context *, + SVGA3dCmdHeader *); + bool user_allow; + bool gb_disable; + bool gb_enable; + const char *cmd_name; +}; + +#define VMW_CMD_DEF(_cmd, _func, _user_allow, _gb_disable, _gb_enable) \ + [(_cmd) - SVGA_3D_CMD_BASE] = {(_func), (_user_allow),\ + (_gb_disable), (_gb_enable), #_cmd} + +static int vmw_resource_context_res_add(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + struct vmw_resource *ctx); +static int vmw_translate_mob_ptr(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGAMobId *id, + struct vmw_bo **vmw_bo_p); +/** + * vmw_ptr_diff - Compute the offset from a to b in bytes + * + * @a: A starting pointer. + * @b: A pointer offset in the same address space. + * + * Returns: The offset in bytes between the two pointers. + */ +static size_t vmw_ptr_diff(void *a, void *b) +{ + return (unsigned long) b - (unsigned long) a; +} + +/** + * vmw_execbuf_bindings_commit - Commit modified binding state + * + * @sw_context: The command submission context + * @backoff: Whether this is part of the error path and binding state changes + * should be ignored + */ +static void vmw_execbuf_bindings_commit(struct vmw_sw_context *sw_context, + bool backoff) +{ + struct vmw_ctx_validation_info *entry; + + list_for_each_entry(entry, &sw_context->ctx_list, head) { + if (!backoff) + vmw_binding_state_commit(entry->cur, entry->staged); + + if (entry->staged != sw_context->staged_bindings) + vmw_binding_state_free(entry->staged); + else + sw_context->staged_bindings_inuse = false; + } + + /* List entries are freed with the validation context */ + INIT_LIST_HEAD(&sw_context->ctx_list); +} + +/** + * vmw_bind_dx_query_mob - Bind the DX query MOB if referenced + * + * @sw_context: The command submission context + */ +static void vmw_bind_dx_query_mob(struct vmw_sw_context *sw_context) +{ + if (sw_context->dx_query_mob) + vmw_context_bind_dx_query(sw_context->dx_query_ctx, + sw_context->dx_query_mob); +} + +/** + * vmw_cmd_ctx_first_setup - Perform the setup needed when a context is added to + * the validate list. + * + * @dev_priv: Pointer to the device private: + * @sw_context: The command submission context + * @res: Pointer to the resource + * @node: The validation node holding the context resource metadata + */ +static int vmw_cmd_ctx_first_setup(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + struct vmw_resource *res, + struct vmw_ctx_validation_info *node) +{ + int ret; + + ret = vmw_resource_context_res_add(dev_priv, sw_context, res); + if (unlikely(ret != 0)) + goto out_err; + + if (!sw_context->staged_bindings) { + sw_context->staged_bindings = vmw_binding_state_alloc(dev_priv); + if (IS_ERR(sw_context->staged_bindings)) { + ret = PTR_ERR(sw_context->staged_bindings); + sw_context->staged_bindings = NULL; + goto out_err; + } + } + + if (sw_context->staged_bindings_inuse) { + node->staged = vmw_binding_state_alloc(dev_priv); + if (IS_ERR(node->staged)) { + ret = PTR_ERR(node->staged); + node->staged = NULL; + goto out_err; + } + } else { + node->staged = sw_context->staged_bindings; + sw_context->staged_bindings_inuse = true; + } + + node->ctx = res; + node->cur = vmw_context_binding_state(res); + list_add_tail(&node->head, &sw_context->ctx_list); + + return 0; + +out_err: + return ret; +} + +/** + * vmw_execbuf_res_size - calculate extra size fore the resource validation node + * + * @dev_priv: Pointer to the device private struct. + * @res_type: The resource type. + * + * Guest-backed contexts and DX contexts require extra size to store execbuf + * private information in the validation node. Typically the binding manager + * associated data structures. + * + * Returns: The extra size requirement based on resource type. + */ +static unsigned int vmw_execbuf_res_size(struct vmw_private *dev_priv, + enum vmw_res_type res_type) +{ + return (res_type == vmw_res_dx_context || + (res_type == vmw_res_context && dev_priv->has_mob)) ? + sizeof(struct vmw_ctx_validation_info) : 0; +} + +/** + * vmw_execbuf_rcache_update - Update a resource-node cache entry + * + * @rcache: Pointer to the entry to update. + * @res: Pointer to the resource. + * @private: Pointer to the execbuf-private space in the resource validation + * node. + */ +static void vmw_execbuf_rcache_update(struct vmw_res_cache_entry *rcache, + struct vmw_resource *res, + void *private) +{ + rcache->res = res; + rcache->private = private; + rcache->valid = 1; + rcache->valid_handle = 0; +} + +enum vmw_val_add_flags { + vmw_val_add_flag_none = 0, + vmw_val_add_flag_noctx = 1 << 0, +}; + +/** + * vmw_execbuf_res_val_add - Add a resource to the validation list. + * + * @sw_context: Pointer to the software context. + * @res: Unreferenced rcu-protected pointer to the resource. + * @dirty: Whether to change dirty status. + * @flags: specifies whether to use the context or not + * + * Returns: 0 on success. Negative error code on failure. Typical error codes + * are %-EINVAL on inconsistency and %-ESRCH if the resource was doomed. + */ +static int vmw_execbuf_res_val_add(struct vmw_sw_context *sw_context, + struct vmw_resource *res, + u32 dirty, + u32 flags) +{ + struct vmw_private *dev_priv = res->dev_priv; + int ret; + enum vmw_res_type res_type = vmw_res_type(res); + struct vmw_res_cache_entry *rcache; + struct vmw_ctx_validation_info *ctx_info; + bool first_usage; + unsigned int priv_size; + + rcache = &sw_context->res_cache[res_type]; + if (likely(rcache->valid && rcache->res == res)) { + if (dirty) + vmw_validation_res_set_dirty(sw_context->ctx, + rcache->private, dirty); + return 0; + } + + if ((flags & vmw_val_add_flag_noctx) != 0) { + ret = vmw_validation_add_resource(sw_context->ctx, res, 0, dirty, + (void **)&ctx_info, NULL); + if (ret) + return ret; + + } else { + priv_size = vmw_execbuf_res_size(dev_priv, res_type); + ret = vmw_validation_add_resource(sw_context->ctx, res, priv_size, + dirty, (void **)&ctx_info, + &first_usage); + if (ret) + return ret; + + if (priv_size && first_usage) { + ret = vmw_cmd_ctx_first_setup(dev_priv, sw_context, res, + ctx_info); + if (ret) { + VMW_DEBUG_USER("Failed first usage context setup.\n"); + return ret; + } + } + } + + vmw_execbuf_rcache_update(rcache, res, ctx_info); + return 0; +} + +/** + * vmw_view_res_val_add - Add a view and the surface it's pointing to to the + * validation list + * + * @sw_context: The software context holding the validation list. + * @view: Pointer to the view resource. + * + * Returns 0 if success, negative error code otherwise. + */ +static int vmw_view_res_val_add(struct vmw_sw_context *sw_context, + struct vmw_resource *view) +{ + int ret; + + /* + * First add the resource the view is pointing to, otherwise it may be + * swapped out when the view is validated. + */ + ret = vmw_execbuf_res_val_add(sw_context, vmw_view_srf(view), + vmw_view_dirtying(view), vmw_val_add_flag_noctx); + if (ret) + return ret; + + return vmw_execbuf_res_val_add(sw_context, view, VMW_RES_DIRTY_NONE, + vmw_val_add_flag_noctx); +} + +/** + * vmw_view_id_val_add - Look up a view and add it and the surface it's pointing + * to to the validation list. + * + * @sw_context: The software context holding the validation list. + * @view_type: The view type to look up. + * @id: view id of the view. + * + * The view is represented by a view id and the DX context it's created on, or + * scheduled for creation on. If there is no DX context set, the function will + * return an -EINVAL error pointer. + * + * Returns: Unreferenced pointer to the resource on success, negative error + * pointer on failure. + */ +static struct vmw_resource * +vmw_view_id_val_add(struct vmw_sw_context *sw_context, + enum vmw_view_type view_type, u32 id) +{ + struct vmw_ctx_validation_info *ctx_node = sw_context->dx_ctx_node; + struct vmw_resource *view; + int ret; + + if (!ctx_node) + return ERR_PTR(-EINVAL); + + view = vmw_view_lookup(sw_context->man, view_type, id); + if (IS_ERR(view)) + return view; + + ret = vmw_view_res_val_add(sw_context, view); + if (ret) + return ERR_PTR(ret); + + return view; +} + +/** + * 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_bindinfo *entry; + int ret = 0; + struct vmw_resource *res; + u32 i; + u32 cotable_max = has_sm5_context(ctx->dev_priv) ? + SVGA_COTABLE_MAX : SVGA_COTABLE_DX10_MAX; + + /* Add all cotables to the validation list. */ + if (has_sm4_context(dev_priv) && + vmw_res_type(ctx) == vmw_res_dx_context) { + for (i = 0; i < cotable_max; ++i) { + res = vmw_context_cotable(ctx, i); + if (IS_ERR(res)) + continue; + + ret = vmw_execbuf_res_val_add(sw_context, res, + VMW_RES_DIRTY_SET, + vmw_val_add_flag_noctx); + if (unlikely(ret != 0)) + return ret; + } + } + + /* Add all resources bound to the context to the validation list */ + mutex_lock(&dev_priv->binding_mutex); + binding_list = vmw_context_binding_list(ctx); + + list_for_each_entry(entry, binding_list, ctx_list) { + if (vmw_res_type(entry->res) == vmw_res_view) + ret = vmw_view_res_val_add(sw_context, entry->res); + else + ret = vmw_execbuf_res_val_add(sw_context, entry->res, + vmw_binding_dirtying(entry->bt), + vmw_val_add_flag_noctx); + if (unlikely(ret != 0)) + break; + } + + if (has_sm4_context(dev_priv) && + vmw_res_type(ctx) == vmw_res_dx_context) { + struct vmw_bo *dx_query_mob; + + dx_query_mob = vmw_context_get_dx_query_mob(ctx); + if (dx_query_mob) { + vmw_bo_placement_set(dx_query_mob, + VMW_BO_DOMAIN_MOB, + VMW_BO_DOMAIN_MOB); + ret = vmw_validation_add_bo(sw_context->ctx, + dx_query_mob); + } + } + + mutex_unlock(&dev_priv->binding_mutex); + return ret; +} + +/** + * vmw_resource_relocation_add - Add a relocation to the relocation list + * + * @sw_context: Pointer to the software context. + * @res: The resource. + * @offset: Offset into the command buffer currently being parsed where the id + * that needs fixup is located. Granularity is one byte. + * @rel_type: Relocation type. + */ +static int vmw_resource_relocation_add(struct vmw_sw_context *sw_context, + const struct vmw_resource *res, + unsigned long offset, + enum vmw_resource_relocation_type + rel_type) +{ + struct vmw_resource_relocation *rel; + + rel = vmw_validation_mem_alloc(sw_context->ctx, sizeof(*rel)); + if (unlikely(!rel)) { + VMW_DEBUG_USER("Failed to allocate a resource relocation.\n"); + return -ENOMEM; + } + + rel->res = res; + rel->offset = offset; + rel->rel_type = rel_type; + list_add_tail(&rel->head, &sw_context->res_relocations); + + 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) +{ + /* Memory is validation context memory, so no need to free it */ + INIT_LIST_HEAD(list); +} + +/** + * 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; + + /* Validate the struct vmw_resource_relocation member size */ + BUILD_BUG_ON(SVGA_CB_MAX_SIZE >= (1 << 29)); + BUILD_BUG_ON(vmw_res_rel_max >= (1 << 3)); + + list_for_each_entry(rel, list, head) { + u32 *addr = (u32 *)((unsigned long) cb + rel->offset); + switch (rel->rel_type) { + case vmw_res_rel_normal: + *addr = rel->res->id; + break; + case vmw_res_rel_nop: + *addr = SVGA_3D_CMD_NOP; + break; + default: + if (rel->res->id == -1) + *addr = SVGA_3D_CMD_NOP; + break; + } + } +} + +static int vmw_cmd_invalid(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + return -EINVAL; +} + +static int vmw_cmd_ok(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + 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) +{ + int ret; + + ret = vmw_validation_res_reserve(sw_context->ctx, true); + if (ret) + return ret; + + if (sw_context->dx_query_mob) { + struct vmw_bo *expected_dx_query_mob; + + expected_dx_query_mob = + vmw_context_get_dx_query_mob(sw_context->dx_query_ctx); + if (expected_dx_query_mob && + expected_dx_query_mob != sw_context->dx_query_mob) { + ret = -EINVAL; + } + } + + return ret; +} + +/** + * 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. + * @dirty: Whether to change dirty status. + * @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_res: 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, + u32 dirty, + const struct vmw_user_resource_conv *converter, + uint32_t *id_loc, + struct vmw_resource **p_res) +{ + struct vmw_res_cache_entry *rcache = &sw_context->res_cache[res_type]; + struct vmw_resource *res; + int ret = 0; + bool needs_unref = false; + + if (p_res) + *p_res = NULL; + + if (*id_loc == SVGA3D_INVALID_ID) { + if (res_type == vmw_res_context) { + VMW_DEBUG_USER("Illegal context invalid id.\n"); + return -EINVAL; + } + return 0; + } + + if (likely(rcache->valid_handle && *id_loc == rcache->handle)) { + res = rcache->res; + if (dirty) + vmw_validation_res_set_dirty(sw_context->ctx, + rcache->private, dirty); + } else { + unsigned int size = vmw_execbuf_res_size(dev_priv, res_type); + + ret = vmw_validation_preload_res(sw_context->ctx, size); + if (ret) + return ret; + + ret = vmw_user_resource_lookup_handle + (dev_priv, sw_context->fp->tfile, *id_loc, converter, &res); + if (ret != 0) { + VMW_DEBUG_USER("Could not find/use resource 0x%08x.\n", + (unsigned int) *id_loc); + return ret; + } + needs_unref = true; + + ret = vmw_execbuf_res_val_add(sw_context, res, dirty, vmw_val_add_flag_none); + if (unlikely(ret != 0)) + goto res_check_done; + + if (rcache->valid && rcache->res == res) { + rcache->valid_handle = true; + rcache->handle = *id_loc; + } + } + + ret = vmw_resource_relocation_add(sw_context, res, + vmw_ptr_diff(sw_context->buf_start, + id_loc), + vmw_res_rel_normal); + if (p_res) + *p_res = res; + +res_check_done: + if (needs_unref) + vmw_resource_unreference(&res); + + return ret; +} + +/** + * vmw_rebind_all_dx_query - Rebind DX query associated with the context + * + * @ctx_res: context the query belongs to + * + * This function assumes binding_mutex is held. + */ +static int vmw_rebind_all_dx_query(struct vmw_resource *ctx_res) +{ + struct vmw_private *dev_priv = ctx_res->dev_priv; + struct vmw_bo *dx_query_mob; + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXBindAllQuery); + + dx_query_mob = vmw_context_get_dx_query_mob(ctx_res); + + if (!dx_query_mob || dx_query_mob->dx_query_ctx) + return 0; + + cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), ctx_res->id); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_BIND_ALL_QUERY; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = ctx_res->id; + cmd->body.mobid = dx_query_mob->tbo.resource->start; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + vmw_context_bind_dx_query(ctx_res, dx_query_mob); + + return 0; +} + +/** + * 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_ctx_validation_info *val; + int ret; + + list_for_each_entry(val, &sw_context->ctx_list, head) { + ret = vmw_binding_rebind_all(val->cur); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + VMW_DEBUG_USER("Failed to rebind context.\n"); + return ret; + } + + ret = vmw_rebind_all_dx_query(val->ctx); + if (ret != 0) { + VMW_DEBUG_USER("Failed to rebind queries.\n"); + return ret; + } + } + + return 0; +} + +/** + * vmw_view_bindings_add - Add an array of view bindings to a context binding + * state tracker. + * + * @sw_context: The execbuf state used for this command. + * @view_type: View type for the bindings. + * @binding_type: Binding type for the bindings. + * @shader_slot: The shader slot to user for the bindings. + * @view_ids: Array of view ids to be bound. + * @num_views: Number of view ids in @view_ids. + * @first_slot: The binding slot to be used for the first view id in @view_ids. + */ +static int vmw_view_bindings_add(struct vmw_sw_context *sw_context, + enum vmw_view_type view_type, + enum vmw_ctx_binding_type binding_type, + uint32 shader_slot, + uint32 view_ids[], u32 num_views, + u32 first_slot) +{ + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + u32 i; + + if (!ctx_node) + return -EINVAL; + + for (i = 0; i < num_views; ++i) { + struct vmw_ctx_bindinfo_view binding; + struct vmw_resource *view = NULL; + + if (view_ids[i] != SVGA3D_INVALID_ID) { + view = vmw_view_id_val_add(sw_context, view_type, + view_ids[i]); + if (IS_ERR(view)) { + VMW_DEBUG_USER("View not found.\n"); + return PTR_ERR(view); + } + } + binding.bi.ctx = ctx_node->ctx; + binding.bi.res = view; + binding.bi.bt = binding_type; + binding.shader_slot = shader_slot; + binding.slot = first_slot + i; + vmw_binding_add(ctx_node->staged, &binding.bi, + shader_slot, binding.slot); + } + + 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, uint32_t) = + container_of(header, typeof(*cmd), header); + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, + VMW_RES_DIRTY_SET, user_context_converter, + &cmd->body, NULL); +} + +/** + * vmw_execbuf_info_from_res - Get the private validation metadata for a + * recently validated resource + * + * @sw_context: Pointer to the command submission context + * @res: The resource + * + * The resource pointed to by @res needs to be present in the command submission + * context's resource cache and hence the last resource of that type to be + * processed by the validation code. + * + * Return: a pointer to the private metadata of the resource, or NULL if it + * wasn't found + */ +static struct vmw_ctx_validation_info * +vmw_execbuf_info_from_res(struct vmw_sw_context *sw_context, + struct vmw_resource *res) +{ + struct vmw_res_cache_entry *rcache = + &sw_context->res_cache[vmw_res_type(res)]; + + if (rcache->valid && rcache->res == res) + return rcache->private; + + WARN_ON_ONCE(true); + return NULL; +} + +static int vmw_cmd_set_render_target_check(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdSetRenderTarget); + struct vmw_resource *ctx; + struct vmw_resource *res; + int ret; + + cmd = container_of(header, typeof(*cmd), header); + + if (cmd->body.type >= SVGA3D_RT_MAX) { + VMW_DEBUG_USER("Illegal render target type %u.\n", + (unsigned int) cmd->body.type); + return -EINVAL; + } + + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, + VMW_RES_DIRTY_SET, user_context_converter, + &cmd->body.cid, &ctx); + if (unlikely(ret != 0)) + return ret; + + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_SET, user_surface_converter, + &cmd->body.target.sid, &res); + if (unlikely(ret)) + return ret; + + if (dev_priv->has_mob) { + struct vmw_ctx_bindinfo_view binding; + struct vmw_ctx_validation_info *node; + + node = vmw_execbuf_info_from_res(sw_context, ctx); + if (!node) + return -EINVAL; + + binding.bi.ctx = ctx; + binding.bi.res = res; + binding.bi.bt = vmw_ctx_binding_rt; + binding.slot = cmd->body.type; + vmw_binding_add(node->staged, &binding.bi, 0, binding.slot); + } + + return 0; +} + +static int vmw_cmd_surface_copy_check(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdSurfaceCopy); + int ret; + + cmd = container_of(header, typeof(*cmd), header); + + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.src.sid, NULL); + if (ret) + return ret; + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_SET, user_surface_converter, + &cmd->body.dest.sid, NULL); +} + +static int vmw_cmd_buffer_copy_check(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXBufferCopy); + int ret; + + cmd = container_of(header, typeof(*cmd), header); + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.src, NULL); + if (ret != 0) + return ret; + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_SET, user_surface_converter, + &cmd->body.dest, NULL); +} + +static int vmw_cmd_pred_copy_check(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXPredCopyRegion); + int ret; + + cmd = container_of(header, typeof(*cmd), header); + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.srcSid, NULL); + if (ret != 0) + return ret; + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_SET, user_surface_converter, + &cmd->body.dstSid, NULL); +} + +static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdSurfaceStretchBlt); + int ret; + + cmd = container_of(header, typeof(*cmd), header); + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.src.sid, NULL); + if (unlikely(ret != 0)) + return ret; + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_SET, user_surface_converter, + &cmd->body.dest.sid, NULL); +} + +static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdBlitSurfaceToScreen) = + container_of(header, typeof(*cmd), header); + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.srcImage.sid, NULL); +} + +static int vmw_cmd_present_check(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdPresent) = + container_of(header, typeof(*cmd), header); + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, 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 vmw_bo *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(PFN_UP(new_query_bo->tbo.resource->size) > 4)) { + VMW_DEBUG_USER("Query buffer too large.\n"); + return -EINVAL; + } + + if (unlikely(sw_context->cur_query_bo != NULL)) { + sw_context->needs_post_query_barrier = true; + vmw_bo_placement_set_default_accelerated(sw_context->cur_query_bo); + ret = vmw_validation_add_bo(sw_context->ctx, + sw_context->cur_query_bo); + if (unlikely(ret != 0)) + return ret; + } + sw_context->cur_query_bo = new_query_bo; + + vmw_bo_placement_set_default_accelerated(dev_priv->dummy_query_bo); + ret = vmw_validation_add_bo(sw_context->ctx, + dev_priv->dummy_query_bo); + 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_cmd_emit_dummy_query(dev_priv, ctx->id); + + if (unlikely(ret != 0)) + VMW_DEBUG_USER("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_reserved(dev_priv->pinned_bo, false); + vmw_bo_unreference(&dev_priv->pinned_bo); + } + + if (!sw_context->needs_post_query_barrier) { + vmw_bo_pin_reserved(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. + */ + if (!dev_priv->dummy_query_bo_pinned) { + vmw_bo_pin_reserved(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 = + vmw_bo_reference(sw_context->cur_query_bo); + } + } +} + +/** + * vmw_translate_mob_ptr - 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 + * non-reference-counted pointer to the buffer object 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_bo **vmw_bo_p) +{ + struct vmw_bo *vmw_bo, *tmp_bo; + uint32_t handle = *id; + struct vmw_relocation *reloc; + int ret; + + vmw_validation_preload_bo(sw_context->ctx); + ret = vmw_user_bo_lookup(sw_context->filp, handle, &vmw_bo); + if (ret != 0) { + drm_dbg(&dev_priv->drm, "Could not find or use MOB buffer.\n"); + return PTR_ERR(vmw_bo); + } + vmw_bo_placement_set(vmw_bo, VMW_BO_DOMAIN_MOB, VMW_BO_DOMAIN_MOB); + ret = vmw_validation_add_bo(sw_context->ctx, vmw_bo); + tmp_bo = vmw_bo; + vmw_user_bo_unref(&tmp_bo); + if (unlikely(ret != 0)) + return ret; + + reloc = vmw_validation_mem_alloc(sw_context->ctx, sizeof(*reloc)); + if (!reloc) + return -ENOMEM; + + reloc->mob_loc = id; + reloc->vbo = vmw_bo; + + *vmw_bo_p = vmw_bo; + list_add_tail(&reloc->head, &sw_context->bo_relocations); + + return 0; +} + +/** + * vmw_translate_guest_ptr - 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 + * non-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, + struct vmw_bo **vmw_bo_p) +{ + struct vmw_bo *vmw_bo, *tmp_bo; + uint32_t handle = ptr->gmrId; + struct vmw_relocation *reloc; + int ret; + + vmw_validation_preload_bo(sw_context->ctx); + ret = vmw_user_bo_lookup(sw_context->filp, handle, &vmw_bo); + if (ret != 0) { + drm_dbg(&dev_priv->drm, "Could not find or use GMR region.\n"); + return PTR_ERR(vmw_bo); + } + vmw_bo_placement_set(vmw_bo, VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM, + VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM); + ret = vmw_validation_add_bo(sw_context->ctx, vmw_bo); + tmp_bo = vmw_bo; + vmw_user_bo_unref(&tmp_bo); + if (unlikely(ret != 0)) + return ret; + + reloc = vmw_validation_mem_alloc(sw_context->ctx, sizeof(*reloc)); + if (!reloc) + return -ENOMEM; + + reloc->location = ptr; + reloc->vbo = vmw_bo; + *vmw_bo_p = vmw_bo; + list_add_tail(&reloc->head, &sw_context->bo_relocations); + + return 0; +} + +/** + * vmw_cmd_dx_define_query - validate SVGA_3D_CMD_DX_DEFINE_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. + * + * This function adds the new query into the query COTABLE + */ +static int vmw_cmd_dx_define_query(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXDefineQuery); + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + struct vmw_resource *cotable_res; + int ret; + + if (!ctx_node) + return -EINVAL; + + cmd = container_of(header, typeof(*cmd), header); + + if (cmd->body.type < SVGA3D_QUERYTYPE_MIN || + cmd->body.type >= SVGA3D_QUERYTYPE_MAX) + return -EINVAL; + + cotable_res = vmw_context_cotable(ctx_node->ctx, SVGA_COTABLE_DXQUERY); + ret = vmw_cotable_notify(cotable_res, cmd->body.queryId); + + return ret; +} + +/** + * vmw_cmd_dx_bind_query - validate SVGA_3D_CMD_DX_BIND_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. + * + * The query bind operation will eventually associate the query ID with its + * backing MOB. In this function, we take the user mode MOB ID and use + * vmw_translate_mob_ptr() to translate it to its kernel mode equivalent. + */ +static int vmw_cmd_dx_bind_query(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXBindQuery); + struct vmw_bo *vmw_bo; + int ret; + + cmd = container_of(header, typeof(*cmd), header); + + /* + * Look up the buffer pointed to by q.mobid, put it on the relocation + * list so its kernel mode MOB ID can be filled in later + */ + ret = vmw_translate_mob_ptr(dev_priv, sw_context, &cmd->body.mobid, + &vmw_bo); + + if (ret != 0) + return ret; + + sw_context->dx_query_mob = vmw_bo; + sw_context->dx_query_ctx = sw_context->dx_ctx_node->ctx; + return 0; +} + +/** + * vmw_cmd_begin_gb_query - validate 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdBeginGBQuery) = + container_of(header, typeof(*cmd), header); + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, + VMW_RES_DIRTY_SET, user_context_converter, + &cmd->body.cid, NULL); +} + +/** + * vmw_cmd_begin_query - validate 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdBeginQuery) = + container_of(header, typeof(*cmd), header); + + if (unlikely(dev_priv->has_mob)) { + VMW_DECLARE_CMD_VAR(gb_cmd, SVGA3dCmdBeginGBQuery); + + 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.body.cid = cmd->body.cid; + gb_cmd.body.type = cmd->body.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, + VMW_RES_DIRTY_SET, user_context_converter, + &cmd->body.cid, NULL); +} + +/** + * vmw_cmd_end_gb_query - validate 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_bo *vmw_bo; + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdEndGBQuery); + int ret; + + cmd = container_of(header, typeof(*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->body.mobid, + &vmw_bo); + if (unlikely(ret != 0)) + return ret; + + ret = vmw_query_bo_switch_prepare(dev_priv, vmw_bo, sw_context); + + return ret; +} + +/** + * vmw_cmd_end_query - validate 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) +{ + struct vmw_bo *vmw_bo; + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdEndQuery); + int ret; + + cmd = container_of(header, typeof(*cmd), header); + if (dev_priv->has_mob) { + VMW_DECLARE_CMD_VAR(gb_cmd, SVGA3dCmdEndGBQuery); + + 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.body.cid = cmd->body.cid; + gb_cmd.body.type = cmd->body.type; + gb_cmd.body.mobid = cmd->body.guestResult.gmrId; + gb_cmd.body.offset = cmd->body.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; + + ret = vmw_translate_guest_ptr(dev_priv, sw_context, + &cmd->body.guestResult, &vmw_bo); + if (unlikely(ret != 0)) + return ret; + + ret = vmw_query_bo_switch_prepare(dev_priv, vmw_bo, sw_context); + + return ret; +} + +/** + * vmw_cmd_wait_gb_query - validate 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_bo *vmw_bo; + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdWaitForGBQuery); + int ret; + + cmd = container_of(header, typeof(*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->body.mobid, + &vmw_bo); + if (unlikely(ret != 0)) + return ret; + + return 0; +} + +/** + * vmw_cmd_wait_query - validate 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) +{ + struct vmw_bo *vmw_bo; + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdWaitForQuery); + int ret; + + cmd = container_of(header, typeof(*cmd), header); + if (dev_priv->has_mob) { + VMW_DECLARE_CMD_VAR(gb_cmd, SVGA3dCmdWaitForGBQuery); + + 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.body.cid = cmd->body.cid; + gb_cmd.body.type = cmd->body.type; + gb_cmd.body.mobid = cmd->body.guestResult.gmrId; + gb_cmd.body.offset = cmd->body.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; + + ret = vmw_translate_guest_ptr(dev_priv, sw_context, + &cmd->body.guestResult, &vmw_bo); + if (unlikely(ret != 0)) + return ret; + + return 0; +} + +static int vmw_cmd_dma(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_bo *vmw_bo = NULL; + struct vmw_surface *srf = NULL; + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdSurfaceDMA); + int ret; + SVGA3dCmdSurfaceDMASuffix *suffix; + uint32_t bo_size; + bool dirty; + + cmd = container_of(header, typeof(*cmd), header); + suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long) &cmd->body + + header->size - sizeof(*suffix)); + + /* Make sure device and verifier stays in sync. */ + if (unlikely(suffix->suffixSize != sizeof(*suffix))) { + VMW_DEBUG_USER("Invalid DMA suffix size.\n"); + return -EINVAL; + } + + ret = vmw_translate_guest_ptr(dev_priv, sw_context, + &cmd->body.guest.ptr, &vmw_bo); + if (unlikely(ret != 0)) + return ret; + + /* Make sure DMA doesn't cross BO boundaries. */ + bo_size = vmw_bo->tbo.base.size; + if (unlikely(cmd->body.guest.ptr.offset > bo_size)) { + VMW_DEBUG_USER("Invalid DMA offset.\n"); + return -EINVAL; + } + + bo_size -= cmd->body.guest.ptr.offset; + if (unlikely(suffix->maximumOffset > bo_size)) + suffix->maximumOffset = bo_size; + + dirty = (cmd->body.transfer == SVGA3D_WRITE_HOST_VRAM) ? + VMW_RES_DIRTY_SET : 0; + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + dirty, user_surface_converter, + &cmd->body.host.sid, NULL); + if (unlikely(ret != 0)) { + if (unlikely(ret != -ERESTARTSYS)) + VMW_DEBUG_USER("could not find surface for DMA.\n"); + return ret; + } + + srf = vmw_res_to_srf(sw_context->res_cache[vmw_res_surface].res); + + vmw_kms_cursor_snoop(srf, sw_context->fp->tfile, &vmw_bo->tbo, header); + + return 0; +} + +static int vmw_cmd_draw(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDrawPrimitives); + SVGA3dVertexDecl *decl = (SVGA3dVertexDecl *)( + (unsigned long)header + sizeof(*cmd)); + SVGA3dPrimitiveRange *range; + uint32_t i; + uint32_t maxnum; + int ret; + + ret = vmw_cmd_cid_check(dev_priv, sw_context, header); + if (unlikely(ret != 0)) + return ret; + + cmd = container_of(header, typeof(*cmd), header); + maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl); + + if (unlikely(cmd->body.numVertexDecls > maxnum)) { + VMW_DEBUG_USER("Illegal number of vertex declarations.\n"); + return -EINVAL; + } + + for (i = 0; i < cmd->body.numVertexDecls; ++i, ++decl) { + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, + user_surface_converter, + &decl->array.surfaceId, NULL); + if (unlikely(ret != 0)) + return ret; + } + + maxnum = (header->size - sizeof(cmd->body) - + cmd->body.numVertexDecls * sizeof(*decl)) / sizeof(*range); + if (unlikely(cmd->body.numRanges > maxnum)) { + VMW_DEBUG_USER("Illegal number of index ranges.\n"); + return -EINVAL; + } + + range = (SVGA3dPrimitiveRange *) decl; + for (i = 0; i < cmd->body.numRanges; ++i, ++range) { + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, + user_surface_converter, + &range->indexArray.surfaceId, NULL); + if (unlikely(ret != 0)) + return ret; + } + return 0; +} + +static int vmw_cmd_tex_state(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdSetTextureState); + SVGA3dTextureState *last_state = (SVGA3dTextureState *) + ((unsigned long) header + header->size + sizeof(*header)); + SVGA3dTextureState *cur_state = (SVGA3dTextureState *) + ((unsigned long) header + sizeof(*cmd)); + struct vmw_resource *ctx; + struct vmw_resource *res; + int ret; + + cmd = container_of(header, typeof(*cmd), header); + + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, + VMW_RES_DIRTY_SET, user_context_converter, + &cmd->body.cid, &ctx); + if (unlikely(ret != 0)) + return ret; + + for (; cur_state < last_state; ++cur_state) { + if (likely(cur_state->name != SVGA3D_TS_BIND_TEXTURE)) + continue; + + if (cur_state->stage >= SVGA3D_NUM_TEXTURE_UNITS) { + VMW_DEBUG_USER("Illegal texture/sampler unit %u.\n", + (unsigned int) cur_state->stage); + return -EINVAL; + } + + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, + user_surface_converter, + &cur_state->value, &res); + if (unlikely(ret != 0)) + return ret; + + if (dev_priv->has_mob) { + struct vmw_ctx_bindinfo_tex binding; + struct vmw_ctx_validation_info *node; + + node = vmw_execbuf_info_from_res(sw_context, ctx); + if (!node) + return -EINVAL; + + binding.bi.ctx = ctx; + binding.bi.res = res; + binding.bi.bt = vmw_ctx_binding_tex; + binding.texture_stage = cur_state->stage; + vmw_binding_add(node->staged, &binding.bi, 0, + binding.texture_stage); + } + } + + return 0; +} + +static int vmw_cmd_check_define_gmrfb(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + void *buf) +{ + struct vmw_bo *vmw_bo; + + struct { + uint32_t header; + SVGAFifoCmdDefineGMRFB body; + } *cmd = buf; + + return vmw_translate_guest_ptr(dev_priv, sw_context, &cmd->body.ptr, + &vmw_bo); +} + +/** + * vmw_cmd_res_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: Pointer to the resource. + * @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. It's basically a wrapper around + * vmw_cmd_res_switch_backup with a different interface. + */ +static int vmw_cmd_res_switch_backup(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + struct vmw_resource *res, uint32_t *buf_id, + unsigned long backup_offset) +{ + struct vmw_bo *vbo; + void *info; + int ret; + + info = vmw_execbuf_info_from_res(sw_context, res); + if (!info) + return -EINVAL; + + ret = vmw_translate_mob_ptr(dev_priv, sw_context, buf_id, &vbo); + if (ret) + return ret; + + vmw_validation_res_switch_backup(sw_context->ctx, info, vbo, + backup_offset); + return 0; +} + +/** + * 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. It's basically a wrapper around + * vmw_cmd_res_switch_backup with a different interface. + */ +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) +{ + struct vmw_resource *res; + int ret; + + ret = vmw_cmd_res_check(dev_priv, sw_context, res_type, + VMW_RES_DIRTY_NONE, converter, res_id, &res); + if (ret) + return ret; + + return vmw_cmd_res_switch_backup(dev_priv, sw_context, res, buf_id, + backup_offset); +} + +/** + * vmw_cmd_bind_gb_surface - Validate 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdBindGBSurface) = + container_of(header, typeof(*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 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdUpdateGBImage) = + container_of(header, typeof(*cmd), header); + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.image.sid, NULL); +} + +/** + * vmw_cmd_update_gb_surface - Validate 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdUpdateGBSurface) = + container_of(header, typeof(*cmd), header); + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_CLEAR, user_surface_converter, + &cmd->body.sid, NULL); +} + +/** + * vmw_cmd_readback_gb_image - Validate 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdReadbackGBImage) = + container_of(header, typeof(*cmd), header); + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.image.sid, NULL); +} + +/** + * vmw_cmd_readback_gb_surface - Validate 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdReadbackGBSurface) = + container_of(header, typeof(*cmd), header); + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_CLEAR, user_surface_converter, + &cmd->body.sid, NULL); +} + +/** + * vmw_cmd_invalidate_gb_image - Validate 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdInvalidateGBImage) = + container_of(header, typeof(*cmd), header); + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.image.sid, NULL); +} + +/** + * vmw_cmd_invalidate_gb_surface - Validate 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdInvalidateGBSurface) = + container_of(header, typeof(*cmd), header); + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_CLEAR, user_surface_converter, + &cmd->body.sid, NULL); +} + +/** + * vmw_cmd_shader_define - Validate 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDefineShader); + int ret; + size_t size; + struct vmw_resource *ctx; + + cmd = container_of(header, typeof(*cmd), header); + + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, + VMW_RES_DIRTY_SET, user_context_converter, + &cmd->body.cid, &ctx); + 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(dev_priv, vmw_context_res_man(ctx), + cmd->body.shid, cmd + 1, cmd->body.type, + size, &sw_context->staged_cmd_res); + if (unlikely(ret != 0)) + return ret; + + return vmw_resource_relocation_add(sw_context, NULL, + vmw_ptr_diff(sw_context->buf_start, + &cmd->header.id), + vmw_res_rel_nop); +} + +/** + * vmw_cmd_shader_destroy - Validate 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDestroyShader); + int ret; + struct vmw_resource *ctx; + + cmd = container_of(header, typeof(*cmd), header); + + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, + VMW_RES_DIRTY_SET, user_context_converter, + &cmd->body.cid, &ctx); + if (unlikely(ret != 0)) + return ret; + + if (unlikely(!dev_priv->has_mob)) + return 0; + + ret = vmw_shader_remove(vmw_context_res_man(ctx), cmd->body.shid, + cmd->body.type, &sw_context->staged_cmd_res); + if (unlikely(ret != 0)) + return ret; + + return vmw_resource_relocation_add(sw_context, NULL, + vmw_ptr_diff(sw_context->buf_start, + &cmd->header.id), + vmw_res_rel_nop); +} + +/** + * vmw_cmd_set_shader - Validate 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdSetShader); + struct vmw_ctx_bindinfo_shader binding; + struct vmw_resource *ctx, *res = NULL; + struct vmw_ctx_validation_info *ctx_info; + int ret; + + cmd = container_of(header, typeof(*cmd), header); + + if (!vmw_shadertype_is_valid(VMW_SM_LEGACY, cmd->body.type)) { + VMW_DEBUG_USER("Illegal shader type %u.\n", + (unsigned int) cmd->body.type); + return -EINVAL; + } + + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, + VMW_RES_DIRTY_SET, user_context_converter, + &cmd->body.cid, &ctx); + if (unlikely(ret != 0)) + return ret; + + if (!dev_priv->has_mob) + return 0; + + if (cmd->body.shid != SVGA3D_INVALID_ID) { + /* + * This is the compat shader path - Per device guest-backed + * shaders, but user-space thinks it's per context host- + * backed shaders. + */ + res = vmw_shader_lookup(vmw_context_res_man(ctx), + cmd->body.shid, cmd->body.type); + if (!IS_ERR(res)) { + ret = vmw_execbuf_res_val_add(sw_context, res, + VMW_RES_DIRTY_NONE, + vmw_val_add_flag_noctx); + if (unlikely(ret != 0)) + return ret; + + ret = vmw_resource_relocation_add + (sw_context, res, + vmw_ptr_diff(sw_context->buf_start, + &cmd->body.shid), + vmw_res_rel_normal); + if (unlikely(ret != 0)) + return ret; + } + } + + if (IS_ERR_OR_NULL(res)) { + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_shader, + VMW_RES_DIRTY_NONE, + user_shader_converter, &cmd->body.shid, + &res); + if (unlikely(ret != 0)) + return ret; + } + + ctx_info = vmw_execbuf_info_from_res(sw_context, ctx); + if (!ctx_info) + return -EINVAL; + + binding.bi.ctx = ctx; + binding.bi.res = res; + binding.bi.bt = vmw_ctx_binding_shader; + binding.shader_slot = cmd->body.type - SVGA3D_SHADERTYPE_MIN; + vmw_binding_add(ctx_info->staged, &binding.bi, binding.shader_slot, 0); + + return 0; +} + +/** + * vmw_cmd_set_shader_const - Validate 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdSetShaderConst); + int ret; + + cmd = container_of(header, typeof(*cmd), header); + + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, + VMW_RES_DIRTY_SET, 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 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) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdBindGBShader) = + container_of(header, typeof(*cmd), header); + + return vmw_cmd_switch_backup(dev_priv, sw_context, vmw_res_shader, + user_shader_converter, &cmd->body.shid, + &cmd->body.mobid, cmd->body.offsetInBytes); +} + +/** + * vmw_cmd_dx_set_single_constant_buffer - Validate + * SVGA_3D_CMD_DX_SET_SINGLE_CONSTANT_BUFFER 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_dx_set_single_constant_buffer(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXSetSingleConstantBuffer); + + struct vmw_resource *res = NULL; + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + struct vmw_ctx_bindinfo_cb binding; + int ret; + + if (!ctx_node) + return -EINVAL; + + cmd = container_of(header, typeof(*cmd), header); + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.sid, &res); + if (unlikely(ret != 0)) + return ret; + + if (!vmw_shadertype_is_valid(dev_priv->sm_type, cmd->body.type) || + cmd->body.slot >= SVGA3D_DX_MAX_CONSTBUFFERS) { + VMW_DEBUG_USER("Illegal const buffer shader %u slot %u.\n", + (unsigned int) cmd->body.type, + (unsigned int) cmd->body.slot); + return -EINVAL; + } + + binding.bi.ctx = ctx_node->ctx; + binding.bi.res = res; + binding.bi.bt = vmw_ctx_binding_cb; + binding.shader_slot = cmd->body.type - SVGA3D_SHADERTYPE_MIN; + binding.offset = cmd->body.offsetInBytes; + binding.size = cmd->body.sizeInBytes; + binding.slot = cmd->body.slot; + + vmw_binding_add(ctx_node->staged, &binding.bi, binding.shader_slot, + binding.slot); + + return 0; +} + +/** + * vmw_cmd_dx_set_constant_buffer_offset - Validate + * SVGA_3D_CMD_DX_SET_VS/PS/GS/HS/DS/CS_CONSTANT_BUFFER_OFFSET 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_dx_set_constant_buffer_offset(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXSetConstantBufferOffset); + + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + u32 shader_slot; + + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + if (!ctx_node) + return -EINVAL; + + cmd = container_of(header, typeof(*cmd), header); + if (cmd->body.slot >= SVGA3D_DX_MAX_CONSTBUFFERS) { + VMW_DEBUG_USER("Illegal const buffer slot %u.\n", + (unsigned int) cmd->body.slot); + return -EINVAL; + } + + shader_slot = cmd->header.id - SVGA_3D_CMD_DX_SET_VS_CONSTANT_BUFFER_OFFSET; + vmw_binding_cb_offset_update(ctx_node->staged, shader_slot, + cmd->body.slot, cmd->body.offsetInBytes); + + return 0; +} + +/** + * vmw_cmd_dx_set_shader_res - Validate SVGA_3D_CMD_DX_SET_SHADER_RESOURCES + * 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_dx_set_shader_res(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXSetShaderResources) = + container_of(header, typeof(*cmd), header); + + u32 num_sr_view = (cmd->header.size - sizeof(cmd->body)) / + sizeof(SVGA3dShaderResourceViewId); + + if ((u64) cmd->body.startView + (u64) num_sr_view > + (u64) SVGA3D_DX_MAX_SRVIEWS || + !vmw_shadertype_is_valid(dev_priv->sm_type, cmd->body.type)) { + VMW_DEBUG_USER("Invalid shader binding.\n"); + return -EINVAL; + } + + return vmw_view_bindings_add(sw_context, vmw_view_sr, + vmw_ctx_binding_sr, + cmd->body.type - SVGA3D_SHADERTYPE_MIN, + (void *) &cmd[1], num_sr_view, + cmd->body.startView); +} + +/** + * vmw_cmd_dx_set_shader - Validate SVGA_3D_CMD_DX_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_dx_set_shader(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXSetShader); + struct vmw_resource *res = NULL; + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + struct vmw_ctx_bindinfo_shader binding; + int ret = 0; + + if (!ctx_node) + return -EINVAL; + + cmd = container_of(header, typeof(*cmd), header); + + if (!vmw_shadertype_is_valid(dev_priv->sm_type, cmd->body.type)) { + VMW_DEBUG_USER("Illegal shader type %u.\n", + (unsigned int) cmd->body.type); + return -EINVAL; + } + + if (cmd->body.shaderId != SVGA3D_INVALID_ID) { + res = vmw_shader_lookup(sw_context->man, cmd->body.shaderId, 0); + if (IS_ERR(res)) { + VMW_DEBUG_USER("Could not find shader for binding.\n"); + return PTR_ERR(res); + } + + ret = vmw_execbuf_res_val_add(sw_context, res, + VMW_RES_DIRTY_NONE, + vmw_val_add_flag_noctx); + if (ret) + return ret; + } + + binding.bi.ctx = ctx_node->ctx; + binding.bi.res = res; + binding.bi.bt = vmw_ctx_binding_dx_shader; + binding.shader_slot = cmd->body.type - SVGA3D_SHADERTYPE_MIN; + + vmw_binding_add(ctx_node->staged, &binding.bi, binding.shader_slot, 0); + + return 0; +} + +/** + * vmw_cmd_dx_set_vertex_buffers - Validates SVGA_3D_CMD_DX_SET_VERTEX_BUFFERS + * 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_dx_set_vertex_buffers(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + struct vmw_ctx_bindinfo_vb binding; + struct vmw_resource *res; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetVertexBuffers body; + SVGA3dVertexBuffer buf[]; + } *cmd; + int i, ret, num; + + if (!ctx_node) + return -EINVAL; + + cmd = container_of(header, typeof(*cmd), header); + num = (cmd->header.size - sizeof(cmd->body)) / + sizeof(SVGA3dVertexBuffer); + if ((u64)num + (u64)cmd->body.startBuffer > + (u64)SVGA3D_DX_MAX_VERTEXBUFFERS) { + VMW_DEBUG_USER("Invalid number of vertex buffers.\n"); + return -EINVAL; + } + + for (i = 0; i < num; i++) { + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, + user_surface_converter, + &cmd->buf[i].sid, &res); + if (unlikely(ret != 0)) + return ret; + + binding.bi.ctx = ctx_node->ctx; + binding.bi.bt = vmw_ctx_binding_vb; + binding.bi.res = res; + binding.offset = cmd->buf[i].offset; + binding.stride = cmd->buf[i].stride; + binding.slot = i + cmd->body.startBuffer; + + vmw_binding_add(ctx_node->staged, &binding.bi, 0, binding.slot); + } + + return 0; +} + +/** + * vmw_cmd_dx_set_index_buffer - Validate + * SVGA_3D_CMD_DX_IA_SET_INDEX_BUFFER 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_dx_set_index_buffer(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + struct vmw_ctx_bindinfo_ib binding; + struct vmw_resource *res; + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXSetIndexBuffer); + int ret; + + if (!ctx_node) + return -EINVAL; + + cmd = container_of(header, typeof(*cmd), header); + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.sid, &res); + if (unlikely(ret != 0)) + return ret; + + binding.bi.ctx = ctx_node->ctx; + binding.bi.res = res; + binding.bi.bt = vmw_ctx_binding_ib; + binding.offset = cmd->body.offset; + binding.format = cmd->body.format; + + vmw_binding_add(ctx_node->staged, &binding.bi, 0, 0); + + return 0; +} + +/** + * vmw_cmd_dx_set_rendertargets - Validate SVGA_3D_CMD_DX_SET_RENDERTARGETS + * 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_dx_set_rendertargets(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXSetRenderTargets) = + container_of(header, typeof(*cmd), header); + u32 num_rt_view = (cmd->header.size - sizeof(cmd->body)) / + sizeof(SVGA3dRenderTargetViewId); + int ret; + + if (num_rt_view > SVGA3D_DX_MAX_RENDER_TARGETS) { + VMW_DEBUG_USER("Invalid DX Rendertarget binding.\n"); + return -EINVAL; + } + + ret = vmw_view_bindings_add(sw_context, vmw_view_ds, vmw_ctx_binding_ds, + 0, &cmd->body.depthStencilViewId, 1, 0); + if (ret) + return ret; + + return vmw_view_bindings_add(sw_context, vmw_view_rt, + vmw_ctx_binding_dx_rt, 0, (void *)&cmd[1], + num_rt_view, 0); +} + +/** + * vmw_cmd_dx_clear_rendertarget_view - Validate + * SVGA_3D_CMD_DX_CLEAR_RENDERTARGET_VIEW 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_dx_clear_rendertarget_view(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXClearRenderTargetView) = + container_of(header, typeof(*cmd), header); + struct vmw_resource *ret; + + ret = vmw_view_id_val_add(sw_context, vmw_view_rt, + cmd->body.renderTargetViewId); + + return PTR_ERR_OR_ZERO(ret); +} + +/** + * vmw_cmd_dx_clear_depthstencil_view - Validate + * SVGA_3D_CMD_DX_CLEAR_DEPTHSTENCIL_VIEW 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_dx_clear_depthstencil_view(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXClearDepthStencilView) = + container_of(header, typeof(*cmd), header); + struct vmw_resource *ret; + + ret = vmw_view_id_val_add(sw_context, vmw_view_ds, + cmd->body.depthStencilViewId); + + return PTR_ERR_OR_ZERO(ret); +} + +static int vmw_cmd_dx_view_define(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + struct vmw_resource *srf; + struct vmw_resource *res; + enum vmw_view_type view_type; + int ret; + /* + * This is based on the fact that all affected define commands have the + * same initial command body layout. + */ + struct { + SVGA3dCmdHeader header; + uint32 defined_id; + uint32 sid; + } *cmd; + + if (!ctx_node) + return -EINVAL; + + view_type = vmw_view_cmd_to_type(header->id); + if (view_type == vmw_view_max) + return -EINVAL; + + cmd = container_of(header, typeof(*cmd), header); + if (unlikely(cmd->sid == SVGA3D_INVALID_ID)) { + VMW_DEBUG_USER("Invalid surface id.\n"); + return -EINVAL; + } + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->sid, &srf); + if (unlikely(ret != 0)) + return ret; + + res = vmw_context_cotable(ctx_node->ctx, vmw_view_cotables[view_type]); + ret = vmw_cotable_notify(res, cmd->defined_id); + if (unlikely(ret != 0)) + return ret; + + return vmw_view_add(sw_context->man, ctx_node->ctx, srf, view_type, + cmd->defined_id, header, + header->size + sizeof(*header), + &sw_context->staged_cmd_res); +} + +/** + * vmw_cmd_dx_set_so_targets - Validate SVGA_3D_CMD_DX_SET_SOTARGETS 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_dx_set_so_targets(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + struct vmw_ctx_bindinfo_so_target binding; + struct vmw_resource *res; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetSOTargets body; + SVGA3dSoTarget targets[]; + } *cmd; + int i, ret, num; + + if (!ctx_node) + return -EINVAL; + + cmd = container_of(header, typeof(*cmd), header); + num = (cmd->header.size - sizeof(cmd->body)) / sizeof(SVGA3dSoTarget); + + if (num > SVGA3D_DX_MAX_SOTARGETS) { + VMW_DEBUG_USER("Invalid DX SO binding.\n"); + return -EINVAL; + } + + for (i = 0; i < num; i++) { + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_SET, + user_surface_converter, + &cmd->targets[i].sid, &res); + if (unlikely(ret != 0)) + return ret; + + binding.bi.ctx = ctx_node->ctx; + binding.bi.res = res; + binding.bi.bt = vmw_ctx_binding_so_target; + binding.offset = cmd->targets[i].offset; + binding.size = cmd->targets[i].sizeInBytes; + binding.slot = i; + + vmw_binding_add(ctx_node->staged, &binding.bi, 0, binding.slot); + } + + return 0; +} + +static int vmw_cmd_dx_so_define(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + struct vmw_resource *res; + /* + * This is based on the fact that all affected define commands have + * the same initial command body layout. + */ + struct { + SVGA3dCmdHeader header; + uint32 defined_id; + } *cmd; + enum vmw_so_type so_type; + int ret; + + if (!ctx_node) + return -EINVAL; + + so_type = vmw_so_cmd_to_type(header->id); + res = vmw_context_cotable(ctx_node->ctx, vmw_so_cotables[so_type]); + if (IS_ERR(res)) + return PTR_ERR(res); + cmd = container_of(header, typeof(*cmd), header); + ret = vmw_cotable_notify(res, cmd->defined_id); + + return ret; +} + +/** + * vmw_cmd_dx_check_subresource - Validate SVGA_3D_CMD_DX_[X]_SUBRESOURCE + * 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_dx_check_subresource(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct { + SVGA3dCmdHeader header; + union { + SVGA3dCmdDXReadbackSubResource r_body; + SVGA3dCmdDXInvalidateSubResource i_body; + SVGA3dCmdDXUpdateSubResource u_body; + SVGA3dSurfaceId sid; + }; + } *cmd; + + BUILD_BUG_ON(offsetof(typeof(*cmd), r_body.sid) != + offsetof(typeof(*cmd), sid)); + BUILD_BUG_ON(offsetof(typeof(*cmd), i_body.sid) != + offsetof(typeof(*cmd), sid)); + BUILD_BUG_ON(offsetof(typeof(*cmd), u_body.sid) != + offsetof(typeof(*cmd), sid)); + + cmd = container_of(header, typeof(*cmd), header); + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->sid, NULL); +} + +static int vmw_cmd_dx_cid_check(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + + if (!ctx_node) + return -EINVAL; + + return 0; +} + +/** + * vmw_cmd_dx_view_remove - validate a view remove command and schedule the view + * resource for removal. + * + * @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. + * + * Check that the view exists, and if it was not created using this command + * batch, conditionally make this command a NOP. + */ +static int vmw_cmd_dx_view_remove(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + struct { + SVGA3dCmdHeader header; + union vmw_view_destroy body; + } *cmd = container_of(header, typeof(*cmd), header); + enum vmw_view_type view_type = vmw_view_cmd_to_type(header->id); + struct vmw_resource *view; + int ret; + + if (!ctx_node) + return -EINVAL; + + ret = vmw_view_remove(sw_context->man, cmd->body.view_id, view_type, + &sw_context->staged_cmd_res, &view); + if (ret || !view) + return ret; + + /* + * If the view wasn't created during this command batch, it might + * have been removed due to a context swapout, so add a + * relocation to conditionally make this command a NOP to avoid + * device errors. + */ + return vmw_resource_relocation_add(sw_context, view, + vmw_ptr_diff(sw_context->buf_start, + &cmd->header.id), + vmw_res_rel_cond_nop); +} + +/** + * vmw_cmd_dx_define_shader - Validate SVGA_3D_CMD_DX_DEFINE_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_dx_define_shader(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + struct vmw_resource *res; + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXDefineShader) = + container_of(header, typeof(*cmd), header); + int ret; + + if (!ctx_node) + return -EINVAL; + + res = vmw_context_cotable(ctx_node->ctx, SVGA_COTABLE_DXSHADER); + ret = vmw_cotable_notify(res, cmd->body.shaderId); + if (ret) + return ret; + + return vmw_dx_shader_add(sw_context->man, ctx_node->ctx, + cmd->body.shaderId, cmd->body.type, + &sw_context->staged_cmd_res); +} + +/** + * vmw_cmd_dx_destroy_shader - Validate SVGA_3D_CMD_DX_DESTROY_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_dx_destroy_shader(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXDestroyShader) = + container_of(header, typeof(*cmd), header); + int ret; + + if (!ctx_node) + return -EINVAL; + + ret = vmw_shader_remove(sw_context->man, cmd->body.shaderId, 0, + &sw_context->staged_cmd_res); + + return ret; +} + +/** + * vmw_cmd_dx_bind_shader - Validate SVGA_3D_CMD_DX_BIND_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_dx_bind_shader(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_resource *ctx; + struct vmw_resource *res; + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXBindShader) = + container_of(header, typeof(*cmd), header); + int ret; + + if (cmd->body.cid != SVGA3D_INVALID_ID) { + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, + VMW_RES_DIRTY_SET, + user_context_converter, &cmd->body.cid, + &ctx); + if (ret) + return ret; + } else { + struct vmw_ctx_validation_info *ctx_node = + VMW_GET_CTX_NODE(sw_context); + + if (!ctx_node) + return -EINVAL; + + ctx = ctx_node->ctx; + } + + res = vmw_shader_lookup(vmw_context_res_man(ctx), cmd->body.shid, 0); + if (IS_ERR(res)) { + VMW_DEBUG_USER("Could not find shader to bind.\n"); + return PTR_ERR(res); + } + + ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_NONE, + vmw_val_add_flag_noctx); + if (ret) { + VMW_DEBUG_USER("Error creating resource validation node.\n"); + return ret; + } + + return vmw_cmd_res_switch_backup(dev_priv, sw_context, res, + &cmd->body.mobid, + cmd->body.offsetInBytes); +} + +/** + * vmw_cmd_dx_genmips - Validate SVGA_3D_CMD_DX_GENMIPS 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_dx_genmips(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXGenMips) = + container_of(header, typeof(*cmd), header); + struct vmw_resource *view; + struct vmw_res_cache_entry *rcache; + + view = vmw_view_id_val_add(sw_context, vmw_view_sr, + cmd->body.shaderResourceViewId); + if (IS_ERR(view)) + return PTR_ERR(view); + + /* + * Normally the shader-resource view is not gpu-dirtying, but for + * this particular command it is... + * So mark the last looked-up surface, which is the surface + * the view points to, gpu-dirty. + */ + rcache = &sw_context->res_cache[vmw_res_surface]; + vmw_validation_res_set_dirty(sw_context->ctx, rcache->private, + VMW_RES_DIRTY_SET); + return 0; +} + +/** + * vmw_cmd_dx_transfer_from_buffer - Validate + * SVGA_3D_CMD_DX_TRANSFER_FROM_BUFFER 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_dx_transfer_from_buffer(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXTransferFromBuffer) = + container_of(header, typeof(*cmd), header); + int ret; + + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.srcSid, NULL); + if (ret != 0) + return ret; + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_SET, user_surface_converter, + &cmd->body.destSid, NULL); +} + +/** + * vmw_cmd_intra_surface_copy - Validate SVGA_3D_CMD_INTRA_SURFACE_COPY 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_intra_surface_copy(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdIntraSurfaceCopy) = + container_of(header, typeof(*cmd), header); + + if (!(dev_priv->capabilities2 & SVGA_CAP2_INTRA_SURFACE_COPY)) + return -EINVAL; + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_SET, user_surface_converter, + &cmd->body.surface.sid, NULL); +} + +static int vmw_cmd_sm5(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + return 0; +} + +static int vmw_cmd_sm5_view_define(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + return vmw_cmd_dx_view_define(dev_priv, sw_context, header); +} + +static int vmw_cmd_sm5_view_remove(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + return vmw_cmd_dx_view_remove(dev_priv, sw_context, header); +} + +static int vmw_cmd_clear_uav_uint(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXClearUAViewUint body; + } *cmd = container_of(header, typeof(*cmd), header); + struct vmw_resource *ret; + + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + ret = vmw_view_id_val_add(sw_context, vmw_view_ua, + cmd->body.uaViewId); + + return PTR_ERR_OR_ZERO(ret); +} + +static int vmw_cmd_clear_uav_float(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXClearUAViewFloat body; + } *cmd = container_of(header, typeof(*cmd), header); + struct vmw_resource *ret; + + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + ret = vmw_view_id_val_add(sw_context, vmw_view_ua, + cmd->body.uaViewId); + + return PTR_ERR_OR_ZERO(ret); +} + +static int vmw_cmd_set_uav(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetUAViews body; + } *cmd = container_of(header, typeof(*cmd), header); + u32 num_uav = (cmd->header.size - sizeof(cmd->body)) / + sizeof(SVGA3dUAViewId); + int ret; + + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + if (num_uav > vmw_max_num_uavs(dev_priv)) { + VMW_DEBUG_USER("Invalid UAV binding.\n"); + return -EINVAL; + } + + ret = vmw_view_bindings_add(sw_context, vmw_view_ua, + vmw_ctx_binding_uav, 0, (void *)&cmd[1], + num_uav, 0); + if (ret) + return ret; + + vmw_binding_add_uav_index(sw_context->dx_ctx_node->staged, 0, + cmd->body.uavSpliceIndex); + + return ret; +} + +static int vmw_cmd_set_cs_uav(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetCSUAViews body; + } *cmd = container_of(header, typeof(*cmd), header); + u32 num_uav = (cmd->header.size - sizeof(cmd->body)) / + sizeof(SVGA3dUAViewId); + int ret; + + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + if (num_uav > vmw_max_num_uavs(dev_priv)) { + VMW_DEBUG_USER("Invalid UAV binding.\n"); + return -EINVAL; + } + + ret = vmw_view_bindings_add(sw_context, vmw_view_ua, + vmw_ctx_binding_cs_uav, 0, (void *)&cmd[1], + num_uav, 0); + if (ret) + return ret; + + vmw_binding_add_uav_index(sw_context->dx_ctx_node->staged, 1, + cmd->body.startIndex); + + return ret; +} + +static int vmw_cmd_dx_define_streamoutput(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = sw_context->dx_ctx_node; + struct vmw_resource *res; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXDefineStreamOutputWithMob body; + } *cmd = container_of(header, typeof(*cmd), header); + int ret; + + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + if (!ctx_node) { + DRM_ERROR("DX Context not set.\n"); + return -EINVAL; + } + + res = vmw_context_cotable(ctx_node->ctx, SVGA_COTABLE_STREAMOUTPUT); + ret = vmw_cotable_notify(res, cmd->body.soid); + if (ret) + return ret; + + return vmw_dx_streamoutput_add(sw_context->man, ctx_node->ctx, + cmd->body.soid, + &sw_context->staged_cmd_res); +} + +static int vmw_cmd_dx_destroy_streamoutput(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = sw_context->dx_ctx_node; + struct vmw_resource *res; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXDestroyStreamOutput body; + } *cmd = container_of(header, typeof(*cmd), header); + + if (!ctx_node) { + DRM_ERROR("DX Context not set.\n"); + return -EINVAL; + } + + /* + * When device does not support SM5 then streamoutput with mob command is + * not available to user-space. Simply return in this case. + */ + if (!has_sm5_context(dev_priv)) + return 0; + + /* + * With SM5 capable device if lookup fails then user-space probably used + * old streamoutput define command. Return without an error. + */ + res = vmw_dx_streamoutput_lookup(vmw_context_res_man(ctx_node->ctx), + cmd->body.soid); + if (IS_ERR(res)) + return 0; + + return vmw_dx_streamoutput_remove(sw_context->man, cmd->body.soid, + &sw_context->staged_cmd_res); +} + +static int vmw_cmd_dx_bind_streamoutput(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = sw_context->dx_ctx_node; + struct vmw_resource *res; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXBindStreamOutput body; + } *cmd = container_of(header, typeof(*cmd), header); + int ret; + + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + if (!ctx_node) { + DRM_ERROR("DX Context not set.\n"); + return -EINVAL; + } + + res = vmw_dx_streamoutput_lookup(vmw_context_res_man(ctx_node->ctx), + cmd->body.soid); + if (IS_ERR(res)) { + DRM_ERROR("Could not find streamoutput to bind.\n"); + return PTR_ERR(res); + } + + vmw_dx_streamoutput_set_size(res, cmd->body.sizeInBytes); + + ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_NONE, + vmw_val_add_flag_noctx); + if (ret) { + DRM_ERROR("Error creating resource validation node.\n"); + return ret; + } + + return vmw_cmd_res_switch_backup(dev_priv, sw_context, res, + &cmd->body.mobid, + cmd->body.offsetInBytes); +} + +static int vmw_cmd_dx_set_streamoutput(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_ctx_validation_info *ctx_node = sw_context->dx_ctx_node; + struct vmw_resource *res; + struct vmw_ctx_bindinfo_so binding; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXSetStreamOutput body; + } *cmd = container_of(header, typeof(*cmd), header); + int ret; + + if (!ctx_node) { + DRM_ERROR("DX Context not set.\n"); + return -EINVAL; + } + + if (cmd->body.soid == SVGA3D_INVALID_ID) + return 0; + + /* + * When device does not support SM5 then streamoutput with mob command is + * not available to user-space. Simply return in this case. + */ + if (!has_sm5_context(dev_priv)) + return 0; + + /* + * With SM5 capable device if lookup fails then user-space probably used + * old streamoutput define command. Return without an error. + */ + res = vmw_dx_streamoutput_lookup(vmw_context_res_man(ctx_node->ctx), + cmd->body.soid); + if (IS_ERR(res)) { + return 0; + } + + ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_NONE, + vmw_val_add_flag_noctx); + if (ret) { + DRM_ERROR("Error creating resource validation node.\n"); + return ret; + } + + binding.bi.ctx = ctx_node->ctx; + binding.bi.res = res; + binding.bi.bt = vmw_ctx_binding_so; + binding.slot = 0; /* Only one SO set to context at a time. */ + + vmw_binding_add(sw_context->dx_ctx_node->staged, &binding.bi, 0, + binding.slot); + + return ret; +} + +static int vmw_cmd_indexed_instanced_indirect(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_draw_indexed_instanced_indirect_cmd { + SVGA3dCmdHeader header; + SVGA3dCmdDXDrawIndexedInstancedIndirect body; + } *cmd = container_of(header, typeof(*cmd), header); + + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.argsBufferSid, NULL); +} + +static int vmw_cmd_instanced_indirect(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_draw_instanced_indirect_cmd { + SVGA3dCmdHeader header; + SVGA3dCmdDXDrawInstancedIndirect body; + } *cmd = container_of(header, typeof(*cmd), header); + + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.argsBufferSid, NULL); +} + +static int vmw_cmd_dispatch_indirect(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_dispatch_indirect_cmd { + SVGA3dCmdHeader header; + SVGA3dCmdDXDispatchIndirect body; + } *cmd = container_of(header, typeof(*cmd), header); + + if (!has_sm5_context(dev_priv)) + return -EINVAL; + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + VMW_RES_DIRTY_NONE, user_surface_converter, + &cmd->body.argsBufferSid, NULL); +} + +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 = ((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: + VMW_DEBUG_USER("Unsupported SVGA command: %u.\n", cmd_id); + return -EINVAL; + } + + if (*size > size_remaining) { + VMW_DEBUG_USER("Invalid SVGA command (size mismatch): %u.\n", + cmd_id); + return -EINVAL; + } + + if (unlikely(!sw_context->kernel)) { + VMW_DEBUG_USER("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 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, 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, 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_DEAD1, &vmw_cmd_invalid, + false, false, false), + VMW_CMD_DEF(SVGA_3D_CMD_DEAD2, &vmw_cmd_invalid, + false, false, false), + VMW_CMD_DEF(SVGA_3D_CMD_DEAD12, &vmw_cmd_invalid, false, false, false), + VMW_CMD_DEF(SVGA_3D_CMD_DEAD13, &vmw_cmd_invalid, false, false, false), + VMW_CMD_DEF(SVGA_3D_CMD_DEAD14, &vmw_cmd_invalid, false, false, false), + VMW_CMD_DEF(SVGA_3D_CMD_DEAD15, &vmw_cmd_invalid, false, false, false), + VMW_CMD_DEF(SVGA_3D_CMD_DEAD16, &vmw_cmd_invalid, false, false, false), + VMW_CMD_DEF(SVGA_3D_CMD_DEAD17, &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_MOB64, &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_NOP_ERROR, &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), + VMW_CMD_DEF(SVGA_3D_CMD_GB_SCREEN_DMA, &vmw_cmd_invalid, + false, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_BIND_GB_SURFACE_WITH_PITCH, &vmw_cmd_invalid, + false, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_GB_MOB_FENCE, &vmw_cmd_invalid, + false, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DEFINE_GB_SURFACE_V2, &vmw_cmd_invalid, + false, false, true), + + /* SM commands */ + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_CONTEXT, &vmw_cmd_invalid, + false, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_CONTEXT, &vmw_cmd_invalid, + false, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_BIND_CONTEXT, &vmw_cmd_invalid, + false, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_READBACK_CONTEXT, &vmw_cmd_invalid, + false, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_INVALIDATE_CONTEXT, &vmw_cmd_invalid, + false, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_SINGLE_CONSTANT_BUFFER, + &vmw_cmd_dx_set_single_constant_buffer, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_SHADER_RESOURCES, + &vmw_cmd_dx_set_shader_res, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_SHADER, &vmw_cmd_dx_set_shader, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_SAMPLERS, &vmw_cmd_dx_cid_check, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DRAW, &vmw_cmd_dx_cid_check, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DRAW_INDEXED, &vmw_cmd_dx_cid_check, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DRAW_INSTANCED, &vmw_cmd_dx_cid_check, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DRAW_INDEXED_INSTANCED, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DRAW_AUTO, &vmw_cmd_dx_cid_check, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_VERTEX_BUFFERS, + &vmw_cmd_dx_set_vertex_buffers, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_INDEX_BUFFER, + &vmw_cmd_dx_set_index_buffer, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_RENDERTARGETS, + &vmw_cmd_dx_set_rendertargets, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_BLEND_STATE, &vmw_cmd_dx_cid_check, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_DEPTHSTENCIL_STATE, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_RASTERIZER_STATE, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_QUERY, &vmw_cmd_dx_define_query, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_QUERY, &vmw_cmd_dx_cid_check, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_BIND_QUERY, &vmw_cmd_dx_bind_query, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_QUERY_OFFSET, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_BEGIN_QUERY, &vmw_cmd_dx_cid_check, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_END_QUERY, &vmw_cmd_dx_cid_check, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_READBACK_QUERY, &vmw_cmd_invalid, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_PREDICATION, &vmw_cmd_dx_cid_check, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_VIEWPORTS, &vmw_cmd_dx_cid_check, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_SCISSORRECTS, &vmw_cmd_dx_cid_check, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_CLEAR_RENDERTARGET_VIEW, + &vmw_cmd_dx_clear_rendertarget_view, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_CLEAR_DEPTHSTENCIL_VIEW, + &vmw_cmd_dx_clear_depthstencil_view, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_PRED_COPY, &vmw_cmd_invalid, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_GENMIPS, &vmw_cmd_dx_genmips, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_UPDATE_SUBRESOURCE, + &vmw_cmd_dx_check_subresource, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_READBACK_SUBRESOURCE, + &vmw_cmd_dx_check_subresource, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_INVALIDATE_SUBRESOURCE, + &vmw_cmd_dx_check_subresource, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW, + &vmw_cmd_dx_view_define, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_SHADERRESOURCE_VIEW, + &vmw_cmd_dx_view_remove, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_RENDERTARGET_VIEW, + &vmw_cmd_dx_view_define, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_RENDERTARGET_VIEW, + &vmw_cmd_dx_view_remove, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_DEPTHSTENCIL_VIEW, + &vmw_cmd_dx_view_define, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_DEPTHSTENCIL_VIEW, + &vmw_cmd_dx_view_remove, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_ELEMENTLAYOUT, + &vmw_cmd_dx_so_define, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_ELEMENTLAYOUT, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_BLEND_STATE, + &vmw_cmd_dx_so_define, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_BLEND_STATE, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_DEPTHSTENCIL_STATE, + &vmw_cmd_dx_so_define, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_DEPTHSTENCIL_STATE, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_RASTERIZER_STATE, + &vmw_cmd_dx_so_define, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_RASTERIZER_STATE, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_SAMPLER_STATE, + &vmw_cmd_dx_so_define, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_SAMPLER_STATE, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_SHADER, + &vmw_cmd_dx_define_shader, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_SHADER, + &vmw_cmd_dx_destroy_shader, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_BIND_SHADER, + &vmw_cmd_dx_bind_shader, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_STREAMOUTPUT, + &vmw_cmd_dx_so_define, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_STREAMOUTPUT, + &vmw_cmd_dx_destroy_streamoutput, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_STREAMOUTPUT, + &vmw_cmd_dx_set_streamoutput, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_SOTARGETS, + &vmw_cmd_dx_set_so_targets, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_INPUT_LAYOUT, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_TOPOLOGY, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_BUFFER_COPY, + &vmw_cmd_buffer_copy_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_PRED_COPY_REGION, + &vmw_cmd_pred_copy_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_TRANSFER_FROM_BUFFER, + &vmw_cmd_dx_transfer_from_buffer, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_VS_CONSTANT_BUFFER_OFFSET, + &vmw_cmd_dx_set_constant_buffer_offset, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_PS_CONSTANT_BUFFER_OFFSET, + &vmw_cmd_dx_set_constant_buffer_offset, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_GS_CONSTANT_BUFFER_OFFSET, + &vmw_cmd_dx_set_constant_buffer_offset, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_HS_CONSTANT_BUFFER_OFFSET, + &vmw_cmd_dx_set_constant_buffer_offset, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_DS_CONSTANT_BUFFER_OFFSET, + &vmw_cmd_dx_set_constant_buffer_offset, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_CS_CONSTANT_BUFFER_OFFSET, + &vmw_cmd_dx_set_constant_buffer_offset, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_INTRA_SURFACE_COPY, &vmw_cmd_intra_surface_copy, + true, false, true), + + /* + * SM5 commands + */ + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_UA_VIEW, &vmw_cmd_sm5_view_define, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_UA_VIEW, &vmw_cmd_sm5_view_remove, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_CLEAR_UA_VIEW_UINT, &vmw_cmd_clear_uav_uint, + true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_CLEAR_UA_VIEW_FLOAT, + &vmw_cmd_clear_uav_float, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_COPY_STRUCTURE_COUNT, &vmw_cmd_invalid, true, + false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_UA_VIEWS, &vmw_cmd_set_uav, true, false, + true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DRAW_INDEXED_INSTANCED_INDIRECT, + &vmw_cmd_indexed_instanced_indirect, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DRAW_INSTANCED_INDIRECT, + &vmw_cmd_instanced_indirect, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DISPATCH, &vmw_cmd_sm5, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DISPATCH_INDIRECT, + &vmw_cmd_dispatch_indirect, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_CS_UA_VIEWS, &vmw_cmd_set_cs_uav, true, + false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_DEPTHSTENCIL_VIEW_V2, + &vmw_cmd_sm5_view_define, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_STREAMOUTPUT_WITH_MOB, + &vmw_cmd_dx_define_streamoutput, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_BIND_STREAMOUTPUT, + &vmw_cmd_dx_bind_streamoutput, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_RASTERIZER_STATE_V2, + &vmw_cmd_dx_so_define, true, false, true), +}; + +bool vmw_cmd_describe(const void *buf, u32 *size, char const **cmd) +{ + u32 cmd_id = ((u32 *) buf)[0]; + + if (cmd_id >= SVGA_CMD_MAX) { + SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) buf; + const struct vmw_cmd_entry *entry; + + *size = header->size + sizeof(SVGA3dCmdHeader); + cmd_id = header->id; + if (cmd_id >= SVGA_3D_CMD_MAX) + return false; + + cmd_id -= SVGA_3D_CMD_BASE; + entry = &vmw_cmd_entries[cmd_id]; + *cmd = entry->cmd_name; + return true; + } + + switch (cmd_id) { + case SVGA_CMD_UPDATE: + *cmd = "SVGA_CMD_UPDATE"; + *size = sizeof(u32) + sizeof(SVGAFifoCmdUpdate); + break; + case SVGA_CMD_DEFINE_GMRFB: + *cmd = "SVGA_CMD_DEFINE_GMRFB"; + *size = sizeof(u32) + sizeof(SVGAFifoCmdDefineGMRFB); + break; + case SVGA_CMD_BLIT_GMRFB_TO_SCREEN: + *cmd = "SVGA_CMD_BLIT_GMRFB_TO_SCREEN"; + *size = sizeof(u32) + sizeof(SVGAFifoCmdBlitGMRFBToScreen); + break; + case SVGA_CMD_BLIT_SCREEN_TO_GMRFB: + *cmd = "SVGA_CMD_BLIT_SCREEN_TO_GMRFB"; + *size = sizeof(u32) + sizeof(SVGAFifoCmdBlitGMRFBToScreen); + break; + default: + *cmd = "UNKNOWN"; + *size = 0; + return false; + } + + return true; +} + +static int vmw_cmd_check(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, void *buf, + uint32_t *size) +{ + uint32_t cmd_id; + 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 = ((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 = header->id; + *size = header->size + sizeof(SVGA3dCmdHeader); + + cmd_id -= SVGA_3D_CMD_BASE; + if (unlikely(*size > size_remaining)) + goto out_invalid; + + if (unlikely(cmd_id >= SVGA_3D_CMD_MAX - SVGA_3D_CMD_BASE)) + goto out_invalid; + + entry = &vmw_cmd_entries[cmd_id]; + if (unlikely(!entry->func)) + goto out_invalid; + + 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)) { + VMW_DEBUG_USER("SVGA3D command: %d failed with error %d\n", + cmd_id + SVGA_3D_CMD_BASE, ret); + return ret; + } + + return 0; +out_invalid: + VMW_DEBUG_USER("Invalid SVGA3D command: %d\n", + cmd_id + SVGA_3D_CMD_BASE); + return -EINVAL; +out_privileged: + VMW_DEBUG_USER("Privileged SVGA3D command: %d\n", + cmd_id + SVGA_3D_CMD_BASE); + return -EPERM; +out_old: + VMW_DEBUG_USER("Deprecated (disallowed) SVGA3D command: %d\n", + cmd_id + SVGA_3D_CMD_BASE); + return -EINVAL; +out_new: + VMW_DEBUG_USER("SVGA3D command: %d not supported by virtual device.\n", + cmd_id + SVGA_3D_CMD_BASE); + return -EINVAL; +} + +static int vmw_cmd_check_all(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, void *buf, + uint32_t size) +{ + int32_t cur_size = size; + int ret; + + sw_context->buf_start = buf; + + while (cur_size > 0) { + size = cur_size; + ret = vmw_cmd_check(dev_priv, sw_context, buf, &size); + if (unlikely(ret != 0)) + return ret; + buf = (void *)((unsigned long) buf + size); + cur_size -= size; + } + + if (unlikely(cur_size != 0)) { + VMW_DEBUG_USER("Command verifier out of sync.\n"); + return -EINVAL; + } + + return 0; +} + +static void vmw_free_relocations(struct vmw_sw_context *sw_context) +{ + /* Memory is validation context memory, so no need to free it */ + INIT_LIST_HEAD(&sw_context->bo_relocations); +} + +static void vmw_apply_relocations(struct vmw_sw_context *sw_context) +{ + struct vmw_relocation *reloc; + struct ttm_buffer_object *bo; + + list_for_each_entry(reloc, &sw_context->bo_relocations, head) { + bo = &reloc->vbo->tbo; + switch (bo->resource->mem_type) { + case TTM_PL_VRAM: + reloc->location->offset += bo->resource->start << PAGE_SHIFT; + reloc->location->gmrId = SVGA_GMR_FRAMEBUFFER; + break; + case VMW_PL_GMR: + reloc->location->gmrId = bo->resource->start; + break; + case VMW_PL_MOB: + *reloc->mob_loc = bo->resource->start; + break; + default: + BUG(); + } + } + vmw_free_relocations(sw_context); +} + +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)); + } + + vfree(sw_context->cmd_bounce); + sw_context->cmd_bounce = vmalloc(sw_context->cmd_bounce_size); + + if (sw_context->cmd_bounce == NULL) { + VMW_DEBUG_USER("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) +{ + uint32_t sequence; + int ret; + bool synced = false; + + /* p_handle implies file_priv. */ + BUG_ON(p_handle != NULL && file_priv == NULL); + + ret = vmw_cmd_send_fence(dev_priv, &sequence); + if (unlikely(ret != 0)) { + VMW_DEBUG_USER("Fence submission error. Syncing.\n"); + synced = true; + } + + if (p_handle != NULL) + ret = vmw_user_fence_create(file_priv, dev_priv->fman, + sequence, p_fence, p_handle); + else + ret = vmw_fence_create(dev_priv->fman, sequence, p_fence); + + if (unlikely(ret != 0 && !synced)) { + (void) vmw_fallback_wait(dev_priv, false, false, sequence, + false, VMW_FENCE_WAIT_TIMEOUT); + *p_fence = NULL; + } + + return ret; +} + +/** + * 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. + * @out_fence_fd: exported file descriptor for the fence. -1 if not used + * + * 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. + */ +int +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, + int32_t out_fence_fd) +{ + struct drm_vmw_fence_rep fence_rep; + + if (user_fence_rep == NULL) + return 0; + + memset(&fence_rep, 0, sizeof(fence_rep)); + + fence_rep.error = ret; + fence_rep.fd = out_fence_fd; + if (ret == 0) { + BUG_ON(fence == NULL); + + fence_rep.handle = fence_handle; + fence_rep.seqno = fence->base.seqno; + vmw_update_seqno(dev_priv); + fence_rep.passed_seqno = dev_priv->last_read_seqno; + } + + /* + * 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); + VMW_DEBUG_USER("Fence copy error. Syncing.\n"); + (void) vmw_fence_obj_wait(fence, false, false, + VMW_FENCE_WAIT_TIMEOUT); + } + + return ret ? -EFAULT : 0; +} + +/** + * vmw_execbuf_submit_fifo - Patch a command batch and submit it using the fifo. + * + * @dev_priv: Pointer to a device private structure. + * @kernel_commands: Pointer to the unpatched command batch. + * @command_size: Size of the unpatched command batch. + * @sw_context: Structure holding the relocation lists. + * + * Side effects: If this function returns 0, then the command batch pointed to + * by @kernel_commands will have been modified. + */ +static int vmw_execbuf_submit_fifo(struct vmw_private *dev_priv, + void *kernel_commands, u32 command_size, + struct vmw_sw_context *sw_context) +{ + void *cmd; + + if (sw_context->dx_ctx_node) + cmd = VMW_CMD_CTX_RESERVE(dev_priv, command_size, + sw_context->dx_ctx_node->ctx->id); + else + cmd = VMW_CMD_RESERVE(dev_priv, command_size); + + if (!cmd) + return -ENOMEM; + + 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_cmd_commit(dev_priv, command_size); + + return 0; +} + +/** + * vmw_execbuf_submit_cmdbuf - Patch a command batch and submit it using the + * command buffer manager. + * + * @dev_priv: Pointer to a device private structure. + * @header: Opaque handle to the command buffer allocation. + * @command_size: Size of the unpatched command batch. + * @sw_context: Structure holding the relocation lists. + * + * Side effects: If this function returns 0, then the command buffer represented + * by @header will have been modified. + */ +static int vmw_execbuf_submit_cmdbuf(struct vmw_private *dev_priv, + struct vmw_cmdbuf_header *header, + u32 command_size, + struct vmw_sw_context *sw_context) +{ + u32 id = ((sw_context->dx_ctx_node) ? sw_context->dx_ctx_node->ctx->id : + SVGA3D_INVALID_ID); + void *cmd = vmw_cmdbuf_reserve(dev_priv->cman, command_size, id, false, + header); + + vmw_apply_relocations(sw_context); + vmw_resource_relocations_apply(cmd, &sw_context->res_relocations); + vmw_resource_relocations_free(&sw_context->res_relocations); + vmw_cmdbuf_commit(dev_priv->cman, command_size, header, false); + + return 0; +} + +/** + * vmw_execbuf_cmdbuf - Prepare, if possible, a user-space command batch for + * submission using a command buffer. + * + * @dev_priv: Pointer to a device private structure. + * @user_commands: User-space pointer to the commands to be submitted. + * @command_size: Size of the unpatched command batch. + * @header: Out parameter returning the opaque pointer to the command buffer. + * + * This function checks whether we can use the command buffer manager for + * submission and if so, creates a command buffer of suitable size and copies + * the user data into that buffer. + * + * On successful return, the function returns a pointer to the data in the + * command buffer and *@header is set to non-NULL. + * + * @kernel_commands: If command buffers could not be used, the function will + * return the value of @kernel_commands on function call. That value may be + * NULL. In that case, the value of *@header will be set to NULL. + * + * If an error is encountered, the function will return a pointer error value. + * If the function is interrupted by a signal while sleeping, it will return + * -ERESTARTSYS casted to a pointer error value. + */ +static void *vmw_execbuf_cmdbuf(struct vmw_private *dev_priv, + void __user *user_commands, + void *kernel_commands, u32 command_size, + struct vmw_cmdbuf_header **header) +{ + size_t cmdbuf_size; + int ret; + + *header = NULL; + if (command_size > SVGA_CB_MAX_SIZE) { + VMW_DEBUG_USER("Command buffer is too large.\n"); + return ERR_PTR(-EINVAL); + } + + if (!dev_priv->cman || kernel_commands) + return kernel_commands; + + /* If possible, add a little space for fencing. */ + cmdbuf_size = command_size + 512; + cmdbuf_size = min_t(size_t, cmdbuf_size, SVGA_CB_MAX_SIZE); + kernel_commands = vmw_cmdbuf_alloc(dev_priv->cman, cmdbuf_size, true, + header); + if (IS_ERR(kernel_commands)) + return kernel_commands; + + ret = copy_from_user(kernel_commands, user_commands, command_size); + if (ret) { + VMW_DEBUG_USER("Failed copying commands.\n"); + vmw_cmdbuf_header_free(*header); + *header = NULL; + return ERR_PTR(-EFAULT); + } + + return kernel_commands; +} + +static int vmw_execbuf_tie_context(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + uint32_t handle) +{ + struct vmw_resource *res; + int ret; + unsigned int size; + + if (handle == SVGA3D_INVALID_ID) + return 0; + + size = vmw_execbuf_res_size(dev_priv, vmw_res_dx_context); + ret = vmw_validation_preload_res(sw_context->ctx, size); + if (ret) + return ret; + + ret = vmw_user_resource_lookup_handle + (dev_priv, sw_context->fp->tfile, handle, + user_context_converter, &res); + if (ret != 0) { + VMW_DEBUG_USER("Could not find or user DX context 0x%08x.\n", + (unsigned int) handle); + return ret; + } + + ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_SET, + vmw_val_add_flag_none); + if (unlikely(ret != 0)) { + vmw_resource_unreference(&res); + return ret; + } + + sw_context->dx_ctx_node = vmw_execbuf_info_from_res(sw_context, res); + sw_context->man = vmw_context_res_man(res); + + vmw_resource_unreference(&res); + return 0; +} + +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, + uint32_t dx_context_handle, + struct drm_vmw_fence_rep __user *user_fence_rep, + struct vmw_fence_obj **out_fence, uint32_t flags) +{ + struct vmw_sw_context *sw_context = &dev_priv->ctx; + struct vmw_fence_obj *fence = NULL; + struct vmw_cmdbuf_header *header; + uint32_t handle = 0; + int ret; + int32_t out_fence_fd = -1; + struct sync_file *sync_file = NULL; + DECLARE_VAL_CONTEXT(val_ctx, sw_context, 1); + + if (flags & DRM_VMW_EXECBUF_FLAG_EXPORT_FENCE_FD) { + out_fence_fd = get_unused_fd_flags(O_CLOEXEC); + if (out_fence_fd < 0) { + VMW_DEBUG_USER("Failed to get a fence fd.\n"); + return out_fence_fd; + } + } + + if (throttle_us) { + VMW_DEBUG_USER("Throttling is no longer supported.\n"); + } + + kernel_commands = vmw_execbuf_cmdbuf(dev_priv, user_commands, + kernel_commands, command_size, + &header); + if (IS_ERR(kernel_commands)) { + ret = PTR_ERR(kernel_commands); + goto out_free_fence_fd; + } + + ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex); + if (ret) { + ret = -ERESTARTSYS; + goto out_free_header; + } + + sw_context->kernel = false; + if (kernel_commands == NULL) { + 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; + VMW_DEBUG_USER("Failed copying commands.\n"); + goto out_unlock; + } + + kernel_commands = sw_context->cmd_bounce; + } else if (!header) { + sw_context->kernel = true; + } + + sw_context->filp = file_priv; + sw_context->fp = vmw_fpriv(file_priv); + INIT_LIST_HEAD(&sw_context->ctx_list); + sw_context->cur_query_bo = dev_priv->pinned_bo; + sw_context->last_query_ctx = NULL; + sw_context->needs_post_query_barrier = false; + sw_context->dx_ctx_node = NULL; + sw_context->dx_query_mob = NULL; + sw_context->dx_query_ctx = NULL; + memset(sw_context->res_cache, 0, sizeof(sw_context->res_cache)); + INIT_LIST_HEAD(&sw_context->res_relocations); + INIT_LIST_HEAD(&sw_context->bo_relocations); + + if (sw_context->staged_bindings) + vmw_binding_state_reset(sw_context->staged_bindings); + + INIT_LIST_HEAD(&sw_context->staged_cmd_res); + sw_context->ctx = &val_ctx; + ret = vmw_execbuf_tie_context(dev_priv, sw_context, dx_context_handle); + if (unlikely(ret != 0)) + goto out_err_nores; + + ret = vmw_cmd_check_all(dev_priv, sw_context, kernel_commands, + command_size); + if (unlikely(ret != 0)) + goto out_err_nores; + + ret = vmw_resources_reserve(sw_context); + if (unlikely(ret != 0)) + goto out_err_nores; + + ret = vmw_validation_bo_reserve(&val_ctx, true); + if (unlikely(ret != 0)) + goto out_err_nores; + + ret = vmw_validation_bo_validate(&val_ctx, true); + if (unlikely(ret != 0)) + goto out_err; + + ret = vmw_validation_res_validate(&val_ctx, true); + if (unlikely(ret != 0)) + goto out_err; + + vmw_validation_drop_ht(&val_ctx); + + ret = mutex_lock_interruptible(&dev_priv->binding_mutex); + if (unlikely(ret != 0)) { + ret = -ERESTARTSYS; + goto out_err; + } + + if (dev_priv->has_mob) { + ret = vmw_rebind_contexts(sw_context); + if (unlikely(ret != 0)) + goto out_unlock_binding; + } + + if (!header) { + ret = vmw_execbuf_submit_fifo(dev_priv, kernel_commands, + command_size, sw_context); + } else { + ret = vmw_execbuf_submit_cmdbuf(dev_priv, header, command_size, + sw_context); + header = NULL; + } + mutex_unlock(&dev_priv->binding_mutex); + if (ret) + goto out_err; + + 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. The error will be propagated to + * user-space in @fence_rep + */ + if (ret != 0) + VMW_DEBUG_USER("Fence submission error. Syncing.\n"); + + vmw_execbuf_bindings_commit(sw_context, false); + vmw_bind_dx_query_mob(sw_context); + vmw_validation_res_unreserve(&val_ctx, false); + + vmw_validation_bo_fence(sw_context->ctx, fence); + + if (unlikely(dev_priv->pinned_bo != NULL && !dev_priv->query_cid_valid)) + __vmw_execbuf_release_pinned_bo(dev_priv, fence); + + /* + * If anything fails here, give up trying to export the fence and do a + * sync since the user mode will not be able to sync the fence itself. + * This ensures we are still functionally correct. + */ + if (flags & DRM_VMW_EXECBUF_FLAG_EXPORT_FENCE_FD) { + + sync_file = sync_file_create(&fence->base); + if (!sync_file) { + VMW_DEBUG_USER("Sync file create failed for fence\n"); + put_unused_fd(out_fence_fd); + out_fence_fd = -1; + + (void) vmw_fence_obj_wait(fence, false, false, + VMW_FENCE_WAIT_TIMEOUT); + } + } + + ret = vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret, + user_fence_rep, fence, handle, out_fence_fd); + + if (sync_file) { + if (ret) { + /* usercopy of fence failed, put the file object */ + fput(sync_file->file); + put_unused_fd(out_fence_fd); + } else { + /* Link the fence with the FD created earlier */ + fd_install(out_fence_fd, sync_file->file); + } + } + + /* 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); + } + + vmw_cmdbuf_res_commit(&sw_context->staged_cmd_res); + mutex_unlock(&dev_priv->cmdbuf_mutex); + + /* + * Unreference resources outside of the cmdbuf_mutex to avoid deadlocks + * in resource destruction paths. + */ + vmw_validation_unref_lists(&val_ctx); + + return ret; + +out_unlock_binding: + mutex_unlock(&dev_priv->binding_mutex); +out_err: + vmw_validation_bo_backoff(&val_ctx); +out_err_nores: + vmw_execbuf_bindings_commit(sw_context, true); + vmw_validation_res_unreserve(&val_ctx, true); + vmw_resource_relocations_free(&sw_context->res_relocations); + vmw_free_relocations(sw_context); + if (unlikely(dev_priv->pinned_bo != NULL && !dev_priv->query_cid_valid)) + __vmw_execbuf_release_pinned_bo(dev_priv, NULL); +out_unlock: + vmw_cmdbuf_res_revert(&sw_context->staged_cmd_res); + vmw_validation_drop_ht(&val_ctx); + WARN_ON(!list_empty(&sw_context->ctx_list)); + mutex_unlock(&dev_priv->cmdbuf_mutex); + + /* + * Unreference resources outside of the cmdbuf_mutex to avoid deadlocks + * in resource destruction paths. + */ + vmw_validation_unref_lists(&val_ctx); +out_free_header: + if (header) + vmw_cmdbuf_header_free(header); +out_free_fence_fd: + if (out_fence_fd >= 0) + put_unused_fd(out_fence_fd); + + 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) +{ + VMW_DEBUG_USER("Can't unpin query buffer. Trying to recover.\n"); + + (void) vmw_fallback_wait(dev_priv, false, true, 0, false, 10*HZ); + vmw_bo_pin_reserved(dev_priv->pinned_bo, false); + if (dev_priv->dummy_query_bo_pinned) { + vmw_bo_pin_reserved(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 vmw_fence_obj *lfence = NULL; + DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); + + if (dev_priv->pinned_bo == NULL) + goto out_unlock; + + vmw_bo_placement_set(dev_priv->pinned_bo, + VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM, + VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM); + ret = vmw_validation_add_bo(&val_ctx, dev_priv->pinned_bo); + if (ret) + goto out_no_reserve; + + vmw_bo_placement_set(dev_priv->dummy_query_bo, + VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM, + VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM); + ret = vmw_validation_add_bo(&val_ctx, dev_priv->dummy_query_bo); + if (ret) + goto out_no_reserve; + + ret = vmw_validation_bo_reserve(&val_ctx, false); + if (ret) + goto out_no_reserve; + + if (dev_priv->query_cid_valid) { + BUG_ON(fence != NULL); + ret = vmw_cmd_emit_dummy_query(dev_priv, dev_priv->query_cid); + if (ret) + goto out_no_emit; + dev_priv->query_cid_valid = false; + } + + vmw_bo_pin_reserved(dev_priv->pinned_bo, false); + if (dev_priv->dummy_query_bo_pinned) { + vmw_bo_pin_reserved(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; + } + vmw_validation_bo_fence(&val_ctx, fence); + if (lfence != NULL) + vmw_fence_obj_unreference(&lfence); + + vmw_validation_unref_lists(&val_ctx); + vmw_bo_unreference(&dev_priv->pinned_bo); + +out_unlock: + return; +out_no_emit: + vmw_validation_bo_backoff(&val_ctx); +out_no_reserve: + vmw_validation_unref_lists(&val_ctx); + vmw_execbuf_unpin_panic(dev_priv); + vmw_bo_unreference(&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); +} + +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 = data; + int ret; + struct dma_fence *in_fence = NULL; + + MKS_STAT_TIME_DECL(MKSSTAT_KERN_EXECBUF); + MKS_STAT_TIME_PUSH(MKSSTAT_KERN_EXECBUF); + + /* + * Extend the ioctl argument while maintaining backwards compatibility: + * We take different code paths depending on the value of arg->version. + * + * Note: The ioctl argument is extended and zeropadded by core DRM. + */ + if (unlikely(arg->version > DRM_VMW_EXECBUF_VERSION || + arg->version == 0)) { + VMW_DEBUG_USER("Incorrect execbuf version.\n"); + ret = -EINVAL; + goto mksstats_out; + } + + switch (arg->version) { + case 1: + /* For v1 core DRM have extended + zeropadded the data */ + arg->context_handle = (uint32_t) -1; + break; + case 2: + default: + /* For v2 and later core DRM would have correctly copied it */ + break; + } + + /* If imported a fence FD from elsewhere, then wait on it */ + if (arg->flags & DRM_VMW_EXECBUF_FLAG_IMPORT_FENCE_FD) { + in_fence = sync_file_get_fence(arg->imported_fence_fd); + + if (!in_fence) { + VMW_DEBUG_USER("Cannot get imported fence\n"); + ret = -EINVAL; + goto mksstats_out; + } + + ret = dma_fence_wait(in_fence, true); + if (ret) + goto out; + } + + ret = vmw_execbuf_process(file_priv, dev_priv, + (void __user *)(unsigned long)arg->commands, + NULL, arg->command_size, arg->throttle_us, + arg->context_handle, + (void __user *)(unsigned long)arg->fence_rep, + NULL, arg->flags); + + if (unlikely(ret != 0)) + goto out; + + vmw_kms_cursor_post_execbuf(dev_priv); + +out: + if (in_fence) + dma_fence_put(in_fence); + +mksstats_out: + MKS_STAT_TIME_POP(MKSSTAT_KERN_EXECBUF); + return ret; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c new file mode 100644 index 0000000000..2a0cda3247 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -0,0 +1,1108 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2011-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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 <linux/sched/signal.h> + +#include "vmwgfx_drv.h" + +#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; + 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. */ + u64 ctx; +}; + +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. + * + * @action: A struct vmw_fence_action to hook up to a fence. + * @event: A pointer to the pending event. + * @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. + * @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 drm_pending_event *event; + struct vmw_fence_obj *fence; + struct drm_device *dev; + + uint32_t *tv_sec; + uint32_t *tv_usec; +}; + +static struct vmw_fence_manager * +fman_from_fence(struct vmw_fence_obj *fence) +{ + return container_of(fence->base.lock, struct vmw_fence_manager, lock); +} + +static u32 vmw_fence_goal_read(struct vmw_private *vmw) +{ + if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0) + return vmw_read(vmw, SVGA_REG_FENCE_GOAL); + else + return vmw_fifo_mem_read(vmw, SVGA_FIFO_FENCE_GOAL); +} + +static void vmw_fence_goal_write(struct vmw_private *vmw, u32 value) +{ + if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0) + vmw_write(vmw, SVGA_REG_FENCE_GOAL, value); + else + vmw_fifo_mem_write(vmw, SVGA_FIFO_FENCE_GOAL, value); +} + +/* + * 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(struct dma_fence *f) +{ + struct vmw_fence_obj *fence = + container_of(f, struct vmw_fence_obj, base); + + struct vmw_fence_manager *fman = fman_from_fence(fence); + + spin_lock(&fman->lock); + list_del_init(&fence->head); + --fman->num_fence_objects; + spin_unlock(&fman->lock); + fence->destroy(fence); +} + +static const char *vmw_fence_get_driver_name(struct dma_fence *f) +{ + return "vmwgfx"; +} + +static const char *vmw_fence_get_timeline_name(struct dma_fence *f) +{ + return "svga"; +} + +static bool vmw_fence_enable_signaling(struct dma_fence *f) +{ + struct vmw_fence_obj *fence = + container_of(f, struct vmw_fence_obj, base); + + struct vmw_fence_manager *fman = fman_from_fence(fence); + struct vmw_private *dev_priv = fman->dev_priv; + + u32 seqno = vmw_fence_read(dev_priv); + if (seqno - fence->base.seqno < VMW_FENCE_WRAP) + return false; + + return true; +} + +struct vmwgfx_wait_cb { + struct dma_fence_cb base; + struct task_struct *task; +}; + +static void +vmwgfx_wait_cb(struct dma_fence *fence, struct dma_fence_cb *cb) +{ + struct vmwgfx_wait_cb *wait = + container_of(cb, struct vmwgfx_wait_cb, base); + + wake_up_process(wait->task); +} + +static void __vmw_fences_update(struct vmw_fence_manager *fman); + +static long vmw_fence_wait(struct dma_fence *f, bool intr, signed long timeout) +{ + struct vmw_fence_obj *fence = + container_of(f, struct vmw_fence_obj, base); + + struct vmw_fence_manager *fman = fman_from_fence(fence); + struct vmw_private *dev_priv = fman->dev_priv; + struct vmwgfx_wait_cb cb; + long ret = timeout; + + if (likely(vmw_fence_obj_signaled(fence))) + return timeout; + + vmw_seqno_waiter_add(dev_priv); + + spin_lock(f->lock); + + if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags)) + goto out; + + if (intr && signal_pending(current)) { + ret = -ERESTARTSYS; + goto out; + } + + cb.base.func = vmwgfx_wait_cb; + cb.task = current; + list_add(&cb.base.node, &f->cb_list); + + for (;;) { + __vmw_fences_update(fman); + + /* + * We can use the barrier free __set_current_state() since + * DMA_FENCE_FLAG_SIGNALED_BIT + wakeup is protected by the + * fence spinlock. + */ + if (intr) + __set_current_state(TASK_INTERRUPTIBLE); + else + __set_current_state(TASK_UNINTERRUPTIBLE); + + if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &f->flags)) { + if (ret == 0 && timeout > 0) + ret = 1; + break; + } + + if (intr && signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + if (ret == 0) + break; + + spin_unlock(f->lock); + + ret = schedule_timeout(ret); + + spin_lock(f->lock); + } + __set_current_state(TASK_RUNNING); + if (!list_empty(&cb.base.node)) + list_del(&cb.base.node); + +out: + spin_unlock(f->lock); + + vmw_seqno_waiter_remove(dev_priv); + + return ret; +} + +static const struct dma_fence_ops vmw_fence_ops = { + .get_driver_name = vmw_fence_get_driver_name, + .get_timeline_name = vmw_fence_get_timeline_name, + .enable_signaling = vmw_fence_enable_signaling, + .wait = vmw_fence_wait, + .release = vmw_fence_obj_destroy, +}; + + +/* + * 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_manager *fman = + container_of(work, struct vmw_fence_manager, work); + struct list_head list; + struct vmw_fence_action *action, *next_action; + bool seqno_valid; + + do { + INIT_LIST_HEAD(&list); + mutex_lock(&fman->goal_irq_mutex); + + spin_lock(&fman->lock); + list_splice_init(&fman->cleanup_list, &list); + seqno_valid = fman->seqno_valid; + spin_unlock(&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)) + 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; + mutex_init(&fman->goal_irq_mutex); + fman->ctx = dma_fence_context_alloc(1); + + return fman; +} + +void vmw_fence_manager_takedown(struct vmw_fence_manager *fman) +{ + bool lists_empty; + + (void) cancel_work_sync(&fman->work); + + spin_lock(&fman->lock); + lists_empty = list_empty(&fman->fence_list) && + list_empty(&fman->cleanup_list); + spin_unlock(&fman->lock); + + BUG_ON(!lists_empty); + kfree(fman); +} + +static int vmw_fence_obj_init(struct vmw_fence_manager *fman, + struct vmw_fence_obj *fence, u32 seqno, + void (*destroy) (struct vmw_fence_obj *fence)) +{ + int ret = 0; + + dma_fence_init(&fence->base, &vmw_fence_ops, &fman->lock, + fman->ctx, seqno); + INIT_LIST_HEAD(&fence->seq_passed_actions); + fence->destroy = destroy; + + spin_lock(&fman->lock); + if (unlikely(fman->fifo_down)) { + ret = -EBUSY; + goto out_unlock; + } + list_add_tail(&fence->head, &fman->fence_list); + ++fman->num_fence_objects; + +out_unlock: + spin_unlock(&fman->lock); + return ret; + +} + +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; + struct vmw_fence_obj *fence; + + if (likely(!fman->seqno_valid)) + return false; + + goal_seqno = vmw_fence_goal_read(fman->dev_priv); + 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; + vmw_fence_goal_write(fman->dev_priv, + fence->base.seqno); + 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) +{ + struct vmw_fence_manager *fman = fman_from_fence(fence); + u32 goal_seqno; + + if (dma_fence_is_signaled_locked(&fence->base)) + return false; + + goal_seqno = vmw_fence_goal_read(fman->dev_priv); + if (likely(fman->seqno_valid && + goal_seqno - fence->base.seqno < VMW_FENCE_WRAP)) + return false; + + vmw_fence_goal_write(fman->dev_priv, fence->base.seqno); + fman->seqno_valid = true; + + return true; +} + +static void __vmw_fences_update(struct vmw_fence_manager *fman) +{ + struct vmw_fence_obj *fence, *next_fence; + struct list_head action_list; + bool needs_rerun; + uint32_t seqno, new_seqno; + + seqno = vmw_fence_read(fman->dev_priv); +rerun: + list_for_each_entry_safe(fence, next_fence, &fman->fence_list, head) { + if (seqno - fence->base.seqno < VMW_FENCE_WRAP) { + list_del_init(&fence->head); + dma_fence_signal_locked(&fence->base); + INIT_LIST_HEAD(&action_list); + list_splice_init(&fence->seq_passed_actions, + &action_list); + vmw_fences_perform_actions(fman, &action_list); + } else + break; + } + + /* + * 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. + */ + + needs_rerun = vmw_fence_goal_new_locked(fman, seqno); + if (unlikely(needs_rerun)) { + new_seqno = vmw_fence_read(fman->dev_priv); + if (new_seqno != seqno) { + seqno = new_seqno; + goto rerun; + } + } + + if (!list_empty(&fman->cleanup_list)) + (void) schedule_work(&fman->work); +} + +void vmw_fences_update(struct vmw_fence_manager *fman) +{ + spin_lock(&fman->lock); + __vmw_fences_update(fman); + spin_unlock(&fman->lock); +} + +bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence) +{ + struct vmw_fence_manager *fman = fman_from_fence(fence); + + if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->base.flags)) + return true; + + vmw_fences_update(fman); + + return dma_fence_is_signaled(&fence->base); +} + +int vmw_fence_obj_wait(struct vmw_fence_obj *fence, bool lazy, + bool interruptible, unsigned long timeout) +{ + long ret = dma_fence_wait_timeout(&fence->base, interruptible, timeout); + + if (likely(ret > 0)) + return 0; + else if (ret == 0) + return -EBUSY; + else + return ret; +} + +static void vmw_fence_destroy(struct vmw_fence_obj *fence) +{ + dma_fence_free(&fence->base); +} + +int vmw_fence_create(struct vmw_fence_manager *fman, + uint32_t seqno, + struct vmw_fence_obj **p_fence) +{ + struct vmw_fence_obj *fence; + int ret; + + fence = kzalloc(sizeof(*fence), GFP_KERNEL); + if (unlikely(!fence)) + return -ENOMEM; + + ret = vmw_fence_obj_init(fman, fence, seqno, + vmw_fence_destroy); + if (unlikely(ret != 0)) + goto out_err_init; + + *p_fence = fence; + return 0; + +out_err_init: + kfree(fence); + return ret; +} + + +static void vmw_user_fence_destroy(struct vmw_fence_obj *fence) +{ + struct vmw_user_fence *ufence = + container_of(fence, struct vmw_user_fence, fence); + + ttm_base_object_kfree(ufence, base); +} + +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, + 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; + int ret; + + ufence = kzalloc(sizeof(*ufence), GFP_KERNEL); + if (unlikely(!ufence)) { + ret = -ENOMEM; + goto out_no_object; + } + + ret = vmw_fence_obj_init(fman, &ufence->fence, seqno, + vmw_user_fence_destroy); + if (unlikely(ret != 0)) { + kfree(ufence); + goto out_no_object; + } + + /* + * 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); + + + 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.handle; + + return 0; +out_err: + tmp = &ufence->fence; + vmw_fence_obj_unreference(&tmp); +out_no_object: + return ret; +} + +/* + * vmw_fence_fifo_down - signal all unsignaled fence objects. + */ + +void vmw_fence_fifo_down(struct vmw_fence_manager *fman) +{ + 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(&fman->lock); + 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); + dma_fence_get(&fence->base); + spin_unlock(&fman->lock); + + ret = vmw_fence_obj_wait(fence, false, false, + VMW_FENCE_WAIT_TIMEOUT); + + if (unlikely(ret != 0)) { + list_del_init(&fence->head); + dma_fence_signal(&fence->base); + INIT_LIST_HEAD(&action_list); + list_splice_init(&fence->seq_passed_actions, + &action_list); + vmw_fences_perform_actions(fman, &action_list); + } + + BUG_ON(!list_empty(&fence->head)); + dma_fence_put(&fence->base); + spin_lock(&fman->lock); + } + spin_unlock(&fman->lock); +} + +void vmw_fence_fifo_up(struct vmw_fence_manager *fman) +{ + spin_lock(&fman->lock); + fman->fifo_down = false; + spin_unlock(&fman->lock); +} + + +/** + * vmw_fence_obj_lookup - Look up a user-space fence object + * + * @tfile: A struct ttm_object_file identifying the caller. + * @handle: A handle identifying the fence object. + * @return: A struct vmw_user_fence base ttm object on success or + * an error pointer on failure. + * + * The fence object is looked up and type-checked. The caller needs + * to have opened the fence object first, but since that happens on + * creation and fence objects aren't shareable, that's not an + * issue currently. + */ +static struct ttm_base_object * +vmw_fence_obj_lookup(struct ttm_object_file *tfile, u32 handle) +{ + struct ttm_base_object *base = ttm_base_object_lookup(tfile, handle); + + if (!base) { + pr_err("Invalid fence object handle 0x%08lx.\n", + (unsigned long)handle); + return ERR_PTR(-EINVAL); + } + + if (base->refcount_release != vmw_user_fence_base_release) { + pr_err("Invalid fence object handle 0x%08lx.\n", + (unsigned long)handle); + ttm_base_object_unref(&base); + return ERR_PTR(-EINVAL); + } + + return base; +} + + +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 = vmw_fence_obj_lookup(tfile, arg->handle); + if (IS_ERR(base)) + return PTR_ERR(base); + + 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)) ? + 0 : -EBUSY); + goto out; + } + + timeout = (unsigned long)arg->kernel_cookie - timeout; + + ret = vmw_fence_obj_wait(fence, 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); + 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 = vmw_fence_obj_lookup(tfile, arg->handle); + if (IS_ERR(base)) + return PTR_ERR(base); + + fence = &(container_of(base, struct vmw_user_fence, base)->fence); + fman = fman_from_fence(fence); + + arg->signaled = vmw_fence_obj_signaled(fence); + + arg->signaled_flags = arg->flags; + spin_lock(&fman->lock); + arg->passed_seqno = dev_priv->last_read_seqno; + spin_unlock(&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); +} + +/** + * 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. + */ +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; + + if (unlikely(event == NULL)) + return; + + spin_lock_irq(&dev->event_lock); + + if (likely(eaction->tv_sec != NULL)) { + struct timespec64 ts; + + ktime_get_ts64(&ts); + /* monotonic time, so no y2038 overflow */ + *eaction->tv_sec = ts.tv_sec; + *eaction->tv_usec = ts.tv_nsec / NSEC_PER_USEC; + } + + drm_send_event_locked(dev, eaction->event); + eaction->event = NULL; + spin_unlock_irq(&dev->event_lock); +} + +/** + * 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); + + vmw_fence_obj_unreference(&eaction->fence); + kfree(eaction); +} + + +/** + * 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) +{ + struct vmw_fence_manager *fman = fman_from_fence(fence); + bool run_update = false; + + mutex_lock(&fman->goal_irq_mutex); + spin_lock(&fman->lock); + + fman->pending_actions[action->type]++; + if (dma_fence_is_signaled_locked(&fence->base)) { + 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(&fman->lock); + + 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); + +} + +/** + * vmw_event_fence_action_queue - 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. + * @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. + * @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 vmw_event_fence_action *eaction; + struct vmw_fence_manager *fman = fman_from_fence(fence); + + eaction = kzalloc(sizeof(*eaction), GFP_KERNEL); + if (unlikely(!eaction)) + 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; + + eaction->fence = vmw_fence_obj_reference(fence); + eaction->dev = &fman->dev_priv->drm; + eaction->tv_sec = tv_sec; + eaction->tv_usec = tv_usec; + + 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 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 vmw_event_fence_pending *event; + struct vmw_fence_manager *fman = fman_from_fence(fence); + struct drm_device *dev = &fman->dev_priv->drm; + int ret; + + event = kzalloc(sizeof(*event), GFP_KERNEL); + if (unlikely(!event)) { + DRM_ERROR("Failed to allocate an event.\n"); + ret = -ENOMEM; + goto out_no_space; + } + + event->event.base.type = DRM_VMW_EVENT_FENCE_SIGNALED; + event->event.base.length = sizeof(*event); + event->event.user_data = user_data; + + ret = drm_event_reserve_init(dev, file_priv, &event->base, &event->event.base); + + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to allocate event space for this file.\n"); + kfree(event); + goto out_no_space; + } + + 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; + + return 0; + +out_no_queue: + drm_event_cancel_free(dev, &event->base); +out_no_space: + return ret; +} + +int vmw_fence_event_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + 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 ttm_object_file *tfile = vmw_fp->tfile; + 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; + + /* + * Look up an existing fence object, + * and if user-space wants a new reference, + * add one. + */ + if (arg->handle) { + struct ttm_base_object *base = + vmw_fence_obj_lookup(tfile, arg->handle); + + if (IS_ERR(base)) + return PTR_ERR(base); + + fence = &(container_of(base, struct vmw_user_fence, + base)->fence); + (void) vmw_fence_obj_reference(fence); + + if (user_fence_rep != NULL) { + ret = ttm_ref_object_add(vmw_fp->tfile, base, + NULL, false); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to reference a fence " + "object.\n"); + goto out_no_ref_obj; + } + handle = base->handle; + } + 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); + + 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, -1); + vmw_fence_obj_unreference(&fence); + return 0; +out_no_create: + if (user_fence_rep != NULL) + ttm_ref_object_base_unref(tfile, handle); +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 0000000000..a7eee579c7 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright 2011-2012 VMware, Inc., Palo Alto, CA., USA + * + * 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_ + +#include <linux/dma-fence.h> +#include <linux/dma-fence-array.h> + +#define VMW_FENCE_WAIT_TIMEOUT (5*HZ) + +struct drm_device; +struct drm_file; +struct drm_pending_event; + +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 dma_fence base; + + struct list_head head; + struct list_head seq_passed_actions; + void (*destroy)(struct vmw_fence_obj *fence); +}; + +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); + +static inline void +vmw_fence_obj_unreference(struct vmw_fence_obj **fence_p) +{ + struct vmw_fence_obj *fence = *fence_p; + + *fence_p = NULL; + if (fence) + dma_fence_put(&fence->base); +} + +static inline struct vmw_fence_obj * +vmw_fence_obj_reference(struct vmw_fence_obj *fence) +{ + if (fence) + dma_fence_get(&fence->base); + return fence; +} + +extern void vmw_fences_update(struct vmw_fence_manager *fman); + +extern bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence); + +extern int vmw_fence_obj_wait(struct vmw_fence_obj *fence, + bool lazy, + bool interruptible, unsigned long timeout); + +extern int vmw_fence_create(struct vmw_fence_manager *fman, + uint32_t seqno, + 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, + 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 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_gem.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c new file mode 100644 index 0000000000..12787bb9c1 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c @@ -0,0 +1,289 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2021-2023 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include "vmwgfx_bo.h" +#include "vmwgfx_drv.h" + +#include "drm/drm_prime.h" +#include "drm/drm_gem_ttm_helper.h" + +static void vmw_gem_object_free(struct drm_gem_object *gobj) +{ + struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(gobj); + if (bo) + ttm_bo_put(bo); +} + +static int vmw_gem_object_open(struct drm_gem_object *obj, + struct drm_file *file_priv) +{ + return 0; +} + +static void vmw_gem_object_close(struct drm_gem_object *obj, + struct drm_file *file_priv) +{ +} + +static int vmw_gem_pin_private(struct drm_gem_object *obj, bool do_pin) +{ + struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(obj); + struct vmw_bo *vbo = to_vmw_bo(obj); + int ret; + + ret = ttm_bo_reserve(bo, false, false, NULL); + if (unlikely(ret != 0)) + goto err; + + vmw_bo_pin_reserved(vbo, do_pin); + + ttm_bo_unreserve(bo); + +err: + return ret; +} + + +static int vmw_gem_object_pin(struct drm_gem_object *obj) +{ + return vmw_gem_pin_private(obj, true); +} + +static void vmw_gem_object_unpin(struct drm_gem_object *obj) +{ + vmw_gem_pin_private(obj, false); +} + +static struct sg_table *vmw_gem_object_get_sg_table(struct drm_gem_object *obj) +{ + struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(obj); + struct vmw_ttm_tt *vmw_tt = + container_of(bo->ttm, struct vmw_ttm_tt, dma_ttm); + + if (vmw_tt->vsgt.sgt) + return vmw_tt->vsgt.sgt; + + return drm_prime_pages_to_sg(obj->dev, vmw_tt->dma_ttm.pages, vmw_tt->dma_ttm.num_pages); +} + +static const struct vm_operations_struct vmw_vm_ops = { + .pfn_mkwrite = vmw_bo_vm_mkwrite, + .page_mkwrite = vmw_bo_vm_mkwrite, + .fault = vmw_bo_vm_fault, + .open = ttm_bo_vm_open, + .close = ttm_bo_vm_close, +}; + +static const struct drm_gem_object_funcs vmw_gem_object_funcs = { + .free = vmw_gem_object_free, + .open = vmw_gem_object_open, + .close = vmw_gem_object_close, + .print_info = drm_gem_ttm_print_info, + .pin = vmw_gem_object_pin, + .unpin = vmw_gem_object_unpin, + .get_sg_table = vmw_gem_object_get_sg_table, + .vmap = drm_gem_ttm_vmap, + .vunmap = drm_gem_ttm_vunmap, + .mmap = drm_gem_ttm_mmap, + .vm_ops = &vmw_vm_ops, +}; + +int vmw_gem_object_create(struct vmw_private *vmw, + struct vmw_bo_params *params, + struct vmw_bo **p_vbo) +{ + int ret = vmw_bo_create(vmw, params, p_vbo); + + if (ret != 0) + goto out_no_bo; + + (*p_vbo)->tbo.base.funcs = &vmw_gem_object_funcs; +out_no_bo: + return ret; +} + +int vmw_gem_object_create_with_handle(struct vmw_private *dev_priv, + struct drm_file *filp, + uint32_t size, + uint32_t *handle, + struct vmw_bo **p_vbo) +{ + int ret; + struct vmw_bo_params params = { + .domain = (dev_priv->has_mob) ? VMW_BO_DOMAIN_SYS : VMW_BO_DOMAIN_VRAM, + .busy_domain = VMW_BO_DOMAIN_SYS, + .bo_type = ttm_bo_type_device, + .size = size, + .pin = false + }; + + ret = vmw_gem_object_create(dev_priv, ¶ms, p_vbo); + if (ret != 0) + goto out_no_bo; + + ret = drm_gem_handle_create(filp, &(*p_vbo)->tbo.base, handle); +out_no_bo: + return ret; +} + + +int vmw_gem_object_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + union drm_vmw_alloc_dmabuf_arg *arg = + (union drm_vmw_alloc_dmabuf_arg *)data; + struct drm_vmw_alloc_dmabuf_req *req = &arg->req; + struct drm_vmw_dmabuf_rep *rep = &arg->rep; + struct vmw_bo *vbo; + uint32_t handle; + int ret; + + ret = vmw_gem_object_create_with_handle(dev_priv, filp, + req->size, &handle, &vbo); + if (ret) + goto out_no_bo; + + rep->handle = handle; + rep->map_handle = drm_vma_node_offset_addr(&vbo->tbo.base.vma_node); + rep->cur_gmr_id = handle; + rep->cur_gmr_offset = 0; + /* drop reference from allocate - handle holds it now */ + drm_gem_object_put(&vbo->tbo.base); +out_no_bo: + return ret; +} + +#if defined(CONFIG_DEBUG_FS) + +static void vmw_bo_print_info(int id, struct vmw_bo *bo, struct seq_file *m) +{ + const char *placement; + const char *type; + + switch (bo->tbo.resource->mem_type) { + case TTM_PL_SYSTEM: + placement = " CPU"; + break; + case VMW_PL_GMR: + placement = " GMR"; + break; + case VMW_PL_MOB: + placement = " MOB"; + break; + case VMW_PL_SYSTEM: + placement = "VCPU"; + break; + case TTM_PL_VRAM: + placement = "VRAM"; + break; + default: + placement = "None"; + break; + } + + switch (bo->tbo.type) { + case ttm_bo_type_device: + type = "device"; + break; + case ttm_bo_type_kernel: + type = "kernel"; + break; + case ttm_bo_type_sg: + type = "sg "; + break; + default: + type = "none "; + break; + } + + seq_printf(m, "\t\t0x%08x: %12zu bytes %s, type = %s", + id, bo->tbo.base.size, placement, type); + seq_printf(m, ", priority = %u, pin_count = %u, GEM refs = %d, TTM refs = %d", + bo->tbo.priority, + bo->tbo.pin_count, + kref_read(&bo->tbo.base.refcount), + kref_read(&bo->tbo.kref)); + seq_puts(m, "\n"); +} + +static int vmw_debugfs_gem_info_show(struct seq_file *m, void *unused) +{ + struct vmw_private *vdev = (struct vmw_private *)m->private; + struct drm_device *dev = &vdev->drm; + struct drm_file *file; + int r; + + r = mutex_lock_interruptible(&dev->filelist_mutex); + if (r) + return r; + + list_for_each_entry(file, &dev->filelist, lhead) { + struct task_struct *task; + struct drm_gem_object *gobj; + struct pid *pid; + int id; + + /* + * Although we have a valid reference on file->pid, that does + * not guarantee that the task_struct who called get_pid() is + * still alive (e.g. get_pid(current) => fork() => exit()). + * Therefore, we need to protect this ->comm access using RCU. + */ + rcu_read_lock(); + pid = rcu_dereference(file->pid); + task = pid_task(pid, PIDTYPE_TGID); + seq_printf(m, "pid %8d command %s:\n", pid_nr(pid), + task ? task->comm : "<unknown>"); + rcu_read_unlock(); + + spin_lock(&file->table_lock); + idr_for_each_entry(&file->object_idr, gobj, id) { + struct vmw_bo *bo = to_vmw_bo(gobj); + + vmw_bo_print_info(id, bo, m); + } + spin_unlock(&file->table_lock); + } + + mutex_unlock(&dev->filelist_mutex); + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(vmw_debugfs_gem_info); + +#endif + +void vmw_debugfs_gem_init(struct vmw_private *vdev) +{ +#if defined(CONFIG_DEBUG_FS) + struct drm_minor *minor = vdev->drm.primary; + struct dentry *root = minor->debugfs_root; + + debugfs_create_file("vmwgfx_gem_info", 0444, root, vdev, + &vmw_debugfs_gem_info_fops); +#endif +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c new file mode 100644 index 0000000000..20158a92ac --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2015 VMware, Inc., Palo Alto, CA., USA + * + * 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" + +#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) +{ + 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_CMD_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_t(unsigned long, num_pages, 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); + } + + num_pages -= nr; + remap_pos += nr; + } + + BUG_ON(cmd != cmd_orig + cmd_size / sizeof(*cmd)); + + vmw_cmd_commit(dev_priv, cmd_size); + + return 0; +} + +static void vmw_gmr2_unbind(struct vmw_private *dev_priv, + int gmr_id) +{ + SVGAFifoCmdDefineGMR2 define_cmd; + uint32_t define_size = sizeof(define_cmd) + 4; + uint32_t *cmd; + + cmd = VMW_CMD_RESERVE(dev_priv, define_size); + if (unlikely(cmd == NULL)) + return; + + define_cmd.gmrId = gmr_id; + define_cmd.numPages = 0; + + *cmd++ = SVGA_CMD_DEFINE_GMR2; + memcpy(cmd, &define_cmd, sizeof(define_cmd)); + + vmw_cmd_commit(dev_priv, define_size); +} + + +int vmw_gmr_bind(struct vmw_private *dev_priv, + const struct vmw_sg_table *vsgt, + unsigned long num_pages, + int gmr_id) +{ + struct vmw_piter data_iter; + + vmw_piter_start(&data_iter, vsgt, 0); + + if (unlikely(!vmw_piter_next(&data_iter))) + return 0; + + if (unlikely(!(dev_priv->capabilities & SVGA_CAP_GMR2))) + return -EINVAL; + + return vmw_gmr2_bind(dev_priv, &data_iter, num_pages, gmr_id); +} + + +void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id) +{ + 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 new file mode 100644 index 0000000000..ceb4d3d3b9 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2007-2010 VMware, Inc., Palo Alto, CA., USA + * + * 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-at-vmware-dot-com> + */ + +#include "vmwgfx_drv.h" +#include <drm/ttm/ttm_placement.h> +#include <linux/idr.h> +#include <linux/spinlock.h> +#include <linux/kernel.h> + +struct vmwgfx_gmrid_man { + struct ttm_resource_manager manager; + spinlock_t lock; + struct ida gmr_ida; + uint32_t max_gmr_ids; + uint32_t max_gmr_pages; + uint32_t used_gmr_pages; + uint8_t type; +}; + +static struct vmwgfx_gmrid_man *to_gmrid_manager(struct ttm_resource_manager *man) +{ + return container_of(man, struct vmwgfx_gmrid_man, manager); +} + +static int vmw_gmrid_man_get_node(struct ttm_resource_manager *man, + struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct ttm_resource **res) +{ + struct vmwgfx_gmrid_man *gman = to_gmrid_manager(man); + int id; + + *res = kmalloc(sizeof(**res), GFP_KERNEL); + if (!*res) + return -ENOMEM; + + ttm_resource_init(bo, place, *res); + + id = ida_alloc_max(&gman->gmr_ida, gman->max_gmr_ids - 1, GFP_KERNEL); + if (id < 0) + return id; + + spin_lock(&gman->lock); + + if (gman->max_gmr_pages > 0) { + gman->used_gmr_pages += PFN_UP((*res)->size); + /* + * Because the graphics memory is a soft limit we can try to + * expand it instead of letting the userspace apps crash. + * We're just going to have a sane limit (half of RAM) + * on the number of MOB's that we create and will try to keep + * the system running until we reach that. + */ + if (unlikely(gman->used_gmr_pages > gman->max_gmr_pages)) { + const unsigned long max_graphics_pages = totalram_pages() / 2; + uint32_t new_max_pages = 0; + + DRM_WARN("vmwgfx: mob memory overflow. Consider increasing guest RAM and graphicsMemory.\n"); + vmw_host_printf("vmwgfx, warning: mob memory overflow. Consider increasing guest RAM and graphicsMemory.\n"); + + if (gman->max_gmr_pages > (max_graphics_pages / 2)) { + DRM_WARN("vmwgfx: guest requires more than half of RAM for graphics.\n"); + new_max_pages = max_graphics_pages; + } else + new_max_pages = gman->max_gmr_pages * 2; + if (new_max_pages > gman->max_gmr_pages && new_max_pages >= gman->used_gmr_pages) { + DRM_WARN("vmwgfx: increasing guest mob limits to %u kB.\n", + ((new_max_pages) << (PAGE_SHIFT - 10))); + + gman->max_gmr_pages = new_max_pages; + } else { + char buf[256]; + snprintf(buf, sizeof(buf), + "vmwgfx, error: guest graphics is out of memory (mob limit at: %ukB).\n", + ((gman->max_gmr_pages) << (PAGE_SHIFT - 10))); + vmw_host_printf(buf); + DRM_WARN("%s", buf); + goto nospace; + } + } + } + + (*res)->start = id; + + spin_unlock(&gman->lock); + return 0; + +nospace: + gman->used_gmr_pages -= PFN_UP((*res)->size); + spin_unlock(&gman->lock); + ida_free(&gman->gmr_ida, id); + ttm_resource_fini(man, *res); + kfree(*res); + return -ENOSPC; +} + +static void vmw_gmrid_man_put_node(struct ttm_resource_manager *man, + struct ttm_resource *res) +{ + struct vmwgfx_gmrid_man *gman = to_gmrid_manager(man); + + ida_free(&gman->gmr_ida, res->start); + spin_lock(&gman->lock); + gman->used_gmr_pages -= PFN_UP(res->size); + spin_unlock(&gman->lock); + ttm_resource_fini(man, res); + kfree(res); +} + +static void vmw_gmrid_man_debug(struct ttm_resource_manager *man, + struct drm_printer *printer) +{ + struct vmwgfx_gmrid_man *gman = to_gmrid_manager(man); + + BUG_ON(gman->type != VMW_PL_GMR && gman->type != VMW_PL_MOB); + + drm_printf(printer, "%s's used: %u pages, max: %u pages, %u id's\n", + (gman->type == VMW_PL_MOB) ? "Mob" : "GMR", + gman->used_gmr_pages, gman->max_gmr_pages, gman->max_gmr_ids); +} + +static const struct ttm_resource_manager_func vmw_gmrid_manager_func; + +int vmw_gmrid_man_init(struct vmw_private *dev_priv, int type) +{ + struct ttm_resource_manager *man; + struct vmwgfx_gmrid_man *gman = + kzalloc(sizeof(*gman), GFP_KERNEL); + + if (unlikely(!gman)) + return -ENOMEM; + + man = &gman->manager; + + man->func = &vmw_gmrid_manager_func; + man->use_tt = true; + ttm_resource_manager_init(man, &dev_priv->bdev, 0); + spin_lock_init(&gman->lock); + gman->used_gmr_pages = 0; + ida_init(&gman->gmr_ida); + gman->type = type; + + switch (type) { + 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(); + } + ttm_set_driver_manager(&dev_priv->bdev, type, &gman->manager); + ttm_resource_manager_set_used(man, true); + return 0; +} + +void vmw_gmrid_man_fini(struct vmw_private *dev_priv, int type) +{ + struct ttm_resource_manager *man = ttm_manager_type(&dev_priv->bdev, type); + struct vmwgfx_gmrid_man *gman = to_gmrid_manager(man); + + ttm_resource_manager_set_used(man, false); + + ttm_resource_manager_evict_all(&dev_priv->bdev, man); + + ttm_resource_manager_cleanup(man); + + ttm_set_driver_manager(&dev_priv->bdev, type, NULL); + ida_destroy(&gman->gmr_ida); + kfree(gman); + +} + +static const struct ttm_resource_manager_func vmw_gmrid_manager_func = { + .alloc = vmw_gmrid_man_get_node, + .free = vmw_gmrid_man_put_node, + .debug = vmw_gmrid_man_debug +}; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c new file mode 100644 index 0000000000..a1da5678c7 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2022 VMware, Inc., Palo Alto, CA., USA + * + * 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_devcaps.h" +#include "vmwgfx_kms.h" + +#include <drm/vmwgfx_drm.h> +#include <linux/pci.h> + +int vmw_getparam_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_vmw_getparam_arg *param = + (struct drm_vmw_getparam_arg *)data; + struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); + + switch (param->param) { + case DRM_VMW_PARAM_NUM_STREAMS: + param->value = vmw_overlay_num_overlays(dev_priv); + break; + case DRM_VMW_PARAM_NUM_FREE_STREAMS: + param->value = vmw_overlay_num_free_overlays(dev_priv); + break; + case DRM_VMW_PARAM_3D: + param->value = vmw_supports_3d(dev_priv) ? 1 : 0; + break; + case DRM_VMW_PARAM_HW_CAPS: + param->value = dev_priv->capabilities; + break; + case DRM_VMW_PARAM_HW_CAPS2: + param->value = dev_priv->capabilities2; + break; + case DRM_VMW_PARAM_FIFO_CAPS: + param->value = vmw_fifo_caps(dev_priv); + break; + case DRM_VMW_PARAM_MAX_FB_SIZE: + param->value = dev_priv->max_primary_mem; + break; + case DRM_VMW_PARAM_FIFO_HW_VERSION: + { + if ((dev_priv->capabilities & SVGA_CAP_GBOBJECTS)) + param->value = SVGA3D_HWVERSION_WS8_B1; + else + param->value = vmw_fifo_mem_read( + dev_priv, + ((vmw_fifo_caps(dev_priv) & + 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: + param->value = vmw_devcaps_size(dev_priv, vmw_fp->gb_aware); + 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; + case DRM_VMW_PARAM_SCREEN_TARGET: + param->value = + (dev_priv->active_display_unit == vmw_du_screen_target); + break; + case DRM_VMW_PARAM_DX: + param->value = has_sm4_context(dev_priv); + break; + case DRM_VMW_PARAM_SM4_1: + param->value = has_sm4_1_context(dev_priv); + break; + case DRM_VMW_PARAM_SM5: + param->value = has_sm5_context(dev_priv); + break; + case DRM_VMW_PARAM_GL43: + param->value = has_gl43_context(dev_priv); + break; + case DRM_VMW_PARAM_DEVICE_ID: + param->value = to_pci_dev(dev_priv->drm.dev)->device; + break; + default: + return -EINVAL; + } + + 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); + uint32_t size; + void __user *buffer = (void __user *)((unsigned long)(arg->buffer)); + void *bounce = NULL; + int ret; + struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); + + if (unlikely(arg->pad64 != 0 || arg->max_size == 0)) { + VMW_DEBUG_USER("Illegal GET_3D_CAP argument.\n"); + return -EINVAL; + } + + size = vmw_devcaps_size(dev_priv, vmw_fp->gb_aware); + if (unlikely(size == 0)) { + DRM_ERROR("Failed to figure out the devcaps size (no 3D).\n"); + return -ENOMEM; + } + + 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; + } + + ret = vmw_devcaps_copy(dev_priv, vmw_fp->gb_aware, bounce, size); + if (unlikely (ret != 0)) + goto out_err; + + 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 __user *)(unsigned long)arg->clips_ptr; + + if (unlikely(num_clips == 0)) + return 0; + + if (clips_ptr == NULL) { + VMW_DEBUG_USER("Variable 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, file_priv, arg->fb_id); + if (!fb) { + VMW_DEBUG_USER("Invalid framebuffer id.\n"); + ret = -ENOENT; + goto out_no_fb; + } + vfb = vmw_framebuffer_to_vfb(fb); + + 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: + drm_framebuffer_put(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 __user *)(unsigned long)arg->clips_ptr; + + if (unlikely(num_clips == 0)) + return 0; + + if (clips_ptr == NULL) { + VMW_DEBUG_USER("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, file_priv, arg->fb_id); + if (!fb) { + VMW_DEBUG_USER("Invalid framebuffer id.\n"); + ret = -ENOENT; + goto out_no_fb; + } + + vfb = vmw_framebuffer_to_vfb(fb); + if (!vfb->bo) { + VMW_DEBUG_USER("Framebuffer not buffer backed.\n"); + ret = -EINVAL; + goto out_no_ttm_lock; + } + + ret = vmw_kms_readback(dev_priv, file_priv, + vfb, user_fence_rep, + clips, num_clips); + +out_no_ttm_lock: + drm_framebuffer_put(fb); +out_no_fb: + drm_modeset_unlock_all(dev); +out_no_copy: + kfree(clips); +out_clips: + return ret; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c new file mode 100644 index 0000000000..086e69a130 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2015 VMware, Inc., Palo Alto, CA., USA + * + * 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 <linux/pci.h> +#include <linux/sched/signal.h> + +#include "vmwgfx_drv.h" + +#define VMW_FENCE_WRAP (1 << 24) + +static u32 vmw_irqflag_fence_goal(struct vmw_private *vmw) +{ + if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0) + return SVGA_IRQFLAG_REG_FENCE_GOAL; + else + return SVGA_IRQFLAG_FENCE_GOAL; +} + +/** + * vmw_thread_fn - Deferred (process context) irq handler + * + * @irq: irq number + * @arg: Closure argument. Pointer to a struct drm_device cast to void * + * + * This function implements the deferred part of irq processing. + * The function is guaranteed to run at least once after the + * vmw_irq_handler has returned with IRQ_WAKE_THREAD. + * + */ +static irqreturn_t vmw_thread_fn(int irq, void *arg) +{ + struct drm_device *dev = (struct drm_device *)arg; + struct vmw_private *dev_priv = vmw_priv(dev); + irqreturn_t ret = IRQ_NONE; + + if (test_and_clear_bit(VMW_IRQTHREAD_FENCE, + dev_priv->irqthread_pending)) { + vmw_fences_update(dev_priv->fman); + wake_up_all(&dev_priv->fence_queue); + ret = IRQ_HANDLED; + } + + if (test_and_clear_bit(VMW_IRQTHREAD_CMDBUF, + dev_priv->irqthread_pending)) { + vmw_cmdbuf_irqthread(dev_priv->cman); + ret = IRQ_HANDLED; + } + + return ret; +} + +/** + * vmw_irq_handler: irq handler + * + * @irq: irq number + * @arg: Closure argument. Pointer to a struct drm_device cast to void * + * + * This function implements the quick part of irq processing. + * The function performs fast actions like clearing the device interrupt + * flags and also reasonably quick actions like waking processes waiting for + * FIFO space. Other IRQ actions are deferred to the IRQ thread. + */ +static 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, masked_status; + irqreturn_t ret = IRQ_HANDLED; + + status = vmw_irq_status_read(dev_priv); + masked_status = status & READ_ONCE(dev_priv->irq_mask); + + if (likely(status)) + vmw_irq_status_write(dev_priv, status); + + if (!status) + return IRQ_NONE; + + if (masked_status & SVGA_IRQFLAG_FIFO_PROGRESS) + wake_up_all(&dev_priv->fifo_queue); + + if ((masked_status & (SVGA_IRQFLAG_ANY_FENCE | + vmw_irqflag_fence_goal(dev_priv))) && + !test_and_set_bit(VMW_IRQTHREAD_FENCE, dev_priv->irqthread_pending)) + ret = IRQ_WAKE_THREAD; + + if ((masked_status & (SVGA_IRQFLAG_COMMAND_BUFFER | + SVGA_IRQFLAG_ERROR)) && + !test_and_set_bit(VMW_IRQTHREAD_CMDBUF, + dev_priv->irqthread_pending)) + ret = IRQ_WAKE_THREAD; + + return ret; +} + +static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t seqno) +{ + + return (vmw_read(dev_priv, SVGA_REG_BUSY) == 0); +} + +void vmw_update_seqno(struct vmw_private *dev_priv) +{ + uint32_t seqno = vmw_fence_read(dev_priv); + + if (dev_priv->last_read_seqno != seqno) { + dev_priv->last_read_seqno = seqno; + vmw_fences_update(dev_priv->fman); + } +} + +bool vmw_seqno_passed(struct vmw_private *dev_priv, + uint32_t seqno) +{ + bool ret; + + if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP)) + return true; + + vmw_update_seqno(dev_priv); + if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP)) + return true; + + if (!vmw_has_fences(dev_priv) && vmw_fifo_idle(dev_priv, seqno)) + return true; + + /** + * 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->marker_seq) - seqno) + > VMW_FENCE_WRAP); + + return ret; +} + +int vmw_fallback_wait(struct vmw_private *dev_priv, + bool lazy, + bool fifo_idle, + uint32_t seqno, + bool interruptible, + unsigned long timeout) +{ + struct vmw_fifo_state *fifo_state = dev_priv->fifo; + bool fifo_down = false; + + uint32_t count = 0; + uint32_t signal_seq; + int ret; + unsigned long end_jiffies = jiffies + timeout; + bool (*wait_condition)(struct vmw_private *, uint32_t); + DEFINE_WAIT(__wait); + + wait_condition = (fifo_idle) ? &vmw_fifo_idle : + &vmw_seqno_passed; + + /** + * Block command submission while waiting for idle. + */ + + if (fifo_idle) { + if (dev_priv->cman) { + ret = vmw_cmdbuf_idle(dev_priv->cman, interruptible, + 10*HZ); + if (ret) + goto out_err; + } else if (fifo_state) { + down_read(&fifo_state->rwsem); + fifo_down = true; + } + } + + 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, seqno)) + break; + if (time_after_eq(jiffies, end_jiffies)) { + DRM_ERROR("SVGA device lockup.\n"); + break; + } + if (lazy) + schedule_timeout(1); + else if ((++count & 0x0F) == 0) { + /** + * FIXME: Use schedule_hr_timeout here for + * newer kernels and lower CPU utilization. + */ + + __set_current_state(TASK_RUNNING); + schedule(); + __set_current_state((interruptible) ? + TASK_INTERRUPTIBLE : + TASK_UNINTERRUPTIBLE); + } + if (interruptible && signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + finish_wait(&dev_priv->fence_queue, &__wait); + if (ret == 0 && fifo_idle && fifo_state) + vmw_fence_write(dev_priv, signal_seq); + + wake_up_all(&dev_priv->fence_queue); +out_err: + if (fifo_down) + up_read(&fifo_state->rwsem); + + return ret; +} + +void vmw_generic_waiter_add(struct vmw_private *dev_priv, + u32 flag, int *waiter_count) +{ + spin_lock_bh(&dev_priv->waiter_lock); + if ((*waiter_count)++ == 0) { + vmw_irq_status_write(dev_priv, flag); + dev_priv->irq_mask |= flag; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); + } + spin_unlock_bh(&dev_priv->waiter_lock); +} + +void vmw_generic_waiter_remove(struct vmw_private *dev_priv, + u32 flag, int *waiter_count) +{ + spin_lock_bh(&dev_priv->waiter_lock); + if (--(*waiter_count) == 0) { + dev_priv->irq_mask &= ~flag; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); + } + spin_unlock_bh(&dev_priv->waiter_lock); +} + +void vmw_seqno_waiter_add(struct vmw_private *dev_priv) +{ + vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_ANY_FENCE, + &dev_priv->fence_queue_waiters); +} + +void vmw_seqno_waiter_remove(struct vmw_private *dev_priv) +{ + vmw_generic_waiter_remove(dev_priv, SVGA_IRQFLAG_ANY_FENCE, + &dev_priv->fence_queue_waiters); +} + +void vmw_goal_waiter_add(struct vmw_private *dev_priv) +{ + vmw_generic_waiter_add(dev_priv, vmw_irqflag_fence_goal(dev_priv), + &dev_priv->goal_queue_waiters); +} + +void vmw_goal_waiter_remove(struct vmw_private *dev_priv) +{ + vmw_generic_waiter_remove(dev_priv, vmw_irqflag_fence_goal(dev_priv), + &dev_priv->goal_queue_waiters); +} + +static void vmw_irq_preinstall(struct drm_device *dev) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + uint32_t status; + + status = vmw_irq_status_read(dev_priv); + vmw_irq_status_write(dev_priv, status); +} + +void vmw_irq_uninstall(struct drm_device *dev) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct pci_dev *pdev = to_pci_dev(dev->dev); + uint32_t status; + u32 i; + + if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) + return; + + vmw_write(dev_priv, SVGA_REG_IRQMASK, 0); + + status = vmw_irq_status_read(dev_priv); + vmw_irq_status_write(dev_priv, status); + + for (i = 0; i < dev_priv->num_irq_vectors; ++i) + free_irq(dev_priv->irqs[i], dev); + + pci_free_irq_vectors(pdev); + dev_priv->num_irq_vectors = 0; +} + +/** + * vmw_irq_install - Install the irq handlers + * + * @dev_priv: Pointer to the vmw_private device. + * Return: Zero if successful. Negative number otherwise. + */ +int vmw_irq_install(struct vmw_private *dev_priv) +{ + struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); + struct drm_device *dev = &dev_priv->drm; + int ret; + int nvec; + int i = 0; + + BUILD_BUG_ON((SVGA_IRQFLAG_MAX >> VMWGFX_MAX_NUM_IRQS) != 1); + BUG_ON(VMWGFX_MAX_NUM_IRQS != get_count_order(SVGA_IRQFLAG_MAX)); + + nvec = pci_alloc_irq_vectors(pdev, 1, VMWGFX_MAX_NUM_IRQS, + PCI_IRQ_ALL_TYPES); + + if (nvec <= 0) { + drm_err(&dev_priv->drm, + "IRQ's are unavailable, nvec: %d\n", nvec); + ret = nvec; + goto done; + } + + vmw_irq_preinstall(dev); + + for (i = 0; i < nvec; ++i) { + ret = pci_irq_vector(pdev, i); + if (ret < 0) { + drm_err(&dev_priv->drm, + "failed getting irq vector: %d\n", ret); + goto done; + } + dev_priv->irqs[i] = ret; + + ret = request_threaded_irq(dev_priv->irqs[i], vmw_irq_handler, vmw_thread_fn, + IRQF_SHARED, VMWGFX_DRIVER_NAME, dev); + if (ret != 0) { + drm_err(&dev_priv->drm, + "Failed installing irq(%d): %d\n", + dev_priv->irqs[i], ret); + goto done; + } + } + +done: + dev_priv->num_irq_vectors = i; + return ret; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c new file mode 100644 index 0000000000..818b7f109f --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -0,0 +1,3018 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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" + +#include "vmwgfx_bo.h" +#include "vmw_surface_cache.h" + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_damage_helper.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_rect.h> +#include <drm/drm_sysfs.h> + +void vmw_du_cleanup(struct vmw_display_unit *du) +{ + struct vmw_private *dev_priv = vmw_priv(du->primary.dev); + drm_plane_cleanup(&du->primary); + if (vmw_cmd_supported(dev_priv)) + drm_plane_cleanup(&du->cursor.base); + + drm_connector_unregister(&du->connector); + drm_crtc_cleanup(&du->crtc); + drm_encoder_cleanup(&du->encoder); + drm_connector_cleanup(&du->connector); +} + +/* + * Display Unit Cursor functions + */ + +static int vmw_du_cursor_plane_unmap_cm(struct vmw_plane_state *vps); +static void vmw_cursor_update_mob(struct vmw_private *dev_priv, + struct vmw_plane_state *vps, + u32 *image, u32 width, u32 height, + u32 hotspotX, u32 hotspotY); + +struct vmw_svga_fifo_cmd_define_cursor { + u32 cmd; + SVGAFifoCmdDefineAlphaCursor cursor; +}; + +/** + * vmw_send_define_cursor_cmd - queue a define cursor command + * @dev_priv: the private driver struct + * @image: buffer which holds the cursor image + * @width: width of the mouse cursor image + * @height: height of the mouse cursor image + * @hotspotX: the horizontal position of mouse hotspot + * @hotspotY: the vertical position of mouse hotspot + */ +static void vmw_send_define_cursor_cmd(struct vmw_private *dev_priv, + u32 *image, u32 width, u32 height, + u32 hotspotX, u32 hotspotY) +{ + struct vmw_svga_fifo_cmd_define_cursor *cmd; + const u32 image_size = width * height * sizeof(*image); + const u32 cmd_size = sizeof(*cmd) + image_size; + + /* Try to reserve fifocmd space and swallow any failures; + such reservations cannot be left unconsumed for long + under the risk of clogging other fifocmd users, so + we treat reservations separtely from the way we treat + other fallible KMS-atomic resources at prepare_fb */ + cmd = VMW_CMD_RESERVE(dev_priv, cmd_size); + + if (unlikely(!cmd)) + return; + + memset(cmd, 0, sizeof(*cmd)); + + memcpy(&cmd[1], image, image_size); + + cmd->cmd = SVGA_CMD_DEFINE_ALPHA_CURSOR; + cmd->cursor.id = 0; + cmd->cursor.width = width; + cmd->cursor.height = height; + cmd->cursor.hotspotX = hotspotX; + cmd->cursor.hotspotY = hotspotY; + + vmw_cmd_commit_flush(dev_priv, cmd_size); +} + +/** + * vmw_cursor_update_image - update the cursor image on the provided plane + * @dev_priv: the private driver struct + * @vps: the plane state of the cursor plane + * @image: buffer which holds the cursor image + * @width: width of the mouse cursor image + * @height: height of the mouse cursor image + * @hotspotX: the horizontal position of mouse hotspot + * @hotspotY: the vertical position of mouse hotspot + */ +static void vmw_cursor_update_image(struct vmw_private *dev_priv, + struct vmw_plane_state *vps, + u32 *image, u32 width, u32 height, + u32 hotspotX, u32 hotspotY) +{ + if (vps->cursor.bo) + vmw_cursor_update_mob(dev_priv, vps, image, + vps->base.crtc_w, vps->base.crtc_h, + hotspotX, hotspotY); + + else + vmw_send_define_cursor_cmd(dev_priv, image, width, height, + hotspotX, hotspotY); +} + + +/** + * vmw_cursor_update_mob - Update cursor vis CursorMob mechanism + * + * Called from inside vmw_du_cursor_plane_atomic_update to actually + * make the cursor-image live. + * + * @dev_priv: device to work with + * @vps: the plane state of the cursor plane + * @image: cursor source data to fill the MOB with + * @width: source data width + * @height: source data height + * @hotspotX: cursor hotspot x + * @hotspotY: cursor hotspot Y + */ +static void vmw_cursor_update_mob(struct vmw_private *dev_priv, + struct vmw_plane_state *vps, + u32 *image, u32 width, u32 height, + u32 hotspotX, u32 hotspotY) +{ + SVGAGBCursorHeader *header; + SVGAGBAlphaCursorHeader *alpha_header; + const u32 image_size = width * height * sizeof(*image); + + header = vmw_bo_map_and_cache(vps->cursor.bo); + alpha_header = &header->header.alphaHeader; + + memset(header, 0, sizeof(*header)); + + header->type = SVGA_ALPHA_CURSOR; + header->sizeInBytes = image_size; + + alpha_header->hotspotX = hotspotX; + alpha_header->hotspotY = hotspotY; + alpha_header->width = width; + alpha_header->height = height; + + memcpy(header + 1, image, image_size); + vmw_write(dev_priv, SVGA_REG_CURSOR_MOBID, + vps->cursor.bo->tbo.resource->start); +} + + +static u32 vmw_du_cursor_mob_size(u32 w, u32 h) +{ + return w * h * sizeof(u32) + sizeof(SVGAGBCursorHeader); +} + +/** + * vmw_du_cursor_plane_acquire_image -- Acquire the image data + * @vps: cursor plane state + */ +static u32 *vmw_du_cursor_plane_acquire_image(struct vmw_plane_state *vps) +{ + bool is_iomem; + if (vps->surf) { + if (vps->surf_mapped) + return vmw_bo_map_and_cache(vps->surf->res.guest_memory_bo); + return vps->surf->snooper.image; + } else if (vps->bo) + return ttm_kmap_obj_virtual(&vps->bo->map, &is_iomem); + return NULL; +} + +static bool vmw_du_cursor_plane_has_changed(struct vmw_plane_state *old_vps, + struct vmw_plane_state *new_vps) +{ + void *old_image; + void *new_image; + u32 size; + bool changed; + + if (old_vps->base.crtc_w != new_vps->base.crtc_w || + old_vps->base.crtc_h != new_vps->base.crtc_h) + return true; + + if (old_vps->cursor.hotspot_x != new_vps->cursor.hotspot_x || + old_vps->cursor.hotspot_y != new_vps->cursor.hotspot_y) + return true; + + size = new_vps->base.crtc_w * new_vps->base.crtc_h * sizeof(u32); + + old_image = vmw_du_cursor_plane_acquire_image(old_vps); + new_image = vmw_du_cursor_plane_acquire_image(new_vps); + + changed = false; + if (old_image && new_image) + changed = memcmp(old_image, new_image, size) != 0; + + return changed; +} + +static void vmw_du_destroy_cursor_mob(struct vmw_bo **vbo) +{ + if (!(*vbo)) + return; + + ttm_bo_unpin(&(*vbo)->tbo); + vmw_bo_unreference(vbo); +} + +static void vmw_du_put_cursor_mob(struct vmw_cursor_plane *vcp, + struct vmw_plane_state *vps) +{ + u32 i; + + if (!vps->cursor.bo) + return; + + vmw_du_cursor_plane_unmap_cm(vps); + + /* Look for a free slot to return this mob to the cache. */ + for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) { + if (!vcp->cursor_mobs[i]) { + vcp->cursor_mobs[i] = vps->cursor.bo; + vps->cursor.bo = NULL; + return; + } + } + + /* Cache is full: See if this mob is bigger than an existing mob. */ + for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) { + if (vcp->cursor_mobs[i]->tbo.base.size < + vps->cursor.bo->tbo.base.size) { + vmw_du_destroy_cursor_mob(&vcp->cursor_mobs[i]); + vcp->cursor_mobs[i] = vps->cursor.bo; + vps->cursor.bo = NULL; + return; + } + } + + /* Destroy it if it's not worth caching. */ + vmw_du_destroy_cursor_mob(&vps->cursor.bo); +} + +static int vmw_du_get_cursor_mob(struct vmw_cursor_plane *vcp, + struct vmw_plane_state *vps) +{ + struct vmw_private *dev_priv = vcp->base.dev->dev_private; + u32 size = vmw_du_cursor_mob_size(vps->base.crtc_w, vps->base.crtc_h); + u32 i; + u32 cursor_max_dim, mob_max_size; + int ret; + + if (!dev_priv->has_mob || + (dev_priv->capabilities2 & SVGA_CAP2_CURSOR_MOB) == 0) + return -EINVAL; + + mob_max_size = vmw_read(dev_priv, SVGA_REG_MOB_MAX_SIZE); + cursor_max_dim = vmw_read(dev_priv, SVGA_REG_CURSOR_MAX_DIMENSION); + + if (size > mob_max_size || vps->base.crtc_w > cursor_max_dim || + vps->base.crtc_h > cursor_max_dim) + return -EINVAL; + + if (vps->cursor.bo) { + if (vps->cursor.bo->tbo.base.size >= size) + return 0; + vmw_du_put_cursor_mob(vcp, vps); + } + + /* Look for an unused mob in the cache. */ + for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) { + if (vcp->cursor_mobs[i] && + vcp->cursor_mobs[i]->tbo.base.size >= size) { + vps->cursor.bo = vcp->cursor_mobs[i]; + vcp->cursor_mobs[i] = NULL; + return 0; + } + } + /* Create a new mob if we can't find an existing one. */ + ret = vmw_bo_create_and_populate(dev_priv, size, + VMW_BO_DOMAIN_MOB, + &vps->cursor.bo); + + if (ret != 0) + return ret; + + /* Fence the mob creation so we are guarateed to have the mob */ + ret = ttm_bo_reserve(&vps->cursor.bo->tbo, false, false, NULL); + if (ret != 0) + goto teardown; + + vmw_bo_fence_single(&vps->cursor.bo->tbo, NULL); + ttm_bo_unreserve(&vps->cursor.bo->tbo); + return 0; + +teardown: + vmw_du_destroy_cursor_mob(&vps->cursor.bo); + return ret; +} + + +static void vmw_cursor_update_position(struct vmw_private *dev_priv, + bool show, int x, int y) +{ + const uint32_t svga_cursor_on = show ? SVGA_CURSOR_ON_SHOW + : SVGA_CURSOR_ON_HIDE; + uint32_t count; + + spin_lock(&dev_priv->cursor_lock); + if (dev_priv->capabilities2 & SVGA_CAP2_EXTRA_REGS) { + vmw_write(dev_priv, SVGA_REG_CURSOR4_X, x); + vmw_write(dev_priv, SVGA_REG_CURSOR4_Y, y); + vmw_write(dev_priv, SVGA_REG_CURSOR4_SCREEN_ID, SVGA3D_INVALID_ID); + vmw_write(dev_priv, SVGA_REG_CURSOR4_ON, svga_cursor_on); + vmw_write(dev_priv, SVGA_REG_CURSOR4_SUBMIT, 1); + } else if (vmw_is_cursor_bypass3_enabled(dev_priv)) { + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_ON, svga_cursor_on); + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_X, x); + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_Y, y); + count = vmw_fifo_mem_read(dev_priv, SVGA_FIFO_CURSOR_COUNT); + vmw_fifo_mem_write(dev_priv, SVGA_FIFO_CURSOR_COUNT, ++count); + } else { + vmw_write(dev_priv, SVGA_REG_CURSOR_X, x); + vmw_write(dev_priv, SVGA_REG_CURSOR_Y, y); + vmw_write(dev_priv, SVGA_REG_CURSOR_ON, svga_cursor_on); + } + spin_unlock(&dev_priv->cursor_lock); +} + +void vmw_kms_cursor_snoop(struct vmw_surface *srf, + struct ttm_object_file *tfile, + struct ttm_buffer_object *bo, + SVGA3dCmdHeader *header) +{ + struct ttm_bo_kmap_obj map; + unsigned long kmap_offset; + unsigned long kmap_num; + SVGA3dCopyBox *box; + unsigned box_count; + void *virtual; + bool is_iomem; + struct vmw_dma_cmd { + SVGA3dCmdHeader header; + SVGA3dCmdSurfaceDMA dma; + } *cmd; + int i, ret; + const struct SVGA3dSurfaceDesc *desc = + vmw_surface_get_desc(VMW_CURSOR_SNOOP_FORMAT); + const u32 image_pitch = VMW_CURSOR_SNOOP_WIDTH * desc->pitchBytesPerBlock; + + cmd = container_of(header, struct vmw_dma_cmd, header); + + /* No snooper installed, nothing to copy */ + if (!srf->snooper.image) + return; + + if (cmd->dma.host.face != 0 || cmd->dma.host.mipmap != 0) { + DRM_ERROR("face and mipmap for cursors should never != 0\n"); + return; + } + + if (cmd->header.size < 64) { + DRM_ERROR("at least one full copy box must be given\n"); + return; + } + + box = (SVGA3dCopyBox *)&cmd[1]; + box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) / + sizeof(SVGA3dCopyBox); + + if (cmd->dma.guest.ptr.offset % PAGE_SIZE || + box->x != 0 || box->y != 0 || box->z != 0 || + box->srcx != 0 || box->srcy != 0 || box->srcz != 0 || + box->d != 1 || box_count != 1 || + box->w > VMW_CURSOR_SNOOP_WIDTH || box->h > VMW_CURSOR_SNOOP_HEIGHT) { + /* TODO handle none page aligned offsets */ + /* TODO handle more dst & src != 0 */ + /* TODO handle more then one copy */ + DRM_ERROR("Can't 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; + } + + kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT; + kmap_num = (VMW_CURSOR_SNOOP_HEIGHT*image_pitch) >> PAGE_SHIFT; + + ret = ttm_bo_reserve(bo, true, false, NULL); + if (unlikely(ret != 0)) { + DRM_ERROR("reserve failed\n"); + return; + } + + ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map); + if (unlikely(ret != 0)) + goto err_unreserve; + + virtual = ttm_kmap_obj_virtual(&map, &is_iomem); + + if (box->w == VMW_CURSOR_SNOOP_WIDTH && cmd->dma.guest.pitch == image_pitch) { + memcpy(srf->snooper.image, virtual, + VMW_CURSOR_SNOOP_HEIGHT*image_pitch); + } else { + /* Image is unsigned pointer. */ + for (i = 0; i < box->h; i++) + memcpy(srf->snooper.image + i * image_pitch, + virtual + i * cmd->dma.guest.pitch, + box->w * desc->pitchBytesPerBlock); + } + + srf->snooper.age++; + + ttm_bo_kunmap(&map); +err_unreserve: + ttm_bo_unreserve(bo); +} + +/** + * vmw_kms_legacy_hotspot_clear - Clear legacy hotspots + * + * @dev_priv: Pointer to the device private struct. + * + * Clears all legacy hotspots. + */ +void vmw_kms_legacy_hotspot_clear(struct vmw_private *dev_priv) +{ + struct drm_device *dev = &dev_priv->drm; + struct vmw_display_unit *du; + struct drm_crtc *crtc; + + drm_modeset_lock_all(dev); + drm_for_each_crtc(crtc, dev) { + du = vmw_crtc_to_du(crtc); + + du->hotspot_x = 0; + du->hotspot_y = 0; + } + drm_modeset_unlock_all(dev); +} + +void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv) +{ + struct drm_device *dev = &dev_priv->drm; + struct vmw_display_unit *du; + struct drm_crtc *crtc; + + mutex_lock(&dev->mode_config.mutex); + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + du = vmw_crtc_to_du(crtc); + if (!du->cursor_surface || + du->cursor_age == du->cursor_surface->snooper.age || + !du->cursor_surface->snooper.image) + continue; + + du->cursor_age = du->cursor_surface->snooper.age; + vmw_send_define_cursor_cmd(dev_priv, + du->cursor_surface->snooper.image, + VMW_CURSOR_SNOOP_WIDTH, + VMW_CURSOR_SNOOP_HEIGHT, + du->hotspot_x + du->core_hotspot_x, + du->hotspot_y + du->core_hotspot_y); + } + + mutex_unlock(&dev->mode_config.mutex); +} + + +void vmw_du_cursor_plane_destroy(struct drm_plane *plane) +{ + struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane); + u32 i; + + vmw_cursor_update_position(plane->dev->dev_private, false, 0, 0); + + for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++) + vmw_du_destroy_cursor_mob(&vcp->cursor_mobs[i]); + + drm_plane_cleanup(plane); +} + + +void vmw_du_primary_plane_destroy(struct drm_plane *plane) +{ + drm_plane_cleanup(plane); + + /* Planes are static in our case so we don't free it */ +} + + +/** + * vmw_du_plane_unpin_surf - unpins resource associated with a framebuffer surface + * + * @vps: plane state associated with the display surface + * @unreference: true if we also want to unreference the display. + */ +void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps, + bool unreference) +{ + if (vps->surf) { + if (vps->pinned) { + vmw_resource_unpin(&vps->surf->res); + vps->pinned--; + } + + if (unreference) { + if (vps->pinned) + DRM_ERROR("Surface still pinned\n"); + vmw_surface_unreference(&vps->surf); + } + } +} + + +/** + * vmw_du_plane_cleanup_fb - Unpins the plane surface + * + * @plane: display plane + * @old_state: Contains the FB to clean up + * + * Unpins the framebuffer surface + * + * Returns 0 on success + */ +void +vmw_du_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); + + vmw_du_plane_unpin_surf(vps, false); +} + + +/** + * vmw_du_cursor_plane_map_cm - Maps the cursor mobs. + * + * @vps: plane_state + * + * Returns 0 on success + */ + +static int +vmw_du_cursor_plane_map_cm(struct vmw_plane_state *vps) +{ + int ret; + u32 size = vmw_du_cursor_mob_size(vps->base.crtc_w, vps->base.crtc_h); + struct ttm_buffer_object *bo; + + if (!vps->cursor.bo) + return -EINVAL; + + bo = &vps->cursor.bo->tbo; + + if (bo->base.size < size) + return -EINVAL; + + if (vps->cursor.bo->map.virtual) + return 0; + + ret = ttm_bo_reserve(bo, false, false, NULL); + if (unlikely(ret != 0)) + return -ENOMEM; + + vmw_bo_map_and_cache(vps->cursor.bo); + + ttm_bo_unreserve(bo); + + if (unlikely(ret != 0)) + return -ENOMEM; + + return 0; +} + + +/** + * vmw_du_cursor_plane_unmap_cm - Unmaps the cursor mobs. + * + * @vps: state of the cursor plane + * + * Returns 0 on success + */ + +static int +vmw_du_cursor_plane_unmap_cm(struct vmw_plane_state *vps) +{ + int ret = 0; + struct vmw_bo *vbo = vps->cursor.bo; + + if (!vbo || !vbo->map.virtual) + return 0; + + ret = ttm_bo_reserve(&vbo->tbo, true, false, NULL); + if (likely(ret == 0)) { + vmw_bo_unmap(vbo); + ttm_bo_unreserve(&vbo->tbo); + } + + return ret; +} + + +/** + * vmw_du_cursor_plane_cleanup_fb - Unpins the plane surface + * + * @plane: cursor plane + * @old_state: contains the state to clean up + * + * Unmaps all cursor bo mappings and unpins the cursor surface + * + * Returns 0 on success + */ +void +vmw_du_cursor_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane); + struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); + bool is_iomem; + + if (vps->surf_mapped) { + vmw_bo_unmap(vps->surf->res.guest_memory_bo); + vps->surf_mapped = false; + } + + if (vps->bo && ttm_kmap_obj_virtual(&vps->bo->map, &is_iomem)) { + const int ret = ttm_bo_reserve(&vps->bo->tbo, true, false, NULL); + + if (likely(ret == 0)) { + ttm_bo_kunmap(&vps->bo->map); + ttm_bo_unreserve(&vps->bo->tbo); + } + } + + vmw_du_cursor_plane_unmap_cm(vps); + vmw_du_put_cursor_mob(vcp, vps); + + vmw_du_plane_unpin_surf(vps, false); + + if (vps->surf) { + vmw_surface_unreference(&vps->surf); + vps->surf = NULL; + } + + if (vps->bo) { + vmw_bo_unreference(&vps->bo); + vps->bo = NULL; + } +} + + +/** + * vmw_du_cursor_plane_prepare_fb - Readies the cursor by referencing it + * + * @plane: display plane + * @new_state: info on the new plane state, including the FB + * + * Returns 0 on success + */ +int +vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_framebuffer *fb = new_state->fb; + struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane); + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + int ret = 0; + + if (vps->surf) { + vmw_surface_unreference(&vps->surf); + vps->surf = NULL; + } + + if (vps->bo) { + vmw_bo_unreference(&vps->bo); + vps->bo = NULL; + } + + if (fb) { + if (vmw_framebuffer_to_vfb(fb)->bo) { + vps->bo = vmw_framebuffer_to_vfbd(fb)->buffer; + vmw_bo_reference(vps->bo); + } else { + vps->surf = vmw_framebuffer_to_vfbs(fb)->surface; + vmw_surface_reference(vps->surf); + } + } + + if (!vps->surf && vps->bo) { + const u32 size = new_state->crtc_w * new_state->crtc_h * sizeof(u32); + + /* + * Not using vmw_bo_map_and_cache() helper here as we need to + * reserve the ttm_buffer_object first which + * vmw_bo_map_and_cache() omits. + */ + ret = ttm_bo_reserve(&vps->bo->tbo, true, false, NULL); + + if (unlikely(ret != 0)) + return -ENOMEM; + + ret = ttm_bo_kmap(&vps->bo->tbo, 0, PFN_UP(size), &vps->bo->map); + + ttm_bo_unreserve(&vps->bo->tbo); + + if (unlikely(ret != 0)) + return -ENOMEM; + } else if (vps->surf && !vps->bo && vps->surf->res.guest_memory_bo) { + + WARN_ON(vps->surf->snooper.image); + ret = ttm_bo_reserve(&vps->surf->res.guest_memory_bo->tbo, true, false, + NULL); + if (unlikely(ret != 0)) + return -ENOMEM; + vmw_bo_map_and_cache(vps->surf->res.guest_memory_bo); + ttm_bo_unreserve(&vps->surf->res.guest_memory_bo->tbo); + vps->surf_mapped = true; + } + + if (vps->surf || vps->bo) { + vmw_du_get_cursor_mob(vcp, vps); + vmw_du_cursor_plane_map_cm(vps); + } + + return 0; +} + + +void +vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, + plane); + struct drm_crtc *crtc = new_state->crtc ?: old_state->crtc; + struct vmw_private *dev_priv = vmw_priv(crtc->dev); + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + struct vmw_plane_state *old_vps = vmw_plane_state_to_vps(old_state); + s32 hotspot_x, hotspot_y; + + hotspot_x = du->hotspot_x; + hotspot_y = du->hotspot_y; + + if (new_state->fb) { + hotspot_x += new_state->fb->hot_x; + hotspot_y += new_state->fb->hot_y; + } + + du->cursor_surface = vps->surf; + du->cursor_bo = vps->bo; + + if (!vps->surf && !vps->bo) { + vmw_cursor_update_position(dev_priv, false, 0, 0); + return; + } + + vps->cursor.hotspot_x = hotspot_x; + vps->cursor.hotspot_y = hotspot_y; + + if (vps->surf) { + du->cursor_age = du->cursor_surface->snooper.age; + } + + if (!vmw_du_cursor_plane_has_changed(old_vps, vps)) { + /* + * If it hasn't changed, avoid making the device do extra + * work by keeping the old cursor active. + */ + struct vmw_cursor_plane_state tmp = old_vps->cursor; + old_vps->cursor = vps->cursor; + vps->cursor = tmp; + } else { + void *image = vmw_du_cursor_plane_acquire_image(vps); + if (image) + vmw_cursor_update_image(dev_priv, vps, image, + new_state->crtc_w, + new_state->crtc_h, + hotspot_x, hotspot_y); + } + + du->cursor_x = new_state->crtc_x + du->set_gui_x; + du->cursor_y = new_state->crtc_y + du->set_gui_y; + + vmw_cursor_update_position(dev_priv, true, + du->cursor_x + hotspot_x, + du->cursor_y + hotspot_y); + + du->core_hotspot_x = hotspot_x - du->hotspot_x; + du->core_hotspot_y = hotspot_y - du->hotspot_y; +} + + +/** + * vmw_du_primary_plane_atomic_check - check if the new state is okay + * + * @plane: display plane + * @state: info on the new plane state, including the FB + * + * Check if the new state is settable given the current state. Other + * than what the atomic helper checks, we care about crtc fitting + * the FB and maintaining one active framebuffer. + * + * Returns 0 on success + */ +int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + plane); + struct drm_crtc_state *crtc_state = NULL; + struct drm_framebuffer *new_fb = new_state->fb; + int ret; + + if (new_state->crtc) + crtc_state = drm_atomic_get_new_crtc_state(state, + new_state->crtc); + + ret = drm_atomic_helper_check_plane_state(new_state, crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, true); + + if (!ret && new_fb) { + struct drm_crtc *crtc = new_state->crtc; + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + + vmw_connector_state_to_vcs(du->connector.state); + } + + + return ret; +} + + +/** + * vmw_du_cursor_plane_atomic_check - check if the new state is okay + * + * @plane: cursor plane + * @state: info on the new plane state + * + * This is a chance to fail if the new cursor state does not fit + * our requirements. + * + * Returns 0 on success + */ +int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + plane); + int ret = 0; + struct drm_crtc_state *crtc_state = NULL; + struct vmw_surface *surface = NULL; + struct drm_framebuffer *fb = new_state->fb; + + if (new_state->crtc) + crtc_state = drm_atomic_get_new_crtc_state(new_state->state, + new_state->crtc); + + ret = drm_atomic_helper_check_plane_state(new_state, crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + true, true); + if (ret) + return ret; + + /* Turning off */ + if (!fb) + return 0; + + /* A lot of the code assumes this */ + if (new_state->crtc_w != 64 || new_state->crtc_h != 64) { + DRM_ERROR("Invalid cursor dimensions (%d, %d)\n", + new_state->crtc_w, new_state->crtc_h); + return -EINVAL; + } + + if (!vmw_framebuffer_to_vfb(fb)->bo) { + surface = vmw_framebuffer_to_vfbs(fb)->surface; + + WARN_ON(!surface); + + if (!surface || + (!surface->snooper.image && !surface->res.guest_memory_bo)) { + DRM_ERROR("surface not suitable for cursor\n"); + return -EINVAL; + } + } + + return 0; +} + + +int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state, + crtc); + struct vmw_display_unit *du = vmw_crtc_to_du(new_state->crtc); + int connector_mask = drm_connector_mask(&du->connector); + bool has_primary = new_state->plane_mask & + drm_plane_mask(crtc->primary); + + /* We always want to have an active plane with an active CRTC */ + if (has_primary != new_state->enable) + return -EINVAL; + + + if (new_state->connector_mask != connector_mask && + new_state->connector_mask != 0) { + DRM_ERROR("Invalid connectors configuration\n"); + return -EINVAL; + } + + /* + * Our virtual device does not have a dot clock, so use the logical + * clock value as the dot clock. + */ + if (new_state->mode.crtc_clock == 0) + new_state->adjusted_mode.crtc_clock = new_state->mode.clock; + + return 0; +} + + +void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ +} + + +void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ +} + + +/** + * vmw_du_crtc_duplicate_state - duplicate crtc state + * @crtc: DRM crtc + * + * Allocates and returns a copy of the crtc state (both common and + * vmw-specific) for the specified crtc. + * + * Returns: The newly allocated crtc state, or NULL on failure. + */ +struct drm_crtc_state * +vmw_du_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *state; + struct vmw_crtc_state *vcs; + + if (WARN_ON(!crtc->state)) + return NULL; + + vcs = kmemdup(crtc->state, sizeof(*vcs), GFP_KERNEL); + + if (!vcs) + return NULL; + + state = &vcs->base; + + __drm_atomic_helper_crtc_duplicate_state(crtc, state); + + return state; +} + + +/** + * vmw_du_crtc_reset - creates a blank vmw crtc state + * @crtc: DRM crtc + * + * Resets the atomic state for @crtc by freeing the state pointer (which + * might be NULL, e.g. at driver load time) and allocating a new empty state + * object. + */ +void vmw_du_crtc_reset(struct drm_crtc *crtc) +{ + struct vmw_crtc_state *vcs; + + + if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); + + kfree(vmw_crtc_state_to_vcs(crtc->state)); + } + + vcs = kzalloc(sizeof(*vcs), GFP_KERNEL); + + if (!vcs) { + DRM_ERROR("Cannot allocate vmw_crtc_state\n"); + return; + } + + __drm_atomic_helper_crtc_reset(crtc, &vcs->base); +} + + +/** + * vmw_du_crtc_destroy_state - destroy crtc state + * @crtc: DRM crtc + * @state: state object to destroy + * + * Destroys the crtc state (both common and vmw-specific) for the + * specified plane. + */ +void +vmw_du_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + drm_atomic_helper_crtc_destroy_state(crtc, state); +} + + +/** + * vmw_du_plane_duplicate_state - duplicate plane state + * @plane: drm plane + * + * Allocates and returns a copy of the plane state (both common and + * vmw-specific) for the specified plane. + * + * Returns: The newly allocated plane state, or NULL on failure. + */ +struct drm_plane_state * +vmw_du_plane_duplicate_state(struct drm_plane *plane) +{ + struct drm_plane_state *state; + struct vmw_plane_state *vps; + + vps = kmemdup(plane->state, sizeof(*vps), GFP_KERNEL); + + if (!vps) + return NULL; + + vps->pinned = 0; + vps->cpp = 0; + + memset(&vps->cursor, 0, sizeof(vps->cursor)); + + /* Each ref counted resource needs to be acquired again */ + if (vps->surf) + (void) vmw_surface_reference(vps->surf); + + if (vps->bo) + (void) vmw_bo_reference(vps->bo); + + state = &vps->base; + + __drm_atomic_helper_plane_duplicate_state(plane, state); + + return state; +} + + +/** + * vmw_du_plane_reset - creates a blank vmw plane state + * @plane: drm plane + * + * Resets the atomic state for @plane by freeing the state pointer (which might + * be NULL, e.g. at driver load time) and allocating a new empty state object. + */ +void vmw_du_plane_reset(struct drm_plane *plane) +{ + struct vmw_plane_state *vps; + + if (plane->state) + vmw_du_plane_destroy_state(plane, plane->state); + + vps = kzalloc(sizeof(*vps), GFP_KERNEL); + + if (!vps) { + DRM_ERROR("Cannot allocate vmw_plane_state\n"); + return; + } + + __drm_atomic_helper_plane_reset(plane, &vps->base); +} + + +/** + * vmw_du_plane_destroy_state - destroy plane state + * @plane: DRM plane + * @state: state object to destroy + * + * Destroys the plane state (both common and vmw-specific) for the + * specified plane. + */ +void +vmw_du_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(state); + + /* Should have been freed by cleanup_fb */ + if (vps->surf) + vmw_surface_unreference(&vps->surf); + + if (vps->bo) + vmw_bo_unreference(&vps->bo); + + drm_atomic_helper_plane_destroy_state(plane, state); +} + + +/** + * vmw_du_connector_duplicate_state - duplicate connector state + * @connector: DRM connector + * + * Allocates and returns a copy of the connector state (both common and + * vmw-specific) for the specified connector. + * + * Returns: The newly allocated connector state, or NULL on failure. + */ +struct drm_connector_state * +vmw_du_connector_duplicate_state(struct drm_connector *connector) +{ + struct drm_connector_state *state; + struct vmw_connector_state *vcs; + + if (WARN_ON(!connector->state)) + return NULL; + + vcs = kmemdup(connector->state, sizeof(*vcs), GFP_KERNEL); + + if (!vcs) + return NULL; + + state = &vcs->base; + + __drm_atomic_helper_connector_duplicate_state(connector, state); + + return state; +} + + +/** + * vmw_du_connector_reset - creates a blank vmw connector state + * @connector: DRM connector + * + * Resets the atomic state for @connector by freeing the state pointer (which + * might be NULL, e.g. at driver load time) and allocating a new empty state + * object. + */ +void vmw_du_connector_reset(struct drm_connector *connector) +{ + struct vmw_connector_state *vcs; + + + if (connector->state) { + __drm_atomic_helper_connector_destroy_state(connector->state); + + kfree(vmw_connector_state_to_vcs(connector->state)); + } + + vcs = kzalloc(sizeof(*vcs), GFP_KERNEL); + + if (!vcs) { + DRM_ERROR("Cannot allocate vmw_connector_state\n"); + return; + } + + __drm_atomic_helper_connector_reset(connector, &vcs->base); +} + + +/** + * vmw_du_connector_destroy_state - destroy connector state + * @connector: DRM connector + * @state: state object to destroy + * + * Destroys the connector state (both common and vmw-specific) for the + * specified plane. + */ +void +vmw_du_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state) +{ + drm_atomic_helper_connector_destroy_state(connector, state); +} +/* + * Generic framebuffer code + */ + +/* + * Surface framebuffer code + */ + +static void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer) +{ + struct vmw_framebuffer_surface *vfbs = + vmw_framebuffer_to_vfbs(framebuffer); + + drm_framebuffer_cleanup(framebuffer); + vmw_surface_unreference(&vfbs->surface); + + kfree(vfbs); +} + +/** + * vmw_kms_readback - Perform a readback from the screen system to + * a buffer-object backed framebuffer. + * + * @dev_priv: Pointer to the device private structure. + * @file_priv: Pointer to a struct drm_file identifying the caller. + * Must be set to NULL if @user_fence_rep is NULL. + * @vfb: Pointer to the buffer-object backed framebuffer. + * @user_fence_rep: User-space provided structure for fence information. + * Must be set to non-NULL if @file_priv is non-NULL. + * @vclips: Array of clip rects. + * @num_clips: Number of clip rects in @vclips. + * + * Returns 0 on success, negative error code on failure. -ERESTARTSYS if + * interrupted. + */ +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 *vclips, + uint32_t num_clips) +{ + switch (dev_priv->active_display_unit) { + case vmw_du_screen_object: + return vmw_kms_sou_readback(dev_priv, file_priv, vfb, + user_fence_rep, vclips, num_clips, + NULL); + case vmw_du_screen_target: + return vmw_kms_stdu_readback(dev_priv, file_priv, vfb, + user_fence_rep, NULL, vclips, num_clips, + 1, NULL); + default: + WARN_ONCE(true, + "Readback called with invalid display system.\n"); +} + + return -ENOSYS; +} + + +static const struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = { + .destroy = vmw_framebuffer_surface_destroy, + .dirty = drm_atomic_helper_dirtyfb, +}; + +static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, + struct vmw_surface *surface, + struct vmw_framebuffer **out, + const struct drm_mode_fb_cmd2 + *mode_cmd, + bool is_bo_proxy) + +{ + struct drm_device *dev = &dev_priv->drm; + struct vmw_framebuffer_surface *vfbs; + enum SVGA3dSurfaceFormat format; + int ret; + + /* 3D is only supported on HWv8 and newer hosts */ + if (dev_priv->active_display_unit == vmw_du_legacy) + return -ENOSYS; + + /* + * Sanity checks. + */ + + if (!drm_any_plane_has_format(&dev_priv->drm, + mode_cmd->pixel_format, + mode_cmd->modifier[0])) { + drm_dbg(&dev_priv->drm, + "unsupported pixel format %p4cc / modifier 0x%llx\n", + &mode_cmd->pixel_format, mode_cmd->modifier[0]); + return -EINVAL; + } + + /* Surface must be marked as a scanout. */ + if (unlikely(!surface->metadata.scanout)) + return -EINVAL; + + if (unlikely(surface->metadata.mip_levels[0] != 1 || + surface->metadata.num_sizes != 1 || + surface->metadata.base_size.width < mode_cmd->width || + surface->metadata.base_size.height < mode_cmd->height || + surface->metadata.base_size.depth != 1)) { + DRM_ERROR("Incompatible surface dimensions " + "for requested mode.\n"); + return -EINVAL; + } + + switch (mode_cmd->pixel_format) { + case DRM_FORMAT_ARGB8888: + format = SVGA3D_A8R8G8B8; + break; + case DRM_FORMAT_XRGB8888: + format = SVGA3D_X8R8G8B8; + break; + case DRM_FORMAT_RGB565: + format = SVGA3D_R5G6B5; + break; + case DRM_FORMAT_XRGB1555: + format = SVGA3D_A1R5G5B5; + break; + default: + DRM_ERROR("Invalid pixel format: %p4cc\n", + &mode_cmd->pixel_format); + return -EINVAL; + } + + /* + * For DX, surface format validation is done when surface->scanout + * is set. + */ + if (!has_sm4_context(dev_priv) && format != surface->metadata.format) { + DRM_ERROR("Invalid surface format for requested mode.\n"); + return -EINVAL; + } + + vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL); + if (!vfbs) { + ret = -ENOMEM; + goto out_err1; + } + + drm_helper_mode_fill_fb_struct(dev, &vfbs->base.base, mode_cmd); + vfbs->surface = vmw_surface_reference(surface); + vfbs->base.user_handle = mode_cmd->handles[0]; + vfbs->is_bo_proxy = is_bo_proxy; + + *out = &vfbs->base; + + ret = drm_framebuffer_init(dev, &vfbs->base.base, + &vmw_framebuffer_surface_funcs); + if (ret) + goto out_err2; + + return 0; + +out_err2: + vmw_surface_unreference(&surface); + kfree(vfbs); +out_err1: + return ret; +} + +/* + * Buffer-object framebuffer code + */ + +static int vmw_framebuffer_bo_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct vmw_framebuffer_bo *vfbd = + vmw_framebuffer_to_vfbd(fb); + + return drm_gem_handle_create(file_priv, &vfbd->buffer->tbo.base, handle); +} + +static void vmw_framebuffer_bo_destroy(struct drm_framebuffer *framebuffer) +{ + struct vmw_framebuffer_bo *vfbd = + vmw_framebuffer_to_vfbd(framebuffer); + + drm_framebuffer_cleanup(framebuffer); + vmw_bo_unreference(&vfbd->buffer); + + kfree(vfbd); +} + +static const struct drm_framebuffer_funcs vmw_framebuffer_bo_funcs = { + .create_handle = vmw_framebuffer_bo_create_handle, + .destroy = vmw_framebuffer_bo_destroy, + .dirty = drm_atomic_helper_dirtyfb, +}; + +/** + * vmw_create_bo_proxy - create a proxy surface for the buffer object + * + * @dev: DRM device + * @mode_cmd: parameters for the new surface + * @bo_mob: MOB backing the buffer object + * @srf_out: newly created surface + * + * When the content FB is a buffer object, we create a surface as a proxy to the + * same buffer. This way we can do a surface copy rather than a surface DMA. + * This is a more efficient approach + * + * RETURNS: + * 0 on success, error code otherwise + */ +static int vmw_create_bo_proxy(struct drm_device *dev, + const struct drm_mode_fb_cmd2 *mode_cmd, + struct vmw_bo *bo_mob, + struct vmw_surface **srf_out) +{ + struct vmw_surface_metadata metadata = {0}; + uint32_t format; + struct vmw_resource *res; + unsigned int bytes_pp; + int ret; + + switch (mode_cmd->pixel_format) { + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + format = SVGA3D_X8R8G8B8; + bytes_pp = 4; + break; + + case DRM_FORMAT_RGB565: + case DRM_FORMAT_XRGB1555: + format = SVGA3D_R5G6B5; + bytes_pp = 2; + break; + + case 8: + format = SVGA3D_P8; + bytes_pp = 1; + break; + + default: + DRM_ERROR("Invalid framebuffer format %p4cc\n", + &mode_cmd->pixel_format); + return -EINVAL; + } + + metadata.format = format; + metadata.mip_levels[0] = 1; + metadata.num_sizes = 1; + metadata.base_size.width = mode_cmd->pitches[0] / bytes_pp; + metadata.base_size.height = mode_cmd->height; + metadata.base_size.depth = 1; + metadata.scanout = true; + + ret = vmw_gb_surface_define(vmw_priv(dev), &metadata, srf_out); + if (ret) { + DRM_ERROR("Failed to allocate proxy content buffer\n"); + return ret; + } + + res = &(*srf_out)->res; + + /* Reserve and switch the backing mob. */ + mutex_lock(&res->dev_priv->cmdbuf_mutex); + (void) vmw_resource_reserve(res, false, true); + vmw_user_bo_unref(&res->guest_memory_bo); + res->guest_memory_bo = vmw_user_bo_ref(bo_mob); + res->guest_memory_offset = 0; + vmw_resource_unreserve(res, false, false, false, NULL, 0); + mutex_unlock(&res->dev_priv->cmdbuf_mutex); + + return 0; +} + + + +static int vmw_kms_new_framebuffer_bo(struct vmw_private *dev_priv, + struct vmw_bo *bo, + struct vmw_framebuffer **out, + const struct drm_mode_fb_cmd2 + *mode_cmd) + +{ + struct drm_device *dev = &dev_priv->drm; + struct vmw_framebuffer_bo *vfbd; + unsigned int requested_size; + int ret; + + requested_size = mode_cmd->height * mode_cmd->pitches[0]; + if (unlikely(requested_size > bo->tbo.base.size)) { + DRM_ERROR("Screen buffer object size is too small " + "for requested mode.\n"); + return -EINVAL; + } + + if (!drm_any_plane_has_format(&dev_priv->drm, + mode_cmd->pixel_format, + mode_cmd->modifier[0])) { + drm_dbg(&dev_priv->drm, + "unsupported pixel format %p4cc / modifier 0x%llx\n", + &mode_cmd->pixel_format, mode_cmd->modifier[0]); + return -EINVAL; + } + + vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL); + if (!vfbd) { + ret = -ENOMEM; + goto out_err1; + } + + vfbd->base.base.obj[0] = &bo->tbo.base; + drm_helper_mode_fill_fb_struct(dev, &vfbd->base.base, mode_cmd); + vfbd->base.bo = true; + vfbd->buffer = vmw_bo_reference(bo); + vfbd->base.user_handle = mode_cmd->handles[0]; + *out = &vfbd->base; + + ret = drm_framebuffer_init(dev, &vfbd->base.base, + &vmw_framebuffer_bo_funcs); + if (ret) + goto out_err2; + + return 0; + +out_err2: + vmw_bo_unreference(&bo); + kfree(vfbd); +out_err1: + return ret; +} + + +/** + * vmw_kms_srf_ok - check if a surface can be created + * + * @dev_priv: Pointer to device private struct. + * @width: requested width + * @height: requested height + * + * Surfaces need to be less than texture size + */ +static bool +vmw_kms_srf_ok(struct vmw_private *dev_priv, uint32_t width, uint32_t height) +{ + if (width > dev_priv->texture_max_width || + height > dev_priv->texture_max_height) + return false; + + return true; +} + +/** + * vmw_kms_new_framebuffer - Create a new framebuffer. + * + * @dev_priv: Pointer to device private struct. + * @bo: Pointer to buffer object to wrap the kms framebuffer around. + * Either @bo or @surface must be NULL. + * @surface: Pointer to a surface to wrap the kms framebuffer around. + * Either @bo or @surface must be NULL. + * @only_2d: No presents will occur to this buffer object based framebuffer. + * This helps the code to do some important optimizations. + * @mode_cmd: Frame-buffer metadata. + */ +struct vmw_framebuffer * +vmw_kms_new_framebuffer(struct vmw_private *dev_priv, + struct vmw_bo *bo, + struct vmw_surface *surface, + bool only_2d, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct vmw_framebuffer *vfb = NULL; + bool is_bo_proxy = false; + int ret; + + /* + * We cannot use the SurfaceDMA command in an non-accelerated VM, + * therefore, wrap the buffer object in a surface so we can use the + * SurfaceCopy command. + */ + if (vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height) && + bo && only_2d && + mode_cmd->width > 64 && /* Don't create a proxy for cursor */ + dev_priv->active_display_unit == vmw_du_screen_target) { + ret = vmw_create_bo_proxy(&dev_priv->drm, mode_cmd, + bo, &surface); + if (ret) + return ERR_PTR(ret); + + is_bo_proxy = true; + } + + /* Create the new framebuffer depending one what we have */ + if (surface) { + ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb, + mode_cmd, + is_bo_proxy); + /* + * vmw_create_bo_proxy() adds a reference that is no longer + * needed + */ + if (is_bo_proxy) + vmw_surface_unreference(&surface); + } else if (bo) { + ret = vmw_kms_new_framebuffer_bo(dev_priv, bo, &vfb, + mode_cmd); + } else { + BUG(); + } + + if (ret) + return ERR_PTR(ret); + + return vfb; +} + +/* + * Generic Kernel modesetting functions + */ + +static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, + struct drm_file *file_priv, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_framebuffer *vfb = NULL; + struct vmw_surface *surface = NULL; + struct vmw_bo *bo = NULL; + int ret; + + /* returns either a bo or surface */ + ret = vmw_user_lookup_handle(dev_priv, file_priv, + mode_cmd->handles[0], + &surface, &bo); + if (ret) { + DRM_ERROR("Invalid buffer object handle %u (0x%x).\n", + mode_cmd->handles[0], mode_cmd->handles[0]); + goto err_out; + } + + + if (!bo && + !vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height)) { + DRM_ERROR("Surface size cannot exceed %dx%d\n", + dev_priv->texture_max_width, + dev_priv->texture_max_height); + goto err_out; + } + + + vfb = vmw_kms_new_framebuffer(dev_priv, bo, surface, + !(dev_priv->capabilities & SVGA_CAP_3D), + mode_cmd); + if (IS_ERR(vfb)) { + ret = PTR_ERR(vfb); + goto err_out; + } + +err_out: + /* vmw_user_lookup_handle takes one ref so does new_fb */ + if (bo) + vmw_user_bo_unref(&bo); + if (surface) + vmw_surface_unreference(&surface); + + if (ret) { + DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret); + return ERR_PTR(ret); + } + + return &vfb->base; +} + +/** + * vmw_kms_check_display_memory - Validates display memory required for a + * topology + * @dev: DRM device + * @num_rects: number of drm_rect in rects + * @rects: array of drm_rect representing the topology to validate indexed by + * crtc index. + * + * Returns: + * 0 on success otherwise negative error code + */ +static int vmw_kms_check_display_memory(struct drm_device *dev, + uint32_t num_rects, + struct drm_rect *rects) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_rect bounding_box = {0}; + u64 total_pixels = 0, pixel_mem, bb_mem; + int i; + + for (i = 0; i < num_rects; i++) { + /* + * For STDU only individual screen (screen target) is limited by + * SCREENTARGET_MAX_WIDTH/HEIGHT registers. + */ + if (dev_priv->active_display_unit == vmw_du_screen_target && + (drm_rect_width(&rects[i]) > dev_priv->stdu_max_width || + drm_rect_height(&rects[i]) > dev_priv->stdu_max_height)) { + VMW_DEBUG_KMS("Screen size not supported.\n"); + return -EINVAL; + } + + /* Bounding box upper left is at (0,0). */ + if (rects[i].x2 > bounding_box.x2) + bounding_box.x2 = rects[i].x2; + + if (rects[i].y2 > bounding_box.y2) + bounding_box.y2 = rects[i].y2; + + total_pixels += (u64) drm_rect_width(&rects[i]) * + (u64) drm_rect_height(&rects[i]); + } + + /* Virtual svga device primary limits are always in 32-bpp. */ + pixel_mem = total_pixels * 4; + + /* + * For HV10 and below prim_bb_mem is vram size. When + * SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM is not present vram size is + * limit on primary bounding box + */ + if (pixel_mem > dev_priv->max_primary_mem) { + VMW_DEBUG_KMS("Combined output size too large.\n"); + return -EINVAL; + } + + /* SVGA_CAP_NO_BB_RESTRICTION is available for STDU only. */ + if (dev_priv->active_display_unit != vmw_du_screen_target || + !(dev_priv->capabilities & SVGA_CAP_NO_BB_RESTRICTION)) { + bb_mem = (u64) bounding_box.x2 * bounding_box.y2 * 4; + + if (bb_mem > dev_priv->max_primary_mem) { + VMW_DEBUG_KMS("Topology is beyond supported limits.\n"); + return -EINVAL; + } + } + + return 0; +} + +/** + * vmw_crtc_state_and_lock - Return new or current crtc state with locked + * crtc mutex + * @state: The atomic state pointer containing the new atomic state + * @crtc: The crtc + * + * This function returns the new crtc state if it's part of the state update. + * Otherwise returns the current crtc state. It also makes sure that the + * crtc mutex is locked. + * + * Returns: A valid crtc state pointer or NULL. It may also return a + * pointer error, in particular -EDEADLK if locking needs to be rerun. + */ +static struct drm_crtc_state * +vmw_crtc_state_and_lock(struct drm_atomic_state *state, struct drm_crtc *crtc) +{ + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (crtc_state) { + lockdep_assert_held(&crtc->mutex.mutex.base); + } else { + int ret = drm_modeset_lock(&crtc->mutex, state->acquire_ctx); + + if (ret != 0 && ret != -EALREADY) + return ERR_PTR(ret); + + crtc_state = crtc->state; + } + + return crtc_state; +} + +/** + * vmw_kms_check_implicit - Verify that all implicit display units scan out + * from the same fb after the new state is committed. + * @dev: The drm_device. + * @state: The new state to be checked. + * + * Returns: + * Zero on success, + * -EINVAL on invalid state, + * -EDEADLK if modeset locking needs to be rerun. + */ +static int vmw_kms_check_implicit(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_framebuffer *implicit_fb = NULL; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_plane_state *plane_state; + + drm_for_each_crtc(crtc, dev) { + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + + if (!du->is_implicit) + continue; + + crtc_state = vmw_crtc_state_and_lock(state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + if (!crtc_state || !crtc_state->enable) + continue; + + /* + * Can't move primary planes across crtcs, so this is OK. + * It also means we don't need to take the plane mutex. + */ + plane_state = du->primary.state; + if (plane_state->crtc != crtc) + continue; + + if (!implicit_fb) + implicit_fb = plane_state->fb; + else if (implicit_fb != plane_state->fb) + return -EINVAL; + } + + return 0; +} + +/** + * vmw_kms_check_topology - Validates topology in drm_atomic_state + * @dev: DRM device + * @state: the driver state object + * + * Returns: + * 0 on success otherwise negative error code + */ +static int vmw_kms_check_topology(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + struct drm_rect *rects; + struct drm_crtc *crtc; + uint32_t i; + int ret = 0; + + rects = kcalloc(dev->mode_config.num_crtc, sizeof(struct drm_rect), + GFP_KERNEL); + if (!rects) + return -ENOMEM; + + drm_for_each_crtc(crtc, dev) { + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct drm_crtc_state *crtc_state; + + i = drm_crtc_index(crtc); + + crtc_state = vmw_crtc_state_and_lock(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto clean; + } + + if (!crtc_state) + continue; + + if (crtc_state->enable) { + rects[i].x1 = du->gui_x; + rects[i].y1 = du->gui_y; + rects[i].x2 = du->gui_x + crtc_state->mode.hdisplay; + rects[i].y2 = du->gui_y + crtc_state->mode.vdisplay; + } else { + rects[i].x1 = 0; + rects[i].y1 = 0; + rects[i].x2 = 0; + rects[i].y2 = 0; + } + } + + /* Determine change to topology due to new atomic state */ + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, + new_crtc_state, i) { + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct drm_connector *connector; + struct drm_connector_state *conn_state; + struct vmw_connector_state *vmw_conn_state; + + if (!du->pref_active && new_crtc_state->enable) { + VMW_DEBUG_KMS("Enabling a disabled display unit\n"); + ret = -EINVAL; + goto clean; + } + + /* + * For vmwgfx each crtc has only one connector attached and it + * is not changed so don't really need to check the + * crtc->connector_mask and iterate over it. + */ + connector = &du->connector; + conn_state = drm_atomic_get_connector_state(state, connector); + if (IS_ERR(conn_state)) { + ret = PTR_ERR(conn_state); + goto clean; + } + + vmw_conn_state = vmw_connector_state_to_vcs(conn_state); + vmw_conn_state->gui_x = du->gui_x; + vmw_conn_state->gui_y = du->gui_y; + } + + ret = vmw_kms_check_display_memory(dev, dev->mode_config.num_crtc, + rects); + +clean: + kfree(rects); + return ret; +} + +/** + * vmw_kms_atomic_check_modeset- validate state object for modeset changes + * + * @dev: DRM device + * @state: the driver state object + * + * This is a simple wrapper around drm_atomic_helper_check_modeset() for + * us to assign a value to mode->crtc_clock so that + * drm_calc_timestamping_constants() won't throw an error message + * + * Returns: + * Zero for success or -errno + */ +static int +vmw_kms_atomic_check_modeset(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + bool need_modeset = false; + int i, ret; + + ret = drm_atomic_helper_check(dev, state); + if (ret) + return ret; + + ret = vmw_kms_check_implicit(dev, state); + if (ret) { + VMW_DEBUG_KMS("Invalid implicit state\n"); + return ret; + } + + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + if (drm_atomic_crtc_needs_modeset(crtc_state)) + need_modeset = true; + } + + if (need_modeset) + return vmw_kms_check_topology(dev, state); + + return ret; +} + +static const struct drm_mode_config_funcs vmw_kms_funcs = { + .fb_create = vmw_kms_fb_create, + .atomic_check = vmw_kms_atomic_check_modeset, + .atomic_commit = drm_atomic_helper_commit, +}; + +static int vmw_kms_generic_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) +{ + return vmw_kms_sou_do_surface_dirty(dev_priv, vfb, NULL, clips, + &surface->res, destX, destY, + num_clips, 1, NULL, NULL); +} + + +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 ret; + + switch (dev_priv->active_display_unit) { + case vmw_du_screen_target: + ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, clips, + &surface->res, destX, destY, + num_clips, 1, NULL, NULL); + break; + case vmw_du_screen_object: + ret = vmw_kms_generic_present(dev_priv, file_priv, vfb, surface, + sid, destX, destY, clips, + num_clips); + break; + default: + WARN_ONCE(true, + "Present called with invalid display system.\n"); + ret = -ENOSYS; + break; + } + if (ret) + return ret; + + vmw_cmd_flush(dev_priv, false); + + return 0; +} + +static void +vmw_kms_create_hotplug_mode_update_property(struct vmw_private *dev_priv) +{ + if (dev_priv->hotplug_mode_update_property) + return; + + dev_priv->hotplug_mode_update_property = + drm_property_create_range(&dev_priv->drm, + DRM_MODE_PROP_IMMUTABLE, + "hotplug_mode_update", 0, 1); +} + +int vmw_kms_init(struct vmw_private *dev_priv) +{ + struct drm_device *dev = &dev_priv->drm; + int ret; + static const char *display_unit_names[] = { + "Invalid", + "Legacy", + "Screen Object", + "Screen Target", + "Invalid (max)" + }; + + drm_mode_config_init(dev); + dev->mode_config.funcs = &vmw_kms_funcs; + dev->mode_config.min_width = 1; + dev->mode_config.min_height = 1; + dev->mode_config.max_width = dev_priv->texture_max_width; + dev->mode_config.max_height = dev_priv->texture_max_height; + dev->mode_config.preferred_depth = dev_priv->assume_16bpp ? 16 : 32; + + drm_mode_create_suggested_offset_properties(dev); + vmw_kms_create_hotplug_mode_update_property(dev_priv); + + ret = vmw_kms_stdu_init_display(dev_priv); + if (ret) { + ret = vmw_kms_sou_init_display(dev_priv); + if (ret) /* Fallback */ + ret = vmw_kms_ldu_init_display(dev_priv); + } + BUILD_BUG_ON(ARRAY_SIZE(display_unit_names) != (vmw_du_max + 1)); + drm_info(&dev_priv->drm, "%s display unit initialized\n", + display_unit_names[dev_priv->active_display_unit]); + + return ret; +} + +int vmw_kms_close(struct vmw_private *dev_priv) +{ + int ret = 0; + + /* + * Docs says we should take the lock before calling this function + * but since it destroys encoders and our destructor calls + * drm_encoder_cleanup which takes the lock we deadlock. + */ + drm_mode_config_cleanup(&dev_priv->drm); + if (dev_priv->active_display_unit == vmw_du_legacy) + ret = vmw_kms_ldu_close_display(dev_priv); + + return ret; +} + +int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_cursor_bypass_arg *arg = data; + struct vmw_display_unit *du; + struct drm_crtc *crtc; + int ret = 0; + + mutex_lock(&dev->mode_config.mutex); + if (arg->flags & DRM_VMW_CURSOR_BYPASS_ALL) { + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + du = vmw_crtc_to_du(crtc); + du->hotspot_x = arg->xhot; + du->hotspot_y = arg->yhot; + } + + mutex_unlock(&dev->mode_config.mutex); + return 0; + } + + crtc = drm_crtc_find(dev, file_priv, arg->crtc_id); + if (!crtc) { + ret = -ENOENT; + goto out; + } + + du = vmw_crtc_to_du(crtc); + + du->hotspot_x = arg->xhot; + du->hotspot_y = arg->yhot; + +out: + mutex_unlock(&dev->mode_config.mutex); + + return ret; +} + +int vmw_kms_write_svga(struct vmw_private *vmw_priv, + unsigned width, unsigned height, unsigned pitch, + unsigned bpp, unsigned depth) +{ + if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK) + vmw_write(vmw_priv, SVGA_REG_PITCHLOCK, pitch); + else if (vmw_fifo_have_pitchlock(vmw_priv)) + vmw_fifo_mem_write(vmw_priv, SVGA_FIFO_PITCHLOCK, pitch); + vmw_write(vmw_priv, SVGA_REG_WIDTH, width); + vmw_write(vmw_priv, SVGA_REG_HEIGHT, height); + if ((vmw_priv->capabilities & SVGA_CAP_8BIT_EMULATION) != 0) + 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; +} + +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->active_display_unit == vmw_du_screen_target) ? + dev_priv->max_primary_mem : dev_priv->vram_size); +} + +/** + * vmw_du_update_layout - Update the display unit with topology from resolution + * plugin and generate DRM uevent + * @dev_priv: device private + * @num_rects: number of drm_rect in rects + * @rects: toplogy to update + */ +static int vmw_du_update_layout(struct vmw_private *dev_priv, + unsigned int num_rects, struct drm_rect *rects) +{ + struct drm_device *dev = &dev_priv->drm; + struct vmw_display_unit *du; + struct drm_connector *con; + struct drm_connector_list_iter conn_iter; + struct drm_modeset_acquire_ctx ctx; + struct drm_crtc *crtc; + int ret; + + /* Currently gui_x/y is protected with the crtc mutex */ + mutex_lock(&dev->mode_config.mutex); + drm_modeset_acquire_init(&ctx, 0); +retry: + drm_for_each_crtc(crtc, dev) { + ret = drm_modeset_lock(&crtc->mutex, &ctx); + if (ret < 0) { + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + goto out_fini; + } + } + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(con, &conn_iter) { + du = vmw_connector_to_du(con); + if (num_rects > du->unit) { + du->pref_width = drm_rect_width(&rects[du->unit]); + du->pref_height = drm_rect_height(&rects[du->unit]); + du->pref_active = true; + du->gui_x = rects[du->unit].x1; + du->gui_y = rects[du->unit].y1; + } else { + du->pref_width = VMWGFX_MIN_INITIAL_WIDTH; + du->pref_height = VMWGFX_MIN_INITIAL_HEIGHT; + du->pref_active = false; + du->gui_x = 0; + du->gui_y = 0; + } + } + drm_connector_list_iter_end(&conn_iter); + + list_for_each_entry(con, &dev->mode_config.connector_list, head) { + du = vmw_connector_to_du(con); + if (num_rects > du->unit) { + drm_object_property_set_value + (&con->base, dev->mode_config.suggested_x_property, + du->gui_x); + drm_object_property_set_value + (&con->base, dev->mode_config.suggested_y_property, + du->gui_y); + } else { + drm_object_property_set_value + (&con->base, dev->mode_config.suggested_x_property, + 0); + drm_object_property_set_value + (&con->base, dev->mode_config.suggested_y_property, + 0); + } + con->status = vmw_du_connector_detect(con, true); + } +out_fini: + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + mutex_unlock(&dev->mode_config.mutex); + + drm_sysfs_hotplug_event(dev); + + return 0; +} + +int vmw_du_crtc_gamma_set(struct drm_crtc *crtc, + u16 *r, u16 *g, u16 *b, + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) +{ + 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); + } + + return 0; +} + +int vmw_du_connector_dpms(struct drm_connector *connector, int mode) +{ + return 0; +} + +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); + + num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS); + + 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) }, + /* 1280x720@60Hz */ + { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74500, 1280, 1344, + 1472, 1664, 0, 720, 723, 728, 748, 0, + DRM_MODE_FLAG_NHSYNC | 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) }, + /* 1920x1080@60Hz */ + { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 173000, 1920, 2048, + 2248, 2576, 0, 1080, 1083, 1088, 1120, 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) }, + /* 2560x1440@60Hz */ + { DRM_MODE("2560x1440", DRM_MODE_TYPE_DRIVER, 241500, 2560, 2608, + 2640, 2720, 0, 1440, 1443, 1448, 1481, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 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) }, + /* 2880x1800@60Hz */ + { DRM_MODE("2880x1800", DRM_MODE_TYPE_DRIVER, 337500, 2880, 2928, + 2960, 3040, 0, 1800, 1803, 1809, 1852, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 3840x2160@60Hz */ + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 533000, 3840, 3888, + 3920, 4000, 0, 2160, 2163, 2168, 2222, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 3840x2400@60Hz */ + { DRM_MODE("3840x2400", DRM_MODE_TYPE_DRIVER, 592250, 3840, 3888, + 3920, 4000, 0, 2400, 2403, 2409, 2469, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 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. + */ +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; +} + + +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; + u32 assumed_bpp = 4; + + if (dev_priv->assume_16bpp) + assumed_bpp = 2; + + max_width = min(max_width, dev_priv->texture_max_width); + max_height = min(max_height, dev_priv->texture_max_height); + + /* + * For STDU extra limit for a mode on SVGA_REG_SCREENTARGET_MAX_WIDTH/ + * HEIGHT registers. + */ + if (dev_priv->active_display_unit == vmw_du_screen_target) { + max_width = min(max_width, dev_priv->stdu_max_width); + max_height = min(max_height, dev_priv->stdu_max_height); + } + + /* 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); + drm_mode_set_name(mode); + + if (vmw_kms_validate_mode_vram(dev_priv, + mode->hdisplay * assumed_bpp, + 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 * assumed_bpp, + bmode->vdisplay)) + continue; + + mode = drm_mode_duplicate(dev, bmode); + if (!mode) + return 0; + + drm_mode_probed_add(connector, mode); + } + + drm_connector_list_update(connector); + /* Move the prefered mode first, help apps pick the right mode. */ + drm_mode_sort(&connector->modes); + + return 1; +} + +/** + * vmw_kms_update_layout_ioctl - Handler for DRM_VMW_UPDATE_LAYOUT ioctl + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Update preferred topology of display unit as per ioctl request. The topology + * is expressed as array of drm_vmw_rect. + * e.g. + * [0 0 640 480] [640 0 800 600] [0 480 640 480] + * + * NOTE: + * The x and y offset (upper left) in drm_vmw_rect cannot be less than 0. Beside + * device limit on topology, x + w and y + h (lower right) cannot be greater + * than INT_MAX. So topology beyond these limits will return with error. + * + * Returns: + * Zero on success, negative errno on failure. + */ +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_mode_config *mode_config = &dev->mode_config; + struct drm_vmw_update_layout_arg *arg = + (struct drm_vmw_update_layout_arg *)data; + void __user *user_rects; + struct drm_vmw_rect *rects; + struct drm_rect *drm_rects; + unsigned rects_size; + int ret, i; + + if (!arg->num_outputs) { + struct drm_rect def_rect = {0, 0, + VMWGFX_MIN_INITIAL_WIDTH, + VMWGFX_MIN_INITIAL_HEIGHT}; + vmw_du_update_layout(dev_priv, 1, &def_rect); + return 0; + } + + rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect); + rects = kcalloc(arg->num_outputs, sizeof(struct drm_vmw_rect), + GFP_KERNEL); + if (unlikely(!rects)) + return -ENOMEM; + + user_rects = (void __user *)(unsigned long)arg->rects; + ret = copy_from_user(rects, user_rects, rects_size); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to get rects.\n"); + ret = -EFAULT; + goto out_free; + } + + drm_rects = (struct drm_rect *)rects; + + VMW_DEBUG_KMS("Layout count = %u\n", arg->num_outputs); + for (i = 0; i < arg->num_outputs; i++) { + struct drm_vmw_rect curr_rect; + + /* Verify user-space for overflow as kernel use drm_rect */ + if ((rects[i].x + rects[i].w > INT_MAX) || + (rects[i].y + rects[i].h > INT_MAX)) { + ret = -ERANGE; + goto out_free; + } + + curr_rect = rects[i]; + drm_rects[i].x1 = curr_rect.x; + drm_rects[i].y1 = curr_rect.y; + drm_rects[i].x2 = curr_rect.x + curr_rect.w; + drm_rects[i].y2 = curr_rect.y + curr_rect.h; + + VMW_DEBUG_KMS(" x1 = %d y1 = %d x2 = %d y2 = %d\n", + drm_rects[i].x1, drm_rects[i].y1, + drm_rects[i].x2, drm_rects[i].y2); + + /* + * Currently this check is limiting the topology within + * mode_config->max (which actually is max texture size + * supported by virtual device). This limit is here to address + * window managers that create a big framebuffer for whole + * topology. + */ + if (drm_rects[i].x1 < 0 || drm_rects[i].y1 < 0 || + drm_rects[i].x2 > mode_config->max_width || + drm_rects[i].y2 > mode_config->max_height) { + VMW_DEBUG_KMS("Invalid layout %d %d %d %d\n", + drm_rects[i].x1, drm_rects[i].y1, + drm_rects[i].x2, drm_rects[i].y2); + ret = -EINVAL; + goto out_free; + } + } + + ret = vmw_kms_check_display_memory(dev, arg->num_outputs, drm_rects); + + if (ret == 0) + vmw_du_update_layout(dev_priv, arg->num_outputs, drm_rects); + +out_free: + kfree(rects); + return ret; +} + +/** + * vmw_kms_helper_dirty - Helper to build commands and perform actions based + * on a set of cliprects and a set of display units. + * + * @dev_priv: Pointer to a device private structure. + * @framebuffer: Pointer to the framebuffer on which to perform the actions. + * @clips: A set of struct drm_clip_rect. Either this os @vclips must be NULL. + * Cliprects are given in framebuffer coordinates. + * @vclips: A set of struct drm_vmw_rect cliprects. Either this or @clips must + * be NULL. Cliprects are given in source coordinates. + * @dest_x: X coordinate offset for the crtc / destination clip rects. + * @dest_y: Y coordinate offset for the crtc / destination clip rects. + * @num_clips: Number of cliprects in the @clips or @vclips array. + * @increment: Integer with which to increment the clip counter when looping. + * Used to skip a predetermined number of clip rects. + * @dirty: Closure structure. See the description of struct vmw_kms_dirty. + */ +int vmw_kms_helper_dirty(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + const struct drm_clip_rect *clips, + const struct drm_vmw_rect *vclips, + s32 dest_x, s32 dest_y, + int num_clips, + int increment, + struct vmw_kms_dirty *dirty) +{ + struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; + struct drm_crtc *crtc; + u32 num_units = 0; + u32 i, k; + + dirty->dev_priv = dev_priv; + + /* If crtc is passed, no need to iterate over other display units */ + if (dirty->crtc) { + units[num_units++] = vmw_crtc_to_du(dirty->crtc); + } else { + list_for_each_entry(crtc, &dev_priv->drm.mode_config.crtc_list, + head) { + struct drm_plane *plane = crtc->primary; + + if (plane->state->fb == &framebuffer->base) + units[num_units++] = vmw_crtc_to_du(crtc); + } + } + + for (k = 0; k < num_units; k++) { + struct vmw_display_unit *unit = units[k]; + s32 crtc_x = unit->crtc.x; + s32 crtc_y = unit->crtc.y; + s32 crtc_width = unit->crtc.mode.hdisplay; + s32 crtc_height = unit->crtc.mode.vdisplay; + const struct drm_clip_rect *clips_ptr = clips; + const struct drm_vmw_rect *vclips_ptr = vclips; + + dirty->unit = unit; + if (dirty->fifo_reserve_size > 0) { + dirty->cmd = VMW_CMD_RESERVE(dev_priv, + dirty->fifo_reserve_size); + if (!dirty->cmd) + return -ENOMEM; + + memset(dirty->cmd, 0, dirty->fifo_reserve_size); + } + dirty->num_hits = 0; + for (i = 0; i < num_clips; i++, clips_ptr += increment, + vclips_ptr += increment) { + s32 clip_left; + s32 clip_top; + + /* + * Select clip array type. Note that integer type + * in @clips is unsigned short, whereas in @vclips + * it's 32-bit. + */ + if (clips) { + dirty->fb_x = (s32) clips_ptr->x1; + dirty->fb_y = (s32) clips_ptr->y1; + dirty->unit_x2 = (s32) clips_ptr->x2 + dest_x - + crtc_x; + dirty->unit_y2 = (s32) clips_ptr->y2 + dest_y - + crtc_y; + } else { + dirty->fb_x = vclips_ptr->x; + dirty->fb_y = vclips_ptr->y; + dirty->unit_x2 = dirty->fb_x + vclips_ptr->w + + dest_x - crtc_x; + dirty->unit_y2 = dirty->fb_y + vclips_ptr->h + + dest_y - crtc_y; + } + + dirty->unit_x1 = dirty->fb_x + dest_x - crtc_x; + dirty->unit_y1 = dirty->fb_y + dest_y - crtc_y; + + /* Skip this clip if it's outside the crtc region */ + if (dirty->unit_x1 >= crtc_width || + dirty->unit_y1 >= crtc_height || + dirty->unit_x2 <= 0 || dirty->unit_y2 <= 0) + continue; + + /* Clip right and bottom to crtc limits */ + dirty->unit_x2 = min_t(s32, dirty->unit_x2, + crtc_width); + dirty->unit_y2 = min_t(s32, dirty->unit_y2, + crtc_height); + + /* Clip left and top to crtc limits */ + clip_left = min_t(s32, dirty->unit_x1, 0); + clip_top = min_t(s32, dirty->unit_y1, 0); + dirty->unit_x1 -= clip_left; + dirty->unit_y1 -= clip_top; + dirty->fb_x -= clip_left; + dirty->fb_y -= clip_top; + + dirty->clip(dirty); + } + + dirty->fifo_commit(dirty); + } + + return 0; +} + +/** + * vmw_kms_helper_validation_finish - Helper for post KMS command submission + * cleanup and fencing + * @dev_priv: Pointer to the device-private struct + * @file_priv: Pointer identifying the client when user-space fencing is used + * @ctx: Pointer to the validation context + * @out_fence: If non-NULL, returned refcounted fence-pointer + * @user_fence_rep: If non-NULL, pointer to user-space address area + * in which to copy user-space fence info + */ +void vmw_kms_helper_validation_finish(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_validation_context *ctx, + struct vmw_fence_obj **out_fence, + struct drm_vmw_fence_rep __user * + user_fence_rep) +{ + struct vmw_fence_obj *fence = NULL; + uint32_t handle = 0; + int ret = 0; + + if (file_priv || user_fence_rep || vmw_validation_has_bos(ctx) || + out_fence) + ret = vmw_execbuf_fence_commands(file_priv, dev_priv, &fence, + file_priv ? &handle : NULL); + vmw_validation_done(ctx, fence); + if (file_priv) + vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), + ret, user_fence_rep, fence, + handle, -1); + if (out_fence) + *out_fence = fence; + else + vmw_fence_obj_unreference(&fence); +} + +/** + * vmw_kms_update_proxy - Helper function to update a proxy surface from + * its backing MOB. + * + * @res: Pointer to the surface resource + * @clips: Clip rects in framebuffer (surface) space. + * @num_clips: Number of clips in @clips. + * @increment: Integer with which to increment the clip counter when looping. + * Used to skip a predetermined number of clip rects. + * + * This function makes sure the proxy surface is updated from its backing MOB + * using the region given by @clips. The surface resource @res and its backing + * MOB needs to be reserved and validated on call. + */ +int vmw_kms_update_proxy(struct vmw_resource *res, + const struct drm_clip_rect *clips, + unsigned num_clips, + int increment) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct drm_vmw_size *size = &vmw_res_to_srf(res)->metadata.base_size; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdUpdateGBImage body; + } *cmd; + SVGA3dBox *box; + size_t copy_size = 0; + int i; + + if (!clips) + return 0; + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd) * num_clips); + if (!cmd) + return -ENOMEM; + + for (i = 0; i < num_clips; ++i, clips += increment, ++cmd) { + box = &cmd->body.box; + + cmd->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE; + cmd->header.size = sizeof(cmd->body); + cmd->body.image.sid = res->id; + cmd->body.image.face = 0; + cmd->body.image.mipmap = 0; + + if (clips->x1 > size->width || clips->x2 > size->width || + clips->y1 > size->height || clips->y2 > size->height) { + DRM_ERROR("Invalid clips outsize of framebuffer.\n"); + return -EINVAL; + } + + box->x = clips->x1; + box->y = clips->y1; + box->z = 0; + box->w = clips->x2 - clips->x1; + box->h = clips->y2 - clips->y1; + box->d = 1; + + copy_size += sizeof(*cmd); + } + + vmw_cmd_commit(dev_priv, copy_size); + + return 0; +} + +/** + * vmw_kms_create_implicit_placement_property - Set up the implicit placement + * property. + * + * @dev_priv: Pointer to a device private struct. + * + * Sets up the implicit placement property unless it's already set up. + */ +void +vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv) +{ + if (dev_priv->implicit_placement_property) + return; + + dev_priv->implicit_placement_property = + drm_property_create_range(&dev_priv->drm, + DRM_MODE_PROP_IMMUTABLE, + "implicit_placement", 0, 1); +} + +/** + * vmw_kms_suspend - Save modesetting state and turn modesetting off. + * + * @dev: Pointer to the drm device + * Return: 0 on success. Negative error code on failure. + */ +int vmw_kms_suspend(struct drm_device *dev) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + + dev_priv->suspend_state = drm_atomic_helper_suspend(dev); + if (IS_ERR(dev_priv->suspend_state)) { + int ret = PTR_ERR(dev_priv->suspend_state); + + DRM_ERROR("Failed kms suspend: %d\n", ret); + dev_priv->suspend_state = NULL; + + return ret; + } + + return 0; +} + + +/** + * vmw_kms_resume - Re-enable modesetting and restore state + * + * @dev: Pointer to the drm device + * Return: 0 on success. Negative error code on failure. + * + * State is resumed from a previous vmw_kms_suspend(). It's illegal + * to call this function without a previous vmw_kms_suspend(). + */ +int vmw_kms_resume(struct drm_device *dev) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + int ret; + + if (WARN_ON(!dev_priv->suspend_state)) + return 0; + + ret = drm_atomic_helper_resume(dev, dev_priv->suspend_state); + dev_priv->suspend_state = NULL; + + return ret; +} + +/** + * vmw_kms_lost_device - Notify kms that modesetting capabilities will be lost + * + * @dev: Pointer to the drm device + */ +void vmw_kms_lost_device(struct drm_device *dev) +{ + drm_atomic_helper_shutdown(dev); +} + +/** + * vmw_du_helper_plane_update - Helper to do plane update on a display unit. + * @update: The closure structure. + * + * Call this helper after setting callbacks in &vmw_du_update_plane to do plane + * update on display unit. + * + * Return: 0 on success or a negative error code on failure. + */ +int vmw_du_helper_plane_update(struct vmw_du_update_plane *update) +{ + struct drm_plane_state *state = update->plane->state; + struct drm_plane_state *old_state = update->old_state; + struct drm_atomic_helper_damage_iter iter; + struct drm_rect clip; + struct drm_rect bb; + DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); + uint32_t reserved_size = 0; + uint32_t submit_size = 0; + uint32_t curr_size = 0; + uint32_t num_hits = 0; + void *cmd_start; + char *cmd_next; + int ret; + + /* + * Iterate in advance to check if really need plane update and find the + * number of clips that actually are in plane src for fifo allocation. + */ + drm_atomic_helper_damage_iter_init(&iter, old_state, state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + if (num_hits == 0) + return 0; + + if (update->vfb->bo) { + struct vmw_framebuffer_bo *vfbbo = + container_of(update->vfb, typeof(*vfbbo), base); + + /* + * For screen targets we want a mappable bo, for everything else we want + * accelerated i.e. host backed (vram or gmr) bo. If the display unit + * is not screen target then mob's shouldn't be available. + */ + if (update->dev_priv->active_display_unit == vmw_du_screen_target) { + vmw_bo_placement_set(vfbbo->buffer, + VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_GMR, + VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_GMR); + } else { + WARN_ON(update->dev_priv->has_mob); + vmw_bo_placement_set_default_accelerated(vfbbo->buffer); + } + ret = vmw_validation_add_bo(&val_ctx, vfbbo->buffer); + } else { + struct vmw_framebuffer_surface *vfbs = + container_of(update->vfb, typeof(*vfbs), base); + + ret = vmw_validation_add_resource(&val_ctx, &vfbs->surface->res, + 0, VMW_RES_DIRTY_NONE, NULL, + NULL); + } + + if (ret) + return ret; + + ret = vmw_validation_prepare(&val_ctx, update->mutex, update->intr); + if (ret) + goto out_unref; + + reserved_size = update->calc_fifo_size(update, num_hits); + cmd_start = VMW_CMD_RESERVE(update->dev_priv, reserved_size); + if (!cmd_start) { + ret = -ENOMEM; + goto out_revert; + } + + cmd_next = cmd_start; + + if (update->post_prepare) { + curr_size = update->post_prepare(update, cmd_next); + cmd_next += curr_size; + submit_size += curr_size; + } + + if (update->pre_clip) { + curr_size = update->pre_clip(update, cmd_next, num_hits); + cmd_next += curr_size; + submit_size += curr_size; + } + + bb.x1 = INT_MAX; + bb.y1 = INT_MAX; + bb.x2 = INT_MIN; + bb.y2 = INT_MIN; + + drm_atomic_helper_damage_iter_init(&iter, old_state, state); + drm_atomic_for_each_plane_damage(&iter, &clip) { + uint32_t fb_x = clip.x1; + uint32_t fb_y = clip.y1; + + vmw_du_translate_to_crtc(state, &clip); + if (update->clip) { + curr_size = update->clip(update, cmd_next, &clip, fb_x, + fb_y); + cmd_next += curr_size; + submit_size += curr_size; + } + bb.x1 = min_t(int, bb.x1, clip.x1); + bb.y1 = min_t(int, bb.y1, clip.y1); + bb.x2 = max_t(int, bb.x2, clip.x2); + bb.y2 = max_t(int, bb.y2, clip.y2); + } + + curr_size = update->post_clip(update, cmd_next, &bb); + submit_size += curr_size; + + if (reserved_size < submit_size) + submit_size = 0; + + vmw_cmd_commit(update->dev_priv, submit_size); + + vmw_kms_helper_validation_finish(update->dev_priv, NULL, &val_ctx, + update->out_fence, NULL); + return ret; + +out_revert: + vmw_validation_revert(&val_ctx); + +out_unref: + vmw_validation_unref_lists(&val_ctx); + return ret; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h new file mode 100644 index 0000000000..db81e635dc --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -0,0 +1,585 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef VMWGFX_KMS_H_ +#define VMWGFX_KMS_H_ + +#include <drm/drm_encoder.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_probe_helper.h> + +#include "vmwgfx_drv.h" + +/** + * struct vmw_du_update_plane - Closure structure for vmw_du_helper_plane_update + * @plane: Plane which is being updated. + * @old_state: Old state of plane. + * @dev_priv: Device private. + * @du: Display unit on which to update the plane. + * @vfb: Framebuffer which is blitted to display unit. + * @out_fence: Out fence for resource finish. + * @mutex: The mutex used to protect resource reservation. + * @cpu_blit: True if need cpu blit. + * @intr: Whether to perform waits interruptible if possible. + * + * This structure loosely represent the set of operations needed to perform a + * plane update on a display unit. Implementer will define that functionality + * according to the function callbacks for this structure. In brief it involves + * surface/buffer object validation, populate FIFO commands and command + * submission to the device. + */ +struct vmw_du_update_plane { + /** + * @calc_fifo_size: Calculate fifo size. + * + * Determine fifo size for the commands needed for update. The number of + * damage clips on display unit @num_hits will be passed to allocate + * sufficient fifo space. + * + * Return: Fifo size needed + */ + uint32_t (*calc_fifo_size)(struct vmw_du_update_plane *update, + uint32_t num_hits); + + /** + * @post_prepare: Populate fifo for resource preparation. + * + * Some surface resource or buffer object need some extra cmd submission + * like update GB image for proxy surface and define a GMRFB for screen + * object. That should be done here as this callback will be + * called after FIFO allocation with the address of command buufer. + * + * This callback is optional. + * + * Return: Size of commands populated to command buffer. + */ + uint32_t (*post_prepare)(struct vmw_du_update_plane *update, void *cmd); + + /** + * @pre_clip: Populate fifo before clip. + * + * This is where pre clip related command should be populated like + * surface copy/DMA, etc. + * + * This callback is optional. + * + * Return: Size of commands populated to command buffer. + */ + uint32_t (*pre_clip)(struct vmw_du_update_plane *update, void *cmd, + uint32_t num_hits); + + /** + * @clip: Populate fifo for clip. + * + * This is where to populate clips for surface copy/dma or blit commands + * if needed. This will be called times have damage in display unit, + * which is one if doing full update. @clip is the damage in destination + * coordinates which is crtc/DU and @src_x, @src_y is damage clip src in + * framebuffer coordinate. + * + * This callback is optional. + * + * Return: Size of commands populated to command buffer. + */ + uint32_t (*clip)(struct vmw_du_update_plane *update, void *cmd, + struct drm_rect *clip, uint32_t src_x, uint32_t src_y); + + /** + * @post_clip: Populate fifo after clip. + * + * This is where to populate display unit update commands or blit + * commands. + * + * Return: Size of commands populated to command buffer. + */ + uint32_t (*post_clip)(struct vmw_du_update_plane *update, void *cmd, + struct drm_rect *bb); + + struct drm_plane *plane; + struct drm_plane_state *old_state; + struct vmw_private *dev_priv; + struct vmw_display_unit *du; + struct vmw_framebuffer *vfb; + struct vmw_fence_obj **out_fence; + struct mutex *mutex; + bool intr; +}; + +/** + * struct vmw_du_update_plane_surface - closure structure for surface + * @base: base closure structure. + * @cmd_start: FIFO command start address (used by SOU only). + */ +struct vmw_du_update_plane_surface { + struct vmw_du_update_plane base; + /* This member is to handle special case SOU surface update */ + void *cmd_start; +}; + +/** + * struct vmw_du_update_plane_buffer - Closure structure for buffer object + * @base: Base closure structure. + * @fb_left: x1 for fb damage bounding box. + * @fb_top: y1 for fb damage bounding box. + */ +struct vmw_du_update_plane_buffer { + struct vmw_du_update_plane base; + int fb_left, fb_top; +}; + +/** + * struct vmw_kms_dirty - closure structure for the vmw_kms_helper_dirty + * function. + * + * @fifo_commit: Callback that is called once for each display unit after + * all clip rects. This function must commit the fifo space reserved by the + * helper. Set up by the caller. + * @clip: Callback that is called for each cliprect on each display unit. + * Set up by the caller. + * @fifo_reserve_size: Fifo size that the helper should try to allocat for + * each display unit. Set up by the caller. + * @dev_priv: Pointer to the device private. Set up by the helper. + * @unit: The current display unit. Set up by the helper before a call to @clip. + * @cmd: The allocated fifo space. Set up by the helper before the first @clip + * call. + * @crtc: The crtc for which to build dirty commands. + * @num_hits: Number of clip rect commands for this display unit. + * Cleared by the helper before the first @clip call. Updated by the @clip + * callback. + * @fb_x: Clip rect left side in framebuffer coordinates. + * @fb_y: Clip rect right side in framebuffer coordinates. + * @unit_x1: Clip rect left side in crtc coordinates. + * @unit_y1: Clip rect top side in crtc coordinates. + * @unit_x2: Clip rect right side in crtc coordinates. + * @unit_y2: Clip rect bottom side in crtc coordinates. + * + * The clip rect coordinates are updated by the helper for each @clip call. + * Note that this may be derived from if more info needs to be passed between + * helper caller and helper callbacks. + */ +struct vmw_kms_dirty { + void (*fifo_commit)(struct vmw_kms_dirty *); + void (*clip)(struct vmw_kms_dirty *); + size_t fifo_reserve_size; + struct vmw_private *dev_priv; + struct vmw_display_unit *unit; + void *cmd; + struct drm_crtc *crtc; + u32 num_hits; + s32 fb_x; + s32 fb_y; + s32 unit_x1; + s32 unit_y1; + s32 unit_x2; + s32 unit_y2; +}; + +#define VMWGFX_NUM_DISPLAY_UNITS 8 + + +#define vmw_framebuffer_to_vfb(x) \ + container_of(x, struct vmw_framebuffer, base) +#define vmw_framebuffer_to_vfbs(x) \ + container_of(x, struct vmw_framebuffer_surface, base.base) +#define vmw_framebuffer_to_vfbd(x) \ + container_of(x, struct vmw_framebuffer_bo, base.base) + +/** + * Base class for framebuffers + * + * @pin is called the when ever a crtc uses this framebuffer + * @unpin is called + */ +struct vmw_framebuffer { + struct drm_framebuffer base; + bool bo; + uint32_t user_handle; +}; + +/* + * Clip rectangle + */ +struct vmw_clip_rect { + int x1, x2, y1, y2; +}; + +struct vmw_framebuffer_surface { + struct vmw_framebuffer base; + struct vmw_surface *surface; + struct vmw_bo *buffer; + struct list_head head; + bool is_bo_proxy; /* true if this is proxy surface for DMA buf */ +}; + + +struct vmw_framebuffer_bo { + struct vmw_framebuffer base; + struct vmw_bo *buffer; +}; + + +static const uint32_t __maybe_unused vmw_primary_plane_formats[] = { + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, +}; + +static const uint32_t __maybe_unused vmw_cursor_plane_formats[] = { + DRM_FORMAT_ARGB8888, +}; + + +#define vmw_crtc_state_to_vcs(x) container_of(x, struct vmw_crtc_state, base) +#define vmw_plane_state_to_vps(x) container_of(x, struct vmw_plane_state, base) +#define vmw_connector_state_to_vcs(x) \ + container_of(x, struct vmw_connector_state, base) +#define vmw_plane_to_vcp(x) container_of(x, struct vmw_cursor_plane, base) + +/** + * Derived class for crtc state object + * + * @base DRM crtc object + */ +struct vmw_crtc_state { + struct drm_crtc_state base; +}; + +struct vmw_cursor_plane_state { + struct vmw_bo *bo; + s32 hotspot_x; + s32 hotspot_y; +}; + +/** + * Derived class for plane state object + * + * @base DRM plane object + * @surf Display surface for STDU + * @bo display bo for SOU + * @content_fb_type Used by STDU. + * @bo_size Size of the bo, used by Screen Object Display Unit + * @pinned pin count for STDU display surface + */ +struct vmw_plane_state { + struct drm_plane_state base; + struct vmw_surface *surf; + struct vmw_bo *bo; + + int content_fb_type; + unsigned long bo_size; + + int pinned; + + /* For CPU Blit */ + unsigned int cpp; + + bool surf_mapped; + struct vmw_cursor_plane_state cursor; +}; + + +/** + * Derived class for connector state object + * + * @base DRM connector object + * @is_implicit connector property + * + */ +struct vmw_connector_state { + struct drm_connector_state base; + + /** + * @gui_x: + * + * vmwgfx connector property representing the x position of this display + * unit (connector is synonymous to display unit) in overall topology. + * This is what the device expect as xRoot while creating screen. + */ + int gui_x; + + /** + * @gui_y: + * + * vmwgfx connector property representing the y position of this display + * unit (connector is synonymous to display unit) in overall topology. + * This is what the device expect as yRoot while creating screen. + */ + int gui_y; +}; + +/** + * Derived class for cursor plane object + * + * @base DRM plane object + * @cursor.cursor_mobs Cursor mobs available for re-use + */ +struct vmw_cursor_plane { + struct drm_plane base; + + struct vmw_bo *cursor_mobs[3]; +}; + +/** + * Base class display unit. + * + * Since the SVGA hw doesn't have a concept of a crtc, encoder or connector + * so the display unit is all of them at the same time. This is true for both + * legacy multimon and screen objects. + */ +struct vmw_display_unit { + struct drm_crtc crtc; + struct drm_encoder encoder; + struct drm_connector connector; + struct drm_plane primary; + struct vmw_cursor_plane cursor; + + struct vmw_surface *cursor_surface; + struct vmw_bo *cursor_bo; + size_t cursor_age; + + int cursor_x; + int cursor_y; + + int hotspot_x; + int hotspot_y; + s32 core_hotspot_x; + s32 core_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; + int set_gui_x; + int set_gui_y; +}; + +struct vmw_validation_ctx { + struct vmw_resource *res; + struct vmw_bo *buf; +}; + +#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_du_cleanup(struct vmw_display_unit *du); +void vmw_du_crtc_save(struct drm_crtc *crtc); +void vmw_du_crtc_restore(struct drm_crtc *crtc); +int vmw_du_crtc_gamma_set(struct drm_crtc *crtc, + u16 *r, u16 *g, u16 *b, + uint32_t size, + struct drm_modeset_acquire_ctx *ctx); +int vmw_du_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val); +int vmw_du_connector_atomic_set_property(struct drm_connector *connector, + struct drm_connector_state *state, + struct drm_property *property, + uint64_t val); +int +vmw_du_connector_atomic_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, + uint64_t *val); +int 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_kms_helper_dirty(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + const struct drm_clip_rect *clips, + const struct drm_vmw_rect *vclips, + s32 dest_x, s32 dest_y, + int num_clips, + int increment, + struct vmw_kms_dirty *dirty); + +void vmw_kms_helper_validation_finish(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_validation_context *ctx, + struct vmw_fence_obj **out_fence, + struct drm_vmw_fence_rep __user * + user_fence_rep); +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 *vclips, + uint32_t num_clips); +struct vmw_framebuffer * +vmw_kms_new_framebuffer(struct vmw_private *dev_priv, + struct vmw_bo *bo, + struct vmw_surface *surface, + bool only_2d, + const struct drm_mode_fb_cmd2 *mode_cmd); +void vmw_guess_mode_timing(struct drm_display_mode *mode); +void vmw_kms_update_implicit_fb(struct vmw_private *dev_priv); +void vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv); + +/* Universal Plane Helpers */ +void vmw_du_primary_plane_destroy(struct drm_plane *plane); +void vmw_du_cursor_plane_destroy(struct drm_plane *plane); + +/* Atomic Helpers */ +int vmw_du_primary_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state); +int vmw_du_cursor_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state); +void vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state); +int vmw_du_cursor_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state); +void vmw_du_cursor_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state); +void vmw_du_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state); +void vmw_du_plane_reset(struct drm_plane *plane); +struct drm_plane_state *vmw_du_plane_duplicate_state(struct drm_plane *plane); +void vmw_du_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state); +void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps, + bool unreference); + +int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state); +void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_atomic_state *state); +void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state); +void vmw_du_crtc_reset(struct drm_crtc *crtc); +struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc); +void vmw_du_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state); +void vmw_du_connector_reset(struct drm_connector *connector); +struct drm_connector_state * +vmw_du_connector_duplicate_state(struct drm_connector *connector); + +void vmw_du_connector_destroy_state(struct drm_connector *connector, + struct drm_connector_state *state); + +/* + * Legacy display unit functions - vmwgfx_ldu.c + */ +int vmw_kms_ldu_init_display(struct vmw_private *dev_priv); +int vmw_kms_ldu_close_display(struct vmw_private *dev_priv); +int vmw_kms_update_proxy(struct vmw_resource *res, + const struct drm_clip_rect *clips, + unsigned num_clips, + int increment); + +/* + * Screen Objects display functions - vmwgfx_scrn.c + */ +int vmw_kms_sou_init_display(struct vmw_private *dev_priv); +int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + struct drm_clip_rect *clips, + struct drm_vmw_rect *vclips, + struct vmw_resource *srf, + s32 dest_x, + s32 dest_y, + unsigned num_clips, int inc, + struct vmw_fence_obj **out_fence, + struct drm_crtc *crtc); +int vmw_kms_sou_do_bo_dirty(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + struct drm_clip_rect *clips, + struct drm_vmw_rect *vclips, + unsigned int num_clips, int increment, + bool interruptible, + struct vmw_fence_obj **out_fence, + struct drm_crtc *crtc); +int vmw_kms_sou_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 *vclips, + uint32_t num_clips, + struct drm_crtc *crtc); + +/* + * Screen Target Display Unit functions - vmwgfx_stdu.c + */ +int vmw_kms_stdu_init_display(struct vmw_private *dev_priv); +int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + struct drm_clip_rect *clips, + struct drm_vmw_rect *vclips, + struct vmw_resource *srf, + s32 dest_x, + s32 dest_y, + unsigned num_clips, int inc, + struct vmw_fence_obj **out_fence, + struct drm_crtc *crtc); +int vmw_kms_stdu_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_clip_rect *clips, + struct drm_vmw_rect *vclips, + uint32_t num_clips, + int increment, + struct drm_crtc *crtc); + +int vmw_du_helper_plane_update(struct vmw_du_update_plane *update); + +/** + * vmw_du_translate_to_crtc - Translate a rect from framebuffer to crtc + * @state: Plane state. + * @r: Rectangle to translate. + */ +static inline void vmw_du_translate_to_crtc(struct drm_plane_state *state, + struct drm_rect *r) +{ + int translate_crtc_x = -((state->src_x >> 16) - state->crtc_x); + int translate_crtc_y = -((state->src_y >> 16) - state->crtc_y); + + drm_rect_translate(r, translate_crtc_x, translate_crtc_y); +} + +#endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c new file mode 100644 index 0000000000..a82fa97003 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -0,0 +1,639 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_kms.h" + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_fourcc.h> + + +#define vmw_crtc_to_ldu(x) \ + container_of(x, struct vmw_legacy_display_unit, base.crtc) +#define vmw_encoder_to_ldu(x) \ + container_of(x, struct vmw_legacy_display_unit, base.encoder) +#define vmw_connector_to_ldu(x) \ + container_of(x, struct vmw_legacy_display_unit, base.connector) + +struct vmw_legacy_display { + struct list_head active; + + unsigned num_active; + unsigned last_num_active; + + struct vmw_framebuffer *fb; +}; + +/* + * Display unit using the legacy register interface. + */ +struct vmw_legacy_display_unit { + struct vmw_display_unit base; + + struct list_head active; +}; + +static void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu) +{ + list_del_init(&ldu->active); + vmw_du_cleanup(&ldu->base); + kfree(ldu); +} + + +/* + * Legacy Display Unit CRTC functions + */ + +static void vmw_ldu_crtc_destroy(struct drm_crtc *crtc) +{ + vmw_ldu_destroy(vmw_crtc_to_ldu(crtc)); +} + +static int vmw_ldu_commit_list(struct vmw_private *dev_priv) +{ + struct vmw_legacy_display *lds = dev_priv->ldu_priv; + struct vmw_legacy_display_unit *entry; + struct drm_framebuffer *fb = NULL; + struct drm_crtc *crtc = NULL; + int i; + + /* If there is no display topology the host just assumes + * that the guest will set the same layout as the host. + */ + if (!(dev_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY)) { + int w = 0, h = 0; + list_for_each_entry(entry, &lds->active, active) { + crtc = &entry->base.crtc; + w = max(w, crtc->x + crtc->mode.hdisplay); + h = max(h, crtc->y + crtc->mode.vdisplay); + } + + if (crtc == NULL) + return 0; + fb = crtc->primary->state->fb; + + return vmw_kms_write_svga(dev_priv, w, h, fb->pitches[0], + fb->format->cpp[0] * 8, + fb->format->depth); + } + + if (!list_empty(&lds->active)) { + entry = list_entry(lds->active.next, typeof(*entry), active); + fb = entry->base.crtc.primary->state->fb; + + vmw_kms_write_svga(dev_priv, fb->width, fb->height, fb->pitches[0], + fb->format->cpp[0] * 8, fb->format->depth); + } + + /* Make sure we always show something. */ + vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, + lds->num_active ? lds->num_active : 1); + + i = 0; + list_for_each_entry(entry, &lds->active, active) { + crtc = &entry->base.crtc; + + vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i); + vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i); + vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, crtc->x); + vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, crtc->y); + vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, crtc->mode.hdisplay); + vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, crtc->mode.vdisplay); + + i++; + } + + BUG_ON(i != lds->num_active); + + lds->last_num_active = lds->num_active; + + return 0; +} + +/* + * Pin the buffer in a location suitable for access by the + * display system. + */ +static int vmw_ldu_fb_pin(struct vmw_framebuffer *vfb) +{ + struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); + struct vmw_bo *buf; + int ret; + + buf = vfb->bo ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer : + vmw_framebuffer_to_vfbs(&vfb->base)->surface->res.guest_memory_bo; + + if (!buf) + return 0; + WARN_ON(dev_priv->active_display_unit != vmw_du_legacy); + + if (dev_priv->active_display_unit == vmw_du_legacy) { + vmw_overlay_pause_all(dev_priv); + ret = vmw_bo_pin_in_start_of_vram(dev_priv, buf, false); + vmw_overlay_resume_all(dev_priv); + } else + ret = -EINVAL; + + return ret; +} + +static int vmw_ldu_fb_unpin(struct vmw_framebuffer *vfb) +{ + struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); + struct vmw_bo *buf; + + buf = vfb->bo ? vmw_framebuffer_to_vfbd(&vfb->base)->buffer : + vmw_framebuffer_to_vfbs(&vfb->base)->surface->res.guest_memory_bo; + + if (WARN_ON(!buf)) + return 0; + + return vmw_bo_unpin(dev_priv, buf, false); +} + +static int vmw_ldu_del_active(struct vmw_private *vmw_priv, + struct vmw_legacy_display_unit *ldu) +{ + struct vmw_legacy_display *ld = vmw_priv->ldu_priv; + if (list_empty(&ldu->active)) + return 0; + + /* Must init otherwise list_empty(&ldu->active) will not work. */ + list_del_init(&ldu->active); + if (--(ld->num_active) == 0) { + BUG_ON(!ld->fb); + WARN_ON(vmw_ldu_fb_unpin(ld->fb)); + ld->fb = NULL; + } + + return 0; +} + +static int vmw_ldu_add_active(struct vmw_private *vmw_priv, + struct vmw_legacy_display_unit *ldu, + struct vmw_framebuffer *vfb) +{ + struct vmw_legacy_display *ld = vmw_priv->ldu_priv; + struct vmw_legacy_display_unit *entry; + struct list_head *at; + + BUG_ON(!ld->num_active && ld->fb); + if (vfb != ld->fb) { + if (ld->fb) + WARN_ON(vmw_ldu_fb_unpin(ld->fb)); + vmw_svga_enable(vmw_priv); + WARN_ON(vmw_ldu_fb_pin(vfb)); + ld->fb = vfb; + } + + if (!list_empty(&ldu->active)) + return 0; + + at = &ld->active; + list_for_each_entry(entry, &ld->active, active) { + if (entry->base.unit > ldu->base.unit) + break; + + at = &entry->active; + } + + list_add(&ldu->active, at); + + ld->num_active++; + + return 0; +} + +/** + * vmw_ldu_crtc_mode_set_nofb - Enable svga + * + * @crtc: CRTC associated with the new screen + * + * For LDU, just enable the svga + */ +static void vmw_ldu_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ +} + +/** + * vmw_ldu_crtc_atomic_enable - Noop + * + * @crtc: CRTC associated with the new screen + * @state: Unused + * + * This is called after a mode set has been completed. Here's + * usually a good place to call vmw_ldu_add_active/vmw_ldu_del_active + * but since for LDU the display plane is closely tied to the + * CRTC, it makes more sense to do those at plane update time. + */ +static void vmw_ldu_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ +} + +/** + * vmw_ldu_crtc_atomic_disable - Turns off CRTC + * + * @crtc: CRTC to be turned off + * @state: Unused + */ +static void vmw_ldu_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ +} + +static const struct drm_crtc_funcs vmw_legacy_crtc_funcs = { + .gamma_set = vmw_du_crtc_gamma_set, + .destroy = vmw_ldu_crtc_destroy, + .reset = vmw_du_crtc_reset, + .atomic_duplicate_state = vmw_du_crtc_duplicate_state, + .atomic_destroy_state = vmw_du_crtc_destroy_state, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, +}; + + +/* + * Legacy Display Unit encoder functions + */ + +static void vmw_ldu_encoder_destroy(struct drm_encoder *encoder) +{ + vmw_ldu_destroy(vmw_encoder_to_ldu(encoder)); +} + +static const struct drm_encoder_funcs vmw_legacy_encoder_funcs = { + .destroy = vmw_ldu_encoder_destroy, +}; + +/* + * Legacy Display Unit connector functions + */ + +static void vmw_ldu_connector_destroy(struct drm_connector *connector) +{ + vmw_ldu_destroy(vmw_connector_to_ldu(connector)); +} + +static const struct drm_connector_funcs vmw_legacy_connector_funcs = { + .dpms = vmw_du_connector_dpms, + .detect = vmw_du_connector_detect, + .fill_modes = vmw_du_connector_fill_modes, + .destroy = vmw_ldu_connector_destroy, + .reset = vmw_du_connector_reset, + .atomic_duplicate_state = vmw_du_connector_duplicate_state, + .atomic_destroy_state = vmw_du_connector_destroy_state, +}; + +static const struct +drm_connector_helper_funcs vmw_ldu_connector_helper_funcs = { +}; + +static int vmw_kms_ldu_do_bo_dirty(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + unsigned int flags, unsigned int color, + struct drm_mode_rect *clips, + unsigned int num_clips); + +/* + * Legacy Display Plane Functions + */ + +static void +vmw_ldu_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, + plane); + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + plane); + struct vmw_private *dev_priv; + struct vmw_legacy_display_unit *ldu; + struct vmw_framebuffer *vfb; + struct drm_framebuffer *fb; + struct drm_crtc *crtc = new_state->crtc ?: old_state->crtc; + + ldu = vmw_crtc_to_ldu(crtc); + dev_priv = vmw_priv(plane->dev); + fb = new_state->fb; + + vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; + + if (vfb) + vmw_ldu_add_active(dev_priv, ldu, vfb); + else + vmw_ldu_del_active(dev_priv, ldu); + + vmw_ldu_commit_list(dev_priv); + + if (vfb && vmw_cmd_supported(dev_priv)) { + struct drm_mode_rect fb_rect = { + .x1 = 0, + .y1 = 0, + .x2 = vfb->base.width, + .y2 = vfb->base.height + }; + struct drm_mode_rect *damage_rects = drm_plane_get_damage_clips(new_state); + u32 rect_count = drm_plane_get_damage_clips_count(new_state); + int ret; + + if (!damage_rects) { + damage_rects = &fb_rect; + rect_count = 1; + } + + ret = vmw_kms_ldu_do_bo_dirty(dev_priv, vfb, 0, 0, damage_rects, rect_count); + + drm_WARN_ONCE(plane->dev, ret, + "vmw_kms_ldu_do_bo_dirty failed with: ret=%d\n", ret); + + vmw_cmd_flush(dev_priv, false); + } +} + +static const struct drm_plane_funcs vmw_ldu_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vmw_du_primary_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, +}; + +static const struct drm_plane_funcs vmw_ldu_cursor_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vmw_du_cursor_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, +}; + +/* + * Atomic Helpers + */ +static const struct +drm_plane_helper_funcs vmw_ldu_cursor_plane_helper_funcs = { + .atomic_check = vmw_du_cursor_plane_atomic_check, + .atomic_update = vmw_du_cursor_plane_atomic_update, + .prepare_fb = vmw_du_cursor_plane_prepare_fb, + .cleanup_fb = vmw_du_cursor_plane_cleanup_fb, +}; + +static const struct +drm_plane_helper_funcs vmw_ldu_primary_plane_helper_funcs = { + .atomic_check = vmw_du_primary_plane_atomic_check, + .atomic_update = vmw_ldu_primary_plane_atomic_update, +}; + +static const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = { + .mode_set_nofb = vmw_ldu_crtc_mode_set_nofb, + .atomic_check = vmw_du_crtc_atomic_check, + .atomic_begin = vmw_du_crtc_atomic_begin, + .atomic_flush = vmw_du_crtc_atomic_flush, + .atomic_enable = vmw_ldu_crtc_atomic_enable, + .atomic_disable = vmw_ldu_crtc_atomic_disable, +}; + + +static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) +{ + struct vmw_legacy_display_unit *ldu; + struct drm_device *dev = &dev_priv->drm; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_plane *primary; + struct vmw_cursor_plane *cursor; + struct drm_crtc *crtc; + int ret; + + ldu = kzalloc(sizeof(*ldu), GFP_KERNEL); + if (!ldu) + return -ENOMEM; + + ldu->base.unit = unit; + crtc = &ldu->base.crtc; + encoder = &ldu->base.encoder; + connector = &ldu->base.connector; + primary = &ldu->base.primary; + cursor = &ldu->base.cursor; + + INIT_LIST_HEAD(&ldu->active); + + 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; + + /* + * Remove this after enabling atomic because property values can + * only exist in a state object + */ + ldu->base.is_implicit = true; + + /* Initialize primary plane */ + ret = drm_universal_plane_init(dev, primary, + 0, &vmw_ldu_plane_funcs, + vmw_primary_plane_formats, + ARRAY_SIZE(vmw_primary_plane_formats), + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + DRM_ERROR("Failed to initialize primary plane"); + goto err_free; + } + + drm_plane_helper_add(primary, &vmw_ldu_primary_plane_helper_funcs); + + /* + * We're going to be using traces and software cursors + */ + if (vmw_cmd_supported(dev_priv)) { + /* Initialize cursor plane */ + ret = drm_universal_plane_init(dev, &cursor->base, + 0, &vmw_ldu_cursor_funcs, + vmw_cursor_plane_formats, + ARRAY_SIZE(vmw_cursor_plane_formats), + NULL, DRM_PLANE_TYPE_CURSOR, NULL); + if (ret) { + DRM_ERROR("Failed to initialize cursor plane"); + drm_plane_cleanup(&ldu->base.primary); + goto err_free; + } + + drm_plane_helper_add(&cursor->base, &vmw_ldu_cursor_plane_helper_funcs); + } + + ret = drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + goto err_free; + } + + drm_connector_helper_add(connector, &vmw_ldu_connector_helper_funcs); + connector->status = vmw_du_connector_detect(connector, true); + + ret = drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) { + DRM_ERROR("Failed to initialize encoder\n"); + goto err_free_connector; + } + + (void) drm_connector_attach_encoder(connector, encoder); + encoder->possible_crtcs = (1 << unit); + encoder->possible_clones = 0; + + ret = drm_connector_register(connector); + if (ret) { + DRM_ERROR("Failed to register connector\n"); + goto err_free_encoder; + } + + ret = drm_crtc_init_with_planes(dev, crtc, primary, + vmw_cmd_supported(dev_priv) ? &cursor->base : NULL, + &vmw_legacy_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("Failed to initialize CRTC\n"); + goto err_free_unregister; + } + + drm_crtc_helper_add(crtc, &vmw_ldu_crtc_helper_funcs); + + drm_mode_crtc_set_gamma_size(crtc, 256); + + drm_object_attach_property(&connector->base, + dev_priv->hotplug_mode_update_property, 1); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_x_property, 0); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_y_property, 0); + if (dev_priv->implicit_placement_property) + drm_object_attach_property + (&connector->base, + dev_priv->implicit_placement_property, + 1); + + return 0; + +err_free_unregister: + drm_connector_unregister(connector); +err_free_encoder: + drm_encoder_cleanup(encoder); +err_free_connector: + drm_connector_cleanup(connector); +err_free: + kfree(ldu); + return ret; +} + +int vmw_kms_ldu_init_display(struct vmw_private *dev_priv) +{ + struct drm_device *dev = &dev_priv->drm; + int i, ret; + int num_display_units = (dev_priv->capabilities & SVGA_CAP_MULTIMON) ? + VMWGFX_NUM_DISPLAY_UNITS : 1; + + if (unlikely(dev_priv->ldu_priv)) { + return -EINVAL; + } + + dev_priv->ldu_priv = kmalloc(sizeof(*dev_priv->ldu_priv), GFP_KERNEL); + if (!dev_priv->ldu_priv) + return -ENOMEM; + + INIT_LIST_HEAD(&dev_priv->ldu_priv->active); + dev_priv->ldu_priv->num_active = 0; + dev_priv->ldu_priv->last_num_active = 0; + dev_priv->ldu_priv->fb = NULL; + + vmw_kms_create_implicit_placement_property(dev_priv); + + for (i = 0; i < num_display_units; ++i) { + ret = vmw_ldu_init(dev_priv, i); + if (ret != 0) + goto err_free; + } + + dev_priv->active_display_unit = vmw_du_legacy; + + drm_mode_config_reset(dev); + + return 0; + +err_free: + kfree(dev_priv->ldu_priv); + dev_priv->ldu_priv = NULL; + return ret; +} + +int vmw_kms_ldu_close_display(struct vmw_private *dev_priv) +{ + if (!dev_priv->ldu_priv) + return -ENOSYS; + + BUG_ON(!list_empty(&dev_priv->ldu_priv->active)); + + kfree(dev_priv->ldu_priv); + + return 0; +} + + +static int vmw_kms_ldu_do_bo_dirty(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + unsigned int flags, unsigned int color, + struct drm_mode_rect *clips, + unsigned int num_clips) +{ + size_t fifo_size; + int i; + + struct { + uint32_t header; + SVGAFifoCmdUpdate body; + } *cmd; + + fifo_size = sizeof(*cmd) * num_clips; + cmd = VMW_CMD_RESERVE(dev_priv, fifo_size); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + memset(cmd, 0, fifo_size); + for (i = 0; i < num_clips; i++, clips++) { + cmd[i].header = SVGA_CMD_UPDATE; + cmd[i].body.x = clips->x1; + cmd[i].body.y = clips->y1; + cmd[i].body.width = clips->x2 - clips->x1; + cmd[i].body.height = clips->y2 - clips->y1; + } + + vmw_cmd_commit(dev_priv, fifo_size); + return 0; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_mksstat.h b/drivers/gpu/drm/vmwgfx/vmwgfx_mksstat.h new file mode 100644 index 0000000000..ede74c7fdb --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_mksstat.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright 2021 VMware, Inc., Palo Alto, CA., USA + * + * 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_MKSSTAT_H_ +#define _VMWGFX_MKSSTAT_H_ + +#include <asm/page.h> +#include <linux/kconfig.h> + +/* Reservation marker for mksstat pid's */ +#define MKSSTAT_PID_RESERVED -1 + +#if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS) +/* + * Kernel-internal mksGuestStat counters. The order of this enum dictates the + * order of instantiation of these counters in the mksGuestStat pages. + */ + +typedef enum { + MKSSTAT_KERN_EXECBUF, /* vmw_execbuf_ioctl */ + MKSSTAT_KERN_COTABLE_RESIZE, + + MKSSTAT_KERN_COUNT /* Reserved entry; always last */ +} mksstat_kern_stats_t; + +/** + * vmw_mksstat_get_kern_pstat: Computes the address of the MKSGuestStatCounterTime + * array from the address of the base page. + * + * @page_addr: Pointer to the base page. + * Return: Pointer to the MKSGuestStatCounterTime array. + */ + +static inline void *vmw_mksstat_get_kern_pstat(void *page_addr) +{ + return page_addr + PAGE_SIZE * 1; +} + +/** + * vmw_mksstat_get_kern_pinfo: Computes the address of the MKSGuestStatInfoEntry + * array from the address of the base page. + * + * @page_addr: Pointer to the base page. + * Return: Pointer to the MKSGuestStatInfoEntry array. + */ + +static inline void *vmw_mksstat_get_kern_pinfo(void *page_addr) +{ + return page_addr + PAGE_SIZE * 2; +} + +/** + * vmw_mksstat_get_kern_pstrs: Computes the address of the mksGuestStat strings + * sequence from the address of the base page. + * + * @page_addr: Pointer to the base page. + * Return: Pointer to the mksGuestStat strings sequence. + */ + +static inline void *vmw_mksstat_get_kern_pstrs(void *page_addr) +{ + return page_addr + PAGE_SIZE * 3; +} + +/* + * MKS_STAT_TIME_DECL/PUSH/POP macros to be used in timer-counted routines. + */ + +struct mksstat_timer_t { +/* mutable */ mksstat_kern_stats_t old_top; + const u64 t0; + const int slot; +}; + +#define MKS_STAT_TIME_DECL(kern_cntr) \ + struct mksstat_timer_t _##kern_cntr = { \ + .t0 = rdtsc(), \ + .slot = vmw_mksstat_get_kern_slot(current->pid, dev_priv) \ + } + +#define MKS_STAT_TIME_PUSH(kern_cntr) \ + do { \ + if (_##kern_cntr.slot >= 0) { \ + _##kern_cntr.old_top = dev_priv->mksstat_kern_top_timer[_##kern_cntr.slot]; \ + dev_priv->mksstat_kern_top_timer[_##kern_cntr.slot] = kern_cntr; \ + } \ + } while (0) + +#define MKS_STAT_TIME_POP(kern_cntr) \ + do { \ + if (_##kern_cntr.slot >= 0) { \ + const pid_t pid = atomic_cmpxchg(&dev_priv->mksstat_kern_pids[_##kern_cntr.slot], current->pid, MKSSTAT_PID_RESERVED); \ + dev_priv->mksstat_kern_top_timer[_##kern_cntr.slot] = _##kern_cntr.old_top; \ + \ + if (pid == current->pid) { \ + const u64 dt = rdtsc() - _##kern_cntr.t0; \ + MKSGuestStatCounterTime *pstat; \ + \ + BUG_ON(!dev_priv->mksstat_kern_pages[_##kern_cntr.slot]); \ + \ + pstat = vmw_mksstat_get_kern_pstat(page_address(dev_priv->mksstat_kern_pages[_##kern_cntr.slot])); \ + \ + atomic64_inc(&pstat[kern_cntr].counter.count); \ + atomic64_add(dt, &pstat[kern_cntr].selfCycles); \ + atomic64_add(dt, &pstat[kern_cntr].totalCycles); \ + \ + if (_##kern_cntr.old_top != MKSSTAT_KERN_COUNT) \ + atomic64_sub(dt, &pstat[_##kern_cntr.old_top].selfCycles); \ + \ + atomic_set(&dev_priv->mksstat_kern_pids[_##kern_cntr.slot], current->pid); \ + } \ + } \ + } while (0) + +#else +#define MKS_STAT_TIME_DECL(kern_cntr) +#define MKS_STAT_TIME_PUSH(kern_cntr) +#define MKS_STAT_TIME_POP(kern_cntr) + +#endif /* IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS */ + +#endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c new file mode 100644 index 0000000000..7055cbefc7 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2012-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_drv.h" + +#include <linux/highmem.h> + +#ifdef CONFIG_64BIT +#define VMW_PPN_SIZE 8 +#define VMW_MOBFMT_PTDEPTH_0 SVGA3D_MOBFMT_PT64_0 +#define VMW_MOBFMT_PTDEPTH_1 SVGA3D_MOBFMT_PT64_1 +#define VMW_MOBFMT_PTDEPTH_2 SVGA3D_MOBFMT_PT64_2 +#else +#define VMW_PPN_SIZE 4 +#define VMW_MOBFMT_PTDEPTH_0 SVGA3D_MOBFMT_PT_0 +#define VMW_MOBFMT_PTDEPTH_1 SVGA3D_MOBFMT_PT_1 +#define VMW_MOBFMT_PTDEPTH_2 SVGA3D_MOBFMT_PT_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 vmw_bo *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. + */ +static const struct vmw_otable pre_dx_tables[] = { + {VMWGFX_NUM_MOB * sizeof(SVGAOTableMobEntry), NULL, true}, + {VMWGFX_NUM_GB_SURFACE * sizeof(SVGAOTableSurfaceEntry), NULL, true}, + {VMWGFX_NUM_GB_CONTEXT * sizeof(SVGAOTableContextEntry), NULL, true}, + {VMWGFX_NUM_GB_SHADER * sizeof(SVGAOTableShaderEntry), NULL, true}, + {VMWGFX_NUM_GB_SCREEN_TARGET * sizeof(SVGAOTableScreenTargetEntry), + NULL, true} +}; + +static const struct vmw_otable dx_tables[] = { + {VMWGFX_NUM_MOB * sizeof(SVGAOTableMobEntry), NULL, true}, + {VMWGFX_NUM_GB_SURFACE * sizeof(SVGAOTableSurfaceEntry), NULL, true}, + {VMWGFX_NUM_GB_CONTEXT * sizeof(SVGAOTableContextEntry), NULL, true}, + {VMWGFX_NUM_GB_SHADER * sizeof(SVGAOTableShaderEntry), NULL, true}, + {VMWGFX_NUM_GB_SCREEN_TARGET * sizeof(SVGAOTableScreenTargetEntry), + NULL, true}, + {VMWGFX_NUM_DXCONTEXT * sizeof(SVGAOTableDXContextEntry), NULL, true}, +}; + +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); + + +static inline void vmw_bo_unpin_unlocked(struct ttm_buffer_object *bo) +{ + int ret = ttm_bo_reserve(bo, false, true, NULL); + BUG_ON(ret != 0); + ttm_bo_unpin(bo); + ttm_bo_unreserve(bo); +} + + +/* + * 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, + struct ttm_buffer_object *otable_bo, + 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(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 { + 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_PT_1; + } + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) { + 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 = 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_cmd_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->tbo; + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + return; + + 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_cmd_commit(dev_priv, sizeof(*cmd)); + + if (bo) { + int ret; + + ret = ttm_bo_reserve(bo, false, true, NULL); + BUG_ON(ret != 0); + + vmw_bo_fence_single(bo, NULL); + ttm_bo_unreserve(bo); + } + + vmw_mob_destroy(otable->page_table); + otable->page_table = NULL; +} + + +static int vmw_otable_batch_setup(struct vmw_private *dev_priv, + struct vmw_otable_batch *batch) +{ + unsigned long offset; + unsigned long bo_size; + struct vmw_otable *otables = batch->otables; + SVGAOTableType i; + int ret; + + bo_size = 0; + for (i = 0; i < batch->num_otables; ++i) { + if (!otables[i].enabled) + continue; + + otables[i].size = PFN_ALIGN(otables[i].size); + bo_size += otables[i].size; + } + + ret = vmw_bo_create_and_populate(dev_priv, bo_size, + VMW_BO_DOMAIN_WAITABLE_SYS, + &batch->otable_bo); + if (unlikely(ret != 0)) + return ret; + + offset = 0; + for (i = 0; i < batch->num_otables; ++i) { + if (!batch->otables[i].enabled) + continue; + + ret = vmw_setup_otable_base(dev_priv, i, + &batch->otable_bo->tbo, + offset, + &otables[i]); + if (unlikely(ret != 0)) + goto out_no_setup; + offset += otables[i].size; + } + + return 0; + +out_no_setup: + for (i = 0; i < batch->num_otables; ++i) { + if (batch->otables[i].enabled) + vmw_takedown_otable_base(dev_priv, i, + &batch->otables[i]); + } + + vmw_bo_unpin_unlocked(&batch->otable_bo->tbo); + ttm_bo_put(&batch->otable_bo->tbo); + batch->otable_bo = NULL; + return ret; +} + +/* + * 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 successful return + * means the object tables can be taken down using the vmw_otables_takedown + * function. + */ +int vmw_otables_setup(struct vmw_private *dev_priv) +{ + struct vmw_otable **otables = &dev_priv->otable_batch.otables; + int ret; + + if (has_sm4_context(dev_priv)) { + *otables = kmemdup(dx_tables, sizeof(dx_tables), GFP_KERNEL); + if (!(*otables)) + return -ENOMEM; + + dev_priv->otable_batch.num_otables = ARRAY_SIZE(dx_tables); + } else { + *otables = kmemdup(pre_dx_tables, sizeof(pre_dx_tables), + GFP_KERNEL); + if (!(*otables)) + return -ENOMEM; + + dev_priv->otable_batch.num_otables = ARRAY_SIZE(pre_dx_tables); + } + + ret = vmw_otable_batch_setup(dev_priv, &dev_priv->otable_batch); + if (unlikely(ret != 0)) + goto out_setup; + + return 0; + +out_setup: + kfree(*otables); + return ret; +} + +static void vmw_otable_batch_takedown(struct vmw_private *dev_priv, + struct vmw_otable_batch *batch) +{ + SVGAOTableType i; + struct ttm_buffer_object *bo = &batch->otable_bo->tbo; + int ret; + + for (i = 0; i < batch->num_otables; ++i) + if (batch->otables[i].enabled) + vmw_takedown_otable_base(dev_priv, i, + &batch->otables[i]); + + ret = ttm_bo_reserve(bo, false, true, NULL); + BUG_ON(ret != 0); + + vmw_bo_fence_single(bo, NULL); + ttm_bo_unpin(bo); + ttm_bo_unreserve(bo); + + vmw_bo_unreference(&batch->otable_bo); +} + +/* + * 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) +{ + vmw_otable_batch_takedown(dev_priv, &dev_priv->otable_batch); + kfree(dev_priv->otable_batch.otables); +} + +/* + * 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 += PFN_ALIGN(data_size); + } + + 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)) + 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. + * Returns ENOMEM if memory resources aren't sufficient and may + * cause TTM buffer objects to be swapped out. + */ +static int vmw_mob_pt_populate(struct vmw_private *dev_priv, + struct vmw_mob *mob) +{ + BUG_ON(mob->pt_bo != NULL); + + return vmw_bo_create_and_populate(dev_priv, mob->num_pages * PAGE_SIZE, + VMW_BO_DOMAIN_WAITABLE_SYS, + &mob->pt_bo); +} + +/** + * 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(u32 **addr, dma_addr_t val) +{ + *((u64 *) *addr) = val >> PAGE_SHIFT; + *addr += 2; +} +#else +static void vmw_mob_assign_ppn(u32 **addr, dma_addr_t val) +{ + *(*addr)++ = 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; + u32 *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->tbo; + struct vmw_piter save_pt_iter = {0}; + struct vmw_piter pt_iter; + const struct vmw_sg_table *vsgt; + int ret; + + BUG_ON(num_data_pages == 0); + + ret = ttm_bo_reserve(bo, false, true, 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) { + vmw_bo_unpin_unlocked(&mob->pt_bo->tbo); + vmw_bo_unreference(&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->tbo; + + if (bo) { + ret = ttm_bo_reserve(bo, false, true, NULL); + /* + * Noone else should be using this buffer. + */ + BUG_ON(ret != 0); + } + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (cmd) { + cmd->header.id = SVGA_3D_CMD_DESTROY_GB_MOB; + cmd->header.size = sizeof(cmd->body); + cmd->body.mobid = mob->id; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + } + + if (bo) { + vmw_bo_fence_single(bo, NULL); + ttm_bo_unreserve(bo); + } + vmw_fifo_resource_dec(dev_priv); +} + +/* + * 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 (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_PT_1; + } + + vmw_fifo_resource_inc(dev_priv); + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + 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 = mob->pt_root_page >> PAGE_SHIFT; + cmd->body.sizeInBytes = num_data_pages * PAGE_SIZE; + + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; + +out_no_cmd_space: + vmw_fifo_resource_dec(dev_priv); + if (pt_set_up) { + vmw_bo_unpin_unlocked(&mob->pt_bo->tbo); + vmw_bo_unreference(&mob->pt_bo); + } + + return -ENOMEM; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c new file mode 100644 index 0000000000..2651fe0ef5 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c @@ -0,0 +1,1190 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright 2016 VMware, Inc., Palo Alto, CA., USA + * + * 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 <linux/objtool.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/cc_platform.h> + +#include <asm/hypervisor.h> +#include <drm/drm_ioctl.h> + +#include "vmwgfx_drv.h" +#include "vmwgfx_msg_x86.h" +#include "vmwgfx_msg_arm64.h" +#include "vmwgfx_mksstat.h" + +#define MESSAGE_STATUS_SUCCESS 0x0001 +#define MESSAGE_STATUS_DORECV 0x0002 +#define MESSAGE_STATUS_CPT 0x0010 +#define MESSAGE_STATUS_HB 0x0080 + +#define RPCI_PROTOCOL_NUM 0x49435052 +#define GUESTMSG_FLAG_COOKIE 0x80000000 + +#define RETRIES 3 + +#define VMW_HYPERVISOR_MAGIC 0x564D5868 + +#define VMW_PORT_CMD_MSG 30 +#define VMW_PORT_CMD_HB_MSG 0 +#define VMW_PORT_CMD_OPEN_CHANNEL (MSG_TYPE_OPEN << 16 | VMW_PORT_CMD_MSG) +#define VMW_PORT_CMD_CLOSE_CHANNEL (MSG_TYPE_CLOSE << 16 | VMW_PORT_CMD_MSG) +#define VMW_PORT_CMD_SENDSIZE (MSG_TYPE_SENDSIZE << 16 | VMW_PORT_CMD_MSG) +#define VMW_PORT_CMD_RECVSIZE (MSG_TYPE_RECVSIZE << 16 | VMW_PORT_CMD_MSG) +#define VMW_PORT_CMD_RECVSTATUS (MSG_TYPE_RECVSTATUS << 16 | VMW_PORT_CMD_MSG) + +#define VMW_PORT_CMD_MKS_GUEST_STATS 85 +#define VMW_PORT_CMD_MKSGS_RESET (0 << 16 | VMW_PORT_CMD_MKS_GUEST_STATS) +#define VMW_PORT_CMD_MKSGS_ADD_PPN (1 << 16 | VMW_PORT_CMD_MKS_GUEST_STATS) +#define VMW_PORT_CMD_MKSGS_REMOVE_PPN (2 << 16 | VMW_PORT_CMD_MKS_GUEST_STATS) + +#define HIGH_WORD(X) ((X & 0xFFFF0000) >> 16) + +#define MAX_USER_MSG_LENGTH PAGE_SIZE + +static u32 vmw_msg_enabled = 1; + +enum rpc_msg_type { + MSG_TYPE_OPEN, + MSG_TYPE_SENDSIZE, + MSG_TYPE_SENDPAYLOAD, + MSG_TYPE_RECVSIZE, + MSG_TYPE_RECVPAYLOAD, + MSG_TYPE_RECVSTATUS, + MSG_TYPE_CLOSE, +}; + +struct rpc_channel { + u16 channel_id; + u32 cookie_high; + u32 cookie_low; +}; + +#if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS) +/* Kernel mksGuestStats counter names and desciptions; same order as enum mksstat_kern_stats_t */ +static const char* const mksstat_kern_name_desc[MKSSTAT_KERN_COUNT][2] = +{ + { "vmw_execbuf_ioctl", "vmw_execbuf_ioctl" }, + { "vmw_cotable_resize", "vmw_cotable_resize" }, +}; +#endif + +/** + * vmw_open_channel + * + * @channel: RPC channel + * @protocol: + * + * Returns: 0 on success + */ +static int vmw_open_channel(struct rpc_channel *channel, unsigned int protocol) +{ + unsigned long eax, ebx, ecx, edx, si = 0, di = 0; + + VMW_PORT(VMW_PORT_CMD_OPEN_CHANNEL, + (protocol | GUESTMSG_FLAG_COOKIE), si, di, + 0, + VMW_HYPERVISOR_MAGIC, + eax, ebx, ecx, edx, si, di); + + if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) + return -EINVAL; + + channel->channel_id = HIGH_WORD(edx); + channel->cookie_high = si; + channel->cookie_low = di; + + return 0; +} + + + +/** + * vmw_close_channel + * + * @channel: RPC channel + * + * Returns: 0 on success + */ +static int vmw_close_channel(struct rpc_channel *channel) +{ + unsigned long eax, ebx, ecx, edx, si, di; + + /* Set up additional parameters */ + si = channel->cookie_high; + di = channel->cookie_low; + + VMW_PORT(VMW_PORT_CMD_CLOSE_CHANNEL, + 0, si, di, + channel->channel_id << 16, + VMW_HYPERVISOR_MAGIC, + eax, ebx, ecx, edx, si, di); + + if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) + return -EINVAL; + + return 0; +} + +/** + * vmw_port_hb_out - Send the message payload either through the + * high-bandwidth port if available, or through the backdoor otherwise. + * @channel: The rpc channel. + * @msg: NULL-terminated message. + * @hb: Whether the high-bandwidth port is available. + * + * Return: The port status. + */ +static unsigned long vmw_port_hb_out(struct rpc_channel *channel, + const char *msg, bool hb) +{ + unsigned long si, di, eax, ebx, ecx, edx; + unsigned long msg_len = strlen(msg); + + /* HB port can't access encrypted memory. */ + if (hb && !cc_platform_has(CC_ATTR_MEM_ENCRYPT)) { + unsigned long bp = channel->cookie_high; + u32 channel_id = (channel->channel_id << 16); + + si = (uintptr_t) msg; + di = channel->cookie_low; + + VMW_PORT_HB_OUT( + (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, + msg_len, si, di, + VMWARE_HYPERVISOR_HB | channel_id | + VMWARE_HYPERVISOR_OUT, + VMW_HYPERVISOR_MAGIC, bp, + eax, ebx, ecx, edx, si, di); + + return ebx; + } + + /* HB port not available. Send the message 4 bytes at a time. */ + ecx = MESSAGE_STATUS_SUCCESS << 16; + while (msg_len && (HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS)) { + unsigned int bytes = min_t(size_t, msg_len, 4); + unsigned long word = 0; + + memcpy(&word, msg, bytes); + msg_len -= bytes; + msg += bytes; + si = channel->cookie_high; + di = channel->cookie_low; + + VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_SENDPAYLOAD << 16), + word, si, di, + channel->channel_id << 16, + VMW_HYPERVISOR_MAGIC, + eax, ebx, ecx, edx, si, di); + } + + return ecx; +} + +/** + * vmw_port_hb_in - Receive the message payload either through the + * high-bandwidth port if available, or through the backdoor otherwise. + * @channel: The rpc channel. + * @reply: Pointer to buffer holding reply. + * @reply_len: Length of the reply. + * @hb: Whether the high-bandwidth port is available. + * + * Return: The port status. + */ +static unsigned long vmw_port_hb_in(struct rpc_channel *channel, char *reply, + unsigned long reply_len, bool hb) +{ + unsigned long si, di, eax, ebx, ecx, edx; + + /* HB port can't access encrypted memory */ + if (hb && !cc_platform_has(CC_ATTR_MEM_ENCRYPT)) { + unsigned long bp = channel->cookie_low; + u32 channel_id = (channel->channel_id << 16); + + si = channel->cookie_high; + di = (uintptr_t) reply; + + VMW_PORT_HB_IN( + (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG, + reply_len, si, di, + VMWARE_HYPERVISOR_HB | channel_id, + VMW_HYPERVISOR_MAGIC, bp, + eax, ebx, ecx, edx, si, di); + + return ebx; + } + + /* HB port not available. Retrieve the message 4 bytes at a time. */ + ecx = MESSAGE_STATUS_SUCCESS << 16; + while (reply_len) { + unsigned int bytes = min_t(unsigned long, reply_len, 4); + + si = channel->cookie_high; + di = channel->cookie_low; + + VMW_PORT(VMW_PORT_CMD_MSG | (MSG_TYPE_RECVPAYLOAD << 16), + MESSAGE_STATUS_SUCCESS, si, di, + channel->channel_id << 16, + VMW_HYPERVISOR_MAGIC, + eax, ebx, ecx, edx, si, di); + + if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) + break; + + memcpy(reply, &ebx, bytes); + reply_len -= bytes; + reply += bytes; + } + + return ecx; +} + + +/** + * vmw_send_msg: Sends a message to the host + * + * @channel: RPC channel + * @msg: NULL terminated string + * + * Returns: 0 on success + */ +static int vmw_send_msg(struct rpc_channel *channel, const char *msg) +{ + unsigned long eax, ebx, ecx, edx, si, di; + size_t msg_len = strlen(msg); + int retries = 0; + + while (retries < RETRIES) { + retries++; + + /* Set up additional parameters */ + si = channel->cookie_high; + di = channel->cookie_low; + + VMW_PORT(VMW_PORT_CMD_SENDSIZE, + msg_len, si, di, + channel->channel_id << 16, + VMW_HYPERVISOR_MAGIC, + eax, ebx, ecx, edx, si, di); + + if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) { + /* Expected success. Give up. */ + return -EINVAL; + } + + /* Send msg */ + ebx = vmw_port_hb_out(channel, msg, + !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB)); + + if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) != 0) { + return 0; + } else if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) { + /* A checkpoint occurred. Retry. */ + continue; + } else { + break; + } + } + + return -EINVAL; +} +STACK_FRAME_NON_STANDARD(vmw_send_msg); + + +/** + * vmw_recv_msg: Receives a message from the host + * + * Note: It is the caller's responsibility to call kfree() on msg. + * + * @channel: channel opened by vmw_open_channel + * @msg: [OUT] message received from the host + * @msg_len: message length + */ +static int vmw_recv_msg(struct rpc_channel *channel, void **msg, + size_t *msg_len) +{ + unsigned long eax, ebx, ecx, edx, si, di; + char *reply; + size_t reply_len; + int retries = 0; + + + *msg_len = 0; + *msg = NULL; + + while (retries < RETRIES) { + retries++; + + /* Set up additional parameters */ + si = channel->cookie_high; + di = channel->cookie_low; + + VMW_PORT(VMW_PORT_CMD_RECVSIZE, + 0, si, di, + channel->channel_id << 16, + VMW_HYPERVISOR_MAGIC, + eax, ebx, ecx, edx, si, di); + + if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) { + DRM_ERROR("Failed to get reply size for host message.\n"); + return -EINVAL; + } + + /* No reply available. This is okay. */ + if ((HIGH_WORD(ecx) & MESSAGE_STATUS_DORECV) == 0) + return 0; + + reply_len = ebx; + reply = kzalloc(reply_len + 1, GFP_KERNEL); + if (!reply) { + DRM_ERROR("Cannot allocate memory for host message reply.\n"); + return -ENOMEM; + } + + + /* Receive buffer */ + ebx = vmw_port_hb_in(channel, reply, reply_len, + !!(HIGH_WORD(ecx) & MESSAGE_STATUS_HB)); + if ((HIGH_WORD(ebx) & MESSAGE_STATUS_SUCCESS) == 0) { + kfree(reply); + reply = NULL; + if ((HIGH_WORD(ebx) & MESSAGE_STATUS_CPT) != 0) { + /* A checkpoint occurred. Retry. */ + continue; + } + + return -EINVAL; + } + + reply[reply_len] = '\0'; + + + /* Ack buffer */ + si = channel->cookie_high; + di = channel->cookie_low; + + VMW_PORT(VMW_PORT_CMD_RECVSTATUS, + MESSAGE_STATUS_SUCCESS, si, di, + channel->channel_id << 16, + VMW_HYPERVISOR_MAGIC, + eax, ebx, ecx, edx, si, di); + + if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0) { + kfree(reply); + reply = NULL; + if ((HIGH_WORD(ecx) & MESSAGE_STATUS_CPT) != 0) { + /* A checkpoint occurred. Retry. */ + continue; + } + + return -EINVAL; + } + + break; + } + + if (!reply) + return -EINVAL; + + *msg_len = reply_len; + *msg = reply; + + return 0; +} +STACK_FRAME_NON_STANDARD(vmw_recv_msg); + + +/** + * vmw_host_get_guestinfo: Gets a GuestInfo parameter + * + * Gets the value of a GuestInfo.* parameter. The value returned will be in + * a string, and it is up to the caller to post-process. + * + * @guest_info_param: Parameter to get, e.g. GuestInfo.svga.gl3 + * @buffer: if NULL, *reply_len will contain reply size. + * @length: size of the reply_buf. Set to size of reply upon return + * + * Returns: 0 on success + */ +int vmw_host_get_guestinfo(const char *guest_info_param, + char *buffer, size_t *length) +{ + struct rpc_channel channel; + char *msg, *reply = NULL; + size_t reply_len = 0; + + if (!vmw_msg_enabled) + return -ENODEV; + + if (!guest_info_param || !length) + return -EINVAL; + + msg = kasprintf(GFP_KERNEL, "info-get %s", guest_info_param); + if (!msg) { + DRM_ERROR("Cannot allocate memory to get guest info \"%s\".", + guest_info_param); + return -ENOMEM; + } + + if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM)) + goto out_open; + + if (vmw_send_msg(&channel, msg) || + vmw_recv_msg(&channel, (void *) &reply, &reply_len)) + goto out_msg; + + vmw_close_channel(&channel); + if (buffer && reply && reply_len > 0) { + /* Remove reply code, which are the first 2 characters of + * the reply + */ + reply_len = max(reply_len - 2, (size_t) 0); + reply_len = min(reply_len, *length); + + if (reply_len > 0) + memcpy(buffer, reply + 2, reply_len); + } + + *length = reply_len; + + kfree(reply); + kfree(msg); + + return 0; + +out_msg: + vmw_close_channel(&channel); + kfree(reply); +out_open: + *length = 0; + kfree(msg); + DRM_ERROR("Failed to get guest info \"%s\".", guest_info_param); + + return -EINVAL; +} + + +/** + * vmw_host_printf: Sends a log message to the host + * + * @fmt: Regular printf format string and arguments + * + * Returns: 0 on success + */ +__printf(1, 2) +int vmw_host_printf(const char *fmt, ...) +{ + va_list ap; + struct rpc_channel channel; + char *msg; + char *log; + int ret = 0; + + if (!vmw_msg_enabled) + return -ENODEV; + + if (!fmt) + return ret; + + va_start(ap, fmt); + log = kvasprintf(GFP_KERNEL, fmt, ap); + va_end(ap); + if (!log) { + DRM_ERROR("Cannot allocate memory for the log message.\n"); + return -ENOMEM; + } + + msg = kasprintf(GFP_KERNEL, "log %s", log); + if (!msg) { + DRM_ERROR("Cannot allocate memory for host log message.\n"); + kfree(log); + return -ENOMEM; + } + + if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM)) + goto out_open; + + if (vmw_send_msg(&channel, msg)) + goto out_msg; + + vmw_close_channel(&channel); + kfree(msg); + kfree(log); + + return 0; + +out_msg: + vmw_close_channel(&channel); +out_open: + kfree(msg); + kfree(log); + DRM_ERROR("Failed to send host log message.\n"); + + return -EINVAL; +} + + +/** + * vmw_msg_ioctl: Sends and receveives a message to/from host from/to user-space + * + * Sends a message from user-space to host. + * Can also receive a result from host and return that to user-space. + * + * @dev: Identifies the drm device. + * @data: Pointer to the ioctl argument. + * @file_priv: Identifies the caller. + * Return: Zero on success, negative error code on error. + */ + +int vmw_msg_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_msg_arg *arg = + (struct drm_vmw_msg_arg *)data; + struct rpc_channel channel; + char *msg; + int length; + + msg = kmalloc(MAX_USER_MSG_LENGTH, GFP_KERNEL); + if (!msg) { + DRM_ERROR("Cannot allocate memory for log message.\n"); + return -ENOMEM; + } + + length = strncpy_from_user(msg, (void __user *)((unsigned long)arg->send), + MAX_USER_MSG_LENGTH); + if (length < 0 || length >= MAX_USER_MSG_LENGTH) { + DRM_ERROR("Userspace message access failure.\n"); + kfree(msg); + return -EINVAL; + } + + + if (vmw_open_channel(&channel, RPCI_PROTOCOL_NUM)) { + DRM_ERROR("Failed to open channel.\n"); + goto out_open; + } + + if (vmw_send_msg(&channel, msg)) { + DRM_ERROR("Failed to send message to host.\n"); + goto out_msg; + } + + if (!arg->send_only) { + char *reply = NULL; + size_t reply_len = 0; + + if (vmw_recv_msg(&channel, (void *) &reply, &reply_len)) { + DRM_ERROR("Failed to receive message from host.\n"); + goto out_msg; + } + if (reply && reply_len > 0) { + if (copy_to_user((void __user *)((unsigned long)arg->receive), + reply, reply_len)) { + DRM_ERROR("Failed to copy message to userspace.\n"); + kfree(reply); + goto out_msg; + } + arg->receive_len = (__u32)reply_len; + } + kfree(reply); + } + + vmw_close_channel(&channel); + kfree(msg); + + return 0; + +out_msg: + vmw_close_channel(&channel); +out_open: + kfree(msg); + + return -EINVAL; +} + +/** + * reset_ppn_array: Resets a PPN64 array to INVALID_PPN64 content + * + * @arr: Array to reset. + * @size: Array length. + */ +static inline void reset_ppn_array(PPN64 *arr, size_t size) +{ + size_t i; + + BUG_ON(!arr || size == 0); + + for (i = 0; i < size; ++i) + arr[i] = INVALID_PPN64; +} + +/** + * hypervisor_ppn_reset_all: Removes all mksGuestStat instance descriptors from + * the hypervisor. All related pages should be subsequently unpinned or freed. + * + */ +static inline void hypervisor_ppn_reset_all(void) +{ + unsigned long eax, ebx, ecx, edx, si = 0, di = 0; + + VMW_PORT(VMW_PORT_CMD_MKSGS_RESET, + 0, si, di, + 0, + VMW_HYPERVISOR_MAGIC, + eax, ebx, ecx, edx, si, di); +} + +/** + * hypervisor_ppn_add: Adds a single mksGuestStat instance descriptor to the + * hypervisor. Any related userspace pages should be pinned in advance. + * + * @pfn: Physical page number of the instance descriptor + */ +static inline void hypervisor_ppn_add(PPN64 pfn) +{ + unsigned long eax, ebx, ecx, edx, si = 0, di = 0; + + VMW_PORT(VMW_PORT_CMD_MKSGS_ADD_PPN, + (unsigned long)pfn, si, di, + 0, + VMW_HYPERVISOR_MAGIC, + eax, ebx, ecx, edx, si, di); +} + +/** + * hypervisor_ppn_remove: Removes a single mksGuestStat instance descriptor from + * the hypervisor. All related pages should be subsequently unpinned or freed. + * + * @pfn: Physical page number of the instance descriptor + */ +static inline void hypervisor_ppn_remove(PPN64 pfn) +{ + unsigned long eax, ebx, ecx, edx, si = 0, di = 0; + + VMW_PORT(VMW_PORT_CMD_MKSGS_REMOVE_PPN, + (unsigned long)pfn, si, di, + 0, + VMW_HYPERVISOR_MAGIC, + eax, ebx, ecx, edx, si, di); +} + +#if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS) + +/* Order of the total number of pages used for kernel-internal mksGuestStat; at least 2 */ +#define MKSSTAT_KERNEL_PAGES_ORDER 2 +/* Header to the text description of mksGuestStat instance descriptor */ +#define MKSSTAT_KERNEL_DESCRIPTION "vmwgfx" + +/** + * mksstat_init_record_time: Initializes an MKSGuestStatCounterTime-based record + * for the respective mksGuestStat index. + * + * @stat_idx: Index of the MKSGuestStatCounterTime-based mksGuestStat record. + * @pstat: Pointer to array of MKSGuestStatCounterTime. + * @pinfo: Pointer to array of MKSGuestStatInfoEntry. + * @pstrs: Pointer to current end of the name/description sequence. + * Return: Pointer to the new end of the names/description sequence. + */ + +static inline char *mksstat_init_record_time(mksstat_kern_stats_t stat_idx, + MKSGuestStatCounterTime *pstat, MKSGuestStatInfoEntry *pinfo, char *pstrs) +{ + char *const pstrd = pstrs + strlen(mksstat_kern_name_desc[stat_idx][0]) + 1; + strcpy(pstrs, mksstat_kern_name_desc[stat_idx][0]); + strcpy(pstrd, mksstat_kern_name_desc[stat_idx][1]); + + pinfo[stat_idx].name.s = pstrs; + pinfo[stat_idx].description.s = pstrd; + pinfo[stat_idx].flags = MKS_GUEST_STAT_FLAG_TIME; + pinfo[stat_idx].stat.counterTime = &pstat[stat_idx]; + + return pstrd + strlen(mksstat_kern_name_desc[stat_idx][1]) + 1; +} + +/** + * mksstat_init_kern_id: Creates a single mksGuestStat instance descriptor and + * kernel-internal counters. Adds PFN mapping to the hypervisor. + * + * Create a single mksGuestStat instance descriptor and corresponding structures + * for all kernel-internal counters. The corresponding PFNs are mapped with the + * hypervisor. + * + * @ppage: Output pointer to page containing the instance descriptor. + * Return: Zero on success, negative error code on error. + */ + +static int mksstat_init_kern_id(struct page **ppage) +{ + MKSGuestStatInstanceDescriptor *pdesc; + MKSGuestStatCounterTime *pstat; + MKSGuestStatInfoEntry *pinfo; + char *pstrs, *pstrs_acc; + + /* Allocate pages for the kernel-internal instance descriptor */ + struct page *page = alloc_pages(GFP_KERNEL | __GFP_ZERO, MKSSTAT_KERNEL_PAGES_ORDER); + + if (!page) + return -ENOMEM; + + pdesc = page_address(page); + pstat = vmw_mksstat_get_kern_pstat(pdesc); + pinfo = vmw_mksstat_get_kern_pinfo(pdesc); + pstrs = vmw_mksstat_get_kern_pstrs(pdesc); + + /* Set up all kernel-internal counters and corresponding structures */ + pstrs_acc = pstrs; + pstrs_acc = mksstat_init_record_time(MKSSTAT_KERN_EXECBUF, pstat, pinfo, pstrs_acc); + pstrs_acc = mksstat_init_record_time(MKSSTAT_KERN_COTABLE_RESIZE, pstat, pinfo, pstrs_acc); + + /* Add new counters above, in their order of appearance in mksstat_kern_stats_t */ + + BUG_ON(pstrs_acc - pstrs > PAGE_SIZE); + + /* Set up the kernel-internal instance descriptor */ + pdesc->reservedMBZ = 0; + pdesc->statStartVA = (uintptr_t)pstat; + pdesc->strsStartVA = (uintptr_t)pstrs; + pdesc->statLength = sizeof(*pstat) * MKSSTAT_KERN_COUNT; + pdesc->infoLength = sizeof(*pinfo) * MKSSTAT_KERN_COUNT; + pdesc->strsLength = pstrs_acc - pstrs; + snprintf(pdesc->description, ARRAY_SIZE(pdesc->description) - 1, "%s pid=%d", + MKSSTAT_KERNEL_DESCRIPTION, current->pid); + + pdesc->statPPNs[0] = page_to_pfn(virt_to_page(pstat)); + reset_ppn_array(pdesc->statPPNs + 1, ARRAY_SIZE(pdesc->statPPNs) - 1); + + pdesc->infoPPNs[0] = page_to_pfn(virt_to_page(pinfo)); + reset_ppn_array(pdesc->infoPPNs + 1, ARRAY_SIZE(pdesc->infoPPNs) - 1); + + pdesc->strsPPNs[0] = page_to_pfn(virt_to_page(pstrs)); + reset_ppn_array(pdesc->strsPPNs + 1, ARRAY_SIZE(pdesc->strsPPNs) - 1); + + *ppage = page; + + hypervisor_ppn_add((PPN64)page_to_pfn(page)); + + return 0; +} + +/** + * vmw_mksstat_get_kern_slot: Acquires a slot for a single kernel-internal + * mksGuestStat instance descriptor. + * + * Find a slot for a single kernel-internal mksGuestStat instance descriptor. + * In case no such was already present, allocate a new one and set up a kernel- + * internal mksGuestStat instance descriptor for the former. + * + * @pid: Process for which a slot is sought. + * @dev_priv: Identifies the drm private device. + * Return: Non-negative slot on success, negative error code on error. + */ + +int vmw_mksstat_get_kern_slot(pid_t pid, struct vmw_private *dev_priv) +{ + const size_t base = (u32)hash_32(pid, MKSSTAT_CAPACITY_LOG2); + size_t i; + + for (i = 0; i < ARRAY_SIZE(dev_priv->mksstat_kern_pids); ++i) { + const size_t slot = (i + base) % ARRAY_SIZE(dev_priv->mksstat_kern_pids); + + /* Check if an instance descriptor for this pid is already present */ + if (pid == (pid_t)atomic_read(&dev_priv->mksstat_kern_pids[slot])) + return (int)slot; + + /* Set up a new instance descriptor for this pid */ + if (!atomic_cmpxchg(&dev_priv->mksstat_kern_pids[slot], 0, MKSSTAT_PID_RESERVED)) { + const int ret = mksstat_init_kern_id(&dev_priv->mksstat_kern_pages[slot]); + + if (!ret) { + /* Reset top-timer tracking for this slot */ + dev_priv->mksstat_kern_top_timer[slot] = MKSSTAT_KERN_COUNT; + + atomic_set(&dev_priv->mksstat_kern_pids[slot], pid); + return (int)slot; + } + + atomic_set(&dev_priv->mksstat_kern_pids[slot], 0); + return ret; + } + } + + return -ENOSPC; +} + +#endif + +/** + * vmw_mksstat_cleanup_descriptor: Frees a single userspace-originating + * mksGuestStat instance-descriptor page and unpins all related user pages. + * + * Unpin all user pages realated to this instance descriptor and free + * the instance-descriptor page itself. + * + * @page: Page of the instance descriptor. + */ + +static void vmw_mksstat_cleanup_descriptor(struct page *page) +{ + MKSGuestStatInstanceDescriptor *pdesc = page_address(page); + size_t i; + + for (i = 0; i < ARRAY_SIZE(pdesc->statPPNs) && pdesc->statPPNs[i] != INVALID_PPN64; ++i) + unpin_user_page(pfn_to_page(pdesc->statPPNs[i])); + + for (i = 0; i < ARRAY_SIZE(pdesc->infoPPNs) && pdesc->infoPPNs[i] != INVALID_PPN64; ++i) + unpin_user_page(pfn_to_page(pdesc->infoPPNs[i])); + + for (i = 0; i < ARRAY_SIZE(pdesc->strsPPNs) && pdesc->strsPPNs[i] != INVALID_PPN64; ++i) + unpin_user_page(pfn_to_page(pdesc->strsPPNs[i])); + + __free_page(page); +} + +/** + * vmw_mksstat_remove_all: Resets all mksGuestStat instance descriptors + * from the hypervisor. + * + * Discard all hypervisor PFN mappings, containing active mksGuestState instance + * descriptors, unpin the related userspace pages and free the related kernel pages. + * + * @dev_priv: Identifies the drm private device. + * Return: Zero on success, negative error code on error. + */ + +int vmw_mksstat_remove_all(struct vmw_private *dev_priv) +{ + int ret = 0; + size_t i; + + /* Discard all PFN mappings with the hypervisor */ + hypervisor_ppn_reset_all(); + + /* Discard all userspace-originating instance descriptors and unpin all related pages */ + for (i = 0; i < ARRAY_SIZE(dev_priv->mksstat_user_pids); ++i) { + const pid_t pid0 = (pid_t)atomic_read(&dev_priv->mksstat_user_pids[i]); + + if (!pid0) + continue; + + if (pid0 != MKSSTAT_PID_RESERVED) { + const pid_t pid1 = atomic_cmpxchg(&dev_priv->mksstat_user_pids[i], pid0, MKSSTAT_PID_RESERVED); + + if (!pid1) + continue; + + if (pid1 == pid0) { + struct page *const page = dev_priv->mksstat_user_pages[i]; + + BUG_ON(!page); + + dev_priv->mksstat_user_pages[i] = NULL; + atomic_set(&dev_priv->mksstat_user_pids[i], 0); + + vmw_mksstat_cleanup_descriptor(page); + continue; + } + } + + ret = -EAGAIN; + } + +#if IS_ENABLED(CONFIG_DRM_VMWGFX_MKSSTATS) + /* Discard all kernel-internal instance descriptors and free all related pages */ + for (i = 0; i < ARRAY_SIZE(dev_priv->mksstat_kern_pids); ++i) { + const pid_t pid0 = (pid_t)atomic_read(&dev_priv->mksstat_kern_pids[i]); + + if (!pid0) + continue; + + if (pid0 != MKSSTAT_PID_RESERVED) { + const pid_t pid1 = atomic_cmpxchg(&dev_priv->mksstat_kern_pids[i], pid0, MKSSTAT_PID_RESERVED); + + if (!pid1) + continue; + + if (pid1 == pid0) { + struct page *const page = dev_priv->mksstat_kern_pages[i]; + + BUG_ON(!page); + + dev_priv->mksstat_kern_pages[i] = NULL; + atomic_set(&dev_priv->mksstat_kern_pids[i], 0); + + __free_pages(page, MKSSTAT_KERNEL_PAGES_ORDER); + continue; + } + } + + ret = -EAGAIN; + } + +#endif + return ret; +} + +/** + * vmw_mksstat_reset_ioctl: Resets all mksGuestStat instance descriptors + * from the hypervisor. + * + * Discard all hypervisor PFN mappings, containing active mksGuestStat instance + * descriptors, unpin the related userspace pages and free the related kernel pages. + * + * @dev: Identifies the drm device. + * @data: Pointer to the ioctl argument. + * @file_priv: Identifies the caller; unused. + * Return: Zero on success, negative error code on error. + */ + +int vmw_mksstat_reset_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *const dev_priv = vmw_priv(dev); + return vmw_mksstat_remove_all(dev_priv); +} + +/** + * vmw_mksstat_add_ioctl: Creates a single userspace-originating mksGuestStat + * instance descriptor and registers that with the hypervisor. + * + * Create a hypervisor PFN mapping, containing a single mksGuestStat instance + * descriptor and pin the corresponding userspace pages. + * + * @dev: Identifies the drm device. + * @data: Pointer to the ioctl argument. + * @file_priv: Identifies the caller; unused. + * Return: Zero on success, negative error code on error. + */ + +int vmw_mksstat_add_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_mksstat_add_arg *arg = + (struct drm_vmw_mksstat_add_arg *) data; + + struct vmw_private *const dev_priv = vmw_priv(dev); + + const size_t num_pages_stat = PFN_UP(arg->stat_len); + const size_t num_pages_info = PFN_UP(arg->info_len); + const size_t num_pages_strs = PFN_UP(arg->strs_len); + long desc_len; + long nr_pinned_stat; + long nr_pinned_info; + long nr_pinned_strs; + MKSGuestStatInstanceDescriptor *pdesc; + struct page *page = NULL; + struct page **pages_stat = NULL; + struct page **pages_info = NULL; + struct page **pages_strs = NULL; + size_t i, slot; + int ret_err = -ENOMEM; + + arg->id = -1; + + if (!arg->stat || !arg->info || !arg->strs) + return -EINVAL; + + if (!arg->stat_len || !arg->info_len || !arg->strs_len) + return -EINVAL; + + if (!arg->description) + return -EINVAL; + + if (num_pages_stat > ARRAY_SIZE(pdesc->statPPNs) || + num_pages_info > ARRAY_SIZE(pdesc->infoPPNs) || + num_pages_strs > ARRAY_SIZE(pdesc->strsPPNs)) + return -EINVAL; + + /* Find an available slot in the mksGuestStats user array and reserve it */ + for (slot = 0; slot < ARRAY_SIZE(dev_priv->mksstat_user_pids); ++slot) + if (!atomic_cmpxchg(&dev_priv->mksstat_user_pids[slot], 0, MKSSTAT_PID_RESERVED)) + break; + + if (slot == ARRAY_SIZE(dev_priv->mksstat_user_pids)) + return -ENOSPC; + + BUG_ON(dev_priv->mksstat_user_pages[slot]); + + /* Allocate statically-sized temp arrays for pages -- too big to keep in frame */ + pages_stat = (struct page **)kmalloc_array( + ARRAY_SIZE(pdesc->statPPNs) + + ARRAY_SIZE(pdesc->infoPPNs) + + ARRAY_SIZE(pdesc->strsPPNs), sizeof(*pages_stat), GFP_KERNEL); + + if (!pages_stat) + goto err_nomem; + + pages_info = pages_stat + ARRAY_SIZE(pdesc->statPPNs); + pages_strs = pages_info + ARRAY_SIZE(pdesc->infoPPNs); + + /* Allocate a page for the instance descriptor */ + page = alloc_page(GFP_KERNEL | __GFP_ZERO); + + if (!page) + goto err_nomem; + + /* Set up the instance descriptor */ + pdesc = page_address(page); + + pdesc->reservedMBZ = 0; + pdesc->statStartVA = arg->stat; + pdesc->strsStartVA = arg->strs; + pdesc->statLength = arg->stat_len; + pdesc->infoLength = arg->info_len; + pdesc->strsLength = arg->strs_len; + desc_len = strncpy_from_user(pdesc->description, u64_to_user_ptr(arg->description), + ARRAY_SIZE(pdesc->description) - 1); + + if (desc_len < 0) { + ret_err = -EFAULT; + goto err_nomem; + } + + reset_ppn_array(pdesc->statPPNs, ARRAY_SIZE(pdesc->statPPNs)); + reset_ppn_array(pdesc->infoPPNs, ARRAY_SIZE(pdesc->infoPPNs)); + reset_ppn_array(pdesc->strsPPNs, ARRAY_SIZE(pdesc->strsPPNs)); + + /* Pin mksGuestStat user pages and store those in the instance descriptor */ + nr_pinned_stat = pin_user_pages_fast(arg->stat, num_pages_stat, FOLL_LONGTERM, pages_stat); + if (num_pages_stat != nr_pinned_stat) + goto err_pin_stat; + + for (i = 0; i < num_pages_stat; ++i) + pdesc->statPPNs[i] = page_to_pfn(pages_stat[i]); + + nr_pinned_info = pin_user_pages_fast(arg->info, num_pages_info, FOLL_LONGTERM, pages_info); + if (num_pages_info != nr_pinned_info) + goto err_pin_info; + + for (i = 0; i < num_pages_info; ++i) + pdesc->infoPPNs[i] = page_to_pfn(pages_info[i]); + + nr_pinned_strs = pin_user_pages_fast(arg->strs, num_pages_strs, FOLL_LONGTERM, pages_strs); + if (num_pages_strs != nr_pinned_strs) + goto err_pin_strs; + + for (i = 0; i < num_pages_strs; ++i) + pdesc->strsPPNs[i] = page_to_pfn(pages_strs[i]); + + /* Send the descriptor to the host via a hypervisor call. The mksGuestStat + pages will remain in use until the user requests a matching remove stats + or a stats reset occurs. */ + hypervisor_ppn_add((PPN64)page_to_pfn(page)); + + dev_priv->mksstat_user_pages[slot] = page; + atomic_set(&dev_priv->mksstat_user_pids[slot], task_pgrp_vnr(current)); + + arg->id = slot; + + DRM_DEV_INFO(dev->dev, "pid=%d arg.description='%.*s' id=%zu\n", current->pid, (int)desc_len, pdesc->description, slot); + + kfree(pages_stat); + return 0; + +err_pin_strs: + if (nr_pinned_strs > 0) + unpin_user_pages(pages_strs, nr_pinned_strs); + +err_pin_info: + if (nr_pinned_info > 0) + unpin_user_pages(pages_info, nr_pinned_info); + +err_pin_stat: + if (nr_pinned_stat > 0) + unpin_user_pages(pages_stat, nr_pinned_stat); + +err_nomem: + atomic_set(&dev_priv->mksstat_user_pids[slot], 0); + if (page) + __free_page(page); + kfree(pages_stat); + + return ret_err; +} + +/** + * vmw_mksstat_remove_ioctl: Removes a single userspace-originating mksGuestStat + * instance descriptor from the hypervisor. + * + * Discard a hypervisor PFN mapping, containing a single mksGuestStat instance + * descriptor and unpin the corresponding userspace pages. + * + * @dev: Identifies the drm device. + * @data: Pointer to the ioctl argument. + * @file_priv: Identifies the caller; unused. + * Return: Zero on success, negative error code on error. + */ + +int vmw_mksstat_remove_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_mksstat_remove_arg *arg = + (struct drm_vmw_mksstat_remove_arg *) data; + + struct vmw_private *const dev_priv = vmw_priv(dev); + + const size_t slot = arg->id; + pid_t pgid, pid; + + if (slot >= ARRAY_SIZE(dev_priv->mksstat_user_pids)) + return -EINVAL; + + DRM_DEV_INFO(dev->dev, "pid=%d arg.id=%zu\n", current->pid, slot); + + pgid = task_pgrp_vnr(current); + pid = atomic_cmpxchg(&dev_priv->mksstat_user_pids[slot], pgid, MKSSTAT_PID_RESERVED); + + if (!pid) + return 0; + + if (pid == pgid) { + struct page *const page = dev_priv->mksstat_user_pages[slot]; + + BUG_ON(!page); + + dev_priv->mksstat_user_pages[slot] = NULL; + atomic_set(&dev_priv->mksstat_user_pids[slot], 0); + + hypervisor_ppn_remove((PPN64)page_to_pfn(page)); + + vmw_mksstat_cleanup_descriptor(page); + return 0; + } + + return -EAGAIN; +} + +/** + * vmw_disable_backdoor: Disables all backdoor communication + * with the hypervisor. + */ +void vmw_disable_backdoor(void) +{ + vmw_msg_enabled = 0; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg_arm64.h b/drivers/gpu/drm/vmwgfx/vmwgfx_msg_arm64.h new file mode 100644 index 0000000000..4f40167ad6 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg_arm64.h @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright 2021 VMware, Inc., Palo Alto, CA., USA + * + * 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_MSG_ARM64_H +#define _VMWGFX_MSG_ARM64_H + +#if defined(__aarch64__) + +#define VMWARE_HYPERVISOR_PORT 0x5658 +#define VMWARE_HYPERVISOR_PORT_HB 0x5659 + +#define VMWARE_HYPERVISOR_HB BIT(0) +#define VMWARE_HYPERVISOR_OUT BIT(1) + +#define X86_IO_MAGIC 0x86 + +#define X86_IO_W7_SIZE_SHIFT 0 +#define X86_IO_W7_SIZE_MASK (0x3 << X86_IO_W7_SIZE_SHIFT) +#define X86_IO_W7_DIR (1 << 2) +#define X86_IO_W7_WITH (1 << 3) +#define X86_IO_W7_STR (1 << 4) +#define X86_IO_W7_DF (1 << 5) +#define X86_IO_W7_IMM_SHIFT 5 +#define X86_IO_W7_IMM_MASK (0xff << X86_IO_W7_IMM_SHIFT) + +static inline void vmw_port(unsigned long cmd, unsigned long in_ebx, + unsigned long in_si, unsigned long in_di, + unsigned long flags, unsigned long magic, + unsigned long *eax, unsigned long *ebx, + unsigned long *ecx, unsigned long *edx, + unsigned long *si, unsigned long *di) +{ + register u64 x0 asm("x0") = magic; + register u64 x1 asm("x1") = in_ebx; + register u64 x2 asm("x2") = cmd; + register u64 x3 asm("x3") = flags | VMWARE_HYPERVISOR_PORT; + register u64 x4 asm("x4") = in_si; + register u64 x5 asm("x5") = in_di; + + register u64 x7 asm("x7") = ((u64)X86_IO_MAGIC << 32) | + X86_IO_W7_WITH | + X86_IO_W7_DIR | + (2 << X86_IO_W7_SIZE_SHIFT); + + asm volatile("mrs xzr, mdccsr_el0 \n\t" + : "+r"(x0), "+r"(x1), "+r"(x2), + "+r"(x3), "+r"(x4), "+r"(x5) + : "r"(x7) + :); + *eax = x0; + *ebx = x1; + *ecx = x2; + *edx = x3; + *si = x4; + *di = x5; +} + +static inline void vmw_port_hb(unsigned long cmd, unsigned long in_ecx, + unsigned long in_si, unsigned long in_di, + unsigned long flags, unsigned long magic, + unsigned long bp, u32 w7dir, + unsigned long *eax, unsigned long *ebx, + unsigned long *ecx, unsigned long *edx, + unsigned long *si, unsigned long *di) +{ + register u64 x0 asm("x0") = magic; + register u64 x1 asm("x1") = cmd; + register u64 x2 asm("x2") = in_ecx; + register u64 x3 asm("x3") = flags | VMWARE_HYPERVISOR_PORT_HB; + register u64 x4 asm("x4") = in_si; + register u64 x5 asm("x5") = in_di; + register u64 x6 asm("x6") = bp; + register u64 x7 asm("x7") = ((u64)X86_IO_MAGIC << 32) | + X86_IO_W7_STR | + X86_IO_W7_WITH | + w7dir; + + asm volatile("mrs xzr, mdccsr_el0 \n\t" + : "+r"(x0), "+r"(x1), "+r"(x2), + "+r"(x3), "+r"(x4), "+r"(x5) + : "r"(x6), "r"(x7) + :); + *eax = x0; + *ebx = x1; + *ecx = x2; + *edx = x3; + *si = x4; + *di = x5; +} + +#define VMW_PORT(cmd, in_ebx, in_si, in_di, flags, magic, eax, ebx, ecx, edx, \ + si, di) \ + vmw_port(cmd, in_ebx, in_si, in_di, flags, magic, &eax, &ebx, &ecx, \ + &edx, &si, &di) + +#define VMW_PORT_HB_OUT(cmd, in_ecx, in_si, in_di, flags, magic, bp, eax, ebx, \ + ecx, edx, si, di) \ + vmw_port_hb(cmd, in_ecx, in_si, in_di, flags, magic, bp, \ + 0, &eax, &ebx, &ecx, &edx, &si, &di) + +#define VMW_PORT_HB_IN(cmd, in_ecx, in_si, in_di, flags, magic, bp, eax, ebx, \ + ecx, edx, si, di) \ + vmw_port_hb(cmd, in_ecx, in_si, in_di, flags, magic, bp, \ + X86_IO_W7_DIR, &eax, &ebx, &ecx, &edx, &si, &di) + +#endif + +#endif /* _VMWGFX_MSG_ARM64_H */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg_x86.h b/drivers/gpu/drm/vmwgfx/vmwgfx_msg_x86.h new file mode 100644 index 0000000000..23899d743a --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg_x86.h @@ -0,0 +1,227 @@ +/* SPDX-License-Identifier: GPL-2.0+ OR MIT */ +/************************************************************************** + * + * Copyright 2016-2021 VMware, Inc., Palo Alto, CA., USA + * + * 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. + * + ************************************************************************** + * + * Based on code from vmware.c and vmmouse.c. + * Author: + * Sinclair Yeh <syeh@vmware.com> + */ +#ifndef _VMWGFX_MSG_X86_H +#define _VMWGFX_MSG_X86_H + + +#if defined(__i386__) || defined(__x86_64__) + +#include <asm/vmware.h> + +/** + * Hypervisor-specific bi-directional communication channel. Should never + * execute on bare metal hardware. The caller must make sure to check for + * supported hypervisor before using these macros. + * + * The last two parameters are both input and output and must be initialized. + * + * @cmd: [IN] Message Cmd + * @in_ebx: [IN] Message Len, through EBX + * @in_si: [IN] Input argument through SI, set to 0 if not used + * @in_di: [IN] Input argument through DI, set ot 0 if not used + * @flags: [IN] hypercall flags + [channel id] + * @magic: [IN] hypervisor magic value + * @eax: [OUT] value of EAX register + * @ebx: [OUT] e.g. status from an HB message status command + * @ecx: [OUT] e.g. status from a non-HB message status command + * @edx: [OUT] e.g. channel id + * @si: [OUT] + * @di: [OUT] + */ +#define VMW_PORT(cmd, in_ebx, in_si, in_di, \ + flags, magic, \ + eax, ebx, ecx, edx, si, di) \ +({ \ + asm volatile (VMWARE_HYPERCALL : \ + "=a"(eax), \ + "=b"(ebx), \ + "=c"(ecx), \ + "=d"(edx), \ + "=S"(si), \ + "=D"(di) : \ + "a"(magic), \ + "b"(in_ebx), \ + "c"(cmd), \ + "d"(flags), \ + "S"(in_si), \ + "D"(in_di) : \ + "memory"); \ +}) + + +/** + * Hypervisor-specific bi-directional communication channel. Should never + * execute on bare metal hardware. The caller must make sure to check for + * supported hypervisor before using these macros. + * + * The last 3 parameters are both input and output and must be initialized. + * + * @cmd: [IN] Message Cmd + * @in_ecx: [IN] Message Len, through ECX + * @in_si: [IN] Input argument through SI, set to 0 if not used + * @in_di: [IN] Input argument through DI, set to 0 if not used + * @flags: [IN] hypercall flags + [channel id] + * @magic: [IN] hypervisor magic value + * @bp: [IN] + * @eax: [OUT] value of EAX register + * @ebx: [OUT] e.g. status from an HB message status command + * @ecx: [OUT] e.g. status from a non-HB message status command + * @edx: [OUT] e.g. channel id + * @si: [OUT] + * @di: [OUT] + */ +#ifdef __x86_64__ + +#define VMW_PORT_HB_OUT(cmd, in_ecx, in_si, in_di, \ + flags, magic, bp, \ + eax, ebx, ecx, edx, si, di) \ +({ \ + asm volatile ( \ + UNWIND_HINT_SAVE \ + "push %%rbp;" \ + UNWIND_HINT_UNDEFINED \ + "mov %12, %%rbp;" \ + VMWARE_HYPERCALL_HB_OUT \ + "pop %%rbp;" \ + UNWIND_HINT_RESTORE : \ + "=a"(eax), \ + "=b"(ebx), \ + "=c"(ecx), \ + "=d"(edx), \ + "=S"(si), \ + "=D"(di) : \ + "a"(magic), \ + "b"(cmd), \ + "c"(in_ecx), \ + "d"(flags), \ + "S"(in_si), \ + "D"(in_di), \ + "r"(bp) : \ + "memory", "cc"); \ +}) + + +#define VMW_PORT_HB_IN(cmd, in_ecx, in_si, in_di, \ + flags, magic, bp, \ + eax, ebx, ecx, edx, si, di) \ +({ \ + asm volatile ( \ + UNWIND_HINT_SAVE \ + "push %%rbp;" \ + UNWIND_HINT_UNDEFINED \ + "mov %12, %%rbp;" \ + VMWARE_HYPERCALL_HB_IN \ + "pop %%rbp;" \ + UNWIND_HINT_RESTORE : \ + "=a"(eax), \ + "=b"(ebx), \ + "=c"(ecx), \ + "=d"(edx), \ + "=S"(si), \ + "=D"(di) : \ + "a"(magic), \ + "b"(cmd), \ + "c"(in_ecx), \ + "d"(flags), \ + "S"(in_si), \ + "D"(in_di), \ + "r"(bp) : \ + "memory", "cc"); \ +}) + +#elif defined(__i386__) + +/* + * In the 32-bit version of this macro, we store bp in a memory location + * because we've ran out of registers. + * Now we can't reference that memory location while we've modified + * %esp or %ebp, so we first push it on the stack, just before we push + * %ebp, and then when we need it we read it from the stack where we + * just pushed it. + */ +#define VMW_PORT_HB_OUT(cmd, in_ecx, in_si, in_di, \ + flags, magic, bp, \ + eax, ebx, ecx, edx, si, di) \ +({ \ + asm volatile ("push %12;" \ + "push %%ebp;" \ + "mov 0x04(%%esp), %%ebp;" \ + VMWARE_HYPERCALL_HB_OUT \ + "pop %%ebp;" \ + "add $0x04, %%esp;" : \ + "=a"(eax), \ + "=b"(ebx), \ + "=c"(ecx), \ + "=d"(edx), \ + "=S"(si), \ + "=D"(di) : \ + "a"(magic), \ + "b"(cmd), \ + "c"(in_ecx), \ + "d"(flags), \ + "S"(in_si), \ + "D"(in_di), \ + "m"(bp) : \ + "memory", "cc"); \ +}) + + +#define VMW_PORT_HB_IN(cmd, in_ecx, in_si, in_di, \ + flags, magic, bp, \ + eax, ebx, ecx, edx, si, di) \ +({ \ + asm volatile ("push %12;" \ + "push %%ebp;" \ + "mov 0x04(%%esp), %%ebp;" \ + VMWARE_HYPERCALL_HB_IN \ + "pop %%ebp;" \ + "add $0x04, %%esp;" : \ + "=a"(eax), \ + "=b"(ebx), \ + "=c"(ecx), \ + "=d"(edx), \ + "=S"(si), \ + "=D"(di) : \ + "a"(magic), \ + "b"(cmd), \ + "c"(in_ecx), \ + "d"(flags), \ + "S"(in_si), \ + "D"(in_di), \ + "m"(bp) : \ + "memory", "cc"); \ +}) +#endif /* defined(__i386__) */ + +#endif /* defined(__i386__) || defined(__x86_64__) */ + +#endif /* _VMWGFX_MSG_X86_H */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c new file mode 100644 index 0000000000..c45b4724e4 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c @@ -0,0 +1,580 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_drv.h" + +#include "device_include/svga_overlay.h" +#include "device_include/svga_escape.h" + +#include <drm/ttm/ttm_placement.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_bo *buf; + bool claimed; + bool paused; + struct drm_vmw_control_stream_arg saved; +}; + +/* + * Overlay control + */ +struct vmw_overlay { + /* + * Each stream is a single overlay. In Xv these are called ports. + */ + struct mutex mutex; + struct vmw_stream stream[VMW_MAX_NUM_STREAMS]; +}; + +struct vmw_escape_header { + uint32_t cmd; + SVGAFifoCmdEscape body; +}; + +struct vmw_escape_video_flush { + struct vmw_escape_header escape; + SVGAEscapeVideoFlush flush; +}; + +static inline void fill_escape(struct vmw_escape_header *header, + uint32_t size) +{ + header->cmd = SVGA_CMD_ESCAPE; + header->body.nsid = SVGA_ESCAPE_NSID_VMWARE; + header->body.size = size; +} + +static inline void fill_flush(struct vmw_escape_video_flush *cmd, + uint32_t stream_id) +{ + fill_escape(&cmd->escape, sizeof(cmd->flush)); + cmd->flush.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_FLUSH; + cmd->flush.streamId = stream_id; +} + +/* + * Send put command to hw. + * + * Returns + * -ERESTARTSYS if interrupted by a signal. + */ +static int vmw_overlay_send_put(struct vmw_private *dev_priv, + struct vmw_bo *buf, + struct drm_vmw_control_stream_arg *arg, + bool interruptible) +{ + struct vmw_escape_video_flush *flush; + size_t fifo_size; + bool have_so = (dev_priv->active_display_unit == vmw_du_screen_object); + int i, num_items; + SVGAGuestPtr ptr; + + struct { + struct vmw_escape_header escape; + struct { + uint32_t cmdType; + uint32_t streamId; + } header; + } *cmds; + struct { + uint32_t registerId; + uint32_t value; + } *items; + + /* 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; + + fifo_size = sizeof(*cmds) + sizeof(*flush) + sizeof(*items) * num_items; + + cmds = VMW_CMD_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->tbo, &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_flush(flush, arg->stream_id); + + vmw_cmd_commit(dev_priv, fifo_size); + + return 0; +} + +/* + * Send stop command to hw. + * + * Returns + * -ERESTARTSYS if interrupted by a signal. + */ +static int vmw_overlay_send_stop(struct vmw_private *dev_priv, + uint32_t stream_id, + bool interruptible) +{ + struct { + struct vmw_escape_header escape; + SVGAEscapeVideoSetRegs body; + struct vmw_escape_video_flush flush; + } *cmds; + int ret; + + for (;;) { + cmds = VMW_CMD_RESERVE(dev_priv, sizeof(*cmds)); + if (cmds) + break; + + ret = vmw_fallback_wait(dev_priv, false, true, 0, + interruptible, 3*HZ); + if (interruptible && ret == -ERESTARTSYS) + return ret; + else + BUG_ON(ret != 0); + } + + fill_escape(&cmds->escape, sizeof(cmds->body)); + cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; + cmds->body.header.streamId = stream_id; + cmds->body.items[0].registerId = SVGA_VIDEO_ENABLED; + cmds->body.items[0].value = false; + fill_flush(&cmds->flush, stream_id); + + vmw_cmd_commit(dev_priv, sizeof(*cmds)); + + return 0; +} + +/* + * 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_bo *buf, + bool pin, bool inter) +{ + if (!pin) + return vmw_bo_unpin(dev_priv, buf, inter); + + if (dev_priv->active_display_unit == vmw_du_legacy) + return vmw_bo_pin_in_vram(dev_priv, buf, inter); + + return vmw_bo_pin_in_vram_or_gmr(dev_priv, buf, inter); +} + +/* + * Stop or pause a stream. + * + * If the stream is paused the no evict flag is removed from the buffer + * but left in vram. This allows for instance mode_set to evict it + * should it need to. + * + * The caller must hold the overlay lock. + * + * @stream_id which stream to stop/pause. + * @pause true to pause, false to stop completely. + */ +static int vmw_overlay_stop(struct vmw_private *dev_priv, + uint32_t stream_id, bool pause, + bool interruptible) +{ + struct vmw_overlay *overlay = dev_priv->overlay_priv; + struct vmw_stream *stream = &overlay->stream[stream_id]; + int ret; + + /* no buffer attached the stream is completely stopped */ + if (!stream->buf) + return 0; + + /* If the stream is paused this is already done */ + if (!stream->paused) { + ret = vmw_overlay_send_stop(dev_priv, stream_id, + interruptible); + if (ret) + return ret; + + /* We just remove the NO_EVICT flag so no -ENOMEM */ + ret = vmw_overlay_move_buffer(dev_priv, stream->buf, false, + interruptible); + if (interruptible && ret == -ERESTARTSYS) + return ret; + else + BUG_ON(ret != 0); + } + + if (!pause) { + vmw_bo_unreference(&stream->buf); + stream->paused = false; + } else { + stream->paused = true; + } + + return 0; +} + +/* + * Update a stream and send any put or stop fifo commands needed. + * + * The caller must hold the overlay lock. + * + * Returns + * -ENOMEM if buffer doesn't fit in vram. + * -ERESTARTSYS if interrupted. + */ +static int vmw_overlay_update_stream(struct vmw_private *dev_priv, + struct vmw_bo *buf, + struct drm_vmw_control_stream_arg *arg, + bool interruptible) +{ + struct vmw_overlay *overlay = dev_priv->overlay_priv; + struct vmw_stream *stream = &overlay->stream[arg->stream_id]; + int ret = 0; + + if (!buf) + return -EINVAL; + + DRM_DEBUG(" %s: old %p, new %p, %spaused\n", __func__, + stream->buf, buf, stream->paused ? "" : "not "); + + if (stream->buf != buf) { + ret = vmw_overlay_stop(dev_priv, arg->stream_id, + false, interruptible); + if (ret) + return ret; + } else if (!stream->paused) { + /* If the buffers match and not paused then just send + * the put command, no need to do anything else. + */ + ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible); + if (ret == 0) + stream->saved = *arg; + else + BUG_ON(!interruptible); + + return ret; + } + + /* We don't start the old stream if we are interrupted. + * Might return -ENOMEM if it can't fit the buffer in vram. + */ + ret = vmw_overlay_move_buffer(dev_priv, buf, true, interruptible); + if (ret) + return ret; + + ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible); + if (ret) { + /* This one needs to happen no matter what. We only remove + * the NO_EVICT flag so this is safe from -ENOMEM. + */ + BUG_ON(vmw_overlay_move_buffer(dev_priv, buf, false, false) + != 0); + return ret; + } + + if (stream->buf != buf) + stream->buf = vmw_bo_reference(buf); + stream->saved = *arg; + /* stream is no longer stopped/paused */ + stream->paused = false; + + return 0; +} + +/* + * Try to resume all paused streams. + * + * Used by the kms code after moving a new scanout buffer to vram. + * + * Takes the overlay lock. + */ +int vmw_overlay_resume_all(struct vmw_private *dev_priv) +{ + struct vmw_overlay *overlay = dev_priv->overlay_priv; + int i, ret; + + if (!overlay) + return 0; + + mutex_lock(&overlay->mutex); + + for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { + struct vmw_stream *stream = &overlay->stream[i]; + if (!stream->paused) + continue; + + ret = vmw_overlay_update_stream(dev_priv, stream->buf, + &stream->saved, false); + if (ret != 0) + DRM_INFO("%s: *warning* failed to resume stream %i\n", + __func__, i); + } + + mutex_unlock(&overlay->mutex); + + return 0; +} + +/* + * Pauses all active streams. + * + * Used by the kms code when moving a new scanout buffer to vram. + * + * Takes the overlay lock. + */ +int vmw_overlay_pause_all(struct vmw_private *dev_priv) +{ + struct vmw_overlay *overlay = dev_priv->overlay_priv; + int i, ret; + + if (!overlay) + return 0; + + mutex_lock(&overlay->mutex); + + for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { + if (overlay->stream[i].paused) + DRM_INFO("%s: *warning* stream %i already paused\n", + __func__, i); + ret = vmw_overlay_stop(dev_priv, i, true, false); + WARN_ON(ret != 0); + } + + mutex_unlock(&overlay->mutex); + + return 0; +} + + +static bool vmw_overlay_available(const struct vmw_private *dev_priv) +{ + return (dev_priv->overlay_priv != NULL && + ((vmw_fifo_caps(dev_priv) & VMW_OVERLAY_CAP_MASK) == + VMW_OVERLAY_CAP_MASK)); +} + +int vmw_overlay_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_overlay *overlay = dev_priv->overlay_priv; + struct drm_vmw_control_stream_arg *arg = + (struct drm_vmw_control_stream_arg *)data; + struct vmw_bo *buf; + struct vmw_resource *res; + int ret; + + if (!vmw_overlay_available(dev_priv)) + return -ENOSYS; + + ret = vmw_user_stream_lookup(dev_priv, tfile, &arg->stream_id, &res); + if (ret) + return ret; + + mutex_lock(&overlay->mutex); + + if (!arg->enabled) { + ret = vmw_overlay_stop(dev_priv, arg->stream_id, false, true); + goto out_unlock; + } + + ret = vmw_user_bo_lookup(file_priv, arg->handle, &buf); + if (ret) + goto out_unlock; + + ret = vmw_overlay_update_stream(dev_priv, buf, arg, true); + + vmw_user_bo_unref(&buf); + +out_unlock: + mutex_unlock(&overlay->mutex); + vmw_resource_unreference(&res); + + return ret; +} + +int vmw_overlay_num_overlays(struct vmw_private *dev_priv) +{ + if (!vmw_overlay_available(dev_priv)) + return 0; + + return VMW_MAX_NUM_STREAMS; +} + +int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv) +{ + struct vmw_overlay *overlay = dev_priv->overlay_priv; + int i, k; + + if (!vmw_overlay_available(dev_priv)) + return 0; + + mutex_lock(&overlay->mutex); + + for (i = 0, k = 0; i < VMW_MAX_NUM_STREAMS; i++) + if (!overlay->stream[i].claimed) + k++; + + mutex_unlock(&overlay->mutex); + + return k; +} + +int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out) +{ + struct vmw_overlay *overlay = dev_priv->overlay_priv; + int i; + + if (!overlay) + return -ENOSYS; + + mutex_lock(&overlay->mutex); + + for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { + + if (overlay->stream[i].claimed) + continue; + + overlay->stream[i].claimed = true; + *out = i; + mutex_unlock(&overlay->mutex); + return 0; + } + + mutex_unlock(&overlay->mutex); + return -ESRCH; +} + +int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id) +{ + struct vmw_overlay *overlay = dev_priv->overlay_priv; + + BUG_ON(stream_id >= VMW_MAX_NUM_STREAMS); + + if (!overlay) + return -ENOSYS; + + mutex_lock(&overlay->mutex); + + WARN_ON(!overlay->stream[stream_id].claimed); + vmw_overlay_stop(dev_priv, stream_id, false, false); + overlay->stream[stream_id].claimed = false; + + mutex_unlock(&overlay->mutex); + return 0; +} + +int vmw_overlay_init(struct vmw_private *dev_priv) +{ + struct vmw_overlay *overlay; + int i; + + if (dev_priv->overlay_priv) + return -EINVAL; + + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); + if (!overlay) + return -ENOMEM; + + mutex_init(&overlay->mutex); + for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { + overlay->stream[i].buf = NULL; + overlay->stream[i].paused = false; + overlay->stream[i].claimed = false; + } + + dev_priv->overlay_priv = overlay; + + return 0; +} + +int vmw_overlay_close(struct vmw_private *dev_priv) +{ + struct vmw_overlay *overlay = dev_priv->overlay_priv; + bool forgotten_buffer = false; + int i; + + if (!overlay) + return -ENOSYS; + + for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { + if (overlay->stream[i].buf) { + forgotten_buffer = true; + vmw_overlay_stop(dev_priv, i, false, false); + } + } + + WARN_ON(forgotten_buffer); + + dev_priv->overlay_priv = NULL; + kfree(overlay); + + return 0; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c new file mode 100644 index 0000000000..74ff2812d6 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2019-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_drv.h" + +/* + * Different methods for tracking dirty: + * VMW_BO_DIRTY_PAGETABLE - Scan the pagetable for hardware dirty bits + * VMW_BO_DIRTY_MKWRITE - Write-protect page table entries and record write- + * accesses in the VM mkwrite() callback + */ +enum vmw_bo_dirty_method { + VMW_BO_DIRTY_PAGETABLE, + VMW_BO_DIRTY_MKWRITE, +}; + +/* + * No dirtied pages at scan trigger a transition to the _MKWRITE method, + * similarly a certain percentage of dirty pages trigger a transition to + * the _PAGETABLE method. How many triggers should we wait for before + * changing method? + */ +#define VMW_DIRTY_NUM_CHANGE_TRIGGERS 2 + +/* Percentage to trigger a transition to the _PAGETABLE method */ +#define VMW_DIRTY_PERCENTAGE 10 + +/** + * struct vmw_bo_dirty - Dirty information for buffer objects + * @start: First currently dirty bit + * @end: Last currently dirty bit + 1 + * @method: The currently used dirty method + * @change_count: Number of consecutive method change triggers + * @ref_count: Reference count for this structure + * @bitmap_size: The size of the bitmap in bits. Typically equal to the + * nuber of pages in the bo. + * @bitmap: A bitmap where each bit represents a page. A set bit means a + * dirty page. + */ +struct vmw_bo_dirty { + unsigned long start; + unsigned long end; + enum vmw_bo_dirty_method method; + unsigned int change_count; + unsigned int ref_count; + unsigned long bitmap_size; + unsigned long bitmap[]; +}; + +/** + * vmw_bo_dirty_scan_pagetable - Perform a pagetable scan for dirty bits + * @vbo: The buffer object to scan + * + * Scans the pagetable for dirty bits. Clear those bits and modify the + * dirty structure with the results. This function may change the + * dirty-tracking method. + */ +static void vmw_bo_dirty_scan_pagetable(struct vmw_bo *vbo) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + pgoff_t offset = drm_vma_node_start(&vbo->tbo.base.vma_node); + struct address_space *mapping = vbo->tbo.bdev->dev_mapping; + pgoff_t num_marked; + + num_marked = clean_record_shared_mapping_range + (mapping, + offset, dirty->bitmap_size, + offset, &dirty->bitmap[0], + &dirty->start, &dirty->end); + if (num_marked == 0) + dirty->change_count++; + else + dirty->change_count = 0; + + if (dirty->change_count > VMW_DIRTY_NUM_CHANGE_TRIGGERS) { + dirty->change_count = 0; + dirty->method = VMW_BO_DIRTY_MKWRITE; + wp_shared_mapping_range(mapping, + offset, dirty->bitmap_size); + clean_record_shared_mapping_range(mapping, + offset, dirty->bitmap_size, + offset, &dirty->bitmap[0], + &dirty->start, &dirty->end); + } +} + +/** + * vmw_bo_dirty_scan_mkwrite - Reset the mkwrite dirty-tracking method + * @vbo: The buffer object to scan + * + * Write-protect pages written to so that consecutive write accesses will + * trigger a call to mkwrite. + * + * This function may change the dirty-tracking method. + */ +static void vmw_bo_dirty_scan_mkwrite(struct vmw_bo *vbo) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + unsigned long offset = drm_vma_node_start(&vbo->tbo.base.vma_node); + struct address_space *mapping = vbo->tbo.bdev->dev_mapping; + pgoff_t num_marked; + + if (dirty->end <= dirty->start) + return; + + num_marked = wp_shared_mapping_range(vbo->tbo.bdev->dev_mapping, + dirty->start + offset, + dirty->end - dirty->start); + + if (100UL * num_marked / dirty->bitmap_size > + VMW_DIRTY_PERCENTAGE) + dirty->change_count++; + else + dirty->change_count = 0; + + if (dirty->change_count > VMW_DIRTY_NUM_CHANGE_TRIGGERS) { + pgoff_t start = 0; + pgoff_t end = dirty->bitmap_size; + + dirty->method = VMW_BO_DIRTY_PAGETABLE; + clean_record_shared_mapping_range(mapping, offset, end, offset, + &dirty->bitmap[0], + &start, &end); + bitmap_clear(&dirty->bitmap[0], 0, dirty->bitmap_size); + if (dirty->start < dirty->end) + bitmap_set(&dirty->bitmap[0], dirty->start, + dirty->end - dirty->start); + dirty->change_count = 0; + } +} + +/** + * vmw_bo_dirty_scan - Scan for dirty pages and add them to the dirty + * tracking structure + * @vbo: The buffer object to scan + * + * This function may change the dirty tracking method. + */ +void vmw_bo_dirty_scan(struct vmw_bo *vbo) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + + if (dirty->method == VMW_BO_DIRTY_PAGETABLE) + vmw_bo_dirty_scan_pagetable(vbo); + else + vmw_bo_dirty_scan_mkwrite(vbo); +} + +/** + * vmw_bo_dirty_pre_unmap - write-protect and pick up dirty pages before + * an unmap_mapping_range operation. + * @vbo: The buffer object, + * @start: First page of the range within the buffer object. + * @end: Last page of the range within the buffer object + 1. + * + * If we're using the _PAGETABLE scan method, we may leak dirty pages + * when calling unmap_mapping_range(). This function makes sure we pick + * up all dirty pages. + */ +static void vmw_bo_dirty_pre_unmap(struct vmw_bo *vbo, + pgoff_t start, pgoff_t end) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + unsigned long offset = drm_vma_node_start(&vbo->tbo.base.vma_node); + struct address_space *mapping = vbo->tbo.bdev->dev_mapping; + + if (dirty->method != VMW_BO_DIRTY_PAGETABLE || start >= end) + return; + + wp_shared_mapping_range(mapping, start + offset, end - start); + clean_record_shared_mapping_range(mapping, start + offset, + end - start, offset, + &dirty->bitmap[0], &dirty->start, + &dirty->end); +} + +/** + * vmw_bo_dirty_unmap - Clear all ptes pointing to a range within a bo + * @vbo: The buffer object, + * @start: First page of the range within the buffer object. + * @end: Last page of the range within the buffer object + 1. + * + * This is similar to ttm_bo_unmap_virtual() except it takes a subrange. + */ +void vmw_bo_dirty_unmap(struct vmw_bo *vbo, + pgoff_t start, pgoff_t end) +{ + unsigned long offset = drm_vma_node_start(&vbo->tbo.base.vma_node); + struct address_space *mapping = vbo->tbo.bdev->dev_mapping; + + vmw_bo_dirty_pre_unmap(vbo, start, end); + unmap_shared_mapping_range(mapping, (offset + start) << PAGE_SHIFT, + (loff_t) (end - start) << PAGE_SHIFT); +} + +/** + * vmw_bo_dirty_add - Add a dirty-tracking user to a buffer object + * @vbo: The buffer object + * + * This function registers a dirty-tracking user to a buffer object. + * A user can be for example a resource or a vma in a special user-space + * mapping. + * + * Return: Zero on success, -ENOMEM on memory allocation failure. + */ +int vmw_bo_dirty_add(struct vmw_bo *vbo) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + pgoff_t num_pages = PFN_UP(vbo->tbo.resource->size); + size_t size; + int ret; + + if (dirty) { + dirty->ref_count++; + return 0; + } + + size = sizeof(*dirty) + BITS_TO_LONGS(num_pages) * sizeof(long); + dirty = kvzalloc(size, GFP_KERNEL); + if (!dirty) { + ret = -ENOMEM; + goto out_no_dirty; + } + + dirty->bitmap_size = num_pages; + dirty->start = dirty->bitmap_size; + dirty->end = 0; + dirty->ref_count = 1; + if (num_pages < PAGE_SIZE / sizeof(pte_t)) { + dirty->method = VMW_BO_DIRTY_PAGETABLE; + } else { + struct address_space *mapping = vbo->tbo.bdev->dev_mapping; + pgoff_t offset = drm_vma_node_start(&vbo->tbo.base.vma_node); + + dirty->method = VMW_BO_DIRTY_MKWRITE; + + /* Write-protect and then pick up already dirty bits */ + wp_shared_mapping_range(mapping, offset, num_pages); + clean_record_shared_mapping_range(mapping, offset, num_pages, + offset, + &dirty->bitmap[0], + &dirty->start, &dirty->end); + } + + vbo->dirty = dirty; + + return 0; + +out_no_dirty: + return ret; +} + +/** + * vmw_bo_dirty_release - Release a dirty-tracking user from a buffer object + * @vbo: The buffer object + * + * This function releases a dirty-tracking user from a buffer object. + * If the reference count reaches zero, then the dirty-tracking object is + * freed and the pointer to it cleared. + * + * Return: Zero on success, -ENOMEM on memory allocation failure. + */ +void vmw_bo_dirty_release(struct vmw_bo *vbo) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + + if (dirty && --dirty->ref_count == 0) { + kvfree(dirty); + vbo->dirty = NULL; + } +} + +/** + * vmw_bo_dirty_transfer_to_res - Pick up a resource's dirty region from + * its backing mob. + * @res: The resource + * + * This function will pick up all dirty ranges affecting the resource from + * it's backup mob, and call vmw_resource_dirty_update() once for each + * range. The transferred ranges will be cleared from the backing mob's + * dirty tracking. + */ +void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res) +{ + struct vmw_bo *vbo = res->guest_memory_bo; + struct vmw_bo_dirty *dirty = vbo->dirty; + pgoff_t start, cur, end; + unsigned long res_start = res->guest_memory_offset; + unsigned long res_end = res->guest_memory_offset + res->guest_memory_size; + + WARN_ON_ONCE(res_start & ~PAGE_MASK); + res_start >>= PAGE_SHIFT; + res_end = DIV_ROUND_UP(res_end, PAGE_SIZE); + + if (res_start >= dirty->end || res_end <= dirty->start) + return; + + cur = max(res_start, dirty->start); + res_end = max(res_end, dirty->end); + while (cur < res_end) { + unsigned long num; + + start = find_next_bit(&dirty->bitmap[0], res_end, cur); + if (start >= res_end) + break; + + end = find_next_zero_bit(&dirty->bitmap[0], res_end, start + 1); + cur = end + 1; + num = end - start; + bitmap_clear(&dirty->bitmap[0], start, num); + vmw_resource_dirty_update(res, start, end); + } + + if (res_start <= dirty->start && res_end > dirty->start) + dirty->start = res_end; + if (res_start < dirty->end && res_end >= dirty->end) + dirty->end = res_start; +} + +/** + * vmw_bo_dirty_clear_res - Clear a resource's dirty region from + * its backing mob. + * @res: The resource + * + * This function will clear all dirty ranges affecting the resource from + * it's backup mob's dirty tracking. + */ +void vmw_bo_dirty_clear_res(struct vmw_resource *res) +{ + unsigned long res_start = res->guest_memory_offset; + unsigned long res_end = res->guest_memory_offset + res->guest_memory_size; + struct vmw_bo *vbo = res->guest_memory_bo; + struct vmw_bo_dirty *dirty = vbo->dirty; + + res_start >>= PAGE_SHIFT; + res_end = DIV_ROUND_UP(res_end, PAGE_SIZE); + + if (res_start >= dirty->end || res_end <= dirty->start) + return; + + res_start = max(res_start, dirty->start); + res_end = min(res_end, dirty->end); + bitmap_clear(&dirty->bitmap[0], res_start, res_end - res_start); + + if (res_start <= dirty->start && res_end > dirty->start) + dirty->start = res_end; + if (res_start < dirty->end && res_end >= dirty->end) + dirty->end = res_start; +} + +vm_fault_t vmw_bo_vm_mkwrite(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; + struct ttm_buffer_object *bo = (struct ttm_buffer_object *) + vma->vm_private_data; + vm_fault_t ret; + unsigned long page_offset; + unsigned int save_flags; + struct vmw_bo *vbo = to_vmw_bo(&bo->base); + + /* + * mkwrite() doesn't handle the VM_FAULT_RETRY return value correctly. + * So make sure the TTM helpers are aware. + */ + save_flags = vmf->flags; + vmf->flags &= ~FAULT_FLAG_ALLOW_RETRY; + ret = ttm_bo_vm_reserve(bo, vmf); + vmf->flags = save_flags; + if (ret) + return ret; + + page_offset = vmf->pgoff - drm_vma_node_start(&bo->base.vma_node); + if (unlikely(page_offset >= PFN_UP(bo->resource->size))) { + ret = VM_FAULT_SIGBUS; + goto out_unlock; + } + + if (vbo->dirty && vbo->dirty->method == VMW_BO_DIRTY_MKWRITE && + !test_bit(page_offset, &vbo->dirty->bitmap[0])) { + struct vmw_bo_dirty *dirty = vbo->dirty; + + __set_bit(page_offset, &dirty->bitmap[0]); + dirty->start = min(dirty->start, page_offset); + dirty->end = max(dirty->end, page_offset + 1); + } + +out_unlock: + dma_resv_unlock(bo->base.resv); + return ret; +} + +vm_fault_t vmw_bo_vm_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; + struct ttm_buffer_object *bo = (struct ttm_buffer_object *) + vma->vm_private_data; + struct vmw_bo *vbo = to_vmw_bo(&bo->base); + pgoff_t num_prefault; + pgprot_t prot; + vm_fault_t ret; + + ret = ttm_bo_vm_reserve(bo, vmf); + if (ret) + return ret; + + num_prefault = (vma->vm_flags & VM_RAND_READ) ? 1 : + TTM_BO_VM_NUM_PREFAULT; + + if (vbo->dirty) { + pgoff_t allowed_prefault; + unsigned long page_offset; + + page_offset = vmf->pgoff - + drm_vma_node_start(&bo->base.vma_node); + if (page_offset >= PFN_UP(bo->resource->size) || + vmw_resources_clean(vbo, page_offset, + page_offset + PAGE_SIZE, + &allowed_prefault)) { + ret = VM_FAULT_SIGBUS; + goto out_unlock; + } + + num_prefault = min(num_prefault, allowed_prefault); + } + + /* + * If we don't track dirty using the MKWRITE method, make sure + * sure the page protection is write-enabled so we don't get + * a lot of unnecessary write faults. + */ + if (vbo->dirty && vbo->dirty->method == VMW_BO_DIRTY_MKWRITE) + prot = vm_get_page_prot(vma->vm_flags & ~VM_SHARED); + else + prot = vm_get_page_prot(vma->vm_flags); + + ret = ttm_bo_vm_fault_reserved(vmf, prot, num_prefault); + if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) + return ret; + +out_unlock: + dma_resv_unlock(bo->base.resv); + + return ret; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c b/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c new file mode 100644 index 0000000000..2d72a5ee7c --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2013 VMware, Inc., Palo Alto, CA., USA + * + * 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 "ttm_object.h" +#include <linux/dma-buf.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 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) +{ +} + +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, +}; + +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_reg.h b/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h new file mode 100644 index 0000000000..cf585dfe56 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright 2009-2014 VMware, Inc., Palo Alto, CA., USA + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * This file contains virtual hardware defines for kernel space. + */ + +#ifndef _VMWGFX_REG_H_ +#define _VMWGFX_REG_H_ + +#include <linux/types.h> + +struct svga_guest_mem_descriptor { + u32 ppn; + u32 num_pages; +}; + +struct svga_fifo_cmd_fence { + u32 fence; +}; + +#define SVGA_SYNC_GENERIC 1 +#define SVGA_SYNC_FIFOFULL 2 + +#include "device_include/svga3d_reg.h" + +#endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c new file mode 100644 index 0000000000..ca300c7427 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -0,0 +1,1144 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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 "vmwgfx_binding.h" +#include "vmwgfx_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" + +#define VMW_RES_EVICT_ERR_COUNT 10 + +/** + * vmw_resource_mob_attach - Mark a resource as attached to its backing mob + * @res: The resource + */ +void vmw_resource_mob_attach(struct vmw_resource *res) +{ + struct vmw_bo *gbo = res->guest_memory_bo; + struct rb_node **new = &gbo->res_tree.rb_node, *parent = NULL; + + dma_resv_assert_held(gbo->tbo.base.resv); + res->used_prio = (res->res_dirty) ? res->func->dirty_prio : + res->func->prio; + + while (*new) { + struct vmw_resource *this = + container_of(*new, struct vmw_resource, mob_node); + + parent = *new; + new = (res->guest_memory_offset < this->guest_memory_offset) ? + &((*new)->rb_left) : &((*new)->rb_right); + } + + rb_link_node(&res->mob_node, parent, new); + rb_insert_color(&res->mob_node, &gbo->res_tree); + + vmw_bo_prio_add(gbo, res->used_prio); +} + +/** + * vmw_resource_mob_detach - Mark a resource as detached from its backing mob + * @res: The resource + */ +void vmw_resource_mob_detach(struct vmw_resource *res) +{ + struct vmw_bo *gbo = res->guest_memory_bo; + + dma_resv_assert_held(gbo->tbo.base.resv); + if (vmw_resource_mob_attached(res)) { + rb_erase(&res->mob_node, &gbo->res_tree); + RB_CLEAR_NODE(&res->mob_node); + vmw_bo_prio_del(gbo, res->used_prio); + } +} + +struct vmw_resource *vmw_resource_reference(struct vmw_resource *res) +{ + kref_get(&res->kref); + 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]; + + spin_lock(&dev_priv->resource_lock); + if (res->id != -1) + idr_remove(idr, res->id); + res->id = -1; + spin_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; + int ret; + struct idr *idr = &dev_priv->res_idr[res->func->res_type]; + + spin_lock(&dev_priv->resource_lock); + list_del_init(&res->lru_head); + spin_unlock(&dev_priv->resource_lock); + if (res->guest_memory_bo) { + struct ttm_buffer_object *bo = &res->guest_memory_bo->tbo; + + ret = ttm_bo_reserve(bo, false, false, NULL); + BUG_ON(ret); + if (vmw_resource_mob_attached(res) && + res->func->unbind != NULL) { + struct ttm_validate_buffer val_buf; + + val_buf.bo = bo; + val_buf.num_shared = 0; + res->func->unbind(res, false, &val_buf); + } + res->guest_memory_size = false; + vmw_resource_mob_detach(res); + if (res->dirty) + res->func->dirty_free(res); + if (res->coherent) + vmw_bo_dirty_release(res->guest_memory_bo); + ttm_bo_unreserve(bo); + vmw_user_bo_unref(&res->guest_memory_bo); + } + + if (likely(res->hw_destroy != NULL)) { + mutex_lock(&dev_priv->binding_mutex); + vmw_binding_res_list_kill(&res->binding_head); + mutex_unlock(&dev_priv->binding_mutex); + res->hw_destroy(res); + } + + id = res->id; + if (res->res_free != NULL) + res->res_free(res); + else + kfree(res); + + spin_lock(&dev_priv->resource_lock); + if (id != -1) + idr_remove(idr, id); + spin_unlock(&dev_priv->resource_lock); +} + +void vmw_resource_unreference(struct vmw_resource **p_res) +{ + struct vmw_resource *res = *p_res; + + *p_res = NULL; + kref_put(&res->kref, vmw_resource_release); +} + + +/** + * 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); + spin_lock(&dev_priv->resource_lock); + + ret = idr_alloc(idr, res, 1, 0, GFP_NOWAIT); + if (ret >= 0) + res->id = ret; + + spin_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. + * @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->dev_priv = dev_priv; + res->func = func; + RB_CLEAR_NODE(&res->mob_node); + INIT_LIST_HEAD(&res->lru_head); + INIT_LIST_HEAD(&res->binding_head); + res->id = -1; + res->guest_memory_bo = NULL; + res->guest_memory_offset = 0; + res->guest_memory_dirty = false; + res->res_dirty = false; + res->coherent = false; + res->used_prio = 3; + res->dirty = NULL; + if (delay_id) + return 0; + else + return vmw_resource_alloc_id(res); +} + + +/** + * 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. + */ +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; + + base = ttm_base_object_lookup(tfile, handle); + if (unlikely(!base)) + return -EINVAL; + + if (unlikely(ttm_base_object_type(base) != converter->object_type)) + goto out_bad_resource; + + res = converter->base_obj_to_res(base); + kref_get(&res->kref); + + *p_res = res; + ret = 0; + +out_bad_resource: + ttm_base_object_unref(&base); + + return ret; +} + +/* + * Helper function that looks either a surface or bo. + * + * The pointer this pointed at by out_surf and out_buf needs to be null. + */ +int vmw_user_lookup_handle(struct vmw_private *dev_priv, + struct drm_file *filp, + uint32_t handle, + struct vmw_surface **out_surf, + struct vmw_bo **out_buf) +{ + struct ttm_object_file *tfile = vmw_fpriv(filp)->tfile; + struct vmw_resource *res; + int ret; + + BUG_ON(*out_surf || *out_buf); + + 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; + } + + *out_surf = NULL; + ret = vmw_user_bo_lookup(filp, handle, out_buf); + return ret; +} + +/** + * vmw_resource_buf_alloc - Allocate a guest memory buffer for a resource. + * + * @res: The resource for which to allocate a gbo 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 = PFN_ALIGN(res->guest_memory_size); + struct vmw_bo *gbo; + struct vmw_bo_params bo_params = { + .domain = res->func->domain, + .busy_domain = res->func->busy_domain, + .bo_type = ttm_bo_type_device, + .size = res->guest_memory_size, + .pin = false + }; + int ret; + + if (likely(res->guest_memory_bo)) { + BUG_ON(res->guest_memory_bo->tbo.base.size < size); + return 0; + } + + ret = vmw_gem_object_create(res->dev_priv, &bo_params, &gbo); + if (unlikely(ret != 0)) + goto out_no_bo; + + res->guest_memory_bo = gbo; + +out_no_bo: + 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. + * @dirtying: Transfer dirty regions. + * + * 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, + bool dirtying) +{ + 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_guest_memory && !vmw_resource_mob_attached(res) && + val_buf->bo) || + (!func->needs_guest_memory && val_buf->bo))) { + ret = func->bind(res, val_buf); + if (unlikely(ret != 0)) + goto out_bind_failed; + if (func->needs_guest_memory) + vmw_resource_mob_attach(res); + } + + /* + * Handle the case where the backup mob is marked coherent but + * the resource isn't. + */ + if (func->dirty_alloc && vmw_resource_mob_attached(res) && + !res->coherent) { + if (res->guest_memory_bo->dirty && !res->dirty) { + ret = func->dirty_alloc(res); + if (ret) + return ret; + } else if (!res->guest_memory_bo->dirty && res->dirty) { + func->dirty_free(res); + } + } + + /* + * Transfer the dirty regions to the resource and update + * the resource. + */ + if (res->dirty) { + if (dirtying && !res->res_dirty) { + pgoff_t start = res->guest_memory_offset >> PAGE_SHIFT; + pgoff_t end = __KERNEL_DIV_ROUND_UP + (res->guest_memory_offset + res->guest_memory_size, + PAGE_SIZE); + + vmw_bo_dirty_unmap(res->guest_memory_bo, start, end); + } + + vmw_bo_dirty_transfer_to_res(res); + return func->dirty_sync(res); + } + + 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. + * @dirty_set: Change dirty status of the resource. + * @dirty: When changing dirty status indicates the new status. + * @switch_guest_memory: Guest memory buffer has been switched. + * @new_guest_memory_bo: Pointer to new guest memory buffer if command submission + * switched. May be NULL. + * @new_guest_memory_offset: New gbo offset if @switch_guest_memory is true. + * + * 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, + bool dirty_set, + bool dirty, + bool switch_guest_memory, + struct vmw_bo *new_guest_memory_bo, + unsigned long new_guest_memory_offset) +{ + struct vmw_private *dev_priv = res->dev_priv; + + if (!list_empty(&res->lru_head)) + return; + + if (switch_guest_memory && new_guest_memory_bo != res->guest_memory_bo) { + if (res->guest_memory_bo) { + vmw_resource_mob_detach(res); + if (res->coherent) + vmw_bo_dirty_release(res->guest_memory_bo); + vmw_user_bo_unref(&res->guest_memory_bo); + } + + if (new_guest_memory_bo) { + res->guest_memory_bo = vmw_user_bo_ref(new_guest_memory_bo); + + /* + * The validation code should already have added a + * dirty tracker here. + */ + WARN_ON(res->coherent && !new_guest_memory_bo->dirty); + + vmw_resource_mob_attach(res); + } else { + res->guest_memory_bo = NULL; + } + } else if (switch_guest_memory && res->coherent) { + vmw_bo_dirty_release(res->guest_memory_bo); + } + + if (switch_guest_memory) + res->guest_memory_offset = new_guest_memory_offset; + + if (dirty_set) + res->res_dirty = dirty; + + if (!res->func->may_evict || res->id == -1 || res->pin_count) + return; + + spin_lock(&dev_priv->resource_lock); + list_add_tail(&res->lru_head, + &res->dev_priv->res_lru[res->func->res_type]); + spin_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. + * + * @ticket: The ww acquire context to use, or NULL if trylocking. + * @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 ww_acquire_ctx *ticket, + struct vmw_resource *res, + bool interruptible, + struct ttm_validate_buffer *val_buf) +{ + struct ttm_operation_ctx ctx = { true, false }; + struct list_head val_list; + bool guest_memory_dirty = false; + int ret; + + if (unlikely(!res->guest_memory_bo)) { + ret = vmw_resource_buf_alloc(res, interruptible); + if (unlikely(ret != 0)) + return ret; + } + + INIT_LIST_HEAD(&val_list); + ttm_bo_get(&res->guest_memory_bo->tbo); + val_buf->bo = &res->guest_memory_bo->tbo; + val_buf->num_shared = 0; + list_add_tail(&val_buf->head, &val_list); + ret = ttm_eu_reserve_buffers(ticket, &val_list, interruptible, NULL); + if (unlikely(ret != 0)) + goto out_no_reserve; + + if (res->func->needs_guest_memory && !vmw_resource_mob_attached(res)) + return 0; + + guest_memory_dirty = res->guest_memory_dirty; + vmw_bo_placement_set(res->guest_memory_bo, res->func->domain, + res->func->busy_domain); + ret = ttm_bo_validate(&res->guest_memory_bo->tbo, + &res->guest_memory_bo->placement, + &ctx); + + if (unlikely(ret != 0)) + goto out_no_validate; + + return 0; + +out_no_validate: + ttm_eu_backoff_reservation(ticket, &val_list); +out_no_reserve: + ttm_bo_put(val_buf->bo); + val_buf->bo = NULL; + if (guest_memory_dirty) + vmw_user_bo_unref(&res->guest_memory_bo); + + 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 guest memory 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 interruptible, + bool no_guest_memory) +{ + struct vmw_private *dev_priv = res->dev_priv; + int ret; + + spin_lock(&dev_priv->resource_lock); + list_del_init(&res->lru_head); + spin_unlock(&dev_priv->resource_lock); + + if (res->func->needs_guest_memory && !res->guest_memory_bo && + !no_guest_memory) { + ret = vmw_resource_buf_alloc(res, interruptible); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to allocate a guest memory buffer " + "of size %lu. bytes\n", + (unsigned long) res->guest_memory_size); + return ret; + } + } + + return 0; +} + +/** + * vmw_resource_backoff_reservation - Unreserve and unreference a + * guest memory buffer + *. + * @ticket: The ww acquire ctx used for reservation. + * @val_buf: Guest memory buffer information. + */ +static void +vmw_resource_backoff_reservation(struct ww_acquire_ctx *ticket, + 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(ticket, &val_list); + ttm_bo_put(val_buf->bo); + val_buf->bo = NULL; +} + +/** + * vmw_resource_do_evict - Evict a resource, and transfer its data + * to a backup buffer. + * + * @ticket: The ww acquire ticket to use, or NULL if trylocking. + * @res: The resource to evict. + * @interruptible: Whether to wait interruptible. + */ +static int vmw_resource_do_evict(struct ww_acquire_ctx *ticket, + 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; + val_buf.num_shared = 0; + ret = vmw_resource_check_buffer(ticket, res, interruptible, &val_buf); + if (unlikely(ret != 0)) + return ret; + + if (unlikely(func->unbind != NULL && + (!func->needs_guest_memory || vmw_resource_mob_attached(res)))) { + ret = func->unbind(res, res->res_dirty, &val_buf); + if (unlikely(ret != 0)) + goto out_no_unbind; + vmw_resource_mob_detach(res); + } + ret = func->destroy(res); + res->guest_memory_dirty = true; + res->res_dirty = false; +out_no_unbind: + vmw_resource_backoff_reservation(ticket, &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. + * @intr: Perform waits interruptible if possible. + * @dirtying: Pending GPU operation will dirty the resource + * + * On successful 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. + * + * Return: Zero on success, -ERESTARTSYS if interrupted, negative error code + * on failure. + */ +int vmw_resource_validate(struct vmw_resource *res, bool intr, + bool dirtying) +{ + 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 (!res->func->create) + return 0; + + val_buf.bo = NULL; + val_buf.num_shared = 0; + if (res->guest_memory_bo) + val_buf.bo = &res->guest_memory_bo->tbo; + do { + ret = vmw_resource_do_validate(res, &val_buf, dirtying); + if (likely(ret != -EBUSY)) + break; + + spin_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; + spin_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); + + spin_unlock(&dev_priv->resource_lock); + + /* Trylock backup buffers with a NULL ticket. */ + ret = vmw_resource_do_evict(NULL, evict_res, intr); + if (unlikely(ret != 0)) { + spin_lock(&dev_priv->resource_lock); + list_add_tail(&evict_res->lru_head, lru_list); + spin_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_guest_memory && res->guest_memory_bo) { + WARN_ON_ONCE(vmw_resource_mob_attached(res)); + vmw_user_bo_unref(&res->guest_memory_bo); + } + + return 0; + +out_no_validate: + return ret; +} + + +/** + * vmw_resource_unbind_list + * + * @vbo: Pointer to the current backing MOB. + * + * Evicts the Guest Backed hardware resource if the backup + * buffer is being moved out of MOB memory. + * Note that this function will not race with the resource + * validation code, since resource validation and eviction + * both require the backup buffer to be reserved. + */ +void vmw_resource_unbind_list(struct vmw_bo *vbo) +{ + struct ttm_validate_buffer val_buf = { + .bo = &vbo->tbo, + .num_shared = 0 + }; + + dma_resv_assert_held(vbo->tbo.base.resv); + while (!RB_EMPTY_ROOT(&vbo->res_tree)) { + struct rb_node *node = vbo->res_tree.rb_node; + struct vmw_resource *res = + container_of(node, struct vmw_resource, mob_node); + + if (!WARN_ON_ONCE(!res->func->unbind)) + (void) res->func->unbind(res, res->res_dirty, &val_buf); + + res->guest_memory_size = true; + res->res_dirty = false; + vmw_resource_mob_detach(res); + } + + (void) ttm_bo_wait(&vbo->tbo, false, false); +} + + +/** + * vmw_query_readback_all - Read back cached query states + * + * @dx_query_mob: Buffer containing the DX query MOB + * + * Read back cached states from the device if they exist. This function + * assumes binding_mutex is held. + */ +int vmw_query_readback_all(struct vmw_bo *dx_query_mob) +{ + struct vmw_resource *dx_query_ctx; + struct vmw_private *dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXReadbackAllQuery body; + } *cmd; + + + /* No query bound, so do nothing */ + if (!dx_query_mob || !dx_query_mob->dx_query_ctx) + return 0; + + dx_query_ctx = dx_query_mob->dx_query_ctx; + dev_priv = dx_query_ctx->dev_priv; + + cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), dx_query_ctx->id); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_READBACK_ALL_QUERY; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = dx_query_ctx->id; + + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + /* Triggers a rebind the next time affected context is bound */ + dx_query_mob->dx_query_ctx = NULL; + + return 0; +} + + + +/** + * vmw_query_move_notify - Read back cached query states + * + * @bo: The TTM buffer object about to move. + * @old_mem: The memory region @bo is moving from. + * @new_mem: The memory region @bo is moving to. + * + * Called before the query MOB is swapped out to read back cached query + * states from the device. + */ +void vmw_query_move_notify(struct ttm_buffer_object *bo, + struct ttm_resource *old_mem, + struct ttm_resource *new_mem) +{ + struct vmw_bo *dx_query_mob; + struct ttm_device *bdev = bo->bdev; + struct vmw_private *dev_priv = vmw_priv_from_ttm(bdev); + + mutex_lock(&dev_priv->binding_mutex); + + /* If BO is being moved from MOB to system memory */ + if (old_mem && + new_mem->mem_type == TTM_PL_SYSTEM && + old_mem->mem_type == VMW_PL_MOB) { + struct vmw_fence_obj *fence; + + dx_query_mob = to_vmw_bo(&bo->base); + if (!dx_query_mob || !dx_query_mob->dx_query_ctx) { + mutex_unlock(&dev_priv->binding_mutex); + return; + } + + (void) vmw_query_readback_all(dx_query_mob); + mutex_unlock(&dev_priv->binding_mutex); + + /* Create a fence and attach the BO to it */ + (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); + vmw_bo_fence_single(bo, fence); + + if (fence != NULL) + vmw_fence_obj_unreference(&fence); + + (void) ttm_bo_wait(bo, false, false); + } else + mutex_unlock(&dev_priv->binding_mutex); +} + +/** + * 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_guest_memory; +} + +/** + * 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; + struct ww_acquire_ctx ticket; + + do { + spin_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); + spin_unlock(&dev_priv->resource_lock); + + /* Wait lock backup buffers with a ticket. */ + ret = vmw_resource_do_evict(&ticket, evict_res, false); + if (unlikely(ret != 0)) { + spin_lock(&dev_priv->resource_lock); + list_add_tail(&evict_res->lru_head, lru_list); + spin_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: + spin_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); +} + +/* + * vmw_resource_pin - Add a pin reference on a resource + * + * @res: The resource to add a pin reference on + * + * This function adds a pin reference, and if needed validates the resource. + * Having a pin reference means that the resource can never be evicted, and + * its id will never change as long as there is a pin reference. + * This function returns 0 on success and a negative error code on failure. + */ +int vmw_resource_pin(struct vmw_resource *res, bool interruptible) +{ + struct ttm_operation_ctx ctx = { interruptible, false }; + struct vmw_private *dev_priv = res->dev_priv; + int ret; + + mutex_lock(&dev_priv->cmdbuf_mutex); + ret = vmw_resource_reserve(res, interruptible, false); + if (ret) + goto out_no_reserve; + + if (res->pin_count == 0) { + struct vmw_bo *vbo = NULL; + + if (res->guest_memory_bo) { + vbo = res->guest_memory_bo; + + ret = ttm_bo_reserve(&vbo->tbo, interruptible, false, NULL); + if (ret) + goto out_no_validate; + if (!vbo->tbo.pin_count) { + vmw_bo_placement_set(vbo, + res->func->domain, + res->func->busy_domain); + ret = ttm_bo_validate + (&vbo->tbo, + &vbo->placement, + &ctx); + if (ret) { + ttm_bo_unreserve(&vbo->tbo); + goto out_no_validate; + } + } + + /* Do we really need to pin the MOB as well? */ + vmw_bo_pin_reserved(vbo, true); + } + ret = vmw_resource_validate(res, interruptible, true); + if (vbo) + ttm_bo_unreserve(&vbo->tbo); + if (ret) + goto out_no_validate; + } + res->pin_count++; + +out_no_validate: + vmw_resource_unreserve(res, false, false, false, NULL, 0UL); +out_no_reserve: + mutex_unlock(&dev_priv->cmdbuf_mutex); + + return ret; +} + +/** + * vmw_resource_unpin - Remove a pin reference from a resource + * + * @res: The resource to remove a pin reference from + * + * Having a pin reference means that the resource can never be evicted, and + * its id will never change as long as there is a pin reference. + */ +void vmw_resource_unpin(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + int ret; + + mutex_lock(&dev_priv->cmdbuf_mutex); + + ret = vmw_resource_reserve(res, false, true); + WARN_ON(ret); + + WARN_ON(res->pin_count == 0); + if (--res->pin_count == 0 && res->guest_memory_bo) { + struct vmw_bo *vbo = res->guest_memory_bo; + + (void) ttm_bo_reserve(&vbo->tbo, false, false, NULL); + vmw_bo_pin_reserved(vbo, false); + ttm_bo_unreserve(&vbo->tbo); + } + + vmw_resource_unreserve(res, false, false, false, NULL, 0UL); + + mutex_unlock(&dev_priv->cmdbuf_mutex); +} + +/** + * vmw_res_type - Return the resource type + * + * @res: Pointer to the resource + */ +enum vmw_res_type vmw_res_type(const struct vmw_resource *res) +{ + return res->func->res_type; +} + +/** + * vmw_resource_dirty_update - Update a resource's dirty tracker with a + * sequential range of touched backing store memory. + * @res: The resource. + * @start: The first page touched. + * @end: The last page touched + 1. + */ +void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start, + pgoff_t end) +{ + if (res->dirty) + res->func->dirty_range_add(res, start << PAGE_SHIFT, + end << PAGE_SHIFT); +} + +/** + * vmw_resources_clean - Clean resources intersecting a mob range + * @vbo: The mob buffer object + * @start: The mob page offset starting the range + * @end: The mob page offset ending the range + * @num_prefault: Returns how many pages including the first have been + * cleaned and are ok to prefault + */ +int vmw_resources_clean(struct vmw_bo *vbo, pgoff_t start, + pgoff_t end, pgoff_t *num_prefault) +{ + struct rb_node *cur = vbo->res_tree.rb_node; + struct vmw_resource *found = NULL; + unsigned long res_start = start << PAGE_SHIFT; + unsigned long res_end = end << PAGE_SHIFT; + unsigned long last_cleaned = 0; + + /* + * Find the resource with lowest backup_offset that intersects the + * range. + */ + while (cur) { + struct vmw_resource *cur_res = + container_of(cur, struct vmw_resource, mob_node); + + if (cur_res->guest_memory_offset >= res_end) { + cur = cur->rb_left; + } else if (cur_res->guest_memory_offset + cur_res->guest_memory_size <= + res_start) { + cur = cur->rb_right; + } else { + found = cur_res; + cur = cur->rb_left; + /* Continue to look for resources with lower offsets */ + } + } + + /* + * In order of increasing guest_memory_offset, clean dirty resources + * intersecting the range. + */ + while (found) { + if (found->res_dirty) { + int ret; + + if (!found->func->clean) + return -EINVAL; + + ret = found->func->clean(found); + if (ret) + return ret; + + found->res_dirty = false; + } + last_cleaned = found->guest_memory_offset + found->guest_memory_size; + cur = rb_next(&found->mob_node); + if (!cur) + break; + + found = container_of(cur, struct vmw_resource, mob_node); + if (found->guest_memory_offset >= res_end) + break; + } + + /* + * Set number of pages allowed prefaulting and fence the buffer object + */ + *num_prefault = 1; + if (last_cleaned > res_start) { + struct ttm_buffer_object *bo = &vbo->tbo; + + *num_prefault = __KERNEL_DIV_ROUND_UP(last_cleaned - res_start, + PAGE_SIZE); + vmw_bo_fence_single(bo, NULL); + } + + return 0; +} 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 0000000000..aa7cbd396b --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright 2012-2014 VMware, Inc., Palo Alto, CA., USA + * + * 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" + +/* + * Extra memory required by the resource id's ida storage, which is allocated + * separately from the base object itself. We estimate an on-average 128 bytes + * per ida. + */ +#define VMW_IDA_ACC_SIZE 128 + +enum vmw_cmdbuf_res_state { + VMW_CMDBUF_RES_COMMITTED, + VMW_CMDBUF_RES_ADD, + VMW_CMDBUF_RES_DEL +}; + +/** + * 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_guest_memory:Whether the resource is guest-backed and needs + * persistent buffer storage. + * @type_name: String that identifies the resource type. + * @domain: TTM placement for guest memory buffers. + * @busy_domain: TTM busy placement for guest memory 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. + * @commit_notify: If the resource is a command buffer managed resource, + * callback to notify that a define or remove command + * has been committed to the device. + * @dirty_alloc: Allocate a dirty tracker. NULL if dirty-tracking is not + * supported. + * @dirty_free: Free the dirty tracker. + * @dirty_sync: Upload the dirty mob contents to the resource. + * @dirty_add_range: Add a sequential dirty range to the resource + * dirty tracker. + * @clean: Clean the resource. + */ +struct vmw_res_func { + enum vmw_res_type res_type; + bool needs_guest_memory; + const char *type_name; + u32 domain; + u32 busy_domain; + bool may_evict; + u32 prio; + u32 dirty_prio; + + 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); + void (*commit_notify)(struct vmw_resource *res, + enum vmw_cmdbuf_res_state state); + int (*dirty_alloc)(struct vmw_resource *res); + void (*dirty_free)(struct vmw_resource *res); + int (*dirty_sync)(struct vmw_resource *res); + void (*dirty_range_add)(struct vmw_resource *res, size_t start, + size_t end); + int (*clean)(struct vmw_resource *res); +}; + +/** + * struct vmw_simple_resource_func - members and functions common for the + * simple resource helpers. + * @res_func: struct vmw_res_func as described above. + * @ttm_res_type: TTM resource type used for handle recognition. + * @size: Size of the simple resource information struct. + * @init: Initialize the simple resource information. + * @hw_destroy: A resource hw_destroy function. + * @set_arg_handle: Set the handle output argument of the ioctl create struct. + */ +struct vmw_simple_resource_func { + const struct vmw_res_func res_func; + int ttm_res_type; + size_t size; + int (*init)(struct vmw_resource *res, void *data); + void (*hw_destroy)(struct vmw_resource *res); + void (*set_arg_handle)(void *data, u32 handle); +}; + +/** + * struct vmw_simple_resource - Kernel only side simple resource + * @res: The resource we derive from. + * @func: The method and member virtual table. + */ +struct vmw_simple_resource { + struct vmw_resource res; + const struct vmw_simple_resource_func *func; +}; + +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); +int +vmw_simple_resource_create_ioctl(struct drm_device *dev, + void *data, + struct drm_file *file_priv, + const struct vmw_simple_resource_func *func); +struct vmw_resource * +vmw_simple_resource_lookup(struct ttm_object_file *tfile, + uint32_t handle, + const struct vmw_simple_resource_func *func); +#endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c new file mode 100644 index 0000000000..556a403b7e --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -0,0 +1,1365 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2011-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_kms.h" + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_damage_helper.h> +#include <drm/drm_fourcc.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_kms_sou_surface_dirty - Closure structure for + * blit surface to screen command. + * @base: The base type we derive from. Used by vmw_kms_helper_dirty(). + * @left: Left side of bounding box. + * @right: Right side of bounding box. + * @top: Top side of bounding box. + * @bottom: Bottom side of bounding box. + * @dst_x: Difference between source clip rects and framebuffer coordinates. + * @dst_y: Difference between source clip rects and framebuffer coordinates. + * @sid: Surface id of surface to copy from. + */ +struct vmw_kms_sou_surface_dirty { + struct vmw_kms_dirty base; + s32 left, right, top, bottom; + s32 dst_x, dst_y; + u32 sid; +}; + +/* + * SVGA commands that are used by this code. Please see the device headers + * for explanation. + */ +struct vmw_kms_sou_readback_blit { + uint32 header; + SVGAFifoCmdBlitScreenToGMRFB body; +}; + +struct vmw_kms_sou_bo_blit { + uint32 header; + SVGAFifoCmdBlitGMRFBToScreen body; +}; + +struct vmw_kms_sou_dirty_cmd { + SVGA3dCmdHeader header; + SVGA3dCmdBlitSurfaceToScreen body; +}; + +struct vmw_kms_sou_define_gmrfb { + uint32_t header; + SVGAFifoCmdDefineGMRFB body; +}; + +/* + * 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_bo *buffer; /**< Backing store buffer */ + + bool defined; +}; + +static void vmw_sou_destroy(struct vmw_screen_object_unit *sou) +{ + vmw_du_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)); +} + +/* + * 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, + int x, int 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_CMD_RESERVE(dev_priv, fifo_size); + if (unlikely(cmd == NULL)) + 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; + cmd->obj.root.x = x; + cmd->obj.root.y = y; + sou->base.set_gui_x = cmd->obj.root.x; + sou->base.set_gui_y = cmd->obj.root.y; + + /* Ok to assume that buffer is pinned in vram */ + vmw_bo_get_guest_ptr(&sou->buffer->tbo, &cmd->obj.backingStore.ptr); + cmd->obj.backingStore.pitch = mode->hdisplay * 4; + + vmw_cmd_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_CMD_RESERVE(dev_priv, fifo_size); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + memset(cmd, 0, fifo_size); + cmd->header.cmdType = SVGA_CMD_DESTROY_SCREEN; + cmd->body.screenId = sou->base.unit; + + vmw_cmd_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; +} + +/** + * vmw_sou_crtc_mode_set_nofb - Create new screen + * + * @crtc: CRTC associated with the new screen + * + * This function creates/destroys a screen. This function cannot fail, so if + * somehow we run into a failure, just do the best we can to get out. + */ +static void vmw_sou_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_object_unit *sou; + struct vmw_framebuffer *vfb; + struct drm_framebuffer *fb; + struct drm_plane_state *ps; + struct vmw_plane_state *vps; + int ret; + + sou = vmw_crtc_to_sou(crtc); + dev_priv = vmw_priv(crtc->dev); + ps = crtc->primary->state; + fb = ps->fb; + vps = vmw_plane_state_to_vps(ps); + + vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; + + if (sou->defined) { + ret = vmw_sou_fifo_destroy(dev_priv, sou); + if (ret) { + DRM_ERROR("Failed to destroy Screen Object\n"); + return; + } + } + + if (vfb) { + struct drm_connector_state *conn_state; + struct vmw_connector_state *vmw_conn_state; + int x, y; + + sou->buffer = vps->bo; + sou->buffer_size = vps->bo_size; + + conn_state = sou->base.connector.state; + vmw_conn_state = vmw_connector_state_to_vcs(conn_state); + + x = vmw_conn_state->gui_x; + y = vmw_conn_state->gui_y; + + ret = vmw_sou_fifo_create(dev_priv, sou, x, y, &crtc->mode); + if (ret) + DRM_ERROR("Failed to define Screen Object %dx%d\n", + crtc->x, crtc->y); + + } else { + sou->buffer = NULL; + sou->buffer_size = 0; + } +} + +/** + * vmw_sou_crtc_helper_prepare - Noop + * + * @crtc: CRTC associated with the new screen + * + * Prepares the CRTC for a mode set, but we don't need to do anything here. + */ +static void vmw_sou_crtc_helper_prepare(struct drm_crtc *crtc) +{ +} + +/** + * vmw_sou_crtc_atomic_enable - Noop + * + * @crtc: CRTC associated with the new screen + * @state: Unused + * + * This is called after a mode set has been completed. + */ +static void vmw_sou_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ +} + +/** + * vmw_sou_crtc_atomic_disable - Turns off CRTC + * + * @crtc: CRTC to be turned off + * @state: Unused + */ +static void vmw_sou_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct vmw_private *dev_priv; + struct vmw_screen_object_unit *sou; + int ret; + + + if (!crtc) { + DRM_ERROR("CRTC is NULL\n"); + return; + } + + sou = vmw_crtc_to_sou(crtc); + dev_priv = vmw_priv(crtc->dev); + + if (sou->defined) { + ret = vmw_sou_fifo_destroy(dev_priv, sou); + if (ret) + DRM_ERROR("Failed to destroy Screen Object\n"); + } +} + +static const struct drm_crtc_funcs vmw_screen_object_crtc_funcs = { + .gamma_set = vmw_du_crtc_gamma_set, + .destroy = vmw_sou_crtc_destroy, + .reset = vmw_du_crtc_reset, + .atomic_duplicate_state = vmw_du_crtc_duplicate_state, + .atomic_destroy_state = vmw_du_crtc_destroy_state, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_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 const 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 const struct drm_connector_funcs vmw_sou_connector_funcs = { + .dpms = vmw_du_connector_dpms, + .detect = vmw_du_connector_detect, + .fill_modes = vmw_du_connector_fill_modes, + .destroy = vmw_sou_connector_destroy, + .reset = vmw_du_connector_reset, + .atomic_duplicate_state = vmw_du_connector_duplicate_state, + .atomic_destroy_state = vmw_du_connector_destroy_state, +}; + + +static const struct +drm_connector_helper_funcs vmw_sou_connector_helper_funcs = { +}; + + + +/* + * Screen Object Display Plane Functions + */ + +/** + * vmw_sou_primary_plane_cleanup_fb - Frees sou backing buffer + * + * @plane: display plane + * @old_state: Contains the FB to clean up + * + * Unpins the display surface + * + * Returns 0 on success + */ +static void +vmw_sou_primary_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); + struct drm_crtc *crtc = plane->state->crtc ? + plane->state->crtc : old_state->crtc; + + if (vps->bo) + vmw_bo_unpin(vmw_priv(crtc->dev), vps->bo, false); + vmw_bo_unreference(&vps->bo); + vps->bo_size = 0; + + vmw_du_plane_cleanup_fb(plane, old_state); +} + + +/** + * vmw_sou_primary_plane_prepare_fb - allocate backing buffer + * + * @plane: display plane + * @new_state: info on the new plane state, including the FB + * + * The SOU backing buffer is our equivalent of the display plane. + * + * Returns 0 on success + */ +static int +vmw_sou_primary_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_framebuffer *new_fb = new_state->fb; + struct drm_crtc *crtc = plane->state->crtc ?: new_state->crtc; + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + struct vmw_private *dev_priv; + int ret; + struct vmw_bo_params bo_params = { + .domain = VMW_BO_DOMAIN_VRAM, + .busy_domain = VMW_BO_DOMAIN_VRAM, + .bo_type = ttm_bo_type_device, + .pin = true + }; + + if (!new_fb) { + vmw_bo_unreference(&vps->bo); + vps->bo_size = 0; + + return 0; + } + + bo_params.size = new_state->crtc_w * new_state->crtc_h * 4; + dev_priv = vmw_priv(crtc->dev); + + if (vps->bo) { + if (vps->bo_size == bo_params.size) { + /* + * Note that this might temporarily up the pin-count + * to 2, until cleanup_fb() is called. + */ + return vmw_bo_pin_in_vram(dev_priv, vps->bo, + true); + } + + vmw_bo_unreference(&vps->bo); + vps->bo_size = 0; + } + + vmw_svga_enable(dev_priv); + + /* 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_bo_create(dev_priv, &bo_params, &vps->bo); + vmw_overlay_resume_all(dev_priv); + if (ret) + return ret; + + vps->bo_size = bo_params.size; + + /* + * TTM already thinks the buffer is pinned, but make sure the + * pin_count is upped. + */ + return vmw_bo_pin_in_vram(dev_priv, vps->bo, true); +} + +static uint32_t vmw_sou_bo_fifo_size(struct vmw_du_update_plane *update, + uint32_t num_hits) +{ + return sizeof(struct vmw_kms_sou_define_gmrfb) + + sizeof(struct vmw_kms_sou_bo_blit) * num_hits; +} + +static uint32_t vmw_sou_bo_define_gmrfb(struct vmw_du_update_plane *update, + void *cmd) +{ + struct vmw_framebuffer_bo *vfbbo = + container_of(update->vfb, typeof(*vfbbo), base); + struct vmw_kms_sou_define_gmrfb *gmr = cmd; + int depth = update->vfb->base.format->depth; + + /* 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; + + gmr->header = SVGA_CMD_DEFINE_GMRFB; + + gmr->body.format.bitsPerPixel = update->vfb->base.format->cpp[0] * 8; + gmr->body.format.colorDepth = depth; + gmr->body.format.reserved = 0; + gmr->body.bytesPerLine = update->vfb->base.pitches[0]; + vmw_bo_get_guest_ptr(&vfbbo->buffer->tbo, &gmr->body.ptr); + + return sizeof(*gmr); +} + +static uint32_t vmw_sou_bo_populate_clip(struct vmw_du_update_plane *update, + void *cmd, struct drm_rect *clip, + uint32_t fb_x, uint32_t fb_y) +{ + struct vmw_kms_sou_bo_blit *blit = cmd; + + blit->header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN; + blit->body.destScreenId = update->du->unit; + blit->body.srcOrigin.x = fb_x; + blit->body.srcOrigin.y = fb_y; + blit->body.destRect.left = clip->x1; + blit->body.destRect.top = clip->y1; + blit->body.destRect.right = clip->x2; + blit->body.destRect.bottom = clip->y2; + + return sizeof(*blit); +} + +static uint32_t vmw_stud_bo_post_clip(struct vmw_du_update_plane *update, + void *cmd, struct drm_rect *bb) +{ + return 0; +} + +/** + * vmw_sou_plane_update_bo - Update display unit for bo backed fb. + * @dev_priv: Device private. + * @plane: Plane state. + * @old_state: Old plane state. + * @vfb: Framebuffer which is blitted to display unit. + * @out_fence: If non-NULL, will return a ref-counted pointer to vmw_fence_obj. + * The returned fence pointer may be NULL in which case the device + * has already synchronized. + * + * Return: 0 on success or a negative error code on failure. + */ +static int vmw_sou_plane_update_bo(struct vmw_private *dev_priv, + struct drm_plane *plane, + struct drm_plane_state *old_state, + struct vmw_framebuffer *vfb, + struct vmw_fence_obj **out_fence) +{ + struct vmw_du_update_plane_buffer bo_update; + + memset(&bo_update, 0, sizeof(struct vmw_du_update_plane_buffer)); + bo_update.base.plane = plane; + bo_update.base.old_state = old_state; + bo_update.base.dev_priv = dev_priv; + bo_update.base.du = vmw_crtc_to_du(plane->state->crtc); + bo_update.base.vfb = vfb; + bo_update.base.out_fence = out_fence; + bo_update.base.mutex = NULL; + bo_update.base.intr = true; + + bo_update.base.calc_fifo_size = vmw_sou_bo_fifo_size; + bo_update.base.post_prepare = vmw_sou_bo_define_gmrfb; + bo_update.base.clip = vmw_sou_bo_populate_clip; + bo_update.base.post_clip = vmw_stud_bo_post_clip; + + return vmw_du_helper_plane_update(&bo_update.base); +} + +static uint32_t vmw_sou_surface_fifo_size(struct vmw_du_update_plane *update, + uint32_t num_hits) +{ + return sizeof(struct vmw_kms_sou_dirty_cmd) + sizeof(SVGASignedRect) * + num_hits; +} + +static uint32_t vmw_sou_surface_post_prepare(struct vmw_du_update_plane *update, + void *cmd) +{ + struct vmw_du_update_plane_surface *srf_update; + + srf_update = container_of(update, typeof(*srf_update), base); + + /* + * SOU SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN is special in the sense that + * its bounding box is filled before iterating over all the clips. So + * store the FIFO start address and revisit to fill the details. + */ + srf_update->cmd_start = cmd; + + return 0; +} + +static uint32_t vmw_sou_surface_pre_clip(struct vmw_du_update_plane *update, + void *cmd, uint32_t num_hits) +{ + struct vmw_kms_sou_dirty_cmd *blit = cmd; + struct vmw_framebuffer_surface *vfbs; + + vfbs = container_of(update->vfb, typeof(*vfbs), base); + + blit->header.id = SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN; + blit->header.size = sizeof(blit->body) + sizeof(SVGASignedRect) * + num_hits; + + blit->body.srcImage.sid = vfbs->surface->res.id; + blit->body.destScreenId = update->du->unit; + + /* Update the source and destination bounding box later in post_clip */ + blit->body.srcRect.left = 0; + blit->body.srcRect.top = 0; + blit->body.srcRect.right = 0; + blit->body.srcRect.bottom = 0; + + blit->body.destRect.left = 0; + blit->body.destRect.top = 0; + blit->body.destRect.right = 0; + blit->body.destRect.bottom = 0; + + return sizeof(*blit); +} + +static uint32_t vmw_sou_surface_clip_rect(struct vmw_du_update_plane *update, + void *cmd, struct drm_rect *clip, + uint32_t src_x, uint32_t src_y) +{ + SVGASignedRect *rect = cmd; + + /* + * rects are relative to dest bounding box rect on screen object, so + * translate to it later in post_clip + */ + rect->left = clip->x1; + rect->top = clip->y1; + rect->right = clip->x2; + rect->bottom = clip->y2; + + return sizeof(*rect); +} + +static uint32_t vmw_sou_surface_post_clip(struct vmw_du_update_plane *update, + void *cmd, struct drm_rect *bb) +{ + struct vmw_du_update_plane_surface *srf_update; + struct drm_plane_state *state = update->plane->state; + struct drm_rect src_bb; + struct vmw_kms_sou_dirty_cmd *blit; + SVGASignedRect *rect; + uint32_t num_hits; + int translate_src_x; + int translate_src_y; + int i; + + srf_update = container_of(update, typeof(*srf_update), base); + + blit = srf_update->cmd_start; + rect = (SVGASignedRect *)&blit[1]; + + num_hits = (blit->header.size - sizeof(blit->body))/ + sizeof(SVGASignedRect); + + src_bb = *bb; + + /* To translate bb back to fb src coord */ + translate_src_x = (state->src_x >> 16) - state->crtc_x; + translate_src_y = (state->src_y >> 16) - state->crtc_y; + + drm_rect_translate(&src_bb, translate_src_x, translate_src_y); + + blit->body.srcRect.left = src_bb.x1; + blit->body.srcRect.top = src_bb.y1; + blit->body.srcRect.right = src_bb.x2; + blit->body.srcRect.bottom = src_bb.y2; + + blit->body.destRect.left = bb->x1; + blit->body.destRect.top = bb->y1; + blit->body.destRect.right = bb->x2; + blit->body.destRect.bottom = bb->y2; + + /* rects are relative to dest bb rect */ + for (i = 0; i < num_hits; i++) { + rect->left -= bb->x1; + rect->top -= bb->y1; + rect->right -= bb->x1; + rect->bottom -= bb->y1; + rect++; + } + + return 0; +} + +/** + * vmw_sou_plane_update_surface - Update display unit for surface backed fb. + * @dev_priv: Device private. + * @plane: Plane state. + * @old_state: Old plane state. + * @vfb: Framebuffer which is blitted to display unit + * @out_fence: If non-NULL, will return a ref-counted pointer to vmw_fence_obj. + * The returned fence pointer may be NULL in which case the device + * has already synchronized. + * + * Return: 0 on success or a negative error code on failure. + */ +static int vmw_sou_plane_update_surface(struct vmw_private *dev_priv, + struct drm_plane *plane, + struct drm_plane_state *old_state, + struct vmw_framebuffer *vfb, + struct vmw_fence_obj **out_fence) +{ + struct vmw_du_update_plane_surface srf_update; + + memset(&srf_update, 0, sizeof(struct vmw_du_update_plane_surface)); + srf_update.base.plane = plane; + srf_update.base.old_state = old_state; + srf_update.base.dev_priv = dev_priv; + srf_update.base.du = vmw_crtc_to_du(plane->state->crtc); + srf_update.base.vfb = vfb; + srf_update.base.out_fence = out_fence; + srf_update.base.mutex = &dev_priv->cmdbuf_mutex; + srf_update.base.intr = true; + + srf_update.base.calc_fifo_size = vmw_sou_surface_fifo_size; + srf_update.base.post_prepare = vmw_sou_surface_post_prepare; + srf_update.base.pre_clip = vmw_sou_surface_pre_clip; + srf_update.base.clip = vmw_sou_surface_clip_rect; + srf_update.base.post_clip = vmw_sou_surface_post_clip; + + return vmw_du_helper_plane_update(&srf_update.base); +} + +static void +vmw_sou_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc *crtc = new_state->crtc; + struct vmw_fence_obj *fence = NULL; + int ret; + + /* In case of device error, maintain consistent atomic state */ + if (crtc && new_state->fb) { + struct vmw_private *dev_priv = vmw_priv(crtc->dev); + struct vmw_framebuffer *vfb = + vmw_framebuffer_to_vfb(new_state->fb); + + if (vfb->bo) + ret = vmw_sou_plane_update_bo(dev_priv, plane, + old_state, vfb, &fence); + else + ret = vmw_sou_plane_update_surface(dev_priv, plane, + old_state, vfb, + &fence); + if (ret != 0) + DRM_ERROR("Failed to update screen.\n"); + } else { + /* Do nothing when fb and crtc is NULL (blank crtc) */ + return; + } + + if (fence) + vmw_fence_obj_unreference(&fence); +} + + +static const struct drm_plane_funcs vmw_sou_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vmw_du_primary_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, +}; + +static const struct drm_plane_funcs vmw_sou_cursor_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vmw_du_cursor_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, +}; + +/* + * Atomic Helpers + */ +static const struct +drm_plane_helper_funcs vmw_sou_cursor_plane_helper_funcs = { + .atomic_check = vmw_du_cursor_plane_atomic_check, + .atomic_update = vmw_du_cursor_plane_atomic_update, + .prepare_fb = vmw_du_cursor_plane_prepare_fb, + .cleanup_fb = vmw_du_cursor_plane_cleanup_fb, +}; + +static const struct +drm_plane_helper_funcs vmw_sou_primary_plane_helper_funcs = { + .atomic_check = vmw_du_primary_plane_atomic_check, + .atomic_update = vmw_sou_primary_plane_atomic_update, + .prepare_fb = vmw_sou_primary_plane_prepare_fb, + .cleanup_fb = vmw_sou_primary_plane_cleanup_fb, +}; + +static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = { + .prepare = vmw_sou_crtc_helper_prepare, + .mode_set_nofb = vmw_sou_crtc_mode_set_nofb, + .atomic_check = vmw_du_crtc_atomic_check, + .atomic_begin = vmw_du_crtc_atomic_begin, + .atomic_flush = vmw_du_crtc_atomic_flush, + .atomic_enable = vmw_sou_crtc_atomic_enable, + .atomic_disable = vmw_sou_crtc_atomic_disable, +}; + + +static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) +{ + struct vmw_screen_object_unit *sou; + struct drm_device *dev = &dev_priv->drm; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_plane *primary; + struct vmw_cursor_plane *cursor; + struct drm_crtc *crtc; + int ret; + + 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; + primary = &sou->base.primary; + cursor = &sou->base.cursor; + + 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; + + /* + * Remove this after enabling atomic because property values can + * only exist in a state object + */ + sou->base.is_implicit = false; + + /* Initialize primary plane */ + ret = drm_universal_plane_init(dev, primary, + 0, &vmw_sou_plane_funcs, + vmw_primary_plane_formats, + ARRAY_SIZE(vmw_primary_plane_formats), + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + DRM_ERROR("Failed to initialize primary plane"); + goto err_free; + } + + drm_plane_helper_add(primary, &vmw_sou_primary_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(primary); + + /* Initialize cursor plane */ + ret = drm_universal_plane_init(dev, &cursor->base, + 0, &vmw_sou_cursor_funcs, + vmw_cursor_plane_formats, + ARRAY_SIZE(vmw_cursor_plane_formats), + NULL, DRM_PLANE_TYPE_CURSOR, NULL); + if (ret) { + DRM_ERROR("Failed to initialize cursor plane"); + drm_plane_cleanup(&sou->base.primary); + goto err_free; + } + + drm_plane_helper_add(&cursor->base, &vmw_sou_cursor_plane_helper_funcs); + + ret = drm_connector_init(dev, connector, &vmw_sou_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + goto err_free; + } + + drm_connector_helper_add(connector, &vmw_sou_connector_helper_funcs); + connector->status = vmw_du_connector_detect(connector, true); + + ret = drm_encoder_init(dev, encoder, &vmw_screen_object_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) { + DRM_ERROR("Failed to initialize encoder\n"); + goto err_free_connector; + } + + (void) drm_connector_attach_encoder(connector, encoder); + encoder->possible_crtcs = (1 << unit); + encoder->possible_clones = 0; + + ret = drm_connector_register(connector); + if (ret) { + DRM_ERROR("Failed to register connector\n"); + goto err_free_encoder; + } + + ret = drm_crtc_init_with_planes(dev, crtc, primary, + &cursor->base, + &vmw_screen_object_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("Failed to initialize CRTC\n"); + goto err_free_unregister; + } + + drm_crtc_helper_add(crtc, &vmw_sou_crtc_helper_funcs); + + drm_mode_crtc_set_gamma_size(crtc, 256); + + drm_object_attach_property(&connector->base, + dev_priv->hotplug_mode_update_property, 1); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_x_property, 0); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_y_property, 0); + return 0; + +err_free_unregister: + drm_connector_unregister(connector); +err_free_encoder: + drm_encoder_cleanup(encoder); +err_free_connector: + drm_connector_cleanup(connector); +err_free: + kfree(sou); + return ret; +} + +int vmw_kms_sou_init_display(struct vmw_private *dev_priv) +{ + struct drm_device *dev = &dev_priv->drm; + int i; + + /* Screen objects won't work if GMR's aren't available */ + if (!dev_priv->has_gmr) + return -ENOSYS; + + if (!(dev_priv->capabilities & SVGA_CAP_SCREEN_OBJECT_2)) { + return -ENOSYS; + } + + for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) + vmw_sou_init(dev_priv, i); + + dev_priv->active_display_unit = vmw_du_screen_object; + + drm_mode_config_reset(dev); + + return 0; +} + +static int do_bo_define_gmrfb(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer) +{ + struct vmw_bo *buf = + container_of(framebuffer, struct vmw_framebuffer_bo, + base)->buffer; + int depth = framebuffer->base.format->depth; + 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; + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (!cmd) + return -ENOMEM; + + cmd->header = SVGA_CMD_DEFINE_GMRFB; + cmd->body.format.bitsPerPixel = framebuffer->base.format->cpp[0] * 8; + cmd->body.format.colorDepth = depth; + cmd->body.format.reserved = 0; + cmd->body.bytesPerLine = framebuffer->base.pitches[0]; + /* Buffer is reserved in vram or GMR */ + vmw_bo_get_guest_ptr(&buf->tbo, &cmd->body.ptr); + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; +} + +/** + * vmw_sou_surface_fifo_commit - Callback to fill in and submit a + * blit surface to screen command. + * + * @dirty: The closure structure. + * + * Fills in the missing fields in the command, and translates the cliprects + * to match the destination bounding box encoded. + */ +static void vmw_sou_surface_fifo_commit(struct vmw_kms_dirty *dirty) +{ + struct vmw_kms_sou_surface_dirty *sdirty = + container_of(dirty, typeof(*sdirty), base); + struct vmw_kms_sou_dirty_cmd *cmd = dirty->cmd; + s32 trans_x = dirty->unit->crtc.x - sdirty->dst_x; + s32 trans_y = dirty->unit->crtc.y - sdirty->dst_y; + size_t region_size = dirty->num_hits * sizeof(SVGASignedRect); + SVGASignedRect *blit = (SVGASignedRect *) &cmd[1]; + int i; + + if (!dirty->num_hits) { + vmw_cmd_commit(dirty->dev_priv, 0); + return; + } + + cmd->header.id = SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN; + cmd->header.size = sizeof(cmd->body) + region_size; + + /* + * Use the destination bounding box to specify destination - and + * source bounding regions. + */ + cmd->body.destRect.left = sdirty->left; + cmd->body.destRect.right = sdirty->right; + cmd->body.destRect.top = sdirty->top; + cmd->body.destRect.bottom = sdirty->bottom; + + cmd->body.srcRect.left = sdirty->left + trans_x; + cmd->body.srcRect.right = sdirty->right + trans_x; + cmd->body.srcRect.top = sdirty->top + trans_y; + cmd->body.srcRect.bottom = sdirty->bottom + trans_y; + + cmd->body.srcImage.sid = sdirty->sid; + cmd->body.destScreenId = dirty->unit->unit; + + /* Blits are relative to the destination rect. Translate. */ + for (i = 0; i < dirty->num_hits; ++i, ++blit) { + blit->left -= sdirty->left; + blit->right -= sdirty->left; + blit->top -= sdirty->top; + blit->bottom -= sdirty->top; + } + + vmw_cmd_commit(dirty->dev_priv, region_size + sizeof(*cmd)); + + sdirty->left = sdirty->top = S32_MAX; + sdirty->right = sdirty->bottom = S32_MIN; +} + +/** + * vmw_sou_surface_clip - Callback to encode a blit surface to screen cliprect. + * + * @dirty: The closure structure + * + * Encodes a SVGASignedRect cliprect and updates the bounding box of the + * BLIT_SURFACE_TO_SCREEN command. + */ +static void vmw_sou_surface_clip(struct vmw_kms_dirty *dirty) +{ + struct vmw_kms_sou_surface_dirty *sdirty = + container_of(dirty, typeof(*sdirty), base); + struct vmw_kms_sou_dirty_cmd *cmd = dirty->cmd; + SVGASignedRect *blit = (SVGASignedRect *) &cmd[1]; + + /* Destination rect. */ + blit += dirty->num_hits; + blit->left = dirty->unit_x1; + blit->top = dirty->unit_y1; + blit->right = dirty->unit_x2; + blit->bottom = dirty->unit_y2; + + /* Destination bounding box */ + sdirty->left = min_t(s32, sdirty->left, dirty->unit_x1); + sdirty->top = min_t(s32, sdirty->top, dirty->unit_y1); + sdirty->right = max_t(s32, sdirty->right, dirty->unit_x2); + sdirty->bottom = max_t(s32, sdirty->bottom, dirty->unit_y2); + + dirty->num_hits++; +} + +/** + * vmw_kms_sou_do_surface_dirty - Dirty part of a surface backed framebuffer + * + * @dev_priv: Pointer to the device private structure. + * @framebuffer: Pointer to the surface-buffer backed framebuffer. + * @clips: Array of clip rects. Either @clips or @vclips must be NULL. + * @vclips: Alternate array of clip rects. Either @clips or @vclips must + * be NULL. + * @srf: Pointer to surface to blit from. If NULL, the surface attached + * to @framebuffer will be used. + * @dest_x: X coordinate offset to align @srf with framebuffer coordinates. + * @dest_y: Y coordinate offset to align @srf with framebuffer coordinates. + * @num_clips: Number of clip rects in @clips. + * @inc: Increment to use when looping over @clips. + * @out_fence: If non-NULL, will return a ref-counted pointer to a + * struct vmw_fence_obj. The returned fence pointer may be NULL in which + * case the device has already synchronized. + * @crtc: If crtc is passed, perform surface dirty on that crtc only. + * + * Returns 0 on success, negative error code on failure. -ERESTARTSYS if + * interrupted. + */ +int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + struct drm_clip_rect *clips, + struct drm_vmw_rect *vclips, + struct vmw_resource *srf, + s32 dest_x, + s32 dest_y, + unsigned num_clips, int inc, + struct vmw_fence_obj **out_fence, + struct drm_crtc *crtc) +{ + struct vmw_framebuffer_surface *vfbs = + container_of(framebuffer, typeof(*vfbs), base); + struct vmw_kms_sou_surface_dirty sdirty; + DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); + int ret; + + if (!srf) + srf = &vfbs->surface->res; + + ret = vmw_validation_add_resource(&val_ctx, srf, 0, VMW_RES_DIRTY_NONE, + NULL, NULL); + if (ret) + return ret; + + ret = vmw_validation_prepare(&val_ctx, &dev_priv->cmdbuf_mutex, true); + if (ret) + goto out_unref; + + sdirty.base.fifo_commit = vmw_sou_surface_fifo_commit; + sdirty.base.clip = vmw_sou_surface_clip; + sdirty.base.dev_priv = dev_priv; + sdirty.base.fifo_reserve_size = sizeof(struct vmw_kms_sou_dirty_cmd) + + sizeof(SVGASignedRect) * num_clips; + sdirty.base.crtc = crtc; + + sdirty.sid = srf->id; + sdirty.left = sdirty.top = S32_MAX; + sdirty.right = sdirty.bottom = S32_MIN; + sdirty.dst_x = dest_x; + sdirty.dst_y = dest_y; + + ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips, + dest_x, dest_y, num_clips, inc, + &sdirty.base); + vmw_kms_helper_validation_finish(dev_priv, NULL, &val_ctx, out_fence, + NULL); + + return ret; + +out_unref: + vmw_validation_unref_lists(&val_ctx); + return ret; +} + +/** + * vmw_sou_bo_fifo_commit - Callback to submit a set of readback clips. + * + * @dirty: The closure structure. + * + * Commits a previously built command buffer of readback clips. + */ +static void vmw_sou_bo_fifo_commit(struct vmw_kms_dirty *dirty) +{ + if (!dirty->num_hits) { + vmw_cmd_commit(dirty->dev_priv, 0); + return; + } + + vmw_cmd_commit(dirty->dev_priv, + sizeof(struct vmw_kms_sou_bo_blit) * + dirty->num_hits); +} + +/** + * vmw_sou_bo_clip - Callback to encode a readback cliprect. + * + * @dirty: The closure structure + * + * Encodes a BLIT_GMRFB_TO_SCREEN cliprect. + */ +static void vmw_sou_bo_clip(struct vmw_kms_dirty *dirty) +{ + struct vmw_kms_sou_bo_blit *blit = dirty->cmd; + + blit += dirty->num_hits; + blit->header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN; + blit->body.destScreenId = dirty->unit->unit; + blit->body.srcOrigin.x = dirty->fb_x; + blit->body.srcOrigin.y = dirty->fb_y; + blit->body.destRect.left = dirty->unit_x1; + blit->body.destRect.top = dirty->unit_y1; + blit->body.destRect.right = dirty->unit_x2; + blit->body.destRect.bottom = dirty->unit_y2; + dirty->num_hits++; +} + +/** + * vmw_kms_sou_do_bo_dirty - Dirty part of a buffer-object backed framebuffer + * + * @dev_priv: Pointer to the device private structure. + * @framebuffer: Pointer to the buffer-object backed framebuffer. + * @clips: Array of clip rects. + * @vclips: Alternate array of clip rects. Either @clips or @vclips must + * be NULL. + * @num_clips: Number of clip rects in @clips. + * @increment: Increment to use when looping over @clips. + * @interruptible: Whether to perform waits interruptible if possible. + * @out_fence: If non-NULL, will return a ref-counted pointer to a + * struct vmw_fence_obj. The returned fence pointer may be NULL in which + * case the device has already synchronized. + * @crtc: If crtc is passed, perform bo dirty on that crtc only. + * + * Returns 0 on success, negative error code on failure. -ERESTARTSYS if + * interrupted. + */ +int vmw_kms_sou_do_bo_dirty(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + struct drm_clip_rect *clips, + struct drm_vmw_rect *vclips, + unsigned num_clips, int increment, + bool interruptible, + struct vmw_fence_obj **out_fence, + struct drm_crtc *crtc) +{ + struct vmw_bo *buf = + container_of(framebuffer, struct vmw_framebuffer_bo, + base)->buffer; + struct vmw_kms_dirty dirty; + DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); + int ret; + + vmw_bo_placement_set(buf, VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM, + VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM); + ret = vmw_validation_add_bo(&val_ctx, buf); + if (ret) + return ret; + + ret = vmw_validation_prepare(&val_ctx, NULL, interruptible); + if (ret) + goto out_unref; + + ret = do_bo_define_gmrfb(dev_priv, framebuffer); + if (unlikely(ret != 0)) + goto out_revert; + + dirty.crtc = crtc; + dirty.fifo_commit = vmw_sou_bo_fifo_commit; + dirty.clip = vmw_sou_bo_clip; + dirty.fifo_reserve_size = sizeof(struct vmw_kms_sou_bo_blit) * + num_clips; + ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips, + 0, 0, num_clips, increment, &dirty); + vmw_kms_helper_validation_finish(dev_priv, NULL, &val_ctx, out_fence, + NULL); + + return ret; + +out_revert: + vmw_validation_revert(&val_ctx); +out_unref: + vmw_validation_unref_lists(&val_ctx); + + return ret; +} + + +/** + * vmw_sou_readback_fifo_commit - Callback to submit a set of readback clips. + * + * @dirty: The closure structure. + * + * Commits a previously built command buffer of readback clips. + */ +static void vmw_sou_readback_fifo_commit(struct vmw_kms_dirty *dirty) +{ + if (!dirty->num_hits) { + vmw_cmd_commit(dirty->dev_priv, 0); + return; + } + + vmw_cmd_commit(dirty->dev_priv, + sizeof(struct vmw_kms_sou_readback_blit) * + dirty->num_hits); +} + +/** + * vmw_sou_readback_clip - Callback to encode a readback cliprect. + * + * @dirty: The closure structure + * + * Encodes a BLIT_SCREEN_TO_GMRFB cliprect. + */ +static void vmw_sou_readback_clip(struct vmw_kms_dirty *dirty) +{ + struct vmw_kms_sou_readback_blit *blit = dirty->cmd; + + blit += dirty->num_hits; + blit->header = SVGA_CMD_BLIT_SCREEN_TO_GMRFB; + blit->body.srcScreenId = dirty->unit->unit; + blit->body.destOrigin.x = dirty->fb_x; + blit->body.destOrigin.y = dirty->fb_y; + blit->body.srcRect.left = dirty->unit_x1; + blit->body.srcRect.top = dirty->unit_y1; + blit->body.srcRect.right = dirty->unit_x2; + blit->body.srcRect.bottom = dirty->unit_y2; + dirty->num_hits++; +} + +/** + * vmw_kms_sou_readback - Perform a readback from the screen object system to + * a buffer-object backed framebuffer. + * + * @dev_priv: Pointer to the device private structure. + * @file_priv: Pointer to a struct drm_file identifying the caller. + * Must be set to NULL if @user_fence_rep is NULL. + * @vfb: Pointer to the buffer-object backed framebuffer. + * @user_fence_rep: User-space provided structure for fence information. + * Must be set to non-NULL if @file_priv is non-NULL. + * @vclips: Array of clip rects. + * @num_clips: Number of clip rects in @vclips. + * @crtc: If crtc is passed, readback on that crtc only. + * + * Returns 0 on success, negative error code on failure. -ERESTARTSYS if + * interrupted. + */ +int vmw_kms_sou_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 *vclips, + uint32_t num_clips, + struct drm_crtc *crtc) +{ + struct vmw_bo *buf = + container_of(vfb, struct vmw_framebuffer_bo, base)->buffer; + struct vmw_kms_dirty dirty; + DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); + int ret; + + vmw_bo_placement_set(buf, VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM, + VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM); + ret = vmw_validation_add_bo(&val_ctx, buf); + if (ret) + return ret; + + ret = vmw_validation_prepare(&val_ctx, NULL, true); + if (ret) + goto out_unref; + + ret = do_bo_define_gmrfb(dev_priv, vfb); + if (unlikely(ret != 0)) + goto out_revert; + + dirty.crtc = crtc; + dirty.fifo_commit = vmw_sou_readback_fifo_commit; + dirty.clip = vmw_sou_readback_clip; + dirty.fifo_reserve_size = sizeof(struct vmw_kms_sou_readback_blit) * + num_clips; + ret = vmw_kms_helper_dirty(dev_priv, vfb, NULL, vclips, + 0, 0, num_clips, 1, &dirty); + vmw_kms_helper_validation_finish(dev_priv, file_priv, &val_ctx, NULL, + user_fence_rep); + + return ret; + +out_revert: + vmw_validation_revert(&val_ctx); +out_unref: + vmw_validation_unref_lists(&val_ctx); + + return ret; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c new file mode 100644 index 0000000000..a01ca3226d --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c @@ -0,0 +1,976 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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 "vmwgfx_binding.h" +#include "vmwgfx_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" + +struct vmw_shader { + struct vmw_resource res; + SVGA3dShaderType type; + uint32_t size; + uint8_t num_input_sig; + uint8_t num_output_sig; +}; + +struct vmw_user_shader { + struct ttm_base_object base; + struct vmw_shader shader; +}; + +struct vmw_dx_shader { + struct vmw_resource res; + struct vmw_resource *ctx; + struct vmw_resource *cotable; + u32 id; + bool committed; + struct list_head cotable_head; +}; + +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 int vmw_dx_shader_create(struct vmw_resource *res); +static int vmw_dx_shader_bind(struct vmw_resource *res, + struct ttm_validate_buffer *val_buf); +static int vmw_dx_shader_unbind(struct vmw_resource *res, + bool readback, + struct ttm_validate_buffer *val_buf); +static void vmw_dx_shader_commit_notify(struct vmw_resource *res, + enum vmw_cmdbuf_res_state state); +static bool vmw_shader_id_ok(u32 user_key, SVGA3dShaderType shader_type); +static u32 vmw_shader_key(u32 user_key, SVGA3dShaderType shader_type); + +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_guest_memory = true, + .may_evict = true, + .prio = 3, + .dirty_prio = 3, + .type_name = "guest backed shaders", + .domain = VMW_BO_DOMAIN_MOB, + .busy_domain = VMW_BO_DOMAIN_MOB, + .create = vmw_gb_shader_create, + .destroy = vmw_gb_shader_destroy, + .bind = vmw_gb_shader_bind, + .unbind = vmw_gb_shader_unbind +}; + +static const struct vmw_res_func vmw_dx_shader_func = { + .res_type = vmw_res_shader, + .needs_guest_memory = true, + .may_evict = true, + .prio = 3, + .dirty_prio = 3, + .type_name = "dx shaders", + .domain = VMW_BO_DOMAIN_MOB, + .busy_domain = VMW_BO_DOMAIN_MOB, + .create = vmw_dx_shader_create, + /* + * The destroy callback is only called with a committed resource on + * context destroy, in which case we destroy the cotable anyway, + * so there's no need to destroy DX shaders separately. + */ + .destroy = NULL, + .bind = vmw_dx_shader_bind, + .unbind = vmw_dx_shader_unbind, + .commit_notify = vmw_dx_shader_commit_notify, +}; + +/* + * Shader management: + */ + +static inline struct vmw_shader * +vmw_res_to_shader(struct vmw_resource *res) +{ + return container_of(res, struct vmw_shader, res); +} + +/** + * vmw_res_to_dx_shader - typecast a struct vmw_resource to a + * struct vmw_dx_shader + * + * @res: Pointer to the struct vmw_resource. + */ +static inline struct vmw_dx_shader * +vmw_res_to_dx_shader(struct vmw_resource *res) +{ + return container_of(res, struct vmw_dx_shader, res); +} + +static void vmw_hw_shader_destroy(struct vmw_resource *res) +{ + if (likely(res->func->destroy)) + (void) res->func->destroy(res); + else + res->id = -1; +} + + +static int vmw_gb_shader_init(struct vmw_private *dev_priv, + struct vmw_resource *res, + uint32_t size, + uint64_t offset, + SVGA3dShaderType type, + uint8_t num_input_sig, + uint8_t num_output_sig, + struct vmw_bo *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->guest_memory_size = size; + if (byte_code) { + res->guest_memory_bo = vmw_user_bo_ref(byte_code); + res->guest_memory_offset = offset; + } + shader->size = size; + shader->type = type; + shader->num_input_sig = num_input_sig; + shader->num_output_sig = num_output_sig; + + res->hw_destroy = vmw_hw_shader_destroy; + return 0; +} + +/* + * GB shader code: + */ + +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_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) { + 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_cmd_commit(dev_priv, sizeof(*cmd)); + vmw_fifo_resource_inc(dev_priv); + + 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->resource->mem_type != VMW_PL_MOB); + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + 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->resource->start; + cmd->body.offsetInBytes = res->guest_memory_offset; + res->guest_memory_dirty = false; + vmw_cmd_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->guest_memory_bo->tbo.resource->mem_type != VMW_PL_MOB); + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + 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_cmd_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_bo_fence_single(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_binding_res_list_scrub(&res->binding_head); + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) { + 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_cmd_commit(dev_priv, sizeof(*cmd)); + mutex_unlock(&dev_priv->binding_mutex); + vmw_resource_release_id(res); + vmw_fifo_resource_dec(dev_priv); + + return 0; +} + +/* + * DX shader code: + */ + +/** + * vmw_dx_shader_commit_notify - Notify that a shader operation has been + * committed to hardware from a user-supplied command stream. + * + * @res: Pointer to the shader resource. + * @state: Indicating whether a creation or removal has been committed. + * + */ +static void vmw_dx_shader_commit_notify(struct vmw_resource *res, + enum vmw_cmdbuf_res_state state) +{ + struct vmw_dx_shader *shader = vmw_res_to_dx_shader(res); + struct vmw_private *dev_priv = res->dev_priv; + + if (state == VMW_CMDBUF_RES_ADD) { + mutex_lock(&dev_priv->binding_mutex); + vmw_cotable_add_resource(shader->cotable, + &shader->cotable_head); + shader->committed = true; + res->id = shader->id; + mutex_unlock(&dev_priv->binding_mutex); + } else { + mutex_lock(&dev_priv->binding_mutex); + list_del_init(&shader->cotable_head); + shader->committed = false; + res->id = -1; + mutex_unlock(&dev_priv->binding_mutex); + } +} + +/** + * vmw_dx_shader_unscrub - Have the device reattach a MOB to a DX shader. + * + * @res: The shader resource + * + * This function reverts a scrub operation. + */ +static int vmw_dx_shader_unscrub(struct vmw_resource *res) +{ + struct vmw_dx_shader *shader = vmw_res_to_dx_shader(res); + struct vmw_private *dev_priv = res->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXBindShader body; + } *cmd; + + if (!list_empty(&shader->cotable_head) || !shader->committed) + return 0; + + cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), shader->ctx->id); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_BIND_SHADER; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = shader->ctx->id; + cmd->body.shid = shader->id; + cmd->body.mobid = res->guest_memory_bo->tbo.resource->start; + cmd->body.offsetInBytes = res->guest_memory_offset; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + vmw_cotable_add_resource(shader->cotable, &shader->cotable_head); + + return 0; +} + +/** + * vmw_dx_shader_create - The DX shader create callback + * + * @res: The DX shader resource + * + * The create callback is called as part of resource validation and + * makes sure that we unscrub the shader if it's previously been scrubbed. + */ +static int vmw_dx_shader_create(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_dx_shader *shader = vmw_res_to_dx_shader(res); + int ret = 0; + + WARN_ON_ONCE(!shader->committed); + + if (vmw_resource_mob_attached(res)) { + mutex_lock(&dev_priv->binding_mutex); + ret = vmw_dx_shader_unscrub(res); + mutex_unlock(&dev_priv->binding_mutex); + } + + res->id = shader->id; + return ret; +} + +/** + * vmw_dx_shader_bind - The DX shader bind callback + * + * @res: The DX shader resource + * @val_buf: Pointer to the validate buffer. + * + */ +static int vmw_dx_shader_bind(struct vmw_resource *res, + struct ttm_validate_buffer *val_buf) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct ttm_buffer_object *bo = val_buf->bo; + + BUG_ON(bo->resource->mem_type != VMW_PL_MOB); + mutex_lock(&dev_priv->binding_mutex); + vmw_dx_shader_unscrub(res); + mutex_unlock(&dev_priv->binding_mutex); + + return 0; +} + +/** + * vmw_dx_shader_scrub - Have the device unbind a MOB from a DX shader. + * + * @res: The shader resource + * + * This function unbinds a MOB from the DX shader without requiring the + * MOB dma_buffer to be reserved. The driver still considers the MOB bound. + * However, once the driver eventually decides to unbind the MOB, it doesn't + * need to access the context. + */ +static int vmw_dx_shader_scrub(struct vmw_resource *res) +{ + struct vmw_dx_shader *shader = vmw_res_to_dx_shader(res); + struct vmw_private *dev_priv = res->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXBindShader body; + } *cmd; + + if (list_empty(&shader->cotable_head)) + return 0; + + WARN_ON_ONCE(!shader->committed); + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_BIND_SHADER; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = shader->ctx->id; + cmd->body.shid = res->id; + cmd->body.mobid = SVGA3D_INVALID_ID; + cmd->body.offsetInBytes = 0; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + res->id = -1; + list_del_init(&shader->cotable_head); + + return 0; +} + +/** + * vmw_dx_shader_unbind - The dx shader unbind callback. + * + * @res: The shader resource + * @readback: Whether this is a readback unbind. Currently unused. + * @val_buf: MOB buffer information. + */ +static int vmw_dx_shader_unbind(struct vmw_resource *res, + bool readback, + struct ttm_validate_buffer *val_buf) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_fence_obj *fence; + int ret; + + BUG_ON(res->guest_memory_bo->tbo.resource->mem_type != VMW_PL_MOB); + + mutex_lock(&dev_priv->binding_mutex); + ret = vmw_dx_shader_scrub(res); + mutex_unlock(&dev_priv->binding_mutex); + + if (ret) + return ret; + + (void) vmw_execbuf_fence_commands(NULL, dev_priv, + &fence, NULL); + vmw_bo_fence_single(val_buf->bo, fence); + + if (likely(fence != NULL)) + vmw_fence_obj_unreference(&fence); + + return 0; +} + +/** + * vmw_dx_shader_cotable_list_scrub - The cotable unbind_func callback for + * DX shaders. + * + * @dev_priv: Pointer to device private structure. + * @list: The list of cotable resources. + * @readback: Whether the call was part of a readback unbind. + * + * Scrubs all shader MOBs so that any subsequent shader unbind or shader + * destroy operation won't need to swap in the context. + */ +void vmw_dx_shader_cotable_list_scrub(struct vmw_private *dev_priv, + struct list_head *list, + bool readback) +{ + struct vmw_dx_shader *entry, *next; + + lockdep_assert_held_once(&dev_priv->binding_mutex); + + list_for_each_entry_safe(entry, next, list, cotable_head) { + WARN_ON(vmw_dx_shader_scrub(&entry->res)); + if (!readback) + entry->committed = false; + } +} + +/** + * vmw_dx_shader_res_free - The DX shader free callback + * + * @res: The shader resource + * + * Frees the DX shader resource. + */ +static void vmw_dx_shader_res_free(struct vmw_resource *res) +{ + struct vmw_dx_shader *shader = vmw_res_to_dx_shader(res); + + vmw_resource_unreference(&shader->cotable); + kfree(shader); +} + +/** + * vmw_dx_shader_add - Add a shader resource as a command buffer managed + * resource. + * + * @man: The command buffer resource manager. + * @ctx: Pointer to the context resource. + * @user_key: The id used for this shader. + * @shader_type: The shader type. + * @list: The list of staged command buffer managed resources. + */ +int vmw_dx_shader_add(struct vmw_cmdbuf_res_manager *man, + struct vmw_resource *ctx, + u32 user_key, + SVGA3dShaderType shader_type, + struct list_head *list) +{ + struct vmw_dx_shader *shader; + struct vmw_resource *res; + struct vmw_private *dev_priv = ctx->dev_priv; + int ret; + + if (!vmw_shader_id_ok(user_key, shader_type)) + return -EINVAL; + + shader = kmalloc(sizeof(*shader), GFP_KERNEL); + if (!shader) { + return -ENOMEM; + } + + res = &shader->res; + shader->ctx = ctx; + shader->cotable = vmw_resource_reference + (vmw_context_cotable(ctx, SVGA_COTABLE_DXSHADER)); + shader->id = user_key; + shader->committed = false; + INIT_LIST_HEAD(&shader->cotable_head); + ret = vmw_resource_init(dev_priv, res, true, + vmw_dx_shader_res_free, &vmw_dx_shader_func); + if (ret) + goto out_resource_init; + + /* + * The user_key name-space is not per shader type for DX shaders, + * so when hashing, use a single zero shader type. + */ + ret = vmw_cmdbuf_res_add(man, vmw_cmdbuf_res_shader, + vmw_shader_key(user_key, 0), + res, list); + if (ret) + goto out_resource_init; + + res->id = shader->id; + res->hw_destroy = vmw_hw_shader_destroy; + +out_resource_init: + vmw_resource_unreference(&res); + + return ret; +} + + + +/* + * 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); + + ttm_base_object_kfree(ushader, base); +} + +static void vmw_shader_free(struct vmw_resource *res) +{ + struct vmw_shader *shader = vmw_res_to_shader(res); + + kfree(shader); +} + +/* + * 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); +} + +static int vmw_user_shader_alloc(struct vmw_private *dev_priv, + struct vmw_bo *buffer, + size_t shader_size, + size_t offset, + SVGA3dShaderType shader_type, + uint8_t num_input_sig, + uint8_t num_output_sig, + struct ttm_object_file *tfile, + u32 *handle) +{ + struct vmw_user_shader *ushader; + struct vmw_resource *res, *tmp; + int ret; + + ushader = kzalloc(sizeof(*ushader), GFP_KERNEL); + if (unlikely(!ushader)) { + 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, num_input_sig, + num_output_sig, 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); + + if (unlikely(ret != 0)) { + vmw_resource_unreference(&tmp); + goto out_err; + } + + if (handle) + *handle = ushader->base.handle; +out_err: + vmw_resource_unreference(&res); +out: + return ret; +} + + +static struct vmw_resource *vmw_shader_alloc(struct vmw_private *dev_priv, + struct vmw_bo *buffer, + size_t shader_size, + size_t offset, + SVGA3dShaderType shader_type) +{ + struct vmw_shader *shader; + struct vmw_resource *res; + int ret; + + shader = kzalloc(sizeof(*shader), GFP_KERNEL); + if (unlikely(!shader)) { + ret = -ENOMEM; + goto out_err; + } + + res = &shader->res; + + /* + * From here on, the destructor takes over resource freeing. + */ + ret = vmw_gb_shader_init(dev_priv, res, shader_size, + offset, shader_type, 0, 0, buffer, + vmw_shader_free); + +out_err: + return ret ? ERR_PTR(ret) : res; +} + + +static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv, + enum drm_vmw_shader_type shader_type_drm, + u32 buffer_handle, size_t size, size_t offset, + uint8_t num_input_sig, uint8_t num_output_sig, + uint32_t *shader_handle) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_bo *buffer = NULL; + SVGA3dShaderType shader_type; + int ret; + + if (buffer_handle != SVGA3D_INVALID_ID) { + ret = vmw_user_bo_lookup(file_priv, buffer_handle, &buffer); + if (unlikely(ret != 0)) { + VMW_DEBUG_USER("Couldn't find buffer for shader creation.\n"); + return ret; + } + + if ((u64)buffer->tbo.base.size < (u64)size + (u64)offset) { + VMW_DEBUG_USER("Illegal buffer- or shader size.\n"); + ret = -EINVAL; + goto out_bad_arg; + } + } + + switch (shader_type_drm) { + case drm_vmw_shader_type_vs: + shader_type = SVGA3D_SHADERTYPE_VS; + break; + case drm_vmw_shader_type_ps: + shader_type = SVGA3D_SHADERTYPE_PS; + break; + default: + VMW_DEBUG_USER("Illegal shader type.\n"); + ret = -EINVAL; + goto out_bad_arg; + } + + ret = vmw_user_shader_alloc(dev_priv, buffer, size, offset, + shader_type, num_input_sig, + num_output_sig, tfile, shader_handle); +out_bad_arg: + vmw_user_bo_unref(&buffer); + return ret; +} + +/** + * vmw_shader_id_ok - Check whether a compat shader user key and + * shader type are within valid bounds. + * + * @user_key: User space id of the shader. + * @shader_type: Shader type. + * + * Returns true if valid false if not. + */ +static bool vmw_shader_id_ok(u32 user_key, SVGA3dShaderType shader_type) +{ + return user_key <= ((1 << 20) - 1) && (unsigned) shader_type < 16; +} + +/** + * vmw_shader_key - Compute a hash key suitable for a compat shader. + * + * @user_key: User space id of the shader. + * @shader_type: Shader type. + * + * Returns a hash key suitable for a command buffer managed resource + * manager hash table. + */ +static u32 vmw_shader_key(u32 user_key, SVGA3dShaderType shader_type) +{ + return user_key | (shader_type << 20); +} + +/** + * vmw_shader_remove - Stage a compat shader for removal. + * + * @man: Pointer to the compat shader manager identifying the shader namespace. + * @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 command buffer resource actions. + */ +int vmw_shader_remove(struct vmw_cmdbuf_res_manager *man, + u32 user_key, SVGA3dShaderType shader_type, + struct list_head *list) +{ + struct vmw_resource *dummy; + + if (!vmw_shader_id_ok(user_key, shader_type)) + return -EINVAL; + + return vmw_cmdbuf_res_remove(man, vmw_cmdbuf_res_shader, + vmw_shader_key(user_key, shader_type), + list, &dummy); +} + +/** + * vmw_compat_shader_add - Create a compat shader and stage it for addition + * as a command buffer managed resource. + * + * @dev_priv: Pointer to device private structure. + * @man: Pointer to the compat shader manager identifying the shader namespace. + * @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. + * @size: Command size. + * @list: Caller's list of staged command buffer resource actions. + * + */ +int vmw_compat_shader_add(struct vmw_private *dev_priv, + struct vmw_cmdbuf_res_manager *man, + u32 user_key, const void *bytecode, + SVGA3dShaderType shader_type, + size_t size, + struct list_head *list) +{ + struct ttm_operation_ctx ctx = { false, true }; + struct vmw_bo *buf; + struct ttm_bo_kmap_obj map; + bool is_iomem; + int ret; + struct vmw_resource *res; + struct vmw_bo_params bo_params = { + .domain = VMW_BO_DOMAIN_SYS, + .busy_domain = VMW_BO_DOMAIN_SYS, + .bo_type = ttm_bo_type_device, + .size = size, + .pin = true + }; + + if (!vmw_shader_id_ok(user_key, shader_type)) + return -EINVAL; + + ret = vmw_bo_create(dev_priv, &bo_params, &buf); + if (unlikely(ret != 0)) + goto out; + + ret = ttm_bo_reserve(&buf->tbo, false, true, NULL); + if (unlikely(ret != 0)) + goto no_reserve; + + /* Map and copy shader bytecode. */ + ret = ttm_bo_kmap(&buf->tbo, 0, PFN_UP(size), &map); + if (unlikely(ret != 0)) { + ttm_bo_unreserve(&buf->tbo); + 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->tbo, &buf->placement, &ctx); + WARN_ON(ret != 0); + ttm_bo_unreserve(&buf->tbo); + + res = vmw_shader_alloc(dev_priv, buf, size, 0, shader_type); + if (unlikely(ret != 0)) + goto no_reserve; + + ret = vmw_cmdbuf_res_add(man, vmw_cmdbuf_res_shader, + vmw_shader_key(user_key, shader_type), + res, list); + vmw_resource_unreference(&res); +no_reserve: + vmw_bo_unreference(&buf); +out: + return ret; +} + +/** + * vmw_shader_lookup - Look up a compat shader + * + * @man: Pointer to the command buffer managed resource manager identifying + * the shader namespace. + * @user_key: The user space id of the shader. + * @shader_type: The shader type. + * + * Returns a refcounted pointer to a struct vmw_resource if the shader was + * found. An error pointer otherwise. + */ +struct vmw_resource * +vmw_shader_lookup(struct vmw_cmdbuf_res_manager *man, + u32 user_key, + SVGA3dShaderType shader_type) +{ + if (!vmw_shader_id_ok(user_key, shader_type)) + return ERR_PTR(-EINVAL); + + return vmw_cmdbuf_res_lookup(man, vmw_cmdbuf_res_shader, + vmw_shader_key(user_key, shader_type)); +} + +int vmw_shader_define_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_shader_create_arg *arg = + (struct drm_vmw_shader_create_arg *)data; + + return vmw_shader_define(dev, file_priv, arg->shader_type, + arg->buffer_handle, + arg->size, arg->offset, + 0, 0, + &arg->shader_handle); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_simple_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_simple_resource.c new file mode 100644 index 0000000000..0d51b45422 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_simple_resource.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2016 VMware, Inc., Palo Alto, CA., USA + * + * 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" + +/** + * struct vmw_user_simple_resource - User-space simple resource struct + * + * @base: The TTM base object implementing user-space visibility. + * @simple: The embedded struct vmw_simple_resource. + */ +struct vmw_user_simple_resource { + struct ttm_base_object base; + struct vmw_simple_resource simple; +/* + * Nothing to be placed after @simple, since size of @simple is + * unknown. + */ +}; + + +/** + * vmw_simple_resource_init - Initialize a simple resource object. + * + * @dev_priv: Pointer to a struct device private. + * @simple: The struct vmw_simple_resource to initialize. + * @data: Data passed to the information initialization function. + * @res_free: Function pointer to destroy the simple resource. + * + * Returns: + * 0 if succeeded. + * Negative error value if error, in which case the resource will have been + * freed. + */ +static int vmw_simple_resource_init(struct vmw_private *dev_priv, + struct vmw_simple_resource *simple, + void *data, + void (*res_free)(struct vmw_resource *res)) +{ + struct vmw_resource *res = &simple->res; + int ret; + + ret = vmw_resource_init(dev_priv, res, false, res_free, + &simple->func->res_func); + + if (ret) { + res_free(res); + return ret; + } + + ret = simple->func->init(res, data); + if (ret) { + vmw_resource_unreference(&res); + return ret; + } + + simple->res.hw_destroy = simple->func->hw_destroy; + + return 0; +} + +/** + * vmw_simple_resource_free - Free a simple resource object. + * + * @res: The struct vmw_resource member of the simple resource object. + * + * Frees memory for the object. + */ +static void vmw_simple_resource_free(struct vmw_resource *res) +{ + struct vmw_user_simple_resource *usimple = + container_of(res, struct vmw_user_simple_resource, + simple.res); + + ttm_base_object_kfree(usimple, base); +} + +/** + * vmw_simple_resource_base_release - TTM object release callback + * + * @p_base: The struct ttm_base_object member of the simple resource object. + * + * Called when the last reference to the embedded struct ttm_base_object is + * gone. Typically results in an object free, unless there are other + * references to the embedded struct vmw_resource. + */ +static void vmw_simple_resource_base_release(struct ttm_base_object **p_base) +{ + struct ttm_base_object *base = *p_base; + struct vmw_user_simple_resource *usimple = + container_of(base, struct vmw_user_simple_resource, base); + struct vmw_resource *res = &usimple->simple.res; + + *p_base = NULL; + vmw_resource_unreference(&res); +} + +/** + * vmw_simple_resource_create_ioctl - Helper to set up an ioctl function to + * create a struct vmw_simple_resource. + * + * @dev: Pointer to a struct drm device. + * @data: Ioctl argument. + * @file_priv: Pointer to a struct drm_file identifying the caller. + * @func: Pointer to a struct vmw_simple_resource_func identifying the + * simple resource type. + * + * Returns: + * 0 if success, + * Negative error value on error. + */ +int +vmw_simple_resource_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv, + const struct vmw_simple_resource_func *func) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_user_simple_resource *usimple; + struct vmw_resource *res; + struct vmw_resource *tmp; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + size_t alloc_size; + int ret; + + alloc_size = offsetof(struct vmw_user_simple_resource, simple) + + func->size; + + usimple = kzalloc(alloc_size, GFP_KERNEL); + if (!usimple) { + ret = -ENOMEM; + goto out_ret; + } + + usimple->simple.func = func; + res = &usimple->simple.res; + usimple->base.shareable = false; + usimple->base.tfile = NULL; + + /* + * From here on, the destructor takes over resource freeing. + */ + ret = vmw_simple_resource_init(dev_priv, &usimple->simple, + data, vmw_simple_resource_free); + if (ret) + goto out_ret; + + tmp = vmw_resource_reference(res); + ret = ttm_base_object_init(tfile, &usimple->base, false, + func->ttm_res_type, + &vmw_simple_resource_base_release); + + if (ret) { + vmw_resource_unreference(&tmp); + goto out_err; + } + + func->set_arg_handle(data, usimple->base.handle); +out_err: + vmw_resource_unreference(&res); +out_ret: + return ret; +} + +/** + * vmw_simple_resource_lookup - Look up a simple resource from its user-space + * handle. + * + * @tfile: struct ttm_object_file identifying the caller. + * @handle: The user-space handle. + * @func: The struct vmw_simple_resource_func identifying the simple resource + * type. + * + * Returns: Refcounted pointer to the embedded struct vmw_resource if + * successful. Error pointer otherwise. + */ +struct vmw_resource * +vmw_simple_resource_lookup(struct ttm_object_file *tfile, + uint32_t handle, + const struct vmw_simple_resource_func *func) +{ + struct vmw_user_simple_resource *usimple; + struct ttm_base_object *base; + struct vmw_resource *res; + + base = ttm_base_object_lookup(tfile, handle); + if (!base) { + VMW_DEBUG_USER("Invalid %s handle 0x%08lx.\n", + func->res_func.type_name, + (unsigned long) handle); + return ERR_PTR(-ESRCH); + } + + if (ttm_base_object_type(base) != func->ttm_res_type) { + ttm_base_object_unref(&base); + VMW_DEBUG_USER("Invalid type of %s handle 0x%08lx.\n", + func->res_func.type_name, + (unsigned long) handle); + return ERR_PTR(-EINVAL); + } + + usimple = container_of(base, typeof(*usimple), base); + res = vmw_resource_reference(&usimple->simple.res); + ttm_base_object_unref(&base); + + return res; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_so.c b/drivers/gpu/drm/vmwgfx/vmwgfx_so.c new file mode 100644 index 0000000000..d199e718cb --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_so.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * Copyright 2014-2015 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" +#include "vmwgfx_so.h" +#include "vmwgfx_binding.h" + +/* + * The currently only reason we need to keep track of views is that if we + * destroy a hardware surface, all views pointing to it must also be destroyed, + * otherwise the device will error. + * So in particular if a surface is evicted, we must destroy all views pointing + * to it, and all context bindings of that view. Similarly we must restore + * the view bindings, views and surfaces pointed to by the views when a + * context is referenced in the command stream. + */ + +/** + * struct vmw_view - view metadata + * + * @rcu: RCU callback head + * @res: The struct vmw_resource we derive from + * @ctx: Non-refcounted pointer to the context this view belongs to. + * @srf: Refcounted pointer to the surface pointed to by this view. + * @cotable: Refcounted pointer to the cotable holding this view. + * @srf_head: List head for the surface-to-view list. + * @cotable_head: List head for the cotable-to_view list. + * @view_type: View type. + * @view_id: User-space per context view id. Currently used also as per + * context device view id. + * @cmd_size: Size of the SVGA3D define view command that we've copied from the + * command stream. + * @committed: Whether the view is actually created or pending creation at the + * device level. + * @cmd: The SVGA3D define view command copied from the command stream. + */ +struct vmw_view { + struct rcu_head rcu; + struct vmw_resource res; + struct vmw_resource *ctx; /* Immutable */ + struct vmw_resource *srf; /* Immutable */ + struct vmw_resource *cotable; /* Immutable */ + struct list_head srf_head; /* Protected by binding_mutex */ + struct list_head cotable_head; /* Protected by binding_mutex */ + unsigned view_type; /* Immutable */ + unsigned view_id; /* Immutable */ + u32 cmd_size; /* Immutable */ + bool committed; /* Protected by binding_mutex */ + u32 cmd[]; /* Immutable */ +}; + +static int vmw_view_create(struct vmw_resource *res); +static int vmw_view_destroy(struct vmw_resource *res); +static void vmw_hw_view_destroy(struct vmw_resource *res); +static void vmw_view_commit_notify(struct vmw_resource *res, + enum vmw_cmdbuf_res_state state); + +static const struct vmw_res_func vmw_view_func = { + .res_type = vmw_res_view, + .needs_guest_memory = false, + .may_evict = false, + .type_name = "DX view", + .domain = VMW_BO_DOMAIN_SYS, + .busy_domain = VMW_BO_DOMAIN_SYS, + .create = vmw_view_create, + .commit_notify = vmw_view_commit_notify, +}; + +/** + * struct vmw_view_define - view define command body stub + * + * @view_id: The device id of the view being defined + * @sid: The surface id of the view being defined + * + * This generic struct is used by the code to change @view_id and @sid of a + * saved view define command. + */ +struct vmw_view_define { + uint32 view_id; + uint32 sid; +}; + +/** + * vmw_view - Convert a struct vmw_resource to a struct vmw_view + * + * @res: Pointer to the resource to convert. + * + * Returns a pointer to a struct vmw_view. + */ +static struct vmw_view *vmw_view(struct vmw_resource *res) +{ + return container_of(res, struct vmw_view, res); +} + +/** + * vmw_view_commit_notify - Notify that a view operation has been committed to + * hardware from a user-supplied command stream. + * + * @res: Pointer to the view resource. + * @state: Indicating whether a creation or removal has been committed. + * + */ +static void vmw_view_commit_notify(struct vmw_resource *res, + enum vmw_cmdbuf_res_state state) +{ + struct vmw_view *view = vmw_view(res); + struct vmw_private *dev_priv = res->dev_priv; + + mutex_lock(&dev_priv->binding_mutex); + if (state == VMW_CMDBUF_RES_ADD) { + struct vmw_surface *srf = vmw_res_to_srf(view->srf); + + list_add_tail(&view->srf_head, &srf->view_list); + vmw_cotable_add_resource(view->cotable, &view->cotable_head); + view->committed = true; + res->id = view->view_id; + + } else { + list_del_init(&view->cotable_head); + list_del_init(&view->srf_head); + view->committed = false; + res->id = -1; + } + mutex_unlock(&dev_priv->binding_mutex); +} + +/** + * vmw_view_create - Create a hardware view. + * + * @res: Pointer to the view resource. + * + * Create a hardware view. Typically used if that view has previously been + * destroyed by an eviction operation. + */ +static int vmw_view_create(struct vmw_resource *res) +{ + struct vmw_view *view = vmw_view(res); + struct vmw_surface *srf = vmw_res_to_srf(view->srf); + struct vmw_private *dev_priv = res->dev_priv; + struct { + SVGA3dCmdHeader header; + struct vmw_view_define body; + } *cmd; + + mutex_lock(&dev_priv->binding_mutex); + if (!view->committed) { + mutex_unlock(&dev_priv->binding_mutex); + return 0; + } + + cmd = VMW_CMD_CTX_RESERVE(res->dev_priv, view->cmd_size, view->ctx->id); + if (!cmd) { + mutex_unlock(&dev_priv->binding_mutex); + return -ENOMEM; + } + + memcpy(cmd, &view->cmd, view->cmd_size); + WARN_ON(cmd->body.view_id != view->view_id); + /* Sid may have changed due to surface eviction. */ + WARN_ON(view->srf->id == SVGA3D_INVALID_ID); + cmd->body.sid = view->srf->id; + vmw_cmd_commit(res->dev_priv, view->cmd_size); + res->id = view->view_id; + list_add_tail(&view->srf_head, &srf->view_list); + vmw_cotable_add_resource(view->cotable, &view->cotable_head); + mutex_unlock(&dev_priv->binding_mutex); + + return 0; +} + +/** + * vmw_view_destroy - Destroy a hardware view. + * + * @res: Pointer to the view resource. + * + * Destroy a hardware view. Typically used on unexpected termination of the + * owning process or if the surface the view is pointing to is destroyed. + */ +static int vmw_view_destroy(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_view *view = vmw_view(res); + struct { + SVGA3dCmdHeader header; + union vmw_view_destroy body; + } *cmd; + + lockdep_assert_held_once(&dev_priv->binding_mutex); + vmw_binding_res_list_scrub(&res->binding_head); + + if (!view->committed || res->id == -1) + return 0; + + cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), view->ctx->id); + if (!cmd) + return -ENOMEM; + + cmd->header.id = vmw_view_destroy_cmds[view->view_type]; + cmd->header.size = sizeof(cmd->body); + cmd->body.view_id = view->view_id; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + res->id = -1; + list_del_init(&view->cotable_head); + list_del_init(&view->srf_head); + + return 0; +} + +/** + * vmw_hw_view_destroy - Destroy a hardware view as part of resource cleanup. + * + * @res: Pointer to the view resource. + * + * Destroy a hardware view if it's still present. + */ +static void vmw_hw_view_destroy(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + + mutex_lock(&dev_priv->binding_mutex); + WARN_ON(vmw_view_destroy(res)); + res->id = -1; + mutex_unlock(&dev_priv->binding_mutex); +} + +/** + * vmw_view_key - Compute a view key suitable for the cmdbuf resource manager + * + * @user_key: The user-space id used for the view. + * @view_type: The view type. + * + * Destroy a hardware view if it's still present. + */ +static u32 vmw_view_key(u32 user_key, enum vmw_view_type view_type) +{ + return user_key | (view_type << 20); +} + +/** + * vmw_view_id_ok - Basic view id and type range checks. + * + * @user_key: The user-space id used for the view. + * @view_type: The view type. + * + * Checks that the view id and type (typically provided by user-space) is + * valid. + */ +static bool vmw_view_id_ok(u32 user_key, enum vmw_view_type view_type) +{ + return (user_key < SVGA_COTABLE_MAX_IDS && + view_type < vmw_view_max); +} + +/** + * vmw_view_res_free - resource res_free callback for view resources + * + * @res: Pointer to a struct vmw_resource + * + * Frees memory held by the struct vmw_view. + */ +static void vmw_view_res_free(struct vmw_resource *res) +{ + struct vmw_view *view = vmw_view(res); + + vmw_resource_unreference(&view->cotable); + vmw_resource_unreference(&view->srf); + kfree_rcu(view, rcu); +} + +/** + * vmw_view_add - Create a view resource and stage it for addition + * as a command buffer managed resource. + * + * @man: Pointer to the compat shader manager identifying the shader namespace. + * @ctx: Pointer to a struct vmw_resource identifying the active context. + * @srf: Pointer to a struct vmw_resource identifying the surface the view + * points to. + * @view_type: The view type deduced from the view create command. + * @user_key: The key that is used to identify the shader. The key is + * unique to the view type and to the context. + * @cmd: Pointer to the view create command in the command stream. + * @cmd_size: Size of the view create command in the command stream. + * @list: Caller's list of staged command buffer resource actions. + */ +int vmw_view_add(struct vmw_cmdbuf_res_manager *man, + struct vmw_resource *ctx, + struct vmw_resource *srf, + enum vmw_view_type view_type, + u32 user_key, + const void *cmd, + size_t cmd_size, + struct list_head *list) +{ + static const size_t vmw_view_define_sizes[] = { + [vmw_view_sr] = sizeof(SVGA3dCmdDXDefineShaderResourceView), + [vmw_view_rt] = sizeof(SVGA3dCmdDXDefineRenderTargetView), + [vmw_view_ds] = sizeof(SVGA3dCmdDXDefineDepthStencilView), + [vmw_view_ua] = sizeof(SVGA3dCmdDXDefineUAView) + }; + + struct vmw_private *dev_priv = ctx->dev_priv; + struct vmw_resource *res; + struct vmw_view *view; + size_t size; + int ret; + + if (cmd_size != vmw_view_define_sizes[view_type] + + sizeof(SVGA3dCmdHeader)) { + VMW_DEBUG_USER("Illegal view create command size.\n"); + return -EINVAL; + } + + if (!vmw_view_id_ok(user_key, view_type)) { + VMW_DEBUG_USER("Illegal view add view id.\n"); + return -EINVAL; + } + + size = offsetof(struct vmw_view, cmd) + cmd_size; + + view = kmalloc(size, GFP_KERNEL); + if (!view) { + return -ENOMEM; + } + + res = &view->res; + view->ctx = ctx; + view->srf = vmw_resource_reference(srf); + view->cotable = vmw_resource_reference + (vmw_context_cotable(ctx, vmw_view_cotables[view_type])); + view->view_type = view_type; + view->view_id = user_key; + view->cmd_size = cmd_size; + view->committed = false; + INIT_LIST_HEAD(&view->srf_head); + INIT_LIST_HEAD(&view->cotable_head); + memcpy(&view->cmd, cmd, cmd_size); + ret = vmw_resource_init(dev_priv, res, true, + vmw_view_res_free, &vmw_view_func); + if (ret) + goto out_resource_init; + + ret = vmw_cmdbuf_res_add(man, vmw_cmdbuf_res_view, + vmw_view_key(user_key, view_type), + res, list); + if (ret) + goto out_resource_init; + + res->id = view->view_id; + res->hw_destroy = vmw_hw_view_destroy; + +out_resource_init: + vmw_resource_unreference(&res); + + return ret; +} + +/** + * vmw_view_remove - Stage a view for removal. + * + * @man: Pointer to the view manager identifying the shader namespace. + * @user_key: The key that is used to identify the view. The key is + * unique to the view type. + * @view_type: View type + * @list: Caller's list of staged command buffer resource actions. + * @res_p: If the resource is in an already committed state, points to the + * struct vmw_resource on successful return. The pointer will be + * non ref-counted. + */ +int vmw_view_remove(struct vmw_cmdbuf_res_manager *man, + u32 user_key, enum vmw_view_type view_type, + struct list_head *list, + struct vmw_resource **res_p) +{ + if (!vmw_view_id_ok(user_key, view_type)) { + VMW_DEBUG_USER("Illegal view remove view id.\n"); + return -EINVAL; + } + + return vmw_cmdbuf_res_remove(man, vmw_cmdbuf_res_view, + vmw_view_key(user_key, view_type), + list, res_p); +} + +/** + * vmw_view_cotable_list_destroy - Evict all views belonging to a cotable. + * + * @dev_priv: Pointer to a device private struct. + * @list: List of views belonging to a cotable. + * @readback: Unused. Needed for function interface only. + * + * This function evicts all views belonging to a cotable. + * It must be called with the binding_mutex held, and the caller must hold + * a reference to the view resource. This is typically called before the + * cotable is paged out. + */ +void vmw_view_cotable_list_destroy(struct vmw_private *dev_priv, + struct list_head *list, + bool readback) +{ + struct vmw_view *entry, *next; + + lockdep_assert_held_once(&dev_priv->binding_mutex); + + list_for_each_entry_safe(entry, next, list, cotable_head) + WARN_ON(vmw_view_destroy(&entry->res)); +} + +/** + * vmw_view_surface_list_destroy - Evict all views pointing to a surface + * + * @dev_priv: Pointer to a device private struct. + * @list: List of views pointing to a surface. + * + * This function evicts all views pointing to a surface. This is typically + * called before the surface is evicted. + */ +void vmw_view_surface_list_destroy(struct vmw_private *dev_priv, + struct list_head *list) +{ + struct vmw_view *entry, *next; + + lockdep_assert_held_once(&dev_priv->binding_mutex); + + list_for_each_entry_safe(entry, next, list, srf_head) + WARN_ON(vmw_view_destroy(&entry->res)); +} + +/** + * vmw_view_srf - Return a non-refcounted pointer to the surface a view is + * pointing to. + * + * @res: pointer to a view resource. + * + * Note that the view itself is holding a reference, so as long + * the view resource is alive, the surface resource will be. + */ +struct vmw_resource *vmw_view_srf(struct vmw_resource *res) +{ + return vmw_view(res)->srf; +} + +/** + * vmw_view_lookup - Look up a view. + * + * @man: The context's cmdbuf ref manager. + * @view_type: The view type. + * @user_key: The view user id. + * + * returns a refcounted pointer to a view or an error pointer if not found. + */ +struct vmw_resource *vmw_view_lookup(struct vmw_cmdbuf_res_manager *man, + enum vmw_view_type view_type, + u32 user_key) +{ + return vmw_cmdbuf_res_lookup(man, vmw_cmdbuf_res_view, + vmw_view_key(user_key, view_type)); +} + +/** + * vmw_view_dirtying - Return whether a view type is dirtying its resource + * @res: Pointer to the view + * + * Each time a resource is put on the validation list as the result of a + * view pointing to it, we need to determine whether that resource will + * be dirtied (written to by the GPU) as a result of the corresponding + * GPU operation. Currently only rendertarget-, depth-stencil and unordered + * access views are capable of dirtying its resource. + * + * Return: Whether the view type of @res dirties the resource it points to. + */ +u32 vmw_view_dirtying(struct vmw_resource *res) +{ + static u32 view_is_dirtying[vmw_view_max] = { + [vmw_view_rt] = VMW_RES_DIRTY_SET, + [vmw_view_ds] = VMW_RES_DIRTY_SET, + [vmw_view_ua] = VMW_RES_DIRTY_SET, + }; + + /* Update this function as we add more view types */ + BUILD_BUG_ON(vmw_view_max != 4); + return view_is_dirtying[vmw_view(res)->view_type]; +} + +const u32 vmw_view_destroy_cmds[] = { + [vmw_view_sr] = SVGA_3D_CMD_DX_DESTROY_SHADERRESOURCE_VIEW, + [vmw_view_rt] = SVGA_3D_CMD_DX_DESTROY_RENDERTARGET_VIEW, + [vmw_view_ds] = SVGA_3D_CMD_DX_DESTROY_DEPTHSTENCIL_VIEW, + [vmw_view_ua] = SVGA_3D_CMD_DX_DESTROY_UA_VIEW, +}; + +const SVGACOTableType vmw_view_cotables[] = { + [vmw_view_sr] = SVGA_COTABLE_SRVIEW, + [vmw_view_rt] = SVGA_COTABLE_RTVIEW, + [vmw_view_ds] = SVGA_COTABLE_DSVIEW, + [vmw_view_ua] = SVGA_COTABLE_UAVIEW, +}; + +const SVGACOTableType vmw_so_cotables[] = { + [vmw_so_el] = SVGA_COTABLE_ELEMENTLAYOUT, + [vmw_so_bs] = SVGA_COTABLE_BLENDSTATE, + [vmw_so_ds] = SVGA_COTABLE_DEPTHSTENCIL, + [vmw_so_rs] = SVGA_COTABLE_RASTERIZERSTATE, + [vmw_so_ss] = SVGA_COTABLE_SAMPLER, + [vmw_so_so] = SVGA_COTABLE_STREAMOUTPUT, + [vmw_so_max]= SVGA_COTABLE_MAX +}; + + +/* To remove unused function warning */ +static void vmw_so_build_asserts(void) __attribute__((used)); + + +/* + * This function is unused at run-time, and only used to dump various build + * asserts important for code optimization assumptions. + */ +static void vmw_so_build_asserts(void) +{ + /* Assert that our vmw_view_cmd_to_type() function is correct. */ + BUILD_BUG_ON(SVGA_3D_CMD_DX_DESTROY_SHADERRESOURCE_VIEW != + SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW + 1); + BUILD_BUG_ON(SVGA_3D_CMD_DX_DEFINE_RENDERTARGET_VIEW != + SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW + 2); + BUILD_BUG_ON(SVGA_3D_CMD_DX_DESTROY_RENDERTARGET_VIEW != + SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW + 3); + BUILD_BUG_ON(SVGA_3D_CMD_DX_DEFINE_DEPTHSTENCIL_VIEW != + SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW + 4); + BUILD_BUG_ON(SVGA_3D_CMD_DX_DESTROY_DEPTHSTENCIL_VIEW != + SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW + 5); + + /* Assert that our "one body fits all" assumption is valid */ + BUILD_BUG_ON(sizeof(union vmw_view_destroy) != sizeof(u32)); + + /* Assert that the view key space can hold all view ids. */ + BUILD_BUG_ON(SVGA_COTABLE_MAX_IDS >= ((1 << 20) - 1)); + + /* + * Assert that the offset of sid in all view define commands + * is what we assume it to be. + */ + BUILD_BUG_ON(offsetof(struct vmw_view_define, sid) != + offsetof(SVGA3dCmdDXDefineShaderResourceView, sid)); + BUILD_BUG_ON(offsetof(struct vmw_view_define, sid) != + offsetof(SVGA3dCmdDXDefineRenderTargetView, sid)); + BUILD_BUG_ON(offsetof(struct vmw_view_define, sid) != + offsetof(SVGA3dCmdDXDefineDepthStencilView, sid)); + BUILD_BUG_ON(offsetof(struct vmw_view_define, sid) != + offsetof(SVGA3dCmdDXDefineUAView, sid)); + BUILD_BUG_ON(offsetof(struct vmw_view_define, sid) != + offsetof(SVGA3dCmdDXDefineDepthStencilView_v2, sid)); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_so.h b/drivers/gpu/drm/vmwgfx/vmwgfx_so.h new file mode 100644 index 0000000000..01c701e746 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_so.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * Copyright 2014-2015 VMware, Inc., Palo Alto, CA., USA + * + * 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 VMW_SO_H +#define VMW_SO_H + +enum vmw_view_type { + vmw_view_sr, + vmw_view_rt, + vmw_view_ds, + vmw_view_ua, + vmw_view_max, +}; + +enum vmw_so_type { + vmw_so_el, + vmw_so_bs, + vmw_so_ds, + vmw_so_rs, + vmw_so_ss, + vmw_so_so, + vmw_so_max, +}; + +/** + * union vmw_view_destroy - view destruction command body + * + * @rtv: RenderTarget view destruction command body + * @srv: ShaderResource view destruction command body + * @dsv: DepthStencil view destruction command body + * @view_id: A single u32 view id. + * + * The assumption here is that all union members are really represented by a + * single u32 in the command stream. If that's not the case, + * the size of this union will not equal the size of an u32, and the + * assumption is invalid, and we detect that at compile time in the + * vmw_so_build_asserts() function. + */ +union vmw_view_destroy { + struct SVGA3dCmdDXDestroyRenderTargetView rtv; + struct SVGA3dCmdDXDestroyShaderResourceView srv; + struct SVGA3dCmdDXDestroyDepthStencilView dsv; + struct SVGA3dCmdDXDestroyUAView uav; + u32 view_id; +}; + +/* Map enum vmw_view_type to view destroy command ids*/ +extern const u32 vmw_view_destroy_cmds[]; + +/* Map enum vmw_view_type to SVGACOTableType */ +extern const SVGACOTableType vmw_view_cotables[]; + +/* Map enum vmw_so_type to SVGACOTableType */ +extern const SVGACOTableType vmw_so_cotables[]; + +/* + * vmw_view_cmd_to_type - Return the view type for a create or destroy command + * + * @id: The SVGA3D command id. + * + * For a given view create or destroy command id, return the corresponding + * enum vmw_view_type. If the command is unknown, return vmw_view_max. + * The validity of the simplified calculation is verified in the + * vmw_so_build_asserts() function. + */ +static inline enum vmw_view_type vmw_view_cmd_to_type(u32 id) +{ + u32 tmp = (id - SVGA_3D_CMD_DX_DEFINE_SHADERRESOURCE_VIEW) / 2; + + if (id == SVGA_3D_CMD_DX_DEFINE_UA_VIEW || + id == SVGA_3D_CMD_DX_DESTROY_UA_VIEW) + return vmw_view_ua; + + if (id == SVGA_3D_CMD_DX_DEFINE_DEPTHSTENCIL_VIEW_V2) + return vmw_view_ds; + + if (tmp > (u32)vmw_view_ds) + return vmw_view_max; + + return (enum vmw_view_type) tmp; +} + +/* + * vmw_so_cmd_to_type - Return the state object type for a + * create or destroy command + * + * @id: The SVGA3D command id. + * + * For a given state object create or destroy command id, + * return the corresponding enum vmw_so_type. If the command is uknown, + * return vmw_so_max. We should perhaps optimize this function using + * a similar strategy as vmw_view_cmd_to_type(). + */ +static inline enum vmw_so_type vmw_so_cmd_to_type(u32 id) +{ + switch (id) { + case SVGA_3D_CMD_DX_DEFINE_ELEMENTLAYOUT: + case SVGA_3D_CMD_DX_DESTROY_ELEMENTLAYOUT: + return vmw_so_el; + case SVGA_3D_CMD_DX_DEFINE_BLEND_STATE: + case SVGA_3D_CMD_DX_DESTROY_BLEND_STATE: + return vmw_so_bs; + case SVGA_3D_CMD_DX_DEFINE_DEPTHSTENCIL_STATE: + case SVGA_3D_CMD_DX_DESTROY_DEPTHSTENCIL_STATE: + return vmw_so_ds; + case SVGA_3D_CMD_DX_DEFINE_RASTERIZER_STATE: + case SVGA_3D_CMD_DX_DEFINE_RASTERIZER_STATE_V2: + case SVGA_3D_CMD_DX_DESTROY_RASTERIZER_STATE: + return vmw_so_rs; + case SVGA_3D_CMD_DX_DEFINE_SAMPLER_STATE: + case SVGA_3D_CMD_DX_DESTROY_SAMPLER_STATE: + return vmw_so_ss; + case SVGA_3D_CMD_DX_DEFINE_STREAMOUTPUT: + case SVGA_3D_CMD_DX_DEFINE_STREAMOUTPUT_WITH_MOB: + case SVGA_3D_CMD_DX_DESTROY_STREAMOUTPUT: + return vmw_so_so; + default: + break; + } + return vmw_so_max; +} + +/* + * View management - vmwgfx_so.c + */ +extern int vmw_view_add(struct vmw_cmdbuf_res_manager *man, + struct vmw_resource *ctx, + struct vmw_resource *srf, + enum vmw_view_type view_type, + u32 user_key, + const void *cmd, + size_t cmd_size, + struct list_head *list); + +extern int vmw_view_remove(struct vmw_cmdbuf_res_manager *man, + u32 user_key, enum vmw_view_type view_type, + struct list_head *list, + struct vmw_resource **res_p); + +extern void vmw_view_surface_list_destroy(struct vmw_private *dev_priv, + struct list_head *view_list); +extern void vmw_view_cotable_list_destroy(struct vmw_private *dev_priv, + struct list_head *list, + bool readback); +extern struct vmw_resource *vmw_view_srf(struct vmw_resource *res); +extern struct vmw_resource *vmw_view_lookup(struct vmw_cmdbuf_res_manager *man, + enum vmw_view_type view_type, + u32 user_key); +extern u32 vmw_view_dirtying(struct vmw_resource *res); +#endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c new file mode 100644 index 0000000000..ba0c0e12cf --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -0,0 +1,1646 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/****************************************************************************** + * + * COPYRIGHT (C) 2014-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_kms.h" +#include "vmw_surface_cache.h" + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_damage_helper.h> +#include <drm/drm_fourcc.h> + +#define vmw_crtc_to_stdu(x) \ + container_of(x, struct vmw_screen_target_display_unit, base.crtc) +#define vmw_encoder_to_stdu(x) \ + container_of(x, struct vmw_screen_target_display_unit, base.encoder) +#define vmw_connector_to_stdu(x) \ + container_of(x, struct vmw_screen_target_display_unit, base.connector) + + + +enum stdu_content_type { + SAME_AS_DISPLAY = 0, + SEPARATE_SURFACE, + SEPARATE_BO +}; + +/** + * struct vmw_stdu_dirty - closure structure for the update functions + * + * @base: The base type we derive from. Used by vmw_kms_helper_dirty(). + * @transfer: Transfer direction for DMA command. + * @left: Left side of bounding box. + * @right: Right side of bounding box. + * @top: Top side of bounding box. + * @bottom: Bottom side of bounding box. + * @fb_left: Left side of the framebuffer/content bounding box + * @fb_top: Top of the framebuffer/content bounding box + * @pitch: framebuffer pitch (stride) + * @buf: buffer object when DMA-ing between buffer and screen targets. + * @sid: Surface ID when copying between surface and screen targets. + */ +struct vmw_stdu_dirty { + struct vmw_kms_dirty base; + s32 left, right, top, bottom; + s32 fb_left, fb_top; + u32 pitch; + union { + struct vmw_bo *buf; + u32 sid; + }; +}; + +/* + * SVGA commands that are used by this code. Please see the device headers + * for explanation. + */ +struct vmw_stdu_update { + SVGA3dCmdHeader header; + SVGA3dCmdUpdateGBScreenTarget body; +}; + +struct vmw_stdu_dma { + SVGA3dCmdHeader header; + SVGA3dCmdSurfaceDMA body; +}; + +struct vmw_stdu_surface_copy { + SVGA3dCmdHeader header; + SVGA3dCmdSurfaceCopy body; +}; + +struct vmw_stdu_update_gb_image { + SVGA3dCmdHeader header; + SVGA3dCmdUpdateGBImage body; +}; + +/** + * struct vmw_screen_target_display_unit + * + * @base: VMW specific DU structure + * @display_srf: surface to be displayed. The dimension of this will always + * match the display mode. If the display mode matches + * content_vfbs dimensions, then this is a pointer into the + * corresponding field in content_vfbs. If not, then this + * is a separate buffer to which content_vfbs will blit to. + * @content_fb_type: content_fb type + * @display_width: display width + * @display_height: display height + * @defined: true if the current display unit has been initialized + * @cpp: Bytes per pixel + */ +struct vmw_screen_target_display_unit { + struct vmw_display_unit base; + struct vmw_surface *display_srf; + enum stdu_content_type content_fb_type; + s32 display_width, display_height; + + bool defined; + + /* For CPU Blit */ + unsigned int cpp; +}; + + + +static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu); + + + +/****************************************************************************** + * Screen Target Display Unit CRTC Functions + *****************************************************************************/ + +/** + * vmw_stdu_crtc_destroy - cleans up the STDU + * + * @crtc: used to get a reference to the containing STDU + */ +static void vmw_stdu_crtc_destroy(struct drm_crtc *crtc) +{ + vmw_stdu_destroy(vmw_crtc_to_stdu(crtc)); +} + +/** + * vmw_stdu_define_st - Defines a Screen Target + * + * @dev_priv: VMW DRM device + * @stdu: display unit to create a Screen Target for + * @mode: The mode to set. + * @crtc_x: X coordinate of screen target relative to framebuffer origin. + * @crtc_y: Y coordinate of screen target relative to framebuffer origin. + * + * Creates a STDU that we can used later. This function is called whenever the + * framebuffer size changes. + * + * RETURNs: + * 0 on success, error code on failure + */ +static int vmw_stdu_define_st(struct vmw_private *dev_priv, + struct vmw_screen_target_display_unit *stdu, + struct drm_display_mode *mode, + int crtc_x, int crtc_y) +{ + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDefineGBScreenTarget body; + } *cmd; + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DEFINE_GB_SCREENTARGET; + cmd->header.size = sizeof(cmd->body); + + cmd->body.stid = stdu->base.unit; + cmd->body.width = mode->hdisplay; + cmd->body.height = mode->vdisplay; + cmd->body.flags = (0 == cmd->body.stid) ? SVGA_STFLAG_PRIMARY : 0; + cmd->body.dpi = 0; + cmd->body.xRoot = crtc_x; + cmd->body.yRoot = crtc_y; + + stdu->base.set_gui_x = cmd->body.xRoot; + stdu->base.set_gui_y = cmd->body.yRoot; + + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + stdu->defined = true; + stdu->display_width = mode->hdisplay; + stdu->display_height = mode->vdisplay; + + return 0; +} + + + +/** + * vmw_stdu_bind_st - Binds a surface to a Screen Target + * + * @dev_priv: VMW DRM device + * @stdu: display unit affected + * @res: Buffer to bind to the screen target. Set to NULL to blank screen. + * + * Binding a surface to a Screen Target the same as flipping + */ +static int vmw_stdu_bind_st(struct vmw_private *dev_priv, + struct vmw_screen_target_display_unit *stdu, + const struct vmw_resource *res) +{ + SVGA3dSurfaceImageId image; + + struct { + SVGA3dCmdHeader header; + SVGA3dCmdBindGBScreenTarget body; + } *cmd; + + + if (!stdu->defined) { + DRM_ERROR("No screen target defined\n"); + return -EINVAL; + } + + /* Set up image using information in vfb */ + memset(&image, 0, sizeof(image)); + image.sid = res ? res->id : SVGA3D_INVALID_ID; + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_BIND_GB_SCREENTARGET; + cmd->header.size = sizeof(cmd->body); + + cmd->body.stid = stdu->base.unit; + cmd->body.image = image; + + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; +} + +/** + * vmw_stdu_populate_update - populate an UPDATE_GB_SCREENTARGET command with a + * bounding box. + * + * @cmd: Pointer to command stream. + * @unit: Screen target unit. + * @left: Left side of bounding box. + * @right: Right side of bounding box. + * @top: Top side of bounding box. + * @bottom: Bottom side of bounding box. + */ +static void vmw_stdu_populate_update(void *cmd, int unit, + s32 left, s32 right, s32 top, s32 bottom) +{ + struct vmw_stdu_update *update = cmd; + + update->header.id = SVGA_3D_CMD_UPDATE_GB_SCREENTARGET; + update->header.size = sizeof(update->body); + + update->body.stid = unit; + update->body.rect.x = left; + update->body.rect.y = top; + update->body.rect.w = right - left; + update->body.rect.h = bottom - top; +} + +/** + * vmw_stdu_update_st - Full update of a Screen Target + * + * @dev_priv: VMW DRM device + * @stdu: display unit affected + * + * This function needs to be called whenever the content of a screen + * target has changed completely. Typically as a result of a backing + * surface change. + * + * RETURNS: + * 0 on success, error code on failure + */ +static int vmw_stdu_update_st(struct vmw_private *dev_priv, + struct vmw_screen_target_display_unit *stdu) +{ + struct vmw_stdu_update *cmd; + + if (!stdu->defined) { + DRM_ERROR("No screen target defined"); + return -EINVAL; + } + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + vmw_stdu_populate_update(cmd, stdu->base.unit, + 0, stdu->display_width, + 0, stdu->display_height); + + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + return 0; +} + + + +/** + * vmw_stdu_destroy_st - Destroy a Screen Target + * + * @dev_priv: VMW DRM device + * @stdu: display unit to destroy + */ +static int vmw_stdu_destroy_st(struct vmw_private *dev_priv, + struct vmw_screen_target_display_unit *stdu) +{ + int ret; + + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDestroyGBScreenTarget body; + } *cmd; + + + /* Nothing to do if not successfully defined */ + if (unlikely(!stdu->defined)) + return 0; + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DESTROY_GB_SCREENTARGET; + cmd->header.size = sizeof(cmd->body); + + cmd->body.stid = stdu->base.unit; + + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + /* 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"); + + stdu->defined = false; + stdu->display_width = 0; + stdu->display_height = 0; + + return ret; +} + + +/** + * vmw_stdu_crtc_mode_set_nofb - Updates screen target size + * + * @crtc: CRTC associated with the screen target + * + * This function defines/destroys a screen target + * + */ +static void vmw_stdu_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_target_display_unit *stdu; + struct drm_connector_state *conn_state; + struct vmw_connector_state *vmw_conn_state; + int x, y, ret; + + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + conn_state = stdu->base.connector.state; + vmw_conn_state = vmw_connector_state_to_vcs(conn_state); + + if (stdu->defined) { + ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); + if (ret) + DRM_ERROR("Failed to blank CRTC\n"); + + (void) vmw_stdu_update_st(dev_priv, stdu); + + ret = vmw_stdu_destroy_st(dev_priv, stdu); + if (ret) + DRM_ERROR("Failed to destroy Screen Target\n"); + + stdu->content_fb_type = SAME_AS_DISPLAY; + } + + if (!crtc->state->enable) + return; + + x = vmw_conn_state->gui_x; + y = vmw_conn_state->gui_y; + + vmw_svga_enable(dev_priv); + ret = vmw_stdu_define_st(dev_priv, stdu, &crtc->mode, x, y); + + if (ret) + DRM_ERROR("Failed to define Screen Target of size %dx%d\n", + crtc->x, crtc->y); +} + + +static void vmw_stdu_crtc_helper_prepare(struct drm_crtc *crtc) +{ +} + +static void vmw_stdu_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ +} + +static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct vmw_private *dev_priv; + struct vmw_screen_target_display_unit *stdu; + int ret; + + + if (!crtc) { + DRM_ERROR("CRTC is NULL\n"); + return; + } + + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + + if (stdu->defined) { + ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); + if (ret) + DRM_ERROR("Failed to blank CRTC\n"); + + (void) vmw_stdu_update_st(dev_priv, stdu); + + ret = vmw_stdu_destroy_st(dev_priv, stdu); + if (ret) + DRM_ERROR("Failed to destroy Screen Target\n"); + + stdu->content_fb_type = SAME_AS_DISPLAY; + } +} + +/** + * vmw_stdu_bo_cpu_clip - Callback to encode a CPU blit + * + * @dirty: The closure structure. + * + * This function calculates the bounding box for all the incoming clips. + */ +static void vmw_stdu_bo_cpu_clip(struct vmw_kms_dirty *dirty) +{ + struct vmw_stdu_dirty *ddirty = + container_of(dirty, struct vmw_stdu_dirty, base); + + dirty->num_hits = 1; + + /* Calculate destination bounding box */ + ddirty->left = min_t(s32, ddirty->left, dirty->unit_x1); + ddirty->top = min_t(s32, ddirty->top, dirty->unit_y1); + ddirty->right = max_t(s32, ddirty->right, dirty->unit_x2); + ddirty->bottom = max_t(s32, ddirty->bottom, dirty->unit_y2); + + /* + * Calculate content bounding box. We only need the top-left + * coordinate because width and height will be the same as the + * destination bounding box above + */ + ddirty->fb_left = min_t(s32, ddirty->fb_left, dirty->fb_x); + ddirty->fb_top = min_t(s32, ddirty->fb_top, dirty->fb_y); +} + + +/** + * vmw_stdu_bo_cpu_commit - Callback to do a CPU blit from buffer object + * + * @dirty: The closure structure. + * + * For the special case when we cannot create a proxy surface in a + * 2D VM, we have to do a CPU blit ourselves. + */ +static void vmw_stdu_bo_cpu_commit(struct vmw_kms_dirty *dirty) +{ + struct vmw_stdu_dirty *ddirty = + container_of(dirty, struct vmw_stdu_dirty, base); + struct vmw_screen_target_display_unit *stdu = + container_of(dirty->unit, typeof(*stdu), base); + s32 width, height; + s32 src_pitch, dst_pitch; + struct ttm_buffer_object *src_bo, *dst_bo; + u32 src_offset, dst_offset; + struct vmw_diff_cpy diff = VMW_CPU_BLIT_DIFF_INITIALIZER(stdu->cpp); + + if (!dirty->num_hits) + return; + + width = ddirty->right - ddirty->left; + height = ddirty->bottom - ddirty->top; + + if (width == 0 || height == 0) + return; + + /* Assume we are blitting from Guest (bo) to Host (display_srf) */ + src_pitch = stdu->display_srf->metadata.base_size.width * stdu->cpp; + src_bo = &stdu->display_srf->res.guest_memory_bo->tbo; + src_offset = ddirty->top * src_pitch + ddirty->left * stdu->cpp; + + dst_pitch = ddirty->pitch; + dst_bo = &ddirty->buf->tbo; + dst_offset = ddirty->fb_top * dst_pitch + ddirty->fb_left * stdu->cpp; + + (void) vmw_bo_cpu_blit(dst_bo, dst_offset, dst_pitch, + src_bo, src_offset, src_pitch, + width * stdu->cpp, height, &diff); +} + +/** + * vmw_kms_stdu_readback - Perform a readback from a buffer-object backed + * framebuffer and the screen target system. + * + * @dev_priv: Pointer to the device private structure. + * @file_priv: Pointer to a struct drm-file identifying the caller. May be + * set to NULL, but then @user_fence_rep must also be set to NULL. + * @vfb: Pointer to the buffer-object backed framebuffer. + * @user_fence_rep: User-space provided structure for fence information. + * @clips: Array of clip rects. Either @clips or @vclips must be NULL. + * @vclips: Alternate array of clip rects. Either @clips or @vclips must + * be NULL. + * @num_clips: Number of clip rects in @clips or @vclips. + * @increment: Increment to use when looping over @clips or @vclips. + * @crtc: If crtc is passed, perform stdu dma on that crtc only. + * + * If DMA-ing till the screen target system, the function will also notify + * the screen target system that a bounding box of the cliprects has been + * updated. + * Returns 0 on success, negative error code on failure. -ERESTARTSYS if + * interrupted. + */ +int vmw_kms_stdu_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_clip_rect *clips, + struct drm_vmw_rect *vclips, + uint32_t num_clips, + int increment, + struct drm_crtc *crtc) +{ + struct vmw_bo *buf = + container_of(vfb, struct vmw_framebuffer_bo, base)->buffer; + struct vmw_stdu_dirty ddirty; + int ret; + DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); + + /* + * The GMR domain might seem confusing because it might seem like it should + * never happen with screen targets but e.g. the xorg vmware driver issues + * CMD_SURFACE_DMA for various pixmap updates which might transition our bo to + * a GMR. Instead of forcing another transition we can optimize the readback + * by reading directly from the GMR. + */ + vmw_bo_placement_set(buf, + VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_GMR, + VMW_BO_DOMAIN_MOB | VMW_BO_DOMAIN_SYS | VMW_BO_DOMAIN_GMR); + ret = vmw_validation_add_bo(&val_ctx, buf); + if (ret) + return ret; + + ret = vmw_validation_prepare(&val_ctx, NULL, true); + if (ret) + goto out_unref; + + ddirty.left = ddirty.top = S32_MAX; + ddirty.right = ddirty.bottom = S32_MIN; + ddirty.fb_left = ddirty.fb_top = S32_MAX; + ddirty.pitch = vfb->base.pitches[0]; + ddirty.buf = buf; + + ddirty.base.fifo_commit = vmw_stdu_bo_cpu_commit; + ddirty.base.clip = vmw_stdu_bo_cpu_clip; + ddirty.base.fifo_reserve_size = 0; + + ddirty.base.crtc = crtc; + + ret = vmw_kms_helper_dirty(dev_priv, vfb, clips, vclips, + 0, 0, num_clips, increment, &ddirty.base); + + vmw_kms_helper_validation_finish(dev_priv, file_priv, &val_ctx, NULL, + user_fence_rep); + return ret; + +out_unref: + vmw_validation_unref_lists(&val_ctx); + return ret; +} + +/** + * vmw_kms_stdu_surface_clip - Callback to encode a surface copy command cliprect + * + * @dirty: The closure structure. + * + * Encodes a surface copy command cliprect and updates the bounding box + * for the copy. + */ +static void vmw_kms_stdu_surface_clip(struct vmw_kms_dirty *dirty) +{ + struct vmw_stdu_dirty *sdirty = + container_of(dirty, struct vmw_stdu_dirty, base); + struct vmw_stdu_surface_copy *cmd = dirty->cmd; + struct vmw_screen_target_display_unit *stdu = + container_of(dirty->unit, typeof(*stdu), base); + + if (sdirty->sid != stdu->display_srf->res.id) { + struct SVGA3dCopyBox *blit = (struct SVGA3dCopyBox *) &cmd[1]; + + blit += dirty->num_hits; + blit->srcx = dirty->fb_x; + blit->srcy = dirty->fb_y; + blit->x = dirty->unit_x1; + blit->y = dirty->unit_y1; + blit->d = 1; + blit->w = dirty->unit_x2 - dirty->unit_x1; + blit->h = dirty->unit_y2 - dirty->unit_y1; + } + + dirty->num_hits++; + + /* Destination bounding box */ + sdirty->left = min_t(s32, sdirty->left, dirty->unit_x1); + sdirty->top = min_t(s32, sdirty->top, dirty->unit_y1); + sdirty->right = max_t(s32, sdirty->right, dirty->unit_x2); + sdirty->bottom = max_t(s32, sdirty->bottom, dirty->unit_y2); +} + +/** + * vmw_kms_stdu_surface_fifo_commit - Callback to fill in and submit a surface + * copy command. + * + * @dirty: The closure structure. + * + * Fills in the missing fields in a surface copy command, and encodes a screen + * target update command. + */ +static void vmw_kms_stdu_surface_fifo_commit(struct vmw_kms_dirty *dirty) +{ + struct vmw_stdu_dirty *sdirty = + container_of(dirty, struct vmw_stdu_dirty, base); + struct vmw_screen_target_display_unit *stdu = + container_of(dirty->unit, typeof(*stdu), base); + struct vmw_stdu_surface_copy *cmd = dirty->cmd; + struct vmw_stdu_update *update; + size_t blit_size = sizeof(SVGA3dCopyBox) * dirty->num_hits; + size_t commit_size; + + if (!dirty->num_hits) { + vmw_cmd_commit(dirty->dev_priv, 0); + return; + } + + if (sdirty->sid != stdu->display_srf->res.id) { + struct SVGA3dCopyBox *blit = (struct SVGA3dCopyBox *) &cmd[1]; + + cmd->header.id = SVGA_3D_CMD_SURFACE_COPY; + cmd->header.size = sizeof(cmd->body) + blit_size; + cmd->body.src.sid = sdirty->sid; + cmd->body.dest.sid = stdu->display_srf->res.id; + update = (struct vmw_stdu_update *) &blit[dirty->num_hits]; + commit_size = sizeof(*cmd) + blit_size + sizeof(*update); + stdu->display_srf->res.res_dirty = true; + } else { + update = dirty->cmd; + commit_size = sizeof(*update); + } + + vmw_stdu_populate_update(update, stdu->base.unit, sdirty->left, + sdirty->right, sdirty->top, sdirty->bottom); + + vmw_cmd_commit(dirty->dev_priv, commit_size); + + sdirty->left = sdirty->top = S32_MAX; + sdirty->right = sdirty->bottom = S32_MIN; +} + +/** + * vmw_kms_stdu_surface_dirty - Dirty part of a surface backed framebuffer + * + * @dev_priv: Pointer to the device private structure. + * @framebuffer: Pointer to the surface-buffer backed framebuffer. + * @clips: Array of clip rects. Either @clips or @vclips must be NULL. + * @vclips: Alternate array of clip rects. Either @clips or @vclips must + * be NULL. + * @srf: Pointer to surface to blit from. If NULL, the surface attached + * to @framebuffer will be used. + * @dest_x: X coordinate offset to align @srf with framebuffer coordinates. + * @dest_y: Y coordinate offset to align @srf with framebuffer coordinates. + * @num_clips: Number of clip rects in @clips. + * @inc: Increment to use when looping over @clips. + * @out_fence: If non-NULL, will return a ref-counted pointer to a + * struct vmw_fence_obj. The returned fence pointer may be NULL in which + * case the device has already synchronized. + * @crtc: If crtc is passed, perform surface dirty on that crtc only. + * + * Returns 0 on success, negative error code on failure. -ERESTARTSYS if + * interrupted. + */ +int vmw_kms_stdu_surface_dirty(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + struct drm_clip_rect *clips, + struct drm_vmw_rect *vclips, + struct vmw_resource *srf, + s32 dest_x, + s32 dest_y, + unsigned num_clips, int inc, + struct vmw_fence_obj **out_fence, + struct drm_crtc *crtc) +{ + struct vmw_framebuffer_surface *vfbs = + container_of(framebuffer, typeof(*vfbs), base); + struct vmw_stdu_dirty sdirty; + DECLARE_VAL_CONTEXT(val_ctx, NULL, 0); + int ret; + + if (!srf) + srf = &vfbs->surface->res; + + ret = vmw_validation_add_resource(&val_ctx, srf, 0, VMW_RES_DIRTY_NONE, + NULL, NULL); + if (ret) + return ret; + + ret = vmw_validation_prepare(&val_ctx, &dev_priv->cmdbuf_mutex, true); + if (ret) + goto out_unref; + + if (vfbs->is_bo_proxy) { + ret = vmw_kms_update_proxy(srf, clips, num_clips, inc); + if (ret) + goto out_finish; + } + + sdirty.base.fifo_commit = vmw_kms_stdu_surface_fifo_commit; + sdirty.base.clip = vmw_kms_stdu_surface_clip; + sdirty.base.fifo_reserve_size = sizeof(struct vmw_stdu_surface_copy) + + sizeof(SVGA3dCopyBox) * num_clips + + sizeof(struct vmw_stdu_update); + sdirty.base.crtc = crtc; + sdirty.sid = srf->id; + sdirty.left = sdirty.top = S32_MAX; + sdirty.right = sdirty.bottom = S32_MIN; + + ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips, + dest_x, dest_y, num_clips, inc, + &sdirty.base); +out_finish: + vmw_kms_helper_validation_finish(dev_priv, NULL, &val_ctx, out_fence, + NULL); + + return ret; + +out_unref: + vmw_validation_unref_lists(&val_ctx); + return ret; +} + + +/* + * Screen Target CRTC dispatch table + */ +static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = { + .gamma_set = vmw_du_crtc_gamma_set, + .destroy = vmw_stdu_crtc_destroy, + .reset = vmw_du_crtc_reset, + .atomic_duplicate_state = vmw_du_crtc_duplicate_state, + .atomic_destroy_state = vmw_du_crtc_destroy_state, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, +}; + + + +/****************************************************************************** + * Screen Target Display Unit Encoder Functions + *****************************************************************************/ + +/** + * vmw_stdu_encoder_destroy - cleans up the STDU + * + * @encoder: used the get the containing STDU + * + * vmwgfx cleans up crtc/encoder/connector all at the same time so technically + * this can be a no-op. Nevertheless, it doesn't hurt of have this in case + * the common KMS code changes and somehow vmw_stdu_crtc_destroy() doesn't + * get called. + */ +static void vmw_stdu_encoder_destroy(struct drm_encoder *encoder) +{ + vmw_stdu_destroy(vmw_encoder_to_stdu(encoder)); +} + +static const struct drm_encoder_funcs vmw_stdu_encoder_funcs = { + .destroy = vmw_stdu_encoder_destroy, +}; + + + +/****************************************************************************** + * Screen Target Display Unit Connector Functions + *****************************************************************************/ + +/** + * vmw_stdu_connector_destroy - cleans up the STDU + * + * @connector: used to get the containing STDU + * + * vmwgfx cleans up crtc/encoder/connector all at the same time so technically + * this can be a no-op. Nevertheless, it doesn't hurt of have this in case + * the common KMS code changes and somehow vmw_stdu_crtc_destroy() doesn't + * get called. + */ +static void vmw_stdu_connector_destroy(struct drm_connector *connector) +{ + vmw_stdu_destroy(vmw_connector_to_stdu(connector)); +} + + + +static const struct drm_connector_funcs vmw_stdu_connector_funcs = { + .dpms = vmw_du_connector_dpms, + .detect = vmw_du_connector_detect, + .fill_modes = vmw_du_connector_fill_modes, + .destroy = vmw_stdu_connector_destroy, + .reset = vmw_du_connector_reset, + .atomic_duplicate_state = vmw_du_connector_duplicate_state, + .atomic_destroy_state = vmw_du_connector_destroy_state, +}; + + +static const struct +drm_connector_helper_funcs vmw_stdu_connector_helper_funcs = { +}; + + + +/****************************************************************************** + * Screen Target Display Plane Functions + *****************************************************************************/ + + + +/** + * vmw_stdu_primary_plane_cleanup_fb - Unpins the display surface + * + * @plane: display plane + * @old_state: Contains the FB to clean up + * + * Unpins the display surface + * + * Returns 0 on success + */ +static void +vmw_stdu_primary_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct vmw_plane_state *vps = vmw_plane_state_to_vps(old_state); + + if (vps->surf) + WARN_ON(!vps->pinned); + + vmw_du_plane_cleanup_fb(plane, old_state); + + vps->content_fb_type = SAME_AS_DISPLAY; + vps->cpp = 0; +} + + + +/** + * vmw_stdu_primary_plane_prepare_fb - Readies the display surface + * + * @plane: display plane + * @new_state: info on the new plane state, including the FB + * + * This function allocates a new display surface if the content is + * backed by a buffer object. The display surface is pinned here, and it'll + * be unpinned in .cleanup_fb() + * + * Returns 0 on success + */ +static int +vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct vmw_private *dev_priv = vmw_priv(plane->dev); + struct drm_framebuffer *new_fb = new_state->fb; + struct vmw_framebuffer *vfb; + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + enum stdu_content_type new_content_type; + struct vmw_framebuffer_surface *new_vfbs; + uint32_t hdisplay = new_state->crtc_w, vdisplay = new_state->crtc_h; + int ret; + + /* No FB to prepare */ + if (!new_fb) { + if (vps->surf) { + WARN_ON(vps->pinned != 0); + vmw_surface_unreference(&vps->surf); + } + + return 0; + } + + vfb = vmw_framebuffer_to_vfb(new_fb); + new_vfbs = (vfb->bo) ? NULL : vmw_framebuffer_to_vfbs(new_fb); + + if (new_vfbs && + new_vfbs->surface->metadata.base_size.width == hdisplay && + new_vfbs->surface->metadata.base_size.height == vdisplay) + new_content_type = SAME_AS_DISPLAY; + else if (vfb->bo) + new_content_type = SEPARATE_BO; + else + new_content_type = SEPARATE_SURFACE; + + if (new_content_type != SAME_AS_DISPLAY) { + struct vmw_surface_metadata metadata = {0}; + + /* + * If content buffer is a buffer object, then we have to + * construct surface info + */ + if (new_content_type == SEPARATE_BO) { + + switch (new_fb->format->cpp[0]*8) { + case 32: + metadata.format = SVGA3D_X8R8G8B8; + break; + + case 16: + metadata.format = SVGA3D_R5G6B5; + break; + + case 8: + metadata.format = SVGA3D_P8; + break; + + default: + DRM_ERROR("Invalid format\n"); + return -EINVAL; + } + + metadata.mip_levels[0] = 1; + metadata.num_sizes = 1; + metadata.scanout = true; + } else { + metadata = new_vfbs->surface->metadata; + } + + metadata.base_size.width = hdisplay; + metadata.base_size.height = vdisplay; + metadata.base_size.depth = 1; + + if (vps->surf) { + struct drm_vmw_size cur_base_size = + vps->surf->metadata.base_size; + + if (cur_base_size.width != metadata.base_size.width || + cur_base_size.height != metadata.base_size.height || + vps->surf->metadata.format != metadata.format) { + WARN_ON(vps->pinned != 0); + vmw_surface_unreference(&vps->surf); + } + + } + + if (!vps->surf) { + ret = vmw_gb_surface_define(dev_priv, &metadata, + &vps->surf); + if (ret != 0) { + DRM_ERROR("Couldn't allocate STDU surface.\n"); + return ret; + } + } + } else { + /* + * prepare_fb and clean_fb should only take care of pinning + * and unpinning. References are tracked by state objects. + * The only time we add a reference in prepare_fb is if the + * state object doesn't have a reference to begin with + */ + if (vps->surf) { + WARN_ON(vps->pinned != 0); + vmw_surface_unreference(&vps->surf); + } + + vps->surf = vmw_surface_reference(new_vfbs->surface); + } + + if (vps->surf) { + + /* Pin new surface before flipping */ + ret = vmw_resource_pin(&vps->surf->res, false); + if (ret) + goto out_srf_unref; + + vps->pinned++; + } + + vps->content_fb_type = new_content_type; + + /* + * This should only happen if the buffer object is too large to create a + * proxy surface for. + */ + if (vps->content_fb_type == SEPARATE_BO) + vps->cpp = new_fb->pitches[0] / new_fb->width; + + return 0; + +out_srf_unref: + vmw_surface_unreference(&vps->surf); + return ret; +} + +static uint32_t vmw_stdu_bo_fifo_size_cpu(struct vmw_du_update_plane *update, + uint32_t num_hits) +{ + return sizeof(struct vmw_stdu_update_gb_image) + + sizeof(struct vmw_stdu_update); +} + +static uint32_t vmw_stdu_bo_pre_clip_cpu(struct vmw_du_update_plane *update, + void *cmd, uint32_t num_hits) +{ + struct vmw_du_update_plane_buffer *bo_update = + container_of(update, typeof(*bo_update), base); + + bo_update->fb_left = INT_MAX; + bo_update->fb_top = INT_MAX; + + return 0; +} + +static uint32_t vmw_stdu_bo_clip_cpu(struct vmw_du_update_plane *update, + void *cmd, struct drm_rect *clip, + uint32_t fb_x, uint32_t fb_y) +{ + struct vmw_du_update_plane_buffer *bo_update = + container_of(update, typeof(*bo_update), base); + + bo_update->fb_left = min_t(int, bo_update->fb_left, fb_x); + bo_update->fb_top = min_t(int, bo_update->fb_top, fb_y); + + return 0; +} + +static uint32_t +vmw_stdu_bo_populate_update_cpu(struct vmw_du_update_plane *update, void *cmd, + struct drm_rect *bb) +{ + struct vmw_du_update_plane_buffer *bo_update; + struct vmw_screen_target_display_unit *stdu; + struct vmw_framebuffer_bo *vfbbo; + struct vmw_diff_cpy diff = VMW_CPU_BLIT_DIFF_INITIALIZER(0); + struct vmw_stdu_update_gb_image *cmd_img = cmd; + struct vmw_stdu_update *cmd_update; + struct ttm_buffer_object *src_bo, *dst_bo; + u32 src_offset, dst_offset; + s32 src_pitch, dst_pitch; + s32 width, height; + + bo_update = container_of(update, typeof(*bo_update), base); + stdu = container_of(update->du, typeof(*stdu), base); + vfbbo = container_of(update->vfb, typeof(*vfbbo), base); + + width = bb->x2 - bb->x1; + height = bb->y2 - bb->y1; + + diff.cpp = stdu->cpp; + + dst_bo = &stdu->display_srf->res.guest_memory_bo->tbo; + dst_pitch = stdu->display_srf->metadata.base_size.width * stdu->cpp; + dst_offset = bb->y1 * dst_pitch + bb->x1 * stdu->cpp; + + src_bo = &vfbbo->buffer->tbo; + src_pitch = update->vfb->base.pitches[0]; + src_offset = bo_update->fb_top * src_pitch + bo_update->fb_left * + stdu->cpp; + + (void) vmw_bo_cpu_blit(dst_bo, dst_offset, dst_pitch, src_bo, + src_offset, src_pitch, width * stdu->cpp, height, + &diff); + + if (drm_rect_visible(&diff.rect)) { + SVGA3dBox *box = &cmd_img->body.box; + + cmd_img->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE; + cmd_img->header.size = sizeof(cmd_img->body); + cmd_img->body.image.sid = stdu->display_srf->res.id; + cmd_img->body.image.face = 0; + cmd_img->body.image.mipmap = 0; + + box->x = diff.rect.x1; + box->y = diff.rect.y1; + box->z = 0; + box->w = drm_rect_width(&diff.rect); + box->h = drm_rect_height(&diff.rect); + box->d = 1; + + cmd_update = (struct vmw_stdu_update *)&cmd_img[1]; + vmw_stdu_populate_update(cmd_update, stdu->base.unit, + diff.rect.x1, diff.rect.x2, + diff.rect.y1, diff.rect.y2); + + return sizeof(*cmd_img) + sizeof(*cmd_update); + } + + return 0; +} + +/** + * vmw_stdu_plane_update_bo - Update display unit for bo backed fb. + * @dev_priv: device private. + * @plane: plane state. + * @old_state: old plane state. + * @vfb: framebuffer which is blitted to display unit. + * @out_fence: If non-NULL, will return a ref-counted pointer to vmw_fence_obj. + * The returned fence pointer may be NULL in which case the device + * has already synchronized. + * + * Return: 0 on success or a negative error code on failure. + */ +static int vmw_stdu_plane_update_bo(struct vmw_private *dev_priv, + struct drm_plane *plane, + struct drm_plane_state *old_state, + struct vmw_framebuffer *vfb, + struct vmw_fence_obj **out_fence) +{ + struct vmw_du_update_plane_buffer bo_update; + + memset(&bo_update, 0, sizeof(struct vmw_du_update_plane_buffer)); + bo_update.base.plane = plane; + bo_update.base.old_state = old_state; + bo_update.base.dev_priv = dev_priv; + bo_update.base.du = vmw_crtc_to_du(plane->state->crtc); + bo_update.base.vfb = vfb; + bo_update.base.out_fence = out_fence; + bo_update.base.mutex = NULL; + bo_update.base.intr = false; + + bo_update.base.calc_fifo_size = vmw_stdu_bo_fifo_size_cpu; + bo_update.base.pre_clip = vmw_stdu_bo_pre_clip_cpu; + bo_update.base.clip = vmw_stdu_bo_clip_cpu; + bo_update.base.post_clip = vmw_stdu_bo_populate_update_cpu; + + return vmw_du_helper_plane_update(&bo_update.base); +} + +static uint32_t +vmw_stdu_surface_fifo_size_same_display(struct vmw_du_update_plane *update, + uint32_t num_hits) +{ + struct vmw_framebuffer_surface *vfbs; + uint32_t size = 0; + + vfbs = container_of(update->vfb, typeof(*vfbs), base); + + if (vfbs->is_bo_proxy) + size += sizeof(struct vmw_stdu_update_gb_image) * num_hits; + + size += sizeof(struct vmw_stdu_update); + + return size; +} + +static uint32_t vmw_stdu_surface_fifo_size(struct vmw_du_update_plane *update, + uint32_t num_hits) +{ + struct vmw_framebuffer_surface *vfbs; + uint32_t size = 0; + + vfbs = container_of(update->vfb, typeof(*vfbs), base); + + if (vfbs->is_bo_proxy) + size += sizeof(struct vmw_stdu_update_gb_image) * num_hits; + + size += sizeof(struct vmw_stdu_surface_copy) + sizeof(SVGA3dCopyBox) * + num_hits + sizeof(struct vmw_stdu_update); + + return size; +} + +static uint32_t +vmw_stdu_surface_update_proxy(struct vmw_du_update_plane *update, void *cmd) +{ + struct vmw_framebuffer_surface *vfbs; + struct drm_plane_state *state = update->plane->state; + struct drm_plane_state *old_state = update->old_state; + struct vmw_stdu_update_gb_image *cmd_update = cmd; + struct drm_atomic_helper_damage_iter iter; + struct drm_rect clip; + uint32_t copy_size = 0; + + vfbs = container_of(update->vfb, typeof(*vfbs), base); + + /* + * proxy surface is special where a buffer object type fb is wrapped + * in a surface and need an update gb image command to sync with device. + */ + drm_atomic_helper_damage_iter_init(&iter, old_state, state); + drm_atomic_for_each_plane_damage(&iter, &clip) { + SVGA3dBox *box = &cmd_update->body.box; + + cmd_update->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE; + cmd_update->header.size = sizeof(cmd_update->body); + cmd_update->body.image.sid = vfbs->surface->res.id; + cmd_update->body.image.face = 0; + cmd_update->body.image.mipmap = 0; + + box->x = clip.x1; + box->y = clip.y1; + box->z = 0; + box->w = drm_rect_width(&clip); + box->h = drm_rect_height(&clip); + box->d = 1; + + copy_size += sizeof(*cmd_update); + cmd_update++; + } + + return copy_size; +} + +static uint32_t +vmw_stdu_surface_populate_copy(struct vmw_du_update_plane *update, void *cmd, + uint32_t num_hits) +{ + struct vmw_screen_target_display_unit *stdu; + struct vmw_framebuffer_surface *vfbs; + struct vmw_stdu_surface_copy *cmd_copy = cmd; + + stdu = container_of(update->du, typeof(*stdu), base); + vfbs = container_of(update->vfb, typeof(*vfbs), base); + + cmd_copy->header.id = SVGA_3D_CMD_SURFACE_COPY; + cmd_copy->header.size = sizeof(cmd_copy->body) + sizeof(SVGA3dCopyBox) * + num_hits; + cmd_copy->body.src.sid = vfbs->surface->res.id; + cmd_copy->body.dest.sid = stdu->display_srf->res.id; + + return sizeof(*cmd_copy); +} + +static uint32_t +vmw_stdu_surface_populate_clip(struct vmw_du_update_plane *update, void *cmd, + struct drm_rect *clip, uint32_t fb_x, + uint32_t fb_y) +{ + struct SVGA3dCopyBox *box = cmd; + + box->srcx = fb_x; + box->srcy = fb_y; + box->srcz = 0; + box->x = clip->x1; + box->y = clip->y1; + box->z = 0; + box->w = drm_rect_width(clip); + box->h = drm_rect_height(clip); + box->d = 1; + + return sizeof(*box); +} + +static uint32_t +vmw_stdu_surface_populate_update(struct vmw_du_update_plane *update, void *cmd, + struct drm_rect *bb) +{ + vmw_stdu_populate_update(cmd, update->du->unit, bb->x1, bb->x2, bb->y1, + bb->y2); + + return sizeof(struct vmw_stdu_update); +} + +/** + * vmw_stdu_plane_update_surface - Update display unit for surface backed fb + * @dev_priv: Device private + * @plane: Plane state + * @old_state: Old plane state + * @vfb: Framebuffer which is blitted to display unit + * @out_fence: If non-NULL, will return a ref-counted pointer to vmw_fence_obj. + * The returned fence pointer may be NULL in which case the device + * has already synchronized. + * + * Return: 0 on success or a negative error code on failure. + */ +static int vmw_stdu_plane_update_surface(struct vmw_private *dev_priv, + struct drm_plane *plane, + struct drm_plane_state *old_state, + struct vmw_framebuffer *vfb, + struct vmw_fence_obj **out_fence) +{ + struct vmw_du_update_plane srf_update; + struct vmw_screen_target_display_unit *stdu; + struct vmw_framebuffer_surface *vfbs; + + stdu = vmw_crtc_to_stdu(plane->state->crtc); + vfbs = container_of(vfb, typeof(*vfbs), base); + + memset(&srf_update, 0, sizeof(struct vmw_du_update_plane)); + srf_update.plane = plane; + srf_update.old_state = old_state; + srf_update.dev_priv = dev_priv; + srf_update.du = vmw_crtc_to_du(plane->state->crtc); + srf_update.vfb = vfb; + srf_update.out_fence = out_fence; + srf_update.mutex = &dev_priv->cmdbuf_mutex; + srf_update.intr = true; + + if (vfbs->is_bo_proxy) + srf_update.post_prepare = vmw_stdu_surface_update_proxy; + + if (vfbs->surface->res.id != stdu->display_srf->res.id) { + srf_update.calc_fifo_size = vmw_stdu_surface_fifo_size; + srf_update.pre_clip = vmw_stdu_surface_populate_copy; + srf_update.clip = vmw_stdu_surface_populate_clip; + } else { + srf_update.calc_fifo_size = + vmw_stdu_surface_fifo_size_same_display; + } + + srf_update.post_clip = vmw_stdu_surface_populate_update; + + return vmw_du_helper_plane_update(&srf_update); +} + +/** + * vmw_stdu_primary_plane_atomic_update - formally switches STDU to new plane + * @plane: display plane + * @state: Only used to get crtc info + * + * Formally update stdu->display_srf to the new plane, and bind the new + * plane STDU. This function is called during the commit phase when + * all the preparation have been done and all the configurations have + * been checked. + */ +static void +vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); + struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state); + struct drm_crtc *crtc = new_state->crtc; + struct vmw_screen_target_display_unit *stdu; + struct vmw_fence_obj *fence = NULL; + struct vmw_private *dev_priv; + int ret; + + /* If case of device error, maintain consistent atomic state */ + if (crtc && new_state->fb) { + struct vmw_framebuffer *vfb = + vmw_framebuffer_to_vfb(new_state->fb); + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + + stdu->display_srf = vps->surf; + stdu->content_fb_type = vps->content_fb_type; + stdu->cpp = vps->cpp; + + ret = vmw_stdu_bind_st(dev_priv, stdu, &stdu->display_srf->res); + if (ret) + DRM_ERROR("Failed to bind surface to STDU.\n"); + + if (vfb->bo) + ret = vmw_stdu_plane_update_bo(dev_priv, plane, + old_state, vfb, &fence); + else + ret = vmw_stdu_plane_update_surface(dev_priv, plane, + old_state, vfb, + &fence); + if (ret) + DRM_ERROR("Failed to update STDU.\n"); + } else { + crtc = old_state->crtc; + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + + /* Blank STDU when fb and crtc are NULL */ + if (!stdu->defined) + return; + + ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); + if (ret) + DRM_ERROR("Failed to blank STDU\n"); + + ret = vmw_stdu_update_st(dev_priv, stdu); + if (ret) + DRM_ERROR("Failed to update STDU.\n"); + + return; + } + + if (fence) + vmw_fence_obj_unreference(&fence); +} + + +static const struct drm_plane_funcs vmw_stdu_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vmw_du_primary_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, +}; + +static const struct drm_plane_funcs vmw_stdu_cursor_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vmw_du_cursor_plane_destroy, + .reset = vmw_du_plane_reset, + .atomic_duplicate_state = vmw_du_plane_duplicate_state, + .atomic_destroy_state = vmw_du_plane_destroy_state, +}; + + +/* + * Atomic Helpers + */ +static const struct +drm_plane_helper_funcs vmw_stdu_cursor_plane_helper_funcs = { + .atomic_check = vmw_du_cursor_plane_atomic_check, + .atomic_update = vmw_du_cursor_plane_atomic_update, + .prepare_fb = vmw_du_cursor_plane_prepare_fb, + .cleanup_fb = vmw_du_cursor_plane_cleanup_fb, +}; + +static const struct +drm_plane_helper_funcs vmw_stdu_primary_plane_helper_funcs = { + .atomic_check = vmw_du_primary_plane_atomic_check, + .atomic_update = vmw_stdu_primary_plane_atomic_update, + .prepare_fb = vmw_stdu_primary_plane_prepare_fb, + .cleanup_fb = vmw_stdu_primary_plane_cleanup_fb, +}; + +static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = { + .prepare = vmw_stdu_crtc_helper_prepare, + .mode_set_nofb = vmw_stdu_crtc_mode_set_nofb, + .atomic_check = vmw_du_crtc_atomic_check, + .atomic_begin = vmw_du_crtc_atomic_begin, + .atomic_flush = vmw_du_crtc_atomic_flush, + .atomic_enable = vmw_stdu_crtc_atomic_enable, + .atomic_disable = vmw_stdu_crtc_atomic_disable, +}; + + +/** + * vmw_stdu_init - Sets up a Screen Target Display Unit + * + * @dev_priv: VMW DRM device + * @unit: unit number range from 0 to VMWGFX_NUM_DISPLAY_UNITS + * + * This function is called once per CRTC, and allocates one Screen Target + * display unit to represent that CRTC. Since the SVGA device does not separate + * out encoder and connector, they are represented as part of the STDU as well. + */ +static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) +{ + struct vmw_screen_target_display_unit *stdu; + struct drm_device *dev = &dev_priv->drm; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_plane *primary; + struct vmw_cursor_plane *cursor; + struct drm_crtc *crtc; + int ret; + + stdu = kzalloc(sizeof(*stdu), GFP_KERNEL); + if (!stdu) + return -ENOMEM; + + stdu->base.unit = unit; + crtc = &stdu->base.crtc; + encoder = &stdu->base.encoder; + connector = &stdu->base.connector; + primary = &stdu->base.primary; + cursor = &stdu->base.cursor; + + stdu->base.pref_active = (unit == 0); + stdu->base.pref_width = dev_priv->initial_width; + stdu->base.pref_height = dev_priv->initial_height; + stdu->base.is_implicit = false; + + /* Initialize primary plane */ + ret = drm_universal_plane_init(dev, primary, + 0, &vmw_stdu_plane_funcs, + vmw_primary_plane_formats, + ARRAY_SIZE(vmw_primary_plane_formats), + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret) { + DRM_ERROR("Failed to initialize primary plane"); + goto err_free; + } + + drm_plane_helper_add(primary, &vmw_stdu_primary_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(primary); + + /* Initialize cursor plane */ + ret = drm_universal_plane_init(dev, &cursor->base, + 0, &vmw_stdu_cursor_funcs, + vmw_cursor_plane_formats, + ARRAY_SIZE(vmw_cursor_plane_formats), + NULL, DRM_PLANE_TYPE_CURSOR, NULL); + if (ret) { + DRM_ERROR("Failed to initialize cursor plane"); + drm_plane_cleanup(&stdu->base.primary); + goto err_free; + } + + drm_plane_helper_add(&cursor->base, &vmw_stdu_cursor_plane_helper_funcs); + + ret = drm_connector_init(dev, connector, &vmw_stdu_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + goto err_free; + } + + drm_connector_helper_add(connector, &vmw_stdu_connector_helper_funcs); + connector->status = vmw_du_connector_detect(connector, false); + + ret = drm_encoder_init(dev, encoder, &vmw_stdu_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, NULL); + if (ret) { + DRM_ERROR("Failed to initialize encoder\n"); + goto err_free_connector; + } + + (void) drm_connector_attach_encoder(connector, encoder); + encoder->possible_crtcs = (1 << unit); + encoder->possible_clones = 0; + + ret = drm_connector_register(connector); + if (ret) { + DRM_ERROR("Failed to register connector\n"); + goto err_free_encoder; + } + + ret = drm_crtc_init_with_planes(dev, crtc, primary, + &cursor->base, + &vmw_stdu_crtc_funcs, NULL); + if (ret) { + DRM_ERROR("Failed to initialize CRTC\n"); + goto err_free_unregister; + } + + drm_crtc_helper_add(crtc, &vmw_stdu_crtc_helper_funcs); + + drm_mode_crtc_set_gamma_size(crtc, 256); + + drm_object_attach_property(&connector->base, + dev_priv->hotplug_mode_update_property, 1); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_x_property, 0); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_y_property, 0); + return 0; + +err_free_unregister: + drm_connector_unregister(connector); +err_free_encoder: + drm_encoder_cleanup(encoder); +err_free_connector: + drm_connector_cleanup(connector); +err_free: + kfree(stdu); + return ret; +} + + + +/** + * vmw_stdu_destroy - Cleans up a vmw_screen_target_display_unit + * + * @stdu: Screen Target Display Unit to be destroyed + * + * Clean up after vmw_stdu_init + */ +static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu) +{ + vmw_du_cleanup(&stdu->base); + kfree(stdu); +} + + + +/****************************************************************************** + * Screen Target Display KMS Functions + * + * These functions are called by the common KMS code in vmwgfx_kms.c + *****************************************************************************/ + +/** + * vmw_kms_stdu_init_display - Initializes a Screen Target based display + * + * @dev_priv: VMW DRM device + * + * This function initialize a Screen Target based display device. It checks + * the capability bits to make sure the underlying hardware can support + * screen targets, and then creates the maximum number of CRTCs, a.k.a Display + * Units, as supported by the display hardware. + * + * RETURNS: + * 0 on success, error code otherwise + */ +int vmw_kms_stdu_init_display(struct vmw_private *dev_priv) +{ + struct drm_device *dev = &dev_priv->drm; + int i, ret; + + + /* Do nothing if there's no support for MOBs */ + if (!dev_priv->has_mob) + return -ENOSYS; + + if (!(dev_priv->capabilities & SVGA_CAP_GBOBJECTS)) + return -ENOSYS; + + dev_priv->active_display_unit = vmw_du_screen_target; + + for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) { + ret = vmw_stdu_init(dev_priv, i); + + if (unlikely(ret != 0)) { + drm_err(&dev_priv->drm, + "Failed to initialize STDU %d", i); + return ret; + } + } + + drm_mode_config_reset(dev); + + return 0; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_streamoutput.c b/drivers/gpu/drm/vmwgfx/vmwgfx_streamoutput.c new file mode 100644 index 0000000000..edcc406590 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_streamoutput.c @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright © 2018-2023 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_binding.h" +#include "vmwgfx_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" + +#include <drm/ttm/ttm_placement.h> + +/** + * struct vmw_dx_streamoutput - Streamoutput resource metadata. + * @res: Base resource struct. + * @ctx: Non-refcounted context to which @res belong. + * @cotable: Refcounted cotable holding this Streamoutput. + * @cotable_head: List head for cotable-so_res list. + * @id: User-space provided identifier. + * @size: User-space provided mob size. + * @committed: Whether streamoutput is actually created or pending creation. + */ +struct vmw_dx_streamoutput { + struct vmw_resource res; + struct vmw_resource *ctx; + struct vmw_resource *cotable; + struct list_head cotable_head; + u32 id; + u32 size; + bool committed; +}; + +static int vmw_dx_streamoutput_create(struct vmw_resource *res); +static int vmw_dx_streamoutput_bind(struct vmw_resource *res, + struct ttm_validate_buffer *val_buf); +static int vmw_dx_streamoutput_unbind(struct vmw_resource *res, bool readback, + struct ttm_validate_buffer *val_buf); +static void vmw_dx_streamoutput_commit_notify(struct vmw_resource *res, + enum vmw_cmdbuf_res_state state); + +static const struct vmw_res_func vmw_dx_streamoutput_func = { + .res_type = vmw_res_streamoutput, + .needs_guest_memory = true, + .may_evict = false, + .type_name = "DX streamoutput", + .domain = VMW_BO_DOMAIN_MOB, + .busy_domain = VMW_BO_DOMAIN_MOB, + .create = vmw_dx_streamoutput_create, + .destroy = NULL, /* Command buffer managed resource. */ + .bind = vmw_dx_streamoutput_bind, + .unbind = vmw_dx_streamoutput_unbind, + .commit_notify = vmw_dx_streamoutput_commit_notify, +}; + +static inline struct vmw_dx_streamoutput * +vmw_res_to_dx_streamoutput(struct vmw_resource *res) +{ + return container_of(res, struct vmw_dx_streamoutput, res); +} + +/** + * vmw_dx_streamoutput_unscrub - Reattach the MOB to streamoutput. + * @res: The streamoutput resource. + * + * Return: 0 on success, negative error code on failure. + */ +static int vmw_dx_streamoutput_unscrub(struct vmw_resource *res) +{ + struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); + struct vmw_private *dev_priv = res->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXBindStreamOutput body; + } *cmd; + + if (!list_empty(&so->cotable_head) || !so->committed ) + return 0; + + cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), so->ctx->id); + if (!cmd) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_BIND_STREAMOUTPUT; + cmd->header.size = sizeof(cmd->body); + cmd->body.soid = so->id; + cmd->body.mobid = res->guest_memory_bo->tbo.resource->start; + cmd->body.offsetInBytes = res->guest_memory_offset; + cmd->body.sizeInBytes = so->size; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + vmw_cotable_add_resource(so->cotable, &so->cotable_head); + + return 0; +} + +static int vmw_dx_streamoutput_create(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); + int ret = 0; + + WARN_ON_ONCE(!so->committed); + + if (vmw_resource_mob_attached(res)) { + mutex_lock(&dev_priv->binding_mutex); + ret = vmw_dx_streamoutput_unscrub(res); + mutex_unlock(&dev_priv->binding_mutex); + } + + res->id = so->id; + + return ret; +} + +static int vmw_dx_streamoutput_bind(struct vmw_resource *res, + struct ttm_validate_buffer *val_buf) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct ttm_buffer_object *bo = val_buf->bo; + int ret; + + if (WARN_ON(bo->resource->mem_type != VMW_PL_MOB)) + return -EINVAL; + + mutex_lock(&dev_priv->binding_mutex); + ret = vmw_dx_streamoutput_unscrub(res); + mutex_unlock(&dev_priv->binding_mutex); + + return ret; +} + +/** + * vmw_dx_streamoutput_scrub - Unbind the MOB from streamoutput. + * @res: The streamoutput resource. + * + * Return: 0 on success, negative error code on failure. + */ +static int vmw_dx_streamoutput_scrub(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXBindStreamOutput body; + } *cmd; + + if (list_empty(&so->cotable_head)) + return 0; + + WARN_ON_ONCE(!so->committed); + + cmd = VMW_CMD_CTX_RESERVE(dev_priv, sizeof(*cmd), so->ctx->id); + if (!cmd) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_DX_BIND_STREAMOUTPUT; + cmd->header.size = sizeof(cmd->body); + cmd->body.soid = res->id; + cmd->body.mobid = SVGA3D_INVALID_ID; + cmd->body.offsetInBytes = 0; + cmd->body.sizeInBytes = so->size; + vmw_cmd_commit(dev_priv, sizeof(*cmd)); + + res->id = -1; + list_del_init(&so->cotable_head); + + return 0; +} + +static int vmw_dx_streamoutput_unbind(struct vmw_resource *res, bool readback, + struct ttm_validate_buffer *val_buf) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_fence_obj *fence; + int ret; + + if (WARN_ON(res->guest_memory_bo->tbo.resource->mem_type != VMW_PL_MOB)) + return -EINVAL; + + mutex_lock(&dev_priv->binding_mutex); + ret = vmw_dx_streamoutput_scrub(res); + mutex_unlock(&dev_priv->binding_mutex); + + if (ret) + return ret; + + (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); + vmw_bo_fence_single(val_buf->bo, fence); + + if (fence != NULL) + vmw_fence_obj_unreference(&fence); + + return 0; +} + +static void vmw_dx_streamoutput_commit_notify(struct vmw_resource *res, + enum vmw_cmdbuf_res_state state) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); + + if (state == VMW_CMDBUF_RES_ADD) { + mutex_lock(&dev_priv->binding_mutex); + vmw_cotable_add_resource(so->cotable, &so->cotable_head); + so->committed = true; + res->id = so->id; + mutex_unlock(&dev_priv->binding_mutex); + } else { + mutex_lock(&dev_priv->binding_mutex); + list_del_init(&so->cotable_head); + so->committed = false; + res->id = -1; + mutex_unlock(&dev_priv->binding_mutex); + } +} + +/** + * vmw_dx_streamoutput_lookup - Do a streamoutput resource lookup by user key. + * @man: Command buffer managed resource manager for current context. + * @user_key: User-space identifier for lookup. + * + * Return: Valid refcounted vmw_resource on success, error pointer on failure. + */ +struct vmw_resource * +vmw_dx_streamoutput_lookup(struct vmw_cmdbuf_res_manager *man, + u32 user_key) +{ + return vmw_cmdbuf_res_lookup(man, vmw_cmdbuf_res_streamoutput, + user_key); +} + +static void vmw_dx_streamoutput_res_free(struct vmw_resource *res) +{ + struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); + + vmw_resource_unreference(&so->cotable); + kfree(so); +} + +static void vmw_dx_streamoutput_hw_destroy(struct vmw_resource *res) +{ + /* Destroyed by user-space cmd buf or as part of context takedown. */ + res->id = -1; +} + +/** + * vmw_dx_streamoutput_add - Add a streamoutput as a cmd buf managed resource. + * @man: Command buffer managed resource manager for current context. + * @ctx: Pointer to context resource. + * @user_key: The identifier for this streamoutput. + * @list: The list of staged command buffer managed resources. + * + * Return: 0 on success, negative error code on failure. + */ +int vmw_dx_streamoutput_add(struct vmw_cmdbuf_res_manager *man, + struct vmw_resource *ctx, u32 user_key, + struct list_head *list) +{ + struct vmw_dx_streamoutput *so; + struct vmw_resource *res; + struct vmw_private *dev_priv = ctx->dev_priv; + int ret; + + so = kmalloc(sizeof(*so), GFP_KERNEL); + if (!so) { + return -ENOMEM; + } + + res = &so->res; + so->ctx = ctx; + so->cotable = vmw_resource_reference + (vmw_context_cotable(ctx, SVGA_COTABLE_STREAMOUTPUT)); + so->id = user_key; + so->committed = false; + INIT_LIST_HEAD(&so->cotable_head); + ret = vmw_resource_init(dev_priv, res, true, + vmw_dx_streamoutput_res_free, + &vmw_dx_streamoutput_func); + if (ret) + goto out_resource_init; + + ret = vmw_cmdbuf_res_add(man, vmw_cmdbuf_res_streamoutput, user_key, + res, list); + if (ret) + goto out_resource_init; + + res->id = so->id; + res->hw_destroy = vmw_dx_streamoutput_hw_destroy; + +out_resource_init: + vmw_resource_unreference(&res); + + return ret; +} + +/** + * vmw_dx_streamoutput_set_size - Sets streamoutput mob size in res struct. + * @res: The streamoutput res for which need to set size. + * @size: The size provided by user-space to set. + */ +void vmw_dx_streamoutput_set_size(struct vmw_resource *res, u32 size) +{ + struct vmw_dx_streamoutput *so = vmw_res_to_dx_streamoutput(res); + + so->size = size; +} + +/** + * vmw_dx_streamoutput_remove - Stage streamoutput for removal. + * @man: Command buffer managed resource manager for current context. + * @user_key: The identifier for this streamoutput. + * @list: The list of staged command buffer managed resources. + * + * Return: 0 on success, negative error code on failure. + */ +int vmw_dx_streamoutput_remove(struct vmw_cmdbuf_res_manager *man, + u32 user_key, + struct list_head *list) +{ + struct vmw_resource *r; + + return vmw_cmdbuf_res_remove(man, vmw_cmdbuf_res_streamoutput, + (u32)user_key, list, &r); +} + +/** + * vmw_dx_streamoutput_cotable_list_scrub - cotable unbind_func callback. + * @dev_priv: Device private. + * @list: The list of cotable resources. + * @readback: Whether the call was part of a readback unbind. + */ +void vmw_dx_streamoutput_cotable_list_scrub(struct vmw_private *dev_priv, + struct list_head *list, + bool readback) +{ + struct vmw_dx_streamoutput *entry, *next; + + lockdep_assert_held_once(&dev_priv->binding_mutex); + + list_for_each_entry_safe(entry, next, list, cotable_head) { + WARN_ON(vmw_dx_streamoutput_scrub(&entry->res)); + if (!readback) + entry->committed =false; + } +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c new file mode 100644 index 0000000000..17463aeeef --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -0,0 +1,2108 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" +#include "vmwgfx_so.h" +#include "vmwgfx_binding.h" +#include "vmw_surface_cache.h" +#include "device_include/svga3d_surfacedefs.h" + +#include <drm/ttm/ttm_placement.h> + +#define SVGA3D_FLAGS_64(upper32, lower32) (((uint64_t)upper32 << 32) | lower32) +#define SVGA3D_FLAGS_UPPER_32(svga3d_flags) (svga3d_flags >> 32) +#define SVGA3D_FLAGS_LOWER_32(svga3d_flags) \ + (svga3d_flags & ((uint64_t)U32_MAX)) + +/** + * struct vmw_user_surface - User-space visible surface resource + * + * @prime: The TTM prime object. + * @base: The TTM base object handling user-space visibility. + * @srf: The surface metadata. + * @master: Master of the creating client. Used for security check. + */ +struct vmw_user_surface { + struct ttm_prime_object prime; + struct vmw_surface srf; + 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; +}; + +/** + * struct vmw_surface_dirty - Surface dirty-tracker + * @cache: Cached layout information of the surface. + * @num_subres: Number of subresources. + * @boxes: Array of SVGA3dBoxes indicating dirty regions. One per subresource. + */ +struct vmw_surface_dirty { + struct vmw_surface_cache cache; + u32 num_subres; + SVGA3dBox boxes[]; +}; + +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 int +vmw_gb_surface_define_internal(struct drm_device *dev, + struct drm_vmw_gb_surface_create_ext_req *req, + struct drm_vmw_gb_surface_create_rep *rep, + struct drm_file *file_priv); +static int +vmw_gb_surface_reference_internal(struct drm_device *dev, + struct drm_vmw_surface_arg *req, + struct drm_vmw_gb_surface_ref_ext_rep *rep, + struct drm_file *file_priv); + +static void vmw_surface_dirty_free(struct vmw_resource *res); +static int vmw_surface_dirty_alloc(struct vmw_resource *res); +static int vmw_surface_dirty_sync(struct vmw_resource *res); +static void vmw_surface_dirty_range_add(struct vmw_resource *res, size_t start, + size_t end); +static int vmw_surface_clean(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 const struct vmw_res_func vmw_legacy_surface_func = { + .res_type = vmw_res_surface, + .needs_guest_memory = false, + .may_evict = true, + .prio = 1, + .dirty_prio = 1, + .type_name = "legacy surfaces", + .domain = VMW_BO_DOMAIN_GMR, + .busy_domain = VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM, + .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_guest_memory = true, + .may_evict = true, + .prio = 1, + .dirty_prio = 2, + .type_name = "guest backed surfaces", + .domain = VMW_BO_DOMAIN_MOB, + .busy_domain = VMW_BO_DOMAIN_MOB, + .create = vmw_gb_surface_create, + .destroy = vmw_gb_surface_destroy, + .bind = vmw_gb_surface_bind, + .unbind = vmw_gb_surface_unbind, + .dirty_alloc = vmw_surface_dirty_alloc, + .dirty_free = vmw_surface_dirty_free, + .dirty_sync = vmw_surface_dirty_sync, + .dirty_range_add = vmw_surface_dirty_range_add, + .clean = vmw_surface_clean, +}; + +/* + * 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->metadata.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->metadata.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->metadata.num_sizes * + sizeof(SVGA3dSize); + + cmd->header.id = SVGA_3D_CMD_SURFACE_DEFINE; + cmd->header.size = cmd_len; + cmd->body.sid = srf->res.id; + /* + * Downcast of surfaceFlags, was upcasted when received from user-space, + * since driver internally stores as 64 bit. + * For legacy surface define only 32 bit flag is supported. + */ + cmd->body.surfaceFlags = (SVGA3dSurface1Flags)srf->metadata.flags; + cmd->body.format = srf->metadata.format; + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) + cmd->body.face[i].numMipLevels = srf->metadata.mip_levels[i]; + + cmd += 1; + cmd_size = (SVGA3dSize *) cmd; + src_size = srf->metadata.sizes; + + for (i = 0; i < srf->metadata.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 SVGA3dSurfaceDesc *desc = + vmw_surface_get_desc(srf->metadata.format); + + for (i = 0; i < srf->metadata.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->metadata.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 = vmw_surface_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 = + vmw_surface_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 resource count accordingly. + */ +static void vmw_hw_surface_destroy(struct vmw_resource *res) +{ + + struct vmw_private *dev_priv = res->dev_priv; + void *cmd; + + if (res->func->destroy == vmw_gb_surface_destroy) { + (void) vmw_gb_surface_destroy(res); + return; + } + + if (res->id != -1) { + + cmd = VMW_CMD_RESERVE(dev_priv, vmw_surface_destroy_size()); + if (unlikely(!cmd)) + return; + + vmw_surface_destroy_encode(res->id, cmd); + vmw_cmd_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); + dev_priv->used_memory_size -= res->guest_memory_size; + mutex_unlock(&dev_priv->cmdbuf_mutex); + } +} + +/** + * 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->guest_memory_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_HB_MAX_SURFACE_IDS)) { + ret = -EBUSY; + goto out_no_fifo; + } + + /* + * Encode surface define- commands. + */ + + submit_size = vmw_surface_define_size(srf); + cmd = VMW_CMD_RESERVE(dev_priv, submit_size); + if (unlikely(!cmd)) { + ret = -ENOMEM; + goto out_no_fifo; + } + + vmw_surface_define_encode(srf, cmd); + vmw_cmd_commit(dev_priv, submit_size); + vmw_fifo_resource_inc(dev_priv); + + /* + * Surface memory usage accounting. + */ + + dev_priv->used_memory_size += res->guest_memory_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); + submit_size = vmw_surface_dma_size(srf); + cmd = VMW_CMD_RESERVE(dev_priv, submit_size); + if (unlikely(!cmd)) + return -ENOMEM; + + vmw_bo_get_guest_ptr(val_buf->bo, &ptr); + vmw_surface_dma_encode(srf, cmd, &ptr, bind); + + vmw_cmd_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_bo_fence_single(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->guest_memory_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. + * @readback: Readback - only true if dirty + * @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_CMD_RESERVE(dev_priv, submit_size); + if (unlikely(!cmd)) + return -ENOMEM; + + vmw_surface_destroy_encode(res->id, cmd); + vmw_cmd_commit(dev_priv, submit_size); + + /* + * Surface memory usage accounting. + */ + + dev_priv->used_memory_size -= res->guest_memory_size; + + /* + * Release the surface ID. + */ + + vmw_resource_release_id(res); + vmw_fifo_resource_dec(dev_priv); + + 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); + 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)) { + res_free(res); + return ret; + } + + /* + * The surface won't be visible to hardware until a + * surface validate. + */ + + INIT_LIST_HEAD(&srf->view_list); + res->hw_destroy = 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); + + WARN_ON_ONCE(res->dirty); + if (user_srf->master) + drm_master_put(&user_srf->master); + kfree(srf->offsets); + kfree(srf->metadata.sizes); + kfree(srf->snooper.image); + ttm_prime_object_kfree(user_srf, prime); +} + +/** + * vmw_user_surface_base_release - 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_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); +} + +/** + * vmw_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_surface_metadata *metadata; + 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; + 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; + const SVGA3dSurfaceDesc *desc; + + num_sizes = 0; + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { + if (req->mip_levels[i] > DRM_VMW_MAX_MIP_LEVELS) + return -EINVAL; + num_sizes += req->mip_levels[i]; + } + + if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * DRM_VMW_MAX_MIP_LEVELS || + num_sizes == 0) + return -EINVAL; + + desc = vmw_surface_get_desc(req->format); + if (unlikely(desc->blockDesc == SVGA3DBLOCKDESC_NONE)) { + VMW_DEBUG_USER("Invalid format %d for surface creation.\n", + req->format); + return -EINVAL; + } + + user_srf = kzalloc(sizeof(*user_srf), GFP_KERNEL); + if (unlikely(!user_srf)) { + ret = -ENOMEM; + goto out_unlock; + } + + srf = &user_srf->srf; + metadata = &srf->metadata; + res = &srf->res; + + /* Driver internally stores as 64-bit flags */ + metadata->flags = (SVGA3dSurfaceAllFlags)req->flags; + metadata->format = req->format; + metadata->scanout = req->scanout; + + memcpy(metadata->mip_levels, req->mip_levels, + sizeof(metadata->mip_levels)); + metadata->num_sizes = num_sizes; + metadata->sizes = + memdup_array_user((struct drm_vmw_size __user *)(unsigned long) + req->size_addr, + metadata->num_sizes, sizeof(*metadata->sizes)); + if (IS_ERR(metadata->sizes)) { + ret = PTR_ERR(metadata->sizes); + goto out_no_sizes; + } + srf->offsets = kmalloc_array(metadata->num_sizes, sizeof(*srf->offsets), + GFP_KERNEL); + if (unlikely(!srf->offsets)) { + ret = -ENOMEM; + goto out_no_offsets; + } + + metadata->base_size = *srf->metadata.sizes; + metadata->autogen_filter = SVGA3D_TEX_FILTER_NONE; + metadata->multisample_count = 0; + metadata->multisample_pattern = SVGA3D_MS_PATTERN_NONE; + metadata->quality_level = SVGA3D_MS_QUALITY_NONE; + + cur_bo_offset = 0; + cur_offset = srf->offsets; + cur_size = metadata->sizes; + + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { + for (j = 0; j < metadata->mip_levels[i]; ++j) { + uint32_t stride = vmw_surface_calculate_pitch( + desc, cur_size); + + cur_offset->face = i; + cur_offset->mip = j; + cur_offset->bo_offset = cur_bo_offset; + cur_bo_offset += vmw_surface_get_image_buffer_size + (desc, cur_size, stride); + ++cur_offset; + ++cur_size; + } + } + res->guest_memory_size = cur_bo_offset; + if (metadata->scanout && + metadata->num_sizes == 1 && + metadata->sizes[0].width == VMW_CURSOR_SNOOP_WIDTH && + metadata->sizes[0].height == VMW_CURSOR_SNOOP_HEIGHT && + metadata->format == VMW_CURSOR_SNOOP_FORMAT) { + const struct SVGA3dSurfaceDesc *desc = + vmw_surface_get_desc(VMW_CURSOR_SNOOP_FORMAT); + const u32 cursor_size_bytes = VMW_CURSOR_SNOOP_WIDTH * + VMW_CURSOR_SNOOP_HEIGHT * + desc->pitchBytesPerBlock; + srf->snooper.image = kzalloc(cursor_size_bytes, GFP_KERNEL); + if (!srf->snooper.image) { + DRM_ERROR("Failed to allocate cursor_image\n"); + ret = -ENOMEM; + goto out_no_copy; + } + } else { + srf->snooper.image = NULL; + } + + user_srf->prime.base.shareable = false; + user_srf->prime.base.tfile = NULL; + if (drm_is_primary_client(file_priv)) + user_srf->master = drm_file_get_master(file_priv); + + /** + * 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) { + struct vmw_bo_params params = { + .domain = VMW_BO_DOMAIN_SYS, + .busy_domain = VMW_BO_DOMAIN_SYS, + .bo_type = ttm_bo_type_device, + .size = res->guest_memory_size, + .pin = false + }; + + ret = vmw_gem_object_create(dev_priv, + ¶ms, + &res->guest_memory_bo); + if (unlikely(ret != 0)) { + vmw_resource_unreference(&res); + goto out_unlock; + } + } + + tmp = vmw_resource_reference(&srf->res); + ret = ttm_prime_object_init(tfile, res->guest_memory_size, &user_srf->prime, + req->shareable, VMW_RES_SURFACE, + &vmw_user_surface_base_release); + + if (unlikely(ret != 0)) { + vmw_resource_unreference(&tmp); + vmw_resource_unreference(&res); + goto out_unlock; + } + + rep->sid = user_srf->prime.base.handle; + vmw_resource_unreference(&res); + + return 0; +out_no_copy: + kfree(srf->offsets); +out_no_offsets: + kfree(metadata->sizes); +out_no_sizes: + ttm_prime_object_kfree(user_srf, prime); +out_unlock: + 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 { + handle = u_handle; + } + + ret = -EINVAL; + base = ttm_base_object_lookup_for_ref(dev_priv->tdev, handle); + if (unlikely(!base)) { + VMW_DEBUG_USER("Could not find surface to reference.\n"); + goto out_no_lookup; + } + + if (unlikely(ttm_base_object_type(base) != VMW_RES_SURFACE)) { + VMW_DEBUG_USER("Referenced object is not a surface.\n"); + goto out_bad_resource; + } + if (handle_type != DRM_VMW_HANDLE_PRIME) { + bool require_exist = false; + + user_srf = container_of(base, struct vmw_user_surface, + prime.base); + + /* Error out if we are unauthenticated primary */ + if (drm_is_primary_client(file_priv) && + !file_priv->authenticated) { + ret = -EACCES; + goto out_bad_resource; + } + + /* + * Make sure the surface creator has the same + * authenticating master, or is already registered with us. + */ + if (drm_is_primary_client(file_priv) && + user_srf->master != file_priv->master) + require_exist = true; + + if (unlikely(drm_is_render_client(file_priv))) + require_exist = true; + + ret = ttm_ref_object_add(tfile, base, NULL, require_exist); + 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); + + return ret; +} + +/** + * vmw_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_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; + + /* Downcast of flags when sending back to user space */ + rep->flags = (uint32_t)srf->metadata.flags; + rep->format = srf->metadata.format; + memcpy(rep->mip_levels, srf->metadata.mip_levels, + sizeof(srf->metadata.mip_levels)); + user_sizes = (struct drm_vmw_size __user *)(unsigned long) + rep->size_addr; + + if (user_sizes) + ret = copy_to_user(user_sizes, &srf->metadata.base_size, + sizeof(srf->metadata.base_size)); + if (unlikely(ret != 0)) { + VMW_DEBUG_USER("copy_to_user failed %p %u\n", user_sizes, + srf->metadata.num_sizes); + ttm_ref_object_base_unref(tfile, base->handle); + ret = -EFAULT; + } + + ttm_base_object_unref(&base); + + return ret; +} + +/** + * vmw_gb_surface_create - Encode a surface_define command. + * + * @res: Pointer to a struct vmw_resource embedded in a struct + * vmw_surface. + */ +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); + struct vmw_surface_metadata *metadata = &srf->metadata; + uint32_t cmd_len, cmd_id, submit_len; + int ret; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDefineGBSurface body; + } *cmd; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDefineGBSurface_v2 body; + } *cmd2; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDefineGBSurface_v3 body; + } *cmd3; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDefineGBSurface_v4 body; + } *cmd4; + + if (likely(res->id != -1)) + return 0; + + vmw_fifo_resource_inc(dev_priv); + 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; + } + + if (has_sm5_context(dev_priv) && metadata->array_size > 0) { + cmd_id = SVGA_3D_CMD_DEFINE_GB_SURFACE_V4; + cmd_len = sizeof(cmd4->body); + submit_len = sizeof(*cmd4); + } else if (has_sm4_1_context(dev_priv) && metadata->array_size > 0) { + cmd_id = SVGA_3D_CMD_DEFINE_GB_SURFACE_V3; + cmd_len = sizeof(cmd3->body); + submit_len = sizeof(*cmd3); + } else if (metadata->array_size > 0) { + /* VMW_SM_4 support verified at creation time. */ + cmd_id = SVGA_3D_CMD_DEFINE_GB_SURFACE_V2; + cmd_len = sizeof(cmd2->body); + submit_len = sizeof(*cmd2); + } else { + cmd_id = SVGA_3D_CMD_DEFINE_GB_SURFACE; + cmd_len = sizeof(cmd->body); + submit_len = sizeof(*cmd); + } + + cmd = VMW_CMD_RESERVE(dev_priv, submit_len); + cmd2 = (typeof(cmd2))cmd; + cmd3 = (typeof(cmd3))cmd; + cmd4 = (typeof(cmd4))cmd; + if (unlikely(!cmd)) { + ret = -ENOMEM; + goto out_no_fifo; + } + + if (has_sm5_context(dev_priv) && metadata->array_size > 0) { + cmd4->header.id = cmd_id; + cmd4->header.size = cmd_len; + cmd4->body.sid = srf->res.id; + cmd4->body.surfaceFlags = metadata->flags; + cmd4->body.format = metadata->format; + cmd4->body.numMipLevels = metadata->mip_levels[0]; + cmd4->body.multisampleCount = metadata->multisample_count; + cmd4->body.multisamplePattern = metadata->multisample_pattern; + cmd4->body.qualityLevel = metadata->quality_level; + cmd4->body.autogenFilter = metadata->autogen_filter; + cmd4->body.size.width = metadata->base_size.width; + cmd4->body.size.height = metadata->base_size.height; + cmd4->body.size.depth = metadata->base_size.depth; + cmd4->body.arraySize = metadata->array_size; + cmd4->body.bufferByteStride = metadata->buffer_byte_stride; + } else if (has_sm4_1_context(dev_priv) && metadata->array_size > 0) { + cmd3->header.id = cmd_id; + cmd3->header.size = cmd_len; + cmd3->body.sid = srf->res.id; + cmd3->body.surfaceFlags = metadata->flags; + cmd3->body.format = metadata->format; + cmd3->body.numMipLevels = metadata->mip_levels[0]; + cmd3->body.multisampleCount = metadata->multisample_count; + cmd3->body.multisamplePattern = metadata->multisample_pattern; + cmd3->body.qualityLevel = metadata->quality_level; + cmd3->body.autogenFilter = metadata->autogen_filter; + cmd3->body.size.width = metadata->base_size.width; + cmd3->body.size.height = metadata->base_size.height; + cmd3->body.size.depth = metadata->base_size.depth; + cmd3->body.arraySize = metadata->array_size; + } else if (metadata->array_size > 0) { + cmd2->header.id = cmd_id; + cmd2->header.size = cmd_len; + cmd2->body.sid = srf->res.id; + cmd2->body.surfaceFlags = metadata->flags; + cmd2->body.format = metadata->format; + cmd2->body.numMipLevels = metadata->mip_levels[0]; + cmd2->body.multisampleCount = metadata->multisample_count; + cmd2->body.autogenFilter = metadata->autogen_filter; + cmd2->body.size.width = metadata->base_size.width; + cmd2->body.size.height = metadata->base_size.height; + cmd2->body.size.depth = metadata->base_size.depth; + cmd2->body.arraySize = metadata->array_size; + } else { + cmd->header.id = cmd_id; + cmd->header.size = cmd_len; + cmd->body.sid = srf->res.id; + cmd->body.surfaceFlags = metadata->flags; + cmd->body.format = metadata->format; + cmd->body.numMipLevels = metadata->mip_levels[0]; + cmd->body.multisampleCount = metadata->multisample_count; + cmd->body.autogenFilter = metadata->autogen_filter; + cmd->body.size.width = metadata->base_size.width; + cmd->body.size.height = metadata->base_size.height; + cmd->body.size.depth = metadata->base_size.depth; + } + + vmw_cmd_commit(dev_priv, submit_len); + + return 0; + +out_no_fifo: + vmw_resource_release_id(res); +out_no_id: + vmw_fifo_resource_dec(dev_priv); + 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->resource->mem_type != VMW_PL_MOB); + + submit_size = sizeof(*cmd1) + (res->guest_memory_dirty ? sizeof(*cmd2) : 0); + + cmd1 = VMW_CMD_RESERVE(dev_priv, submit_size); + if (unlikely(!cmd1)) + 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->resource->start; + if (res->guest_memory_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; + } + vmw_cmd_commit(dev_priv, submit_size); + + if (res->guest_memory_bo->dirty && res->guest_memory_dirty) { + /* We've just made a full upload. Cear dirty regions. */ + vmw_bo_dirty_clear_res(res); + } + + res->guest_memory_dirty = false; + + 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->resource->mem_type != VMW_PL_MOB); + + submit_size = sizeof(*cmd3) + (readback ? sizeof(*cmd1) : sizeof(*cmd2)); + cmd = VMW_CMD_RESERVE(dev_priv, submit_size); + if (unlikely(!cmd)) + 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_cmd_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_bo_fence_single(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 vmw_surface *srf = vmw_res_to_srf(res); + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDestroyGBSurface body; + } *cmd; + + if (likely(res->id == -1)) + return 0; + + mutex_lock(&dev_priv->binding_mutex); + vmw_view_surface_list_destroy(dev_priv, &srf->view_list); + vmw_binding_res_list_scrub(&res->binding_head); + + cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); + if (unlikely(!cmd)) { + 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_cmd_commit(dev_priv, sizeof(*cmd)); + mutex_unlock(&dev_priv->binding_mutex); + vmw_resource_release_id(res); + vmw_fifo_resource_dec(dev_priv); + + 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) +{ + union drm_vmw_gb_surface_create_arg *arg = + (union drm_vmw_gb_surface_create_arg *)data; + struct drm_vmw_gb_surface_create_rep *rep = &arg->rep; + struct drm_vmw_gb_surface_create_ext_req req_ext; + + req_ext.base = arg->req; + req_ext.version = drm_vmw_gb_surface_v1; + req_ext.svga3d_flags_upper_32_bits = 0; + req_ext.multisample_pattern = SVGA3D_MS_PATTERN_NONE; + req_ext.quality_level = SVGA3D_MS_QUALITY_NONE; + req_ext.buffer_byte_stride = 0; + req_ext.must_be_zero = 0; + + return vmw_gb_surface_define_internal(dev, &req_ext, rep, file_priv); +} + +/** + * 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) +{ + 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 drm_vmw_gb_surface_ref_ext_rep rep_ext; + int ret; + + ret = vmw_gb_surface_reference_internal(dev, req, &rep_ext, file_priv); + + if (unlikely(ret != 0)) + return ret; + + rep->creq = rep_ext.creq.base; + rep->crep = rep_ext.crep; + + return ret; +} + +/** + * vmw_gb_surface_define_ext_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_ext_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + union drm_vmw_gb_surface_create_ext_arg *arg = + (union drm_vmw_gb_surface_create_ext_arg *)data; + struct drm_vmw_gb_surface_create_ext_req *req = &arg->req; + struct drm_vmw_gb_surface_create_rep *rep = &arg->rep; + + return vmw_gb_surface_define_internal(dev, req, rep, file_priv); +} + +/** + * vmw_gb_surface_reference_ext_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_ext_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + union drm_vmw_gb_surface_reference_ext_arg *arg = + (union drm_vmw_gb_surface_reference_ext_arg *)data; + struct drm_vmw_surface_arg *req = &arg->req; + struct drm_vmw_gb_surface_ref_ext_rep *rep = &arg->rep; + + return vmw_gb_surface_reference_internal(dev, req, rep, file_priv); +} + +/** + * vmw_gb_surface_define_internal - Ioctl function implementing + * the user surface define functionality. + * + * @dev: Pointer to a struct drm_device. + * @req: Request argument from user-space. + * @rep: Response argument to user-space. + * @file_priv: Pointer to a drm file private structure. + */ +static int +vmw_gb_surface_define_internal(struct drm_device *dev, + struct drm_vmw_gb_surface_create_ext_req *req, + struct drm_vmw_gb_surface_create_rep *rep, + struct drm_file *file_priv) +{ + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_user_surface *user_srf; + struct vmw_surface_metadata metadata = {0}; + struct vmw_surface *srf; + struct vmw_resource *res; + struct vmw_resource *tmp; + int ret = 0; + uint32_t backup_handle = 0; + SVGA3dSurfaceAllFlags svga3d_flags_64 = + SVGA3D_FLAGS_64(req->svga3d_flags_upper_32_bits, + req->base.svga3d_flags); + + /* array_size must be null for non-GL3 host. */ + if (req->base.array_size > 0 && !has_sm4_context(dev_priv)) { + VMW_DEBUG_USER("SM4 surface not supported.\n"); + return -EINVAL; + } + + if (!has_sm4_1_context(dev_priv)) { + if (req->svga3d_flags_upper_32_bits != 0) + ret = -EINVAL; + + if (req->base.multisample_count != 0) + ret = -EINVAL; + + if (req->multisample_pattern != SVGA3D_MS_PATTERN_NONE) + ret = -EINVAL; + + if (req->quality_level != SVGA3D_MS_QUALITY_NONE) + ret = -EINVAL; + + if (ret) { + VMW_DEBUG_USER("SM4.1 surface not supported.\n"); + return ret; + } + } + + if (req->buffer_byte_stride > 0 && !has_sm5_context(dev_priv)) { + VMW_DEBUG_USER("SM5 surface not supported.\n"); + return -EINVAL; + } + + if ((svga3d_flags_64 & SVGA3D_SURFACE_MULTISAMPLE) && + req->base.multisample_count == 0) { + VMW_DEBUG_USER("Invalid sample count.\n"); + return -EINVAL; + } + + if (req->base.mip_levels > DRM_VMW_MAX_MIP_LEVELS) { + VMW_DEBUG_USER("Invalid mip level.\n"); + return -EINVAL; + } + + metadata.flags = svga3d_flags_64; + metadata.format = req->base.format; + metadata.mip_levels[0] = req->base.mip_levels; + metadata.multisample_count = req->base.multisample_count; + metadata.multisample_pattern = req->multisample_pattern; + metadata.quality_level = req->quality_level; + metadata.array_size = req->base.array_size; + metadata.buffer_byte_stride = req->buffer_byte_stride; + metadata.num_sizes = 1; + metadata.base_size = req->base.base_size; + metadata.scanout = req->base.drm_surface_flags & + drm_vmw_surface_flag_scanout; + + /* Define a surface based on the parameters. */ + ret = vmw_gb_surface_define(dev_priv, &metadata, &srf); + if (ret != 0) { + VMW_DEBUG_USER("Failed to define surface.\n"); + return ret; + } + + user_srf = container_of(srf, struct vmw_user_surface, srf); + if (drm_is_primary_client(file_priv)) + user_srf->master = drm_file_get_master(file_priv); + + res = &user_srf->srf.res; + + if (req->base.buffer_handle != SVGA3D_INVALID_ID) { + ret = vmw_user_bo_lookup(file_priv, req->base.buffer_handle, + &res->guest_memory_bo); + if (ret == 0) { + if (res->guest_memory_bo->tbo.base.size < res->guest_memory_size) { + VMW_DEBUG_USER("Surface backup buffer too small.\n"); + vmw_user_bo_unref(&res->guest_memory_bo); + ret = -EINVAL; + goto out_unlock; + } else { + backup_handle = req->base.buffer_handle; + } + } + } else if (req->base.drm_surface_flags & + (drm_vmw_surface_flag_create_buffer | + drm_vmw_surface_flag_coherent)) { + ret = vmw_gem_object_create_with_handle(dev_priv, file_priv, + res->guest_memory_size, + &backup_handle, + &res->guest_memory_bo); + } + + if (unlikely(ret != 0)) { + vmw_resource_unreference(&res); + goto out_unlock; + } + + if (req->base.drm_surface_flags & drm_vmw_surface_flag_coherent) { + struct vmw_bo *backup = res->guest_memory_bo; + + ttm_bo_reserve(&backup->tbo, false, false, NULL); + if (!res->func->dirty_alloc) + ret = -EINVAL; + if (!ret) + ret = vmw_bo_dirty_add(backup); + if (!ret) { + res->coherent = true; + ret = res->func->dirty_alloc(res); + } + ttm_bo_unreserve(&backup->tbo); + if (ret) { + vmw_resource_unreference(&res); + goto out_unlock; + } + + } + + tmp = vmw_resource_reference(res); + ret = ttm_prime_object_init(tfile, res->guest_memory_size, &user_srf->prime, + req->base.drm_surface_flags & + drm_vmw_surface_flag_shareable, + VMW_RES_SURFACE, + &vmw_user_surface_base_release); + + if (unlikely(ret != 0)) { + vmw_resource_unreference(&tmp); + vmw_resource_unreference(&res); + goto out_unlock; + } + + rep->handle = user_srf->prime.base.handle; + rep->backup_size = res->guest_memory_size; + if (res->guest_memory_bo) { + rep->buffer_map_handle = + drm_vma_node_offset_addr(&res->guest_memory_bo->tbo.base.vma_node); + rep->buffer_size = res->guest_memory_bo->tbo.base.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); + +out_unlock: + return ret; +} + +/** + * vmw_gb_surface_reference_internal - Ioctl function implementing + * the user surface reference functionality. + * + * @dev: Pointer to a struct drm_device. + * @req: Pointer to user-space request surface arg. + * @rep: Pointer to response to user-space. + * @file_priv: Pointer to a drm file private structure. + */ +static int +vmw_gb_surface_reference_internal(struct drm_device *dev, + struct drm_vmw_surface_arg *req, + struct drm_vmw_gb_surface_ref_ext_rep *rep, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_surface *srf; + struct vmw_user_surface *user_srf; + struct vmw_surface_metadata *metadata; + struct ttm_base_object *base; + u32 backup_handle; + 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; + if (!srf->res.guest_memory_bo) { + DRM_ERROR("Shared GB surface is missing a backup buffer.\n"); + goto out_bad_resource; + } + metadata = &srf->metadata; + + mutex_lock(&dev_priv->cmdbuf_mutex); /* Protect res->backup */ + ret = drm_gem_handle_create(file_priv, &srf->res.guest_memory_bo->tbo.base, + &backup_handle); + mutex_unlock(&dev_priv->cmdbuf_mutex); + if (ret != 0) { + drm_err(dev, "Wasn't able to create a backing handle for surface sid = %u.\n", + req->sid); + goto out_bad_resource; + } + + rep->creq.base.svga3d_flags = SVGA3D_FLAGS_LOWER_32(metadata->flags); + rep->creq.base.format = metadata->format; + rep->creq.base.mip_levels = metadata->mip_levels[0]; + rep->creq.base.drm_surface_flags = 0; + rep->creq.base.multisample_count = metadata->multisample_count; + rep->creq.base.autogen_filter = metadata->autogen_filter; + rep->creq.base.array_size = metadata->array_size; + rep->creq.base.buffer_handle = backup_handle; + rep->creq.base.base_size = metadata->base_size; + rep->crep.handle = user_srf->prime.base.handle; + rep->crep.backup_size = srf->res.guest_memory_size; + rep->crep.buffer_handle = backup_handle; + rep->crep.buffer_map_handle = + drm_vma_node_offset_addr(&srf->res.guest_memory_bo->tbo.base.vma_node); + rep->crep.buffer_size = srf->res.guest_memory_bo->tbo.base.size; + + rep->creq.version = drm_vmw_gb_surface_v1; + rep->creq.svga3d_flags_upper_32_bits = + SVGA3D_FLAGS_UPPER_32(metadata->flags); + rep->creq.multisample_pattern = metadata->multisample_pattern; + rep->creq.quality_level = metadata->quality_level; + rep->creq.must_be_zero = 0; + +out_bad_resource: + ttm_base_object_unref(&base); + + return ret; +} + +/** + * vmw_subres_dirty_add - Add a dirty region to a subresource + * @dirty: The surfaces's dirty tracker. + * @loc_start: The location corresponding to the start of the region. + * @loc_end: The location corresponding to the end of the region. + * + * As we are assuming that @loc_start and @loc_end represent a sequential + * range of backing store memory, if the region spans multiple lines then + * regardless of the x coordinate, the full lines are dirtied. + * Correspondingly if the region spans multiple z slices, then full rather + * than partial z slices are dirtied. + */ +static void vmw_subres_dirty_add(struct vmw_surface_dirty *dirty, + const struct vmw_surface_loc *loc_start, + const struct vmw_surface_loc *loc_end) +{ + const struct vmw_surface_cache *cache = &dirty->cache; + SVGA3dBox *box = &dirty->boxes[loc_start->sub_resource]; + u32 mip = loc_start->sub_resource % cache->num_mip_levels; + const struct drm_vmw_size *size = &cache->mip[mip].size; + u32 box_c2 = box->z + box->d; + + if (WARN_ON(loc_start->sub_resource >= dirty->num_subres)) + return; + + if (box->d == 0 || box->z > loc_start->z) + box->z = loc_start->z; + if (box_c2 < loc_end->z) + box->d = loc_end->z - box->z; + + if (loc_start->z + 1 == loc_end->z) { + box_c2 = box->y + box->h; + if (box->h == 0 || box->y > loc_start->y) + box->y = loc_start->y; + if (box_c2 < loc_end->y) + box->h = loc_end->y - box->y; + + if (loc_start->y + 1 == loc_end->y) { + box_c2 = box->x + box->w; + if (box->w == 0 || box->x > loc_start->x) + box->x = loc_start->x; + if (box_c2 < loc_end->x) + box->w = loc_end->x - box->x; + } else { + box->x = 0; + box->w = size->width; + } + } else { + box->y = 0; + box->h = size->height; + box->x = 0; + box->w = size->width; + } +} + +/** + * vmw_subres_dirty_full - Mark a full subresource as dirty + * @dirty: The surface's dirty tracker. + * @subres: The subresource + */ +static void vmw_subres_dirty_full(struct vmw_surface_dirty *dirty, u32 subres) +{ + const struct vmw_surface_cache *cache = &dirty->cache; + u32 mip = subres % cache->num_mip_levels; + const struct drm_vmw_size *size = &cache->mip[mip].size; + SVGA3dBox *box = &dirty->boxes[subres]; + + box->x = 0; + box->y = 0; + box->z = 0; + box->w = size->width; + box->h = size->height; + box->d = size->depth; +} + +/* + * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for texture + * surfaces. + */ +static void vmw_surface_tex_dirty_range_add(struct vmw_resource *res, + size_t start, size_t end) +{ + struct vmw_surface_dirty *dirty = + (struct vmw_surface_dirty *) res->dirty; + size_t backup_end = res->guest_memory_offset + res->guest_memory_size; + struct vmw_surface_loc loc1, loc2; + const struct vmw_surface_cache *cache; + + start = max_t(size_t, start, res->guest_memory_offset) - res->guest_memory_offset; + end = min(end, backup_end) - res->guest_memory_offset; + cache = &dirty->cache; + vmw_surface_get_loc(cache, &loc1, start); + vmw_surface_get_loc(cache, &loc2, end - 1); + vmw_surface_inc_loc(cache, &loc2); + + if (loc1.sheet != loc2.sheet) { + u32 sub_res; + + /* + * Multiple multisample sheets. To do this in an optimized + * fashion, compute the dirty region for each sheet and the + * resulting union. Since this is not a common case, just dirty + * the whole surface. + */ + for (sub_res = 0; sub_res < dirty->num_subres; ++sub_res) + vmw_subres_dirty_full(dirty, sub_res); + return; + } + if (loc1.sub_resource + 1 == loc2.sub_resource) { + /* Dirty range covers a single sub-resource */ + vmw_subres_dirty_add(dirty, &loc1, &loc2); + } else { + /* Dirty range covers multiple sub-resources */ + struct vmw_surface_loc loc_min, loc_max; + u32 sub_res; + + vmw_surface_max_loc(cache, loc1.sub_resource, &loc_max); + vmw_subres_dirty_add(dirty, &loc1, &loc_max); + vmw_surface_min_loc(cache, loc2.sub_resource - 1, &loc_min); + vmw_subres_dirty_add(dirty, &loc_min, &loc2); + for (sub_res = loc1.sub_resource + 1; + sub_res < loc2.sub_resource - 1; ++sub_res) + vmw_subres_dirty_full(dirty, sub_res); + } +} + +/* + * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for buffer + * surfaces. + */ +static void vmw_surface_buf_dirty_range_add(struct vmw_resource *res, + size_t start, size_t end) +{ + struct vmw_surface_dirty *dirty = + (struct vmw_surface_dirty *) res->dirty; + const struct vmw_surface_cache *cache = &dirty->cache; + size_t backup_end = res->guest_memory_offset + cache->mip_chain_bytes; + SVGA3dBox *box = &dirty->boxes[0]; + u32 box_c2; + + box->h = box->d = 1; + start = max_t(size_t, start, res->guest_memory_offset) - res->guest_memory_offset; + end = min(end, backup_end) - res->guest_memory_offset; + box_c2 = box->x + box->w; + if (box->w == 0 || box->x > start) + box->x = start; + if (box_c2 < end) + box->w = end - box->x; +} + +/* + * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for surfaces + */ +static void vmw_surface_dirty_range_add(struct vmw_resource *res, size_t start, + size_t end) +{ + struct vmw_surface *srf = vmw_res_to_srf(res); + + if (WARN_ON(end <= res->guest_memory_offset || + start >= res->guest_memory_offset + res->guest_memory_size)) + return; + + if (srf->metadata.format == SVGA3D_BUFFER) + vmw_surface_buf_dirty_range_add(res, start, end); + else + vmw_surface_tex_dirty_range_add(res, start, end); +} + +/* + * vmw_surface_dirty_sync - The surface's dirty_sync callback. + */ +static int vmw_surface_dirty_sync(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + u32 i, num_dirty; + struct vmw_surface_dirty *dirty = + (struct vmw_surface_dirty *) res->dirty; + size_t alloc_size; + const struct vmw_surface_cache *cache = &dirty->cache; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXUpdateSubResource body; + } *cmd1; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdUpdateGBImage body; + } *cmd2; + void *cmd; + + num_dirty = 0; + for (i = 0; i < dirty->num_subres; ++i) { + const SVGA3dBox *box = &dirty->boxes[i]; + + if (box->d) + num_dirty++; + } + + if (!num_dirty) + goto out; + + alloc_size = num_dirty * ((has_sm4_context(dev_priv)) ? sizeof(*cmd1) : sizeof(*cmd2)); + cmd = VMW_CMD_RESERVE(dev_priv, alloc_size); + if (!cmd) + return -ENOMEM; + + cmd1 = cmd; + cmd2 = cmd; + + for (i = 0; i < dirty->num_subres; ++i) { + const SVGA3dBox *box = &dirty->boxes[i]; + + if (!box->d) + continue; + + /* + * DX_UPDATE_SUBRESOURCE is aware of array surfaces. + * UPDATE_GB_IMAGE is not. + */ + if (has_sm4_context(dev_priv)) { + cmd1->header.id = SVGA_3D_CMD_DX_UPDATE_SUBRESOURCE; + cmd1->header.size = sizeof(cmd1->body); + cmd1->body.sid = res->id; + cmd1->body.subResource = i; + cmd1->body.box = *box; + cmd1++; + } else { + cmd2->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE; + cmd2->header.size = sizeof(cmd2->body); + cmd2->body.image.sid = res->id; + cmd2->body.image.face = i / cache->num_mip_levels; + cmd2->body.image.mipmap = i - + (cache->num_mip_levels * cmd2->body.image.face); + cmd2->body.box = *box; + cmd2++; + } + + } + vmw_cmd_commit(dev_priv, alloc_size); + out: + memset(&dirty->boxes[0], 0, sizeof(dirty->boxes[0]) * + dirty->num_subres); + + return 0; +} + +/* + * vmw_surface_dirty_alloc - The surface's dirty_alloc callback. + */ +static int vmw_surface_dirty_alloc(struct vmw_resource *res) +{ + struct vmw_surface *srf = vmw_res_to_srf(res); + const struct vmw_surface_metadata *metadata = &srf->metadata; + struct vmw_surface_dirty *dirty; + u32 num_layers = 1; + u32 num_mip; + u32 num_subres; + u32 num_samples; + size_t dirty_size; + int ret; + + if (metadata->array_size) + num_layers = metadata->array_size; + else if (metadata->flags & SVGA3D_SURFACE_CUBEMAP) + num_layers *= SVGA3D_MAX_SURFACE_FACES; + + num_mip = metadata->mip_levels[0]; + if (!num_mip) + num_mip = 1; + + num_subres = num_layers * num_mip; + dirty_size = struct_size(dirty, boxes, num_subres); + + dirty = kvzalloc(dirty_size, GFP_KERNEL); + if (!dirty) { + ret = -ENOMEM; + goto out_no_dirty; + } + + num_samples = max_t(u32, 1, metadata->multisample_count); + ret = vmw_surface_setup_cache(&metadata->base_size, metadata->format, + num_mip, num_layers, num_samples, + &dirty->cache); + if (ret) + goto out_no_cache; + + dirty->num_subres = num_subres; + res->dirty = (struct vmw_resource_dirty *) dirty; + + return 0; + +out_no_cache: + kvfree(dirty); +out_no_dirty: + return ret; +} + +/* + * vmw_surface_dirty_free - The surface's dirty_free callback + */ +static void vmw_surface_dirty_free(struct vmw_resource *res) +{ + struct vmw_surface_dirty *dirty = + (struct vmw_surface_dirty *) res->dirty; + + kvfree(dirty); + res->dirty = NULL; +} + +/* + * vmw_surface_clean - The surface's clean callback + */ +static int vmw_surface_clean(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + size_t alloc_size; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdReadbackGBSurface body; + } *cmd; + + alloc_size = sizeof(*cmd); + cmd = VMW_CMD_RESERVE(dev_priv, alloc_size); + if (!cmd) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_READBACK_GB_SURFACE; + cmd->header.size = sizeof(cmd->body); + cmd->body.sid = res->id; + vmw_cmd_commit(dev_priv, alloc_size); + + return 0; +} + +/* + * vmw_gb_surface_define - Define a private GB surface + * + * @dev_priv: Pointer to a device private. + * @metadata: Metadata representing the surface to create. + * @user_srf_out: allocated user_srf. Set to NULL on failure. + * + * GB surfaces allocated by this function will not have a user mode handle, and + * thus will only be visible to vmwgfx. For optimization reasons the + * surface may later be given a user mode handle by another function to make + * it available to user mode drivers. + */ +int vmw_gb_surface_define(struct vmw_private *dev_priv, + const struct vmw_surface_metadata *req, + struct vmw_surface **srf_out) +{ + struct vmw_surface_metadata *metadata; + struct vmw_user_surface *user_srf; + struct vmw_surface *srf; + u32 sample_count = 1; + u32 num_layers = 1; + int ret; + + *srf_out = NULL; + + if (req->scanout) { + if (!vmw_surface_is_screen_target_format(req->format)) { + VMW_DEBUG_USER("Invalid Screen Target surface format."); + return -EINVAL; + } + + if (req->base_size.width > dev_priv->texture_max_width || + req->base_size.height > dev_priv->texture_max_height) { + VMW_DEBUG_USER("%ux%u\n, exceed max surface size %ux%u", + req->base_size.width, + req->base_size.height, + dev_priv->texture_max_width, + dev_priv->texture_max_height); + return -EINVAL; + } + } else { + const SVGA3dSurfaceDesc *desc = + vmw_surface_get_desc(req->format); + + if (desc->blockDesc == SVGA3DBLOCKDESC_NONE) { + VMW_DEBUG_USER("Invalid surface format.\n"); + return -EINVAL; + } + } + + if (req->autogen_filter != SVGA3D_TEX_FILTER_NONE) + return -EINVAL; + + if (req->num_sizes != 1) + return -EINVAL; + + if (req->sizes != NULL) + return -EINVAL; + + user_srf = kzalloc(sizeof(*user_srf), GFP_KERNEL); + if (unlikely(!user_srf)) { + ret = -ENOMEM; + goto out_unlock; + } + + *srf_out = &user_srf->srf; + user_srf->prime.base.shareable = false; + user_srf->prime.base.tfile = NULL; + + srf = &user_srf->srf; + srf->metadata = *req; + srf->offsets = NULL; + + metadata = &srf->metadata; + + if (metadata->array_size) + num_layers = req->array_size; + else if (metadata->flags & SVGA3D_SURFACE_CUBEMAP) + num_layers = SVGA3D_MAX_SURFACE_FACES; + + if (metadata->flags & SVGA3D_SURFACE_MULTISAMPLE) + sample_count = metadata->multisample_count; + + srf->res.guest_memory_size = + vmw_surface_get_serialized_size_extended( + metadata->format, + metadata->base_size, + metadata->mip_levels[0], + num_layers, + sample_count); + + if (metadata->flags & SVGA3D_SURFACE_BIND_STREAM_OUTPUT) + srf->res.guest_memory_size += sizeof(SVGA3dDXSOState); + + /* + * Don't set SVGA3D_SURFACE_SCREENTARGET flag for a scanout surface with + * size greater than STDU max width/height. This is really a workaround + * to support creation of big framebuffer requested by some user-space + * for whole topology. That big framebuffer won't really be used for + * binding with screen target as during prepare_fb a separate surface is + * created so it's safe to ignore SVGA3D_SURFACE_SCREENTARGET flag. + */ + if (dev_priv->active_display_unit == vmw_du_screen_target && + metadata->scanout && + metadata->base_size.width <= dev_priv->stdu_max_width && + metadata->base_size.height <= dev_priv->stdu_max_height) + metadata->flags |= SVGA3D_SURFACE_SCREENTARGET; + + /* + * From this point, the generic resource management functions + * destroy the object on failure. + */ + ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free); + + return ret; + +out_unlock: + return ret; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_system_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_system_manager.c new file mode 100644 index 0000000000..ee7964cbda --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_system_manager.c @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2021 VMware, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include "vmwgfx_drv.h" + +#include <drm/ttm/ttm_device.h> +#include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_resource.h> +#include <linux/slab.h> + + +static int vmw_sys_man_alloc(struct ttm_resource_manager *man, + struct ttm_buffer_object *bo, + const struct ttm_place *place, + struct ttm_resource **res) +{ + *res = kzalloc(sizeof(**res), GFP_KERNEL); + if (!*res) + return -ENOMEM; + + ttm_resource_init(bo, place, *res); + return 0; +} + +static void vmw_sys_man_free(struct ttm_resource_manager *man, + struct ttm_resource *res) +{ + ttm_resource_fini(man, res); + kfree(res); +} + +static const struct ttm_resource_manager_func vmw_sys_manager_func = { + .alloc = vmw_sys_man_alloc, + .free = vmw_sys_man_free, +}; + +int vmw_sys_man_init(struct vmw_private *dev_priv) +{ + struct ttm_device *bdev = &dev_priv->bdev; + struct ttm_resource_manager *man = + kzalloc(sizeof(*man), GFP_KERNEL); + + if (!man) + return -ENOMEM; + + man->use_tt = true; + man->func = &vmw_sys_manager_func; + + ttm_resource_manager_init(man, bdev, 0); + ttm_set_driver_manager(bdev, VMW_PL_SYSTEM, man); + ttm_resource_manager_set_used(man, true); + return 0; +} + +void vmw_sys_man_fini(struct vmw_private *dev_priv) +{ + struct ttm_resource_manager *man = ttm_manager_type(&dev_priv->bdev, + VMW_PL_SYSTEM); + + ttm_resource_manager_evict_all(&dev_priv->bdev, man); + + ttm_resource_manager_set_used(man, false); + ttm_resource_manager_cleanup(man); + + ttm_set_driver_manager(&dev_priv->bdev, VMW_PL_SYSTEM, NULL); + kfree(man); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c new file mode 100644 index 0000000000..af8562c95c --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c @@ -0,0 +1,612 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_drv.h" +#include <drm/ttm/ttm_placement.h> + +static const struct ttm_place vram_placement_flags = { + .fpfn = 0, + .lpfn = 0, + .mem_type = TTM_PL_VRAM, + .flags = 0 +}; + +static const struct ttm_place sys_placement_flags = { + .fpfn = 0, + .lpfn = 0, + .mem_type = TTM_PL_SYSTEM, + .flags = 0 +}; + +static const struct ttm_place gmr_placement_flags = { + .fpfn = 0, + .lpfn = 0, + .mem_type = VMW_PL_GMR, + .flags = 0 +}; + +struct ttm_placement vmw_vram_placement = { + .num_placement = 1, + .placement = &vram_placement_flags, + .num_busy_placement = 1, + .busy_placement = &vram_placement_flags +}; + +static const struct ttm_place vram_gmr_placement_flags[] = { + { + .fpfn = 0, + .lpfn = 0, + .mem_type = TTM_PL_VRAM, + .flags = 0 + }, { + .fpfn = 0, + .lpfn = 0, + .mem_type = VMW_PL_GMR, + .flags = 0 + } +}; + +struct ttm_placement vmw_vram_gmr_placement = { + .num_placement = 2, + .placement = vram_gmr_placement_flags, + .num_busy_placement = 1, + .busy_placement = &gmr_placement_flags +}; + +struct ttm_placement vmw_sys_placement = { + .num_placement = 1, + .placement = &sys_placement_flags, + .num_busy_placement = 1, + .busy_placement = &sys_placement_flags +}; + +const size_t vmw_tt_size = sizeof(struct vmw_ttm_tt); + +/** + * __vmw_piter_non_sg_next: 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) +{ + bool ret = __vmw_piter_non_sg_next(viter); + + return __sg_page_iter_dma_next(&viter->iter) && ret; +} + + +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) +{ + return sg_page_iter_dma_address(&viter->iter); +} + + +/** + * 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 + * @p_offset: Pointer offset used to update current array position + * + * 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; + viter->pages = vsgt->pages; + switch (vsgt->mode) { + case vmw_dma_alloc_coherent: + viter->next = &__vmw_piter_non_sg_next; + viter->dma_address = &__vmw_piter_dma_addr; + viter->addrs = vsgt->addrs; + break; + case vmw_dma_map_populate: + case vmw_dma_map_bind: + viter->next = &__vmw_piter_sg_next; + viter->dma_address = &__vmw_piter_sg_addr; + __sg_page_iter_start(&viter->iter.base, 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->drm.dev; + + dma_unmap_sgtable(dev, &vmw_tt->sgt, DMA_BIDIRECTIONAL, 0); + 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->drm.dev; + + return dma_map_sgtable(dev, &vmw_tt->sgt, DMA_BIDIRECTIONAL, 0); +} + +/** + * 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_private *dev_priv = vmw_tt->dev_priv; + struct vmw_sg_table *vsgt = &vmw_tt->vsgt; + int ret = 0; + + if (vmw_tt->mapped) + return 0; + + vsgt->mode = dev_priv->map_mode; + vsgt->pages = vmw_tt->dma_ttm.pages; + vsgt->num_pages = vmw_tt->dma_ttm.num_pages; + vsgt->addrs = vmw_tt->dma_ttm.dma_address; + vsgt->sgt = NULL; + + switch (dev_priv->map_mode) { + case vmw_dma_map_bind: + case vmw_dma_map_populate: + vsgt->sgt = &vmw_tt->sgt; + ret = sg_alloc_table_from_pages_segment( + &vmw_tt->sgt, vsgt->pages, vsgt->num_pages, 0, + (unsigned long)vsgt->num_pages << PAGE_SHIFT, + dma_get_max_seg_size(dev_priv->drm.dev), GFP_KERNEL); + if (ret) + goto out_sg_alloc_fail; + + ret = vmw_ttm_map_for_dma(vmw_tt); + if (unlikely(ret != 0)) + goto out_map_fail; + + break; + default: + break; + } + + 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: + return ret; +} + +/** + * 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; + break; + default: + break; + } + vmw_tt->mapped = false; +} + +/** + * 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); + + return &vmw_tt->vsgt; +} + + +static int vmw_ttm_bind(struct ttm_device *bdev, + struct ttm_tt *ttm, struct ttm_resource *bo_mem) +{ + struct vmw_ttm_tt *vmw_be = + container_of(ttm, struct vmw_ttm_tt, dma_ttm); + int ret = 0; + + if (!bo_mem) + return -EINVAL; + + if (vmw_be->bound) + return 0; + + 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: + ret = vmw_gmr_bind(vmw_be->dev_priv, &vmw_be->vsgt, + ttm->num_pages, vmw_be->gmr_id); + break; + 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; + } + + ret = vmw_mob_bind(vmw_be->dev_priv, vmw_be->mob, + &vmw_be->vsgt, ttm->num_pages, + vmw_be->gmr_id); + break; + case VMW_PL_SYSTEM: + /* Nothing to be done for a system bind */ + break; + default: + BUG(); + } + vmw_be->bound = true; + return ret; +} + +static void vmw_ttm_unbind(struct ttm_device *bdev, + struct ttm_tt *ttm) +{ + struct vmw_ttm_tt *vmw_be = + container_of(ttm, struct vmw_ttm_tt, dma_ttm); + + if (!vmw_be->bound) + return; + + 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; + case VMW_PL_SYSTEM: + break; + default: + BUG(); + } + + if (vmw_be->dev_priv->map_mode == vmw_dma_map_bind) + vmw_ttm_unmap_dma(vmw_be); + vmw_be->bound = false; +} + + +static void vmw_ttm_destroy(struct ttm_device *bdev, struct ttm_tt *ttm) +{ + struct vmw_ttm_tt *vmw_be = + container_of(ttm, struct vmw_ttm_tt, dma_ttm); + + vmw_ttm_unmap_dma(vmw_be); + ttm_tt_fini(ttm); + if (vmw_be->mob) + vmw_mob_destroy(vmw_be->mob); + + kfree(vmw_be); +} + + +static int vmw_ttm_populate(struct ttm_device *bdev, + struct ttm_tt *ttm, struct ttm_operation_ctx *ctx) +{ + int ret; + + /* TODO: maybe completely drop this ? */ + if (ttm_tt_is_populated(ttm)) + return 0; + + ret = ttm_pool_alloc(&bdev->pool, ttm, ctx); + + return ret; +} + +static void vmw_ttm_unpopulate(struct ttm_device *bdev, + struct ttm_tt *ttm) +{ + struct vmw_ttm_tt *vmw_tt = container_of(ttm, struct vmw_ttm_tt, + dma_ttm); + + vmw_ttm_unbind(bdev, ttm); + + if (vmw_tt->mob) { + vmw_mob_destroy(vmw_tt->mob); + vmw_tt->mob = NULL; + } + + vmw_ttm_unmap_dma(vmw_tt); + + ttm_pool_free(&bdev->pool, ttm); +} + +static struct ttm_tt *vmw_ttm_tt_create(struct ttm_buffer_object *bo, + uint32_t page_flags) +{ + struct vmw_ttm_tt *vmw_be; + int ret; + + vmw_be = kzalloc(sizeof(*vmw_be), GFP_KERNEL); + if (!vmw_be) + return NULL; + + vmw_be->dev_priv = vmw_priv_from_ttm(bo->bdev); + vmw_be->mob = NULL; + + if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent) + ret = ttm_sg_tt_init(&vmw_be->dma_ttm, bo, page_flags, + ttm_cached); + else + ret = ttm_tt_init(&vmw_be->dma_ttm, bo, page_flags, + ttm_cached, 0); + if (unlikely(ret != 0)) + goto out_no_init; + + return &vmw_be->dma_ttm; +out_no_init: + kfree(vmw_be); + return NULL; +} + +static void vmw_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *placement) +{ + *placement = vmw_sys_placement; +} + +static int vmw_ttm_io_mem_reserve(struct ttm_device *bdev, struct ttm_resource *mem) +{ + struct vmw_private *dev_priv = vmw_priv_from_ttm(bdev); + + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + case VMW_PL_SYSTEM: + case VMW_PL_GMR: + case VMW_PL_MOB: + return 0; + case TTM_PL_VRAM: + mem->bus.offset = (mem->start << PAGE_SHIFT) + + dev_priv->vram_start; + mem->bus.is_iomem = true; + mem->bus.caching = ttm_cached; + break; + default: + return -EINVAL; + } + return 0; +} + +/** + * vmw_move_notify - TTM move_notify_callback + * + * @bo: The TTM buffer object about to move. + * @old_mem: The old memory where we move from + * @new_mem: The struct ttm_resource 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_resource *old_mem, + struct ttm_resource *new_mem) +{ + vmw_bo_move_notify(bo, new_mem); + vmw_query_move_notify(bo, old_mem, new_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) +{ + vmw_bo_swap_notify(bo); + (void) ttm_bo_wait(bo, false, false); +} + +static bool vmw_memtype_is_system(uint32_t mem_type) +{ + return mem_type == TTM_PL_SYSTEM || mem_type == VMW_PL_SYSTEM; +} + +static int vmw_move(struct ttm_buffer_object *bo, + bool evict, + struct ttm_operation_ctx *ctx, + struct ttm_resource *new_mem, + struct ttm_place *hop) +{ + struct ttm_resource_manager *new_man; + struct ttm_resource_manager *old_man = NULL; + int ret = 0; + + new_man = ttm_manager_type(bo->bdev, new_mem->mem_type); + if (bo->resource) + old_man = ttm_manager_type(bo->bdev, bo->resource->mem_type); + + if (new_man->use_tt && !vmw_memtype_is_system(new_mem->mem_type)) { + ret = vmw_ttm_bind(bo->bdev, bo->ttm, new_mem); + if (ret) + return ret; + } + + if (!bo->resource || (bo->resource->mem_type == TTM_PL_SYSTEM && + bo->ttm == NULL)) { + ttm_bo_move_null(bo, new_mem); + return 0; + } + + vmw_move_notify(bo, bo->resource, new_mem); + + if (old_man && old_man->use_tt && new_man->use_tt) { + if (vmw_memtype_is_system(bo->resource->mem_type)) { + ttm_bo_move_null(bo, new_mem); + return 0; + } + ret = ttm_bo_wait_ctx(bo, ctx); + if (ret) + goto fail; + + vmw_ttm_unbind(bo->bdev, bo->ttm); + ttm_resource_free(bo, &bo->resource); + ttm_bo_assign_mem(bo, new_mem); + return 0; + } else { + ret = ttm_bo_move_memcpy(bo, ctx, new_mem); + if (ret) + goto fail; + } + return 0; +fail: + vmw_move_notify(bo, new_mem, bo->resource); + return ret; +} + +struct ttm_device_funcs vmw_bo_driver = { + .ttm_tt_create = &vmw_ttm_tt_create, + .ttm_tt_populate = &vmw_ttm_populate, + .ttm_tt_unpopulate = &vmw_ttm_unpopulate, + .ttm_tt_destroy = &vmw_ttm_destroy, + .eviction_valuable = ttm_bo_eviction_valuable, + .evict_flags = vmw_evict_flags, + .move = vmw_move, + .swap_notify = vmw_swap_notify, + .io_mem_reserve = &vmw_ttm_io_mem_reserve, +}; + +int vmw_bo_create_and_populate(struct vmw_private *dev_priv, + size_t bo_size, u32 domain, + struct vmw_bo **bo_p) +{ + struct ttm_operation_ctx ctx = { + .interruptible = false, + .no_wait_gpu = false + }; + struct vmw_bo *vbo; + int ret; + struct vmw_bo_params bo_params = { + .domain = domain, + .busy_domain = domain, + .bo_type = ttm_bo_type_kernel, + .size = bo_size, + .pin = true + }; + + ret = vmw_bo_create(dev_priv, &bo_params, &vbo); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_bo_reserve(&vbo->tbo, false, true, NULL); + BUG_ON(ret != 0); + ret = vmw_ttm_populate(vbo->tbo.bdev, vbo->tbo.ttm, &ctx); + if (likely(ret == 0)) { + struct vmw_ttm_tt *vmw_tt = + container_of(vbo->tbo.ttm, struct vmw_ttm_tt, dma_ttm); + ret = vmw_ttm_map_dma(vmw_tt); + } + + ttm_bo_unreserve(&vbo->tbo); + + if (likely(ret == 0)) + *bo_p = vbo; + return ret; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c new file mode 100644 index 0000000000..90097d04b4 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2009-2011 VMware, Inc., Palo Alto, CA., USA + * + * 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" + +static int vmw_bo_vm_lookup(struct ttm_device *bdev, + struct drm_file *filp, + unsigned long offset, + unsigned long pages, + struct ttm_buffer_object **p_bo) +{ + struct vmw_private *dev_priv = container_of(bdev, struct vmw_private, bdev); + struct drm_device *drm = &dev_priv->drm; + struct drm_vma_offset_node *node; + int ret; + + *p_bo = NULL; + + drm_vma_offset_lock_lookup(bdev->vma_manager); + + node = drm_vma_offset_lookup_locked(bdev->vma_manager, offset, pages); + if (likely(node)) { + *p_bo = container_of(node, struct ttm_buffer_object, + base.vma_node); + *p_bo = ttm_bo_get_unless_zero(*p_bo); + } + + drm_vma_offset_unlock_lookup(bdev->vma_manager); + + if (!*p_bo) { + drm_err(drm, "Could not find buffer object to map\n"); + return -EINVAL; + } + + if (!drm_vma_node_is_allowed(node, filp)) { + ret = -EACCES; + goto out_no_access; + } + + return 0; +out_no_access: + ttm_bo_put(*p_bo); + return ret; +} + +int vmw_mmap(struct file *filp, struct vm_area_struct *vma) +{ + static const struct vm_operations_struct vmw_vm_ops = { + .pfn_mkwrite = vmw_bo_vm_mkwrite, + .page_mkwrite = vmw_bo_vm_mkwrite, + .fault = vmw_bo_vm_fault, + .open = ttm_bo_vm_open, + .close = ttm_bo_vm_close, + }; + struct drm_file *file_priv = filp->private_data; + struct vmw_private *dev_priv = vmw_priv(file_priv->minor->dev); + struct ttm_device *bdev = &dev_priv->bdev; + struct ttm_buffer_object *bo; + int ret; + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET_START)) + return -EINVAL; + + ret = vmw_bo_vm_lookup(bdev, file_priv, vma->vm_pgoff, vma_pages(vma), &bo); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_bo_mmap_obj(vma, bo); + if (unlikely(ret != 0)) + goto out_unref; + + vma->vm_ops = &vmw_vm_ops; + + /* Use VM_PFNMAP rather than VM_MIXEDMAP if not a COW mapping */ + if (!is_cow_mapping(vma->vm_flags)) + vm_flags_mod(vma, VM_PFNMAP, VM_MIXEDMAP); + + ttm_bo_put(bo); /* release extra ref taken by ttm_bo_mmap_obj() */ + + return 0; + +out_unref: + ttm_bo_put(bo); + return ret; +} + diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_va.c b/drivers/gpu/drm/vmwgfx/vmwgfx_va.c new file mode 100644 index 0000000000..d140089e53 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_va.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2012-2016 VMware, Inc., Palo Alto, CA., USA + * + * 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_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" + +/** + * struct vmw_stream - Overlay stream simple resource. + * @sres: The simple resource we derive from. + * @stream_id: The overlay stream id. + */ +struct vmw_stream { + struct vmw_simple_resource sres; + u32 stream_id; +}; + +/** + * vmw_stream - Typecast a struct vmw_resource to a struct vmw_stream. + * @res: Pointer to the struct vmw_resource. + * + * Returns: Returns a pointer to the struct vmw_stream. + */ +static struct vmw_stream * +vmw_stream(struct vmw_resource *res) +{ + return container_of(res, struct vmw_stream, sres.res); +} + +/*************************************************************************** + * Simple resource callbacks for struct vmw_stream + **************************************************************************/ +static void vmw_stream_hw_destroy(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_stream *stream = vmw_stream(res); + int ret; + + ret = vmw_overlay_unref(dev_priv, stream->stream_id); + WARN_ON_ONCE(ret != 0); +} + +static int vmw_stream_init(struct vmw_resource *res, void *data) +{ + struct vmw_stream *stream = vmw_stream(res); + + return vmw_overlay_claim(res->dev_priv, &stream->stream_id); +} + +static void vmw_stream_set_arg_handle(void *data, u32 handle) +{ + struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; + + arg->stream_id = handle; +} + +static const struct vmw_simple_resource_func va_stream_func = { + .res_func = { + .res_type = vmw_res_stream, + .needs_guest_memory = false, + .may_evict = false, + .type_name = "overlay stream", + .domain = VMW_BO_DOMAIN_SYS, + .busy_domain = VMW_BO_DOMAIN_SYS, + .create = NULL, + .destroy = NULL, + .bind = NULL, + .unbind = NULL + }, + .ttm_res_type = VMW_RES_STREAM, + .size = sizeof(struct vmw_stream), + .init = vmw_stream_init, + .hw_destroy = vmw_stream_hw_destroy, + .set_arg_handle = vmw_stream_set_arg_handle, +}; + +/*************************************************************************** + * End simple resource callbacks for struct vmw_stream + **************************************************************************/ + +/** + * vmw_stream_unref_ioctl - Ioctl to unreference a user-space handle to + * a struct vmw_stream. + * @dev: Pointer to the drm device. + * @data: The ioctl argument + * @file_priv: Pointer to a struct drm_file identifying the caller. + * + * Return: + * 0 if successful. + * Negative error value on failure. + */ +int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; + + return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, + arg->stream_id); +} + +/** + * vmw_stream_claim_ioctl - Ioctl to claim a struct vmw_stream overlay. + * @dev: Pointer to the drm device. + * @data: The ioctl argument + * @file_priv: Pointer to a struct drm_file identifying the caller. + * + * Return: + * 0 if successful. + * Negative error value on failure. + */ +int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + return vmw_simple_resource_create_ioctl(dev, data, file_priv, + &va_stream_func); +} + +/** + * vmw_user_stream_lookup - Look up a struct vmw_user_stream from a handle. + * @dev_priv: Pointer to a struct vmw_private. + * @tfile: struct ttm_object_file identifying the caller. + * @inout_id: In: The user-space handle. Out: The stream id. + * @out: On output contains a refcounted pointer to the embedded + * struct vmw_resource. + * + * Return: + * 0 if successful. + * Negative error value on failure. + */ +int vmw_user_stream_lookup(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t *inout_id, struct vmw_resource **out) +{ + struct vmw_stream *stream; + struct vmw_resource *res = + vmw_simple_resource_lookup(tfile, *inout_id, &va_stream_func); + + if (IS_ERR(res)) + return PTR_ERR(res); + + stream = vmw_stream(res); + *inout_id = stream->stream_id; + *out = res; + + return 0; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c new file mode 100644 index 0000000000..aaacbdcbd7 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c @@ -0,0 +1,864 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright © 2018 - 2023 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_bo.h" +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" +#include "vmwgfx_validation.h" + +#include <linux/slab.h> + + +#define VMWGFX_VALIDATION_MEM_GRAN (16*PAGE_SIZE) + +/** + * struct vmw_validation_bo_node - Buffer object validation metadata. + * @base: Metadata used for TTM reservation- and validation. + * @hash: A hash entry used for the duplicate detection hash table. + * @coherent_count: If switching backup buffers, number of new coherent + * resources that will have this buffer as a backup buffer. + * + * Bit fields are used since these structures are allocated and freed in + * large numbers and space conservation is desired. + */ +struct vmw_validation_bo_node { + struct ttm_validate_buffer base; + struct vmwgfx_hash_item hash; + unsigned int coherent_count; +}; +/** + * struct vmw_validation_res_node - Resource validation metadata. + * @head: List head for the resource validation list. + * @hash: A hash entry used for the duplicate detection hash table. + * @res: Reference counted resource pointer. + * @new_guest_memory_bo: Non ref-counted pointer to new guest memory buffer + * to be assigned to a resource. + * @new_guest_memory_offset: Offset into the new backup mob for resources + * that can share MOBs. + * @no_buffer_needed: Kernel does not need to allocate a MOB during validation, + * the command stream provides a mob bind operation. + * @switching_guest_memory_bo: The validation process is switching backup MOB. + * @first_usage: True iff the resource has been seen only once in the current + * validation batch. + * @reserved: Whether the resource is currently reserved by this process. + * @dirty_set: Change dirty status of the resource. + * @dirty: Dirty information VMW_RES_DIRTY_XX. + * @private: Optionally additional memory for caller-private data. + * + * Bit fields are used since these structures are allocated and freed in + * large numbers and space conservation is desired. + */ +struct vmw_validation_res_node { + struct list_head head; + struct vmwgfx_hash_item hash; + struct vmw_resource *res; + struct vmw_bo *new_guest_memory_bo; + unsigned long new_guest_memory_offset; + u32 no_buffer_needed : 1; + u32 switching_guest_memory_bo : 1; + u32 first_usage : 1; + u32 reserved : 1; + u32 dirty : 1; + u32 dirty_set : 1; + unsigned long private[]; +}; + +/** + * vmw_validation_mem_alloc - Allocate kernel memory from the validation + * context based allocator + * @ctx: The validation context + * @size: The number of bytes to allocated. + * + * The memory allocated may not exceed PAGE_SIZE, and the returned + * address is aligned to sizeof(long). All memory allocated this way is + * reclaimed after validation when calling any of the exported functions: + * vmw_validation_unref_lists() + * vmw_validation_revert() + * vmw_validation_done() + * + * Return: Pointer to the allocated memory on success. NULL on failure. + */ +void *vmw_validation_mem_alloc(struct vmw_validation_context *ctx, + unsigned int size) +{ + void *addr; + + size = vmw_validation_align(size); + if (size > PAGE_SIZE) + return NULL; + + if (ctx->mem_size_left < size) { + struct page *page; + + if (ctx->vm && ctx->vm_size_left < PAGE_SIZE) { + ctx->vm_size_left += VMWGFX_VALIDATION_MEM_GRAN; + ctx->total_mem += VMWGFX_VALIDATION_MEM_GRAN; + } + + page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!page) + return NULL; + + if (ctx->vm) + ctx->vm_size_left -= PAGE_SIZE; + + list_add_tail(&page->lru, &ctx->page_list); + ctx->page_address = page_address(page); + ctx->mem_size_left = PAGE_SIZE; + } + + addr = (void *) (ctx->page_address + (PAGE_SIZE - ctx->mem_size_left)); + ctx->mem_size_left -= size; + + return addr; +} + +/** + * vmw_validation_mem_free - Free all memory allocated using + * vmw_validation_mem_alloc() + * @ctx: The validation context + * + * All memory previously allocated for this context using + * vmw_validation_mem_alloc() is freed. + */ +static void vmw_validation_mem_free(struct vmw_validation_context *ctx) +{ + struct page *entry, *next; + + list_for_each_entry_safe(entry, next, &ctx->page_list, lru) { + list_del_init(&entry->lru); + __free_page(entry); + } + + ctx->mem_size_left = 0; + if (ctx->vm && ctx->total_mem) { + ctx->total_mem = 0; + ctx->vm_size_left = 0; + } +} + +/** + * vmw_validation_find_bo_dup - Find a duplicate buffer object entry in the + * validation context's lists. + * @ctx: The validation context to search. + * @vbo: The buffer object to search for. + * + * Return: Pointer to the struct vmw_validation_bo_node referencing the + * duplicate, or NULL if none found. + */ +static struct vmw_validation_bo_node * +vmw_validation_find_bo_dup(struct vmw_validation_context *ctx, + struct vmw_bo *vbo) +{ + struct vmw_validation_bo_node *bo_node = NULL; + + if (!ctx->merge_dups) + return NULL; + + if (ctx->sw_context) { + struct vmwgfx_hash_item *hash; + unsigned long key = (unsigned long) vbo; + + hash_for_each_possible_rcu(ctx->sw_context->res_ht, hash, head, key) { + if (hash->key == key) { + bo_node = container_of(hash, typeof(*bo_node), hash); + break; + } + } + } else { + struct vmw_validation_bo_node *entry; + + list_for_each_entry(entry, &ctx->bo_list, base.head) { + if (entry->base.bo == &vbo->tbo) { + bo_node = entry; + break; + } + } + } + + return bo_node; +} + +/** + * vmw_validation_find_res_dup - Find a duplicate resource entry in the + * validation context's lists. + * @ctx: The validation context to search. + * @res: Reference counted resource pointer. + * + * Return: Pointer to the struct vmw_validation_bo_node referencing the + * duplicate, or NULL if none found. + */ +static struct vmw_validation_res_node * +vmw_validation_find_res_dup(struct vmw_validation_context *ctx, + struct vmw_resource *res) +{ + struct vmw_validation_res_node *res_node = NULL; + + if (!ctx->merge_dups) + return NULL; + + if (ctx->sw_context) { + struct vmwgfx_hash_item *hash; + unsigned long key = (unsigned long) res; + + hash_for_each_possible_rcu(ctx->sw_context->res_ht, hash, head, key) { + if (hash->key == key) { + res_node = container_of(hash, typeof(*res_node), hash); + break; + } + } + } else { + struct vmw_validation_res_node *entry; + + list_for_each_entry(entry, &ctx->resource_ctx_list, head) { + if (entry->res == res) { + res_node = entry; + goto out; + } + } + + list_for_each_entry(entry, &ctx->resource_list, head) { + if (entry->res == res) { + res_node = entry; + break; + } + } + + } +out: + return res_node; +} + +/** + * vmw_validation_add_bo - Add a buffer object to the validation context. + * @ctx: The validation context. + * @vbo: The buffer object. + * + * Return: Zero on success, negative error code otherwise. + */ +int vmw_validation_add_bo(struct vmw_validation_context *ctx, + struct vmw_bo *vbo) +{ + struct vmw_validation_bo_node *bo_node; + + bo_node = vmw_validation_find_bo_dup(ctx, vbo); + if (!bo_node) { + struct ttm_validate_buffer *val_buf; + + bo_node = vmw_validation_mem_alloc(ctx, sizeof(*bo_node)); + if (!bo_node) + return -ENOMEM; + + if (ctx->sw_context) { + bo_node->hash.key = (unsigned long) vbo; + hash_add_rcu(ctx->sw_context->res_ht, &bo_node->hash.head, + bo_node->hash.key); + } + val_buf = &bo_node->base; + val_buf->bo = ttm_bo_get_unless_zero(&vbo->tbo); + if (!val_buf->bo) + return -ESRCH; + val_buf->num_shared = 0; + list_add_tail(&val_buf->head, &ctx->bo_list); + } + + return 0; +} + +/** + * vmw_validation_add_resource - Add a resource to the validation context. + * @ctx: The validation context. + * @res: The resource. + * @priv_size: Size of private, additional metadata. + * @dirty: Whether to change dirty status. + * @p_node: Output pointer of additional metadata address. + * @first_usage: Whether this was the first time this resource was seen. + * + * Return: Zero on success, negative error code otherwise. + */ +int vmw_validation_add_resource(struct vmw_validation_context *ctx, + struct vmw_resource *res, + size_t priv_size, + u32 dirty, + void **p_node, + bool *first_usage) +{ + struct vmw_validation_res_node *node; + + node = vmw_validation_find_res_dup(ctx, res); + if (node) { + node->first_usage = 0; + goto out_fill; + } + + node = vmw_validation_mem_alloc(ctx, sizeof(*node) + priv_size); + if (!node) { + VMW_DEBUG_USER("Failed to allocate a resource validation entry.\n"); + return -ENOMEM; + } + + if (ctx->sw_context) { + node->hash.key = (unsigned long) res; + hash_add_rcu(ctx->sw_context->res_ht, &node->hash.head, node->hash.key); + } + node->res = vmw_resource_reference_unless_doomed(res); + if (!node->res) + return -ESRCH; + + node->first_usage = 1; + if (!res->dev_priv->has_mob) { + list_add_tail(&node->head, &ctx->resource_list); + } else { + switch (vmw_res_type(res)) { + case vmw_res_context: + case vmw_res_dx_context: + list_add(&node->head, &ctx->resource_ctx_list); + break; + case vmw_res_cotable: + list_add_tail(&node->head, &ctx->resource_ctx_list); + break; + default: + list_add_tail(&node->head, &ctx->resource_list); + break; + } + } + +out_fill: + if (dirty) { + node->dirty_set = 1; + /* Overwriting previous information here is intentional! */ + node->dirty = (dirty & VMW_RES_DIRTY_SET) ? 1 : 0; + } + if (first_usage) + *first_usage = node->first_usage; + if (p_node) + *p_node = &node->private; + + return 0; +} + +/** + * vmw_validation_res_set_dirty - Register a resource dirty set or clear during + * validation. + * @ctx: The validation context. + * @val_private: The additional meta-data pointer returned when the + * resource was registered with the validation context. Used to identify + * the resource. + * @dirty: Dirty information VMW_RES_DIRTY_XX + */ +void vmw_validation_res_set_dirty(struct vmw_validation_context *ctx, + void *val_private, u32 dirty) +{ + struct vmw_validation_res_node *val; + + if (!dirty) + return; + + val = container_of(val_private, typeof(*val), private); + val->dirty_set = 1; + /* Overwriting previous information here is intentional! */ + val->dirty = (dirty & VMW_RES_DIRTY_SET) ? 1 : 0; +} + +/** + * vmw_validation_res_switch_backup - Register a backup MOB switch during + * validation. + * @ctx: The validation context. + * @val_private: The additional meta-data pointer returned when the + * resource was registered with the validation context. Used to identify + * the resource. + * @vbo: The new backup buffer object MOB. This buffer object needs to have + * already been registered with the validation context. + * @guest_memory_offset: Offset into the new backup MOB. + */ +void vmw_validation_res_switch_backup(struct vmw_validation_context *ctx, + void *val_private, + struct vmw_bo *vbo, + unsigned long guest_memory_offset) +{ + struct vmw_validation_res_node *val; + + val = container_of(val_private, typeof(*val), private); + + val->switching_guest_memory_bo = 1; + if (val->first_usage) + val->no_buffer_needed = 1; + + val->new_guest_memory_bo = vbo; + val->new_guest_memory_offset = guest_memory_offset; +} + +/** + * vmw_validation_res_reserve - Reserve all resources registered with this + * validation context. + * @ctx: The validation context. + * @intr: Use interruptible waits when possible. + * + * Return: Zero on success, -ERESTARTSYS if interrupted. Negative error + * code on failure. + */ +int vmw_validation_res_reserve(struct vmw_validation_context *ctx, + bool intr) +{ + struct vmw_validation_res_node *val; + int ret = 0; + + list_splice_init(&ctx->resource_ctx_list, &ctx->resource_list); + + list_for_each_entry(val, &ctx->resource_list, head) { + struct vmw_resource *res = val->res; + + ret = vmw_resource_reserve(res, intr, val->no_buffer_needed); + if (ret) + goto out_unreserve; + + val->reserved = 1; + if (res->guest_memory_bo) { + struct vmw_bo *vbo = res->guest_memory_bo; + + vmw_bo_placement_set(vbo, + res->func->domain, + res->func->busy_domain); + ret = vmw_validation_add_bo(ctx, vbo); + if (ret) + goto out_unreserve; + } + + if (val->switching_guest_memory_bo && val->new_guest_memory_bo && + res->coherent) { + struct vmw_validation_bo_node *bo_node = + vmw_validation_find_bo_dup(ctx, + val->new_guest_memory_bo); + + if (WARN_ON(!bo_node)) { + ret = -EINVAL; + goto out_unreserve; + } + bo_node->coherent_count++; + } + } + + return 0; + +out_unreserve: + vmw_validation_res_unreserve(ctx, true); + return ret; +} + +/** + * vmw_validation_res_unreserve - Unreserve all reserved resources + * registered with this validation context. + * @ctx: The validation context. + * @backoff: Whether this is a backoff- of a commit-type operation. This + * is used to determine whether to switch backup MOBs or not. + */ +void vmw_validation_res_unreserve(struct vmw_validation_context *ctx, + bool backoff) +{ + struct vmw_validation_res_node *val; + + list_splice_init(&ctx->resource_ctx_list, &ctx->resource_list); + if (backoff) + list_for_each_entry(val, &ctx->resource_list, head) { + if (val->reserved) + vmw_resource_unreserve(val->res, + false, false, false, + NULL, 0); + } + else + list_for_each_entry(val, &ctx->resource_list, head) { + if (val->reserved) + vmw_resource_unreserve(val->res, + val->dirty_set, + val->dirty, + val->switching_guest_memory_bo, + val->new_guest_memory_bo, + val->new_guest_memory_offset); + } +} + +/** + * vmw_validation_bo_validate_single - Validate a single buffer object. + * @bo: The TTM buffer object base. + * @interruptible: Whether to perform waits interruptible if possible. + * + * Return: Zero on success, -ERESTARTSYS if interrupted. Negative error + * code on failure. + */ +static int vmw_validation_bo_validate_single(struct ttm_buffer_object *bo, + bool interruptible) +{ + struct vmw_bo *vbo = to_vmw_bo(&bo->base); + struct ttm_operation_ctx ctx = { + .interruptible = interruptible, + .no_wait_gpu = false + }; + int ret; + + if (atomic_read(&vbo->cpu_writers)) + return -EBUSY; + + if (vbo->tbo.pin_count > 0) + return 0; + + ret = ttm_bo_validate(bo, &vbo->placement, &ctx); + if (ret == 0 || ret == -ERESTARTSYS) + return ret; + + /* + * If that failed, try again, this time evicting + * previous contents. + */ + ctx.allow_res_evict = true; + + return ttm_bo_validate(bo, &vbo->placement, &ctx); +} + +/** + * vmw_validation_bo_validate - Validate all buffer objects registered with + * the validation context. + * @ctx: The validation context. + * @intr: Whether to perform waits interruptible if possible. + * + * Return: Zero on success, -ERESTARTSYS if interrupted, + * negative error code on failure. + */ +int vmw_validation_bo_validate(struct vmw_validation_context *ctx, bool intr) +{ + struct vmw_validation_bo_node *entry; + int ret; + + list_for_each_entry(entry, &ctx->bo_list, base.head) { + struct vmw_bo *vbo = to_vmw_bo(&entry->base.bo->base); + + ret = vmw_validation_bo_validate_single(entry->base.bo, intr); + + if (ret) + return ret; + + /* + * Rather than having the resource code allocating the bo + * dirty tracker in resource_unreserve() where we can't fail, + * Do it here when validating the buffer object. + */ + if (entry->coherent_count) { + unsigned int coherent_count = entry->coherent_count; + + while (coherent_count) { + ret = vmw_bo_dirty_add(vbo); + if (ret) + return ret; + + coherent_count--; + } + entry->coherent_count -= coherent_count; + } + + if (vbo->dirty) + vmw_bo_dirty_scan(vbo); + } + return 0; +} + +/** + * vmw_validation_res_validate - Validate all resources registered with the + * validation context. + * @ctx: The validation context. + * @intr: Whether to perform waits interruptible if possible. + * + * Before this function is called, all resource backup buffers must have + * been validated. + * + * Return: Zero on success, -ERESTARTSYS if interrupted, + * negative error code on failure. + */ +int vmw_validation_res_validate(struct vmw_validation_context *ctx, bool intr) +{ + struct vmw_validation_res_node *val; + int ret; + + list_for_each_entry(val, &ctx->resource_list, head) { + struct vmw_resource *res = val->res; + struct vmw_bo *backup = res->guest_memory_bo; + + ret = vmw_resource_validate(res, intr, val->dirty_set && + val->dirty); + if (ret) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Failed to validate resource.\n"); + return ret; + } + + /* Check if the resource switched backup buffer */ + if (backup && res->guest_memory_bo && backup != res->guest_memory_bo) { + struct vmw_bo *vbo = res->guest_memory_bo; + + vmw_bo_placement_set(vbo, res->func->domain, + res->func->busy_domain); + ret = vmw_validation_add_bo(ctx, vbo); + if (ret) + return ret; + } + } + return 0; +} + +/** + * vmw_validation_drop_ht - Reset the hash table used for duplicate finding + * and unregister it from this validation context. + * @ctx: The validation context. + * + * The hash table used for duplicate finding is an expensive resource and + * may be protected by mutexes that may cause deadlocks during resource + * unreferencing if held. After resource- and buffer object registering, + * there is no longer any use for this hash table, so allow freeing it + * either to shorten any mutex locking time, or before resources- and + * buffer objects are freed during validation context cleanup. + */ +void vmw_validation_drop_ht(struct vmw_validation_context *ctx) +{ + struct vmw_validation_bo_node *entry; + struct vmw_validation_res_node *val; + + if (!ctx->sw_context) + return; + + list_for_each_entry(entry, &ctx->bo_list, base.head) + hash_del_rcu(&entry->hash.head); + + list_for_each_entry(val, &ctx->resource_list, head) + hash_del_rcu(&val->hash.head); + + list_for_each_entry(val, &ctx->resource_ctx_list, head) + hash_del_rcu(&entry->hash.head); + + ctx->sw_context = NULL; +} + +/** + * vmw_validation_unref_lists - Unregister previously registered buffer + * object and resources. + * @ctx: The validation context. + * + * Note that this function may cause buffer object- and resource destructors + * to be invoked. + */ +void vmw_validation_unref_lists(struct vmw_validation_context *ctx) +{ + struct vmw_validation_bo_node *entry; + struct vmw_validation_res_node *val; + + list_for_each_entry(entry, &ctx->bo_list, base.head) { + ttm_bo_put(entry->base.bo); + entry->base.bo = NULL; + } + + list_splice_init(&ctx->resource_ctx_list, &ctx->resource_list); + list_for_each_entry(val, &ctx->resource_list, head) + vmw_resource_unreference(&val->res); + + /* + * No need to detach each list entry since they are all freed with + * vmw_validation_free_mem. Just make the inaccessible. + */ + INIT_LIST_HEAD(&ctx->bo_list); + INIT_LIST_HEAD(&ctx->resource_list); + + vmw_validation_mem_free(ctx); +} + +/** + * vmw_validation_prepare - Prepare a validation context for command + * submission. + * @ctx: The validation context. + * @mutex: The mutex used to protect resource reservation. + * @intr: Whether to perform waits interruptible if possible. + * + * Note that the single reservation mutex @mutex is an unfortunate + * construct. Ideally resource reservation should be moved to per-resource + * ww_mutexes. + * If this functions doesn't return Zero to indicate success, all resources + * are left unreserved but still referenced. + * Return: Zero on success, -ERESTARTSYS if interrupted, negative error code + * on error. + */ +int vmw_validation_prepare(struct vmw_validation_context *ctx, + struct mutex *mutex, + bool intr) +{ + int ret = 0; + + if (mutex) { + if (intr) + ret = mutex_lock_interruptible(mutex); + else + mutex_lock(mutex); + if (ret) + return -ERESTARTSYS; + } + + ctx->res_mutex = mutex; + ret = vmw_validation_res_reserve(ctx, intr); + if (ret) + goto out_no_res_reserve; + + ret = vmw_validation_bo_reserve(ctx, intr); + if (ret) + goto out_no_bo_reserve; + + ret = vmw_validation_bo_validate(ctx, intr); + if (ret) + goto out_no_validate; + + ret = vmw_validation_res_validate(ctx, intr); + if (ret) + goto out_no_validate; + + return 0; + +out_no_validate: + vmw_validation_bo_backoff(ctx); +out_no_bo_reserve: + vmw_validation_res_unreserve(ctx, true); +out_no_res_reserve: + if (mutex) + mutex_unlock(mutex); + + return ret; +} + +/** + * vmw_validation_revert - Revert validation actions if command submission + * failed. + * + * @ctx: The validation context. + * + * The caller still needs to unref resources after a call to this function. + */ +void vmw_validation_revert(struct vmw_validation_context *ctx) +{ + vmw_validation_bo_backoff(ctx); + vmw_validation_res_unreserve(ctx, true); + if (ctx->res_mutex) + mutex_unlock(ctx->res_mutex); + vmw_validation_unref_lists(ctx); +} + +/** + * vmw_validation_done - Commit validation actions after command submission + * success. + * @ctx: The validation context. + * @fence: Fence with which to fence all buffer objects taking part in the + * command submission. + * + * The caller does NOT need to unref resources after a call to this function. + */ +void vmw_validation_done(struct vmw_validation_context *ctx, + struct vmw_fence_obj *fence) +{ + vmw_validation_bo_fence(ctx, fence); + vmw_validation_res_unreserve(ctx, false); + if (ctx->res_mutex) + mutex_unlock(ctx->res_mutex); + vmw_validation_unref_lists(ctx); +} + +/** + * vmw_validation_preload_bo - Preload the validation memory allocator for a + * call to vmw_validation_add_bo(). + * @ctx: Pointer to the validation context. + * + * Iff this function returns successfully, the next call to + * vmw_validation_add_bo() is guaranteed not to sleep. An error is not fatal + * but voids the guarantee. + * + * Returns: Zero if successful, %-EINVAL otherwise. + */ +int vmw_validation_preload_bo(struct vmw_validation_context *ctx) +{ + unsigned int size = sizeof(struct vmw_validation_bo_node); + + if (!vmw_validation_mem_alloc(ctx, size)) + return -ENOMEM; + + ctx->mem_size_left += size; + return 0; +} + +/** + * vmw_validation_preload_res - Preload the validation memory allocator for a + * call to vmw_validation_add_res(). + * @ctx: Pointer to the validation context. + * @size: Size of the validation node extra data. See below. + * + * Iff this function returns successfully, the next call to + * vmw_validation_add_res() with the same or smaller @size is guaranteed not to + * sleep. An error is not fatal but voids the guarantee. + * + * Returns: Zero if successful, %-EINVAL otherwise. + */ +int vmw_validation_preload_res(struct vmw_validation_context *ctx, + unsigned int size) +{ + size = vmw_validation_align(sizeof(struct vmw_validation_res_node) + + size) + + vmw_validation_align(sizeof(struct vmw_validation_bo_node)); + if (!vmw_validation_mem_alloc(ctx, size)) + return -ENOMEM; + + ctx->mem_size_left += size; + return 0; +} + +/** + * vmw_validation_bo_backoff - Unreserve buffer objects registered with a + * validation context + * @ctx: The validation context + * + * This function unreserves the buffer objects previously reserved using + * vmw_validation_bo_reserve. It's typically used as part of an error path + */ +void vmw_validation_bo_backoff(struct vmw_validation_context *ctx) +{ + struct vmw_validation_bo_node *entry; + + /* + * Switching coherent resource backup buffers failed. + * Release corresponding buffer object dirty trackers. + */ + list_for_each_entry(entry, &ctx->bo_list, base.head) { + if (entry->coherent_count) { + unsigned int coherent_count = entry->coherent_count; + struct vmw_bo *vbo = to_vmw_bo(&entry->base.bo->base); + + while (coherent_count--) + vmw_bo_dirty_release(vbo); + } + } + + ttm_eu_backoff_reservation(&ctx->ticket, &ctx->bo_list); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h new file mode 100644 index 0000000000..240ee0c4eb --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/************************************************************************** + * + * Copyright © 2018 - 2022 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_VALIDATION_H_ +#define _VMWGFX_VALIDATION_H_ + +#include <linux/list.h> +#include <linux/hashtable.h> +#include <linux/ww_mutex.h> + +#include <drm/ttm/ttm_execbuf_util.h> + +#define VMW_RES_DIRTY_NONE 0 +#define VMW_RES_DIRTY_SET BIT(0) +#define VMW_RES_DIRTY_CLEAR BIT(1) + +/** + * struct vmw_validation_context - Per command submission validation context + * @ht: Hash table used to find resource- or buffer object duplicates + * @resource_list: List head for resource validation metadata + * @resource_ctx_list: List head for resource validation metadata for + * resources that need to be validated before those in @resource_list + * @bo_list: List head for buffer objects + * @page_list: List of pages used by the memory allocator + * @ticket: Ticked used for ww mutex locking + * @res_mutex: Pointer to mutex used for resource reserving + * @merge_dups: Whether to merge metadata for duplicate resources or + * buffer objects + * @mem_size_left: Free memory left in the last page in @page_list + * @page_address: Kernel virtual address of the last page in @page_list + * @vm: A pointer to the memory reservation interface or NULL if no + * memory reservation is needed. + * @vm_size_left: Amount of reserved memory that so far has not been allocated. + * @total_mem: Amount of reserved memory. + */ +struct vmw_validation_context { + struct vmw_sw_context *sw_context; + struct list_head resource_list; + struct list_head resource_ctx_list; + struct list_head bo_list; + struct list_head page_list; + struct ww_acquire_ctx ticket; + struct mutex *res_mutex; + unsigned int merge_dups; + unsigned int mem_size_left; + u8 *page_address; + struct vmw_validation_mem *vm; + size_t vm_size_left; + size_t total_mem; +}; + +struct vmw_bo; +struct vmw_resource; +struct vmw_fence_obj; + +#if 0 +/** + * DECLARE_VAL_CONTEXT - Declare a validation context with initialization + * @_name: The name of the variable + * @_sw_context: Contains the hash table used to find dups or NULL if none + * @_merge_dups: Whether to merge duplicate buffer object- or resource + * entries. If set to true, ideally a hash table pointer should be supplied + * as well unless the number of resources and buffer objects per validation + * is known to be very small + */ +#endif +#define DECLARE_VAL_CONTEXT(_name, _sw_context, _merge_dups) \ + struct vmw_validation_context _name = \ + { .sw_context = _sw_context, \ + .resource_list = LIST_HEAD_INIT((_name).resource_list), \ + .resource_ctx_list = LIST_HEAD_INIT((_name).resource_ctx_list), \ + .bo_list = LIST_HEAD_INIT((_name).bo_list), \ + .page_list = LIST_HEAD_INIT((_name).page_list), \ + .res_mutex = NULL, \ + .merge_dups = _merge_dups, \ + .mem_size_left = 0, \ + } + +/** + * vmw_validation_has_bos - return whether the validation context has + * any buffer objects registered. + * + * @ctx: The validation context + * Returns: Whether any buffer objects are registered + */ +static inline bool +vmw_validation_has_bos(struct vmw_validation_context *ctx) +{ + return !list_empty(&ctx->bo_list); +} + +/** + * vmw_validation_bo_reserve - Reserve buffer objects registered with a + * validation context + * @ctx: The validation context + * @intr: Perform waits interruptible + * + * Return: Zero on success, -ERESTARTSYS when interrupted, negative error + * code on failure + */ +static inline int +vmw_validation_bo_reserve(struct vmw_validation_context *ctx, + bool intr) +{ + return ttm_eu_reserve_buffers(&ctx->ticket, &ctx->bo_list, intr, + NULL); +} + +/** + * vmw_validation_bo_fence - Unreserve and fence buffer objects registered + * with a validation context + * @ctx: The validation context + * + * This function unreserves the buffer objects previously reserved using + * vmw_validation_bo_reserve, and fences them with a fence object. + */ +static inline void +vmw_validation_bo_fence(struct vmw_validation_context *ctx, + struct vmw_fence_obj *fence) +{ + ttm_eu_fence_buffer_objects(&ctx->ticket, &ctx->bo_list, + (void *) fence); +} + +/** + * vmw_validation_align - Align a validation memory allocation + * @val: The size to be aligned + * + * Returns: @val aligned to the granularity used by the validation memory + * allocator. + */ +static inline unsigned int vmw_validation_align(unsigned int val) +{ + return ALIGN(val, sizeof(long)); +} + +int vmw_validation_add_bo(struct vmw_validation_context *ctx, + struct vmw_bo *vbo); +int vmw_validation_bo_validate(struct vmw_validation_context *ctx, bool intr); +void vmw_validation_unref_lists(struct vmw_validation_context *ctx); +int vmw_validation_add_resource(struct vmw_validation_context *ctx, + struct vmw_resource *res, + size_t priv_size, + u32 dirty, + void **p_node, + bool *first_usage); +void vmw_validation_drop_ht(struct vmw_validation_context *ctx); +int vmw_validation_res_reserve(struct vmw_validation_context *ctx, + bool intr); +void vmw_validation_res_unreserve(struct vmw_validation_context *ctx, + bool backoff); +void vmw_validation_res_switch_backup(struct vmw_validation_context *ctx, + void *val_private, + struct vmw_bo *vbo, + unsigned long backup_offset); +int vmw_validation_res_validate(struct vmw_validation_context *ctx, bool intr); + +int vmw_validation_prepare(struct vmw_validation_context *ctx, + struct mutex *mutex, bool intr); +void vmw_validation_revert(struct vmw_validation_context *ctx); +void vmw_validation_done(struct vmw_validation_context *ctx, + struct vmw_fence_obj *fence); + +void *vmw_validation_mem_alloc(struct vmw_validation_context *ctx, + unsigned int size); +int vmw_validation_preload_bo(struct vmw_validation_context *ctx); +int vmw_validation_preload_res(struct vmw_validation_context *ctx, + unsigned int size); +void vmw_validation_res_set_dirty(struct vmw_validation_context *ctx, + void *val_private, u32 dirty); +void vmw_validation_bo_backoff(struct vmw_validation_context *ctx); + +#endif |