summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/vmwgfx
diff options
context:
space:
mode:
authorJinke Han <jinkehan@didiglobal.com>2026-09-08 15:37:42 +0800
committerIngo Molnar <mingo@kernel.org>2026-09-17 09:48:42 +0200
commita5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 (patch)
tree73fef824f9a846cf935869d0dbc5070ef6e2f741 /drivers/gpu/drm/vmwgfx
downloadlinux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.tar.gz
linux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.zip
x86/kprobes: Fix crash when probing CS CALL instructionsgrafted
When using eBPF to probe CS CALL instructions within a function, a crash can be triggered. The eBPF tool probes offset 257 of the __hrtimer_run_queues() function: <__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1) <__hrtimer_run_queues+254>: mov %r14,%rdi <__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12> <__hrtimer_run_queues+263>: mov %eax,%r12d <__hrtimer_run_queues+266>: xchg %ax,%ax <__hrtimer_run_queues+268>: mov %r13,%rdi Which triggers this crash: BUG: unable to handle page fault for address: 00000000000f41c9 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 0 P4D 0 Oops: 0002 [#1] SMP NOPTI CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P RIP: 0010:__hrtimer_run_queues+0x106/0x230 Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is at the 6th byte of the above CS CALL instruction. Since the CS CALL instruction occupies 6 bytes, the exception occurred in the middle of that call instruction. The root cause is that when using eBPF tools to probe in the middle of a function, a kprobe with INT3 is used as the underlying implementation. During single-step emulation of the original CALL instruction, int3_emulate_call() assumes that the probed CALL instruction is 5 bytes long. However, the actual CS-prefixed CALL instruction occupies 6 bytes, so it constructs an incorrect exception return address. When the CPU returns from the kprobe handler, the next instruction to be executed is at the address of the last byte of that CS CALL instruction. Coincidentally, starting from that address, the CPU fetches and decodes a completely different instruction, which ultimately triggers a kernel crash. Fix the issue by using the actual instruction length obtained from the instruction decoder when constructing the exception return address, rather than relying on the hardcoded CALL_INSN_SIZE macro. [ mingo: Refined the changelog ] Fixes: 6256e668b7af ("x86/kprobes: Use int3 instead of debug trap for single-step") Suggested-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Signed-off-by: Jinke Han <jinkehan@didiglobal.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Acked-by: Yafang Shao <laoar.shao@gmail.com> Acked-by: Borislav Petkov <bp@alien8.de> Cc: Peter Zijlstra <peterz@infradead.org> Link: https://patch.msgid.link/20260908073742.GA10517@didi-ThinkCentre-M920t-N000
Diffstat (limited to 'drivers/gpu/drm/vmwgfx')
-rw-r--r--drivers/gpu/drm/vmwgfx/Kconfig27
-rw-r--r--drivers/gpu/drm/vmwgfx/Makefile15
-rw-r--r--drivers/gpu/drm/vmwgfx/device_include/svga3d_cmd.h1513
-rw-r--r--drivers/gpu/drm/vmwgfx/device_include/svga3d_devcaps.h375
-rw-r--r--drivers/gpu/drm/vmwgfx/device_include/svga3d_dx.h1724
-rw-r--r--drivers/gpu/drm/vmwgfx/device_include/svga3d_limits.h87
-rw-r--r--drivers/gpu/drm/vmwgfx/device_include/svga3d_reg.h46
-rw-r--r--drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h1561
-rw-r--r--drivers/gpu/drm/vmwgfx/device_include/svga3d_types.h1555
-rw-r--r--drivers/gpu/drm/vmwgfx/device_include/svga_escape.h56
-rw-r--r--drivers/gpu/drm/vmwgfx/device_include/svga_overlay.h117
-rw-r--r--drivers/gpu/drm/vmwgfx/device_include/svga_reg.h901
-rw-r--r--drivers/gpu/drm/vmwgfx/device_include/vm_basic_types.h146
-rw-r--r--drivers/gpu/drm/vmwgfx/ttm_object.c670
-rw-r--r--drivers/gpu/drm/vmwgfx/ttm_object.h319
-rw-r--r--drivers/gpu/drm/vmwgfx/vmw_surface_cache.h545
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_binding.c1468
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_binding.h241
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_blit.c663
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_bo.c891
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_bo.h238
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c689
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c1410
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c316
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_context.c899
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c681
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c897
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h82
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.c143
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.h50
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c1672
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h1524
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c4531
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.c749
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.h110
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gem.c350
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c149
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c210
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c318
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_irq.c370
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c2028
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.h515
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c623
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_mksstat.h146
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_mob.c655
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_msg.c1137
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_msg_arm64.h204
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_msg_x86.h42
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c580
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c494
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_prime.c124
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_reg.h51
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource.c1161
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h154
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c1366
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_shader.c980
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_simple_resource.c231
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_so.c576
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_so.h172
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c1714
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_streamoutput.c368
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_surface.c2350
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_system_manager.c89
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c595
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_va.c170
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_validation.c849
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_validation.h190
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c604
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_vkms.h75
69 files changed, 46551 insertions, 0 deletions
diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig
new file mode 100644
index 000000000..aab646b91
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+config DRM_VMWGFX
+ tristate "DRM driver for VMware Virtual GPU"
+ depends on DRM && PCI
+ depends on (X86 && HYPERVISOR_GUEST) || ARM64
+ select DRM_CLIENT_SELECTION
+ 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 000000000..b168fd7fe
--- /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 vmwgfx_vkms.o vmwgfx_cursor_plane.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 000000000..d90d940ad
--- /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 000000000..815d0ab00
--- /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 000000000..925bf4b93
--- /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 000000000..6103b41fe
--- /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 000000000..b24b4f55c
--- /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 000000000..7d98fc484
--- /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 000000000..e9219eb38
--- /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 000000000..405f20fc2
--- /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 000000000..691f48f77
--- /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 000000000..acabdb550
--- /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 000000000..f84376718
--- /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 000000000..f9042bafd
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/ttm_object.c
@@ -0,0 +1,670 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/**************************************************************************
+ *
+ * Copyright (c) 2009-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.
+ *
+ **************************************************************************/
+/*
+ * 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.
+ *
+ * This is the per-device data structure needed for ttm object management.
+ */
+
+struct ttm_object_device {
+ spinlock_t object_lock;
+ 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_obj(*ref);
+ 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_obj(*tfile);
+
+ 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_obj(*tdev);
+
+ if (unlikely(tdev == NULL))
+ return NULL;
+
+ spin_lock_init(&tdev->object_lock);
+
+ /*
+ * 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 file_ref_get(&dmabuf->file->f_ref);
+}
+
+/**
+ * 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) {
+ ret = -ENOSYS;
+ goto out;
+ }
+
+ prime = (struct ttm_prime_object *) dma_buf->priv;
+ base = &prime->base;
+ *handle = base->handle;
+ ret = ttm_ref_object_add(tfile, base, NULL, false);
+
+out:
+ 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.
+ * @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,
+ enum ttm_object_type type,
+ void (*refcount_release) (struct ttm_base_object **))
+{
+ bool shareable = !!(type == VMW_RES_SURFACE);
+ 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 000000000..573e038c0
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/ttm_object.h
@@ -0,0 +1,319 @@
+/**************************************************************************
+ *
+ * Copyright (c) 2006-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.
+ *
+ **************************************************************************/
+/*
+ * 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,
+ 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 000000000..1ac3cb151
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmw_surface_cache.h
@@ -0,0 +1,545 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/**********************************************************
+ *
+ * Copyright (c) 2021-2024 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ * 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>
+
+#define SVGA3D_FLAGS_UPPER_32(svga3d_flags) ((svga3d_flags) >> 32)
+#define SVGA3D_FLAGS_LOWER_32(svga3d_flags) \
+ ((svga3d_flags) & ((uint64_t)U32_MAX))
+
+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 000000000..2731f6ded
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_binding.c
@@ -0,0 +1,1468 @@
+// 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"
+#include <linux/vmalloc.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 000000000..85b90f7d3
--- /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 000000000..56f965ec9
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
@@ -0,0 +1,663 @@
+// 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 "vmwgfx_bo.h"
+#include <linux/highmem.h>
+#include <linux/overflow.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;
+}
+
+static void *map_external(struct vmw_bo *bo, struct iosys_map *map)
+{
+ struct vmw_private *vmw =
+ container_of(bo->tbo.bdev, struct vmw_private, bdev);
+ void *ptr = NULL;
+ int ret;
+
+ if (drm_gem_is_imported(&bo->tbo.base)) {
+ ret = dma_buf_vmap(bo->tbo.base.dma_buf, map);
+ if (ret) {
+ drm_dbg_driver(&vmw->drm,
+ "Wasn't able to map external bo!\n");
+ goto out;
+ }
+ ptr = map->vaddr;
+ } else {
+ ptr = vmw_bo_map_and_cache(bo);
+ }
+
+out:
+ return ptr;
+}
+
+static void unmap_external(struct vmw_bo *bo, struct iosys_map *map)
+{
+ if (drm_gem_is_imported(&bo->tbo.base))
+ dma_buf_vunmap(bo->tbo.base.dma_buf, map);
+ else
+ vmw_bo_unmap(bo);
+}
+
+static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset,
+ u32 dst_stride, struct vmw_bo *src,
+ u32 src_offset, u32 src_stride,
+ u32 width_in_bytes, u32 height,
+ struct vmw_diff_cpy *diff)
+{
+ struct vmw_private *vmw =
+ container_of(dst->tbo.bdev, struct vmw_private, bdev);
+ size_t dst_size = dst->tbo.resource->size;
+ size_t src_size = src->tbo.resource->size;
+ size_t dst_end, src_end;
+ struct iosys_map dst_map = {0};
+ struct iosys_map src_map = {0};
+ bool dst_mapped = false;
+ bool src_mapped = false;
+ int ret, i;
+ int x_in_bytes;
+ u8 *vsrc;
+ u8 *vdst;
+
+ if (!height || !width_in_bytes)
+ return 0;
+
+ if (!dst_stride || !src_stride)
+ return -EINVAL;
+ if (dst_stride < width_in_bytes || src_stride < width_in_bytes)
+ return -EINVAL;
+ if (check_mul_overflow((size_t)dst_stride, (size_t)height - 1, &dst_end) ||
+ check_add_overflow(dst_end, (size_t)width_in_bytes, &dst_end) ||
+ check_add_overflow((size_t)dst_offset, dst_end, &dst_end) ||
+ dst_end > dst_size ||
+ check_mul_overflow((size_t)src_stride, (size_t)height - 1, &src_end) ||
+ check_add_overflow(src_end, (size_t)width_in_bytes, &src_end) ||
+ check_add_overflow((size_t)src_offset, src_end, &src_end) ||
+ src_end > src_size) {
+ drm_dbg_driver(&vmw->drm, "Out-of-bounds external BO copy\n");
+ return -EINVAL;
+ }
+
+ vsrc = map_external(src, &src_map);
+ if (!vsrc) {
+ drm_dbg_driver(&vmw->drm, "Wasn't able to map src\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ src_mapped = true;
+
+ vdst = map_external(dst, &dst_map);
+ if (!vdst) {
+ drm_dbg_driver(&vmw->drm, "Wasn't able to map dst\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ dst_mapped = true;
+
+ vsrc += src_offset;
+ vdst += dst_offset;
+ if (src_stride == dst_stride && width_in_bytes == dst_stride) {
+ memcpy(vdst, vsrc, dst_stride * (size_t)height);
+ } else {
+ for (i = 0; i < height; ++i) {
+ memcpy(vdst, vsrc, width_in_bytes);
+ vsrc += src_stride;
+ vdst += dst_stride;
+ }
+ }
+
+ x_in_bytes = (dst_offset % dst_stride);
+ diff->rect.x1 = x_in_bytes / diff->cpp;
+ diff->rect.y1 = ((dst_offset - x_in_bytes) / dst_stride);
+ diff->rect.x2 = diff->rect.x1 + width_in_bytes / diff->cpp;
+ diff->rect.y2 = diff->rect.y1 + height;
+
+ ret = 0;
+out:
+ if (src_mapped)
+ unmap_external(src, &src_map);
+ if (dst_mapped)
+ unmap_external(dst, &dst_map);
+
+ return ret;
+}
+
+/**
+ * vmw_bo_cpu_blit - in-kernel cpu blit.
+ *
+ * @vmw_dst: Destination buffer object.
+ * @dst_offset: Destination offset of blit start in bytes.
+ * @dst_stride: Destination stride in bytes.
+ * @vmw_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 vmw_bo *vmw_dst,
+ u32 dst_offset, u32 dst_stride,
+ struct vmw_bo *vmw_src,
+ u32 src_offset, u32 src_stride,
+ u32 w, u32 h,
+ struct vmw_diff_cpy *diff)
+{
+ struct ttm_buffer_object *src = &vmw_src->tbo;
+ struct ttm_buffer_object *dst = &vmw_dst->tbo;
+ 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 = {0};
+ int ret = 0;
+ struct page **dst_pages = NULL;
+ struct page **src_pages = NULL;
+ bool src_external = (src->ttm->page_flags & TTM_TT_FLAG_EXTERNAL) != 0;
+ bool dst_external = (dst->ttm->page_flags & TTM_TT_FLAG_EXTERNAL) != 0;
+
+ if (WARN_ON(dst == src))
+ return -EINVAL;
+
+ /* 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;
+ }
+
+ if (src_external || dst_external)
+ return vmw_external_bo_copy(vmw_dst, dst_offset, dst_stride,
+ vmw_src, src_offset, src_stride,
+ w, h, diff);
+
+ if (!src->ttm->pages && src->ttm->sg) {
+ src_pages = kvmalloc_objs(struct page *, src->ttm->num_pages);
+ if (!src_pages)
+ return -ENOMEM;
+ ret = drm_prime_sg_to_page_array(src->ttm->sg, src_pages,
+ src->ttm->num_pages);
+ if (ret)
+ goto out;
+ }
+ if (!dst->ttm->pages && dst->ttm->sg) {
+ dst_pages = kvmalloc_objs(struct page *, dst->ttm->num_pages);
+ if (!dst_pages) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = drm_prime_sg_to_page_array(dst->ttm->sg, dst_pages,
+ dst->ttm->num_pages);
+ if (ret)
+ goto out;
+ }
+
+ d.mapped_dst = 0;
+ d.mapped_src = 0;
+ d.dst_addr = NULL;
+ d.src_addr = NULL;
+ d.dst_pages = dst->ttm->pages ? dst->ttm->pages : dst_pages;
+ d.src_pages = src->ttm->pages ? src->ttm->pages : src_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);
+ kvfree(src_pages);
+ kvfree(dst_pages);
+
+ 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 000000000..9c7a73c0b
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
@@ -0,0 +1,891 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2011-2024 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ * 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 <drm/ttm/ttm_placement.h>
+
+/**
+ * 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_resource *res;
+ struct vmw_bo *vbo = to_vmw_bo(&bo->base);
+
+ WARN_ON(kref_read(&vbo->tbo.base.refcount) != 0);
+ vmw_bo_unmap(vbo);
+
+ xa_destroy(&vbo->detached_resources);
+ WARN_ON(vbo->is_dumb && !vbo->dumb_surface);
+ if (vbo->is_dumb && vbo->dumb_surface) {
+ res = &vbo->dumb_surface->res;
+ WARN_ON(vbo != res->guest_memory_bo);
+ WARN_ON(!res->guest_memory_bo);
+ if (res->guest_memory_bo) {
+ /* Reserve and switch the backing mob. */
+ mutex_lock(&res->dev_priv->cmdbuf_mutex);
+ (void)vmw_resource_reserve(res, false, true);
+ vmw_resource_mob_detach(res);
+ if (res->dirty)
+ res->func->dirty_free(res);
+ if (res->coherent)
+ vmw_bo_dirty_release(res->guest_memory_bo);
+ res->guest_memory_bo = NULL;
+ res->guest_memory_offset = 0;
+ vmw_resource_unreserve(res, true, false, false, NULL,
+ 0);
+ mutex_unlock(&res->dev_priv->cmdbuf_mutex);
+ }
+ vmw_surface_unreference(&vbo->dumb_surface);
+ }
+ WARN_ON(!RB_EMPTY_ROOT(&vbo->res_tree));
+ drm_gem_object_release(&vbo->tbo.base);
+ WARN_ON(vbo->dirty);
+ 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)
+{
+ return vmw_bo_map_and_cache_size(vbo, vbo->tbo.base.size);
+}
+
+void *vmw_bo_map_and_cache_size(struct vmw_bo *vbo, size_t size)
+{
+ struct ttm_buffer_object *bo = &vbo->tbo;
+ bool not_used;
+ void *virtual;
+ int ret;
+
+ atomic_inc(&vbo->map_count);
+
+ virtual = ttm_kmap_obj_virtual(&vbo->map, &not_used);
+ if (virtual)
+ return virtual;
+
+ ret = ttm_bo_kmap(bo, 0, PFN_UP(size), &vbo->map);
+ if (ret)
+ DRM_ERROR("Buffer object map failed: %d (size: bo = %zu, map = %zu).\n",
+ ret, bo->base.size, size);
+
+ return ttm_kmap_obj_virtual(&vbo->map, &not_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)
+{
+ int map_count;
+
+ if (vbo->map.bo == NULL)
+ return;
+
+ map_count = atomic_dec_return(&vbo->map_count);
+
+ if (!map_count) {
+ 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,
+ .resv = params->resv,
+ };
+ 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;
+ xa_init(&vmw_bo->detached_resources);
+ atomic_set(&vmw_bo->map_count, 0);
+
+ 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,
+ params->sg, params->resv, destroy);
+ if (unlikely(ret))
+ return ret;
+
+ if (params->pin)
+ ttm_bo_pin(&vmw_bo->tbo);
+ if (!params->keep_resv)
+ 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_obj(**p_bo);
+ 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;
+
+ (*p_bo)->tbo.base.funcs = &vmw_gem_object_funcs;
+ 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_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 placement_flags(u32 domain, u32 desired, u32 fallback)
+{
+ if (desired & fallback & domain)
+ return 0;
+
+ if (desired & domain)
+ return TTM_PL_FLAG_DESIRED;
+
+ return TTM_PL_FLAG_FALLBACK;
+}
+
+static u32
+set_placement_list(struct ttm_place *pl, u32 desired, u32 fallback)
+{
+ u32 domain = desired | fallback;
+ 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 = placement_flags(VMW_BO_DOMAIN_MOB, desired,
+ fallback);
+ 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 = placement_flags(VMW_BO_DOMAIN_GMR, desired,
+ fallback);
+ 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 = placement_flags(VMW_BO_DOMAIN_VRAM, desired,
+ fallback);
+ 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 = placement_flags(VMW_BO_DOMAIN_WAITABLE_SYS,
+ desired, fallback);
+ 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 = placement_flags(VMW_BO_DOMAIN_SYS, desired,
+ fallback);
+ 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, busy_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);
+ }
+
+}
+
+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);
+}
+
+int vmw_bo_add_detached_resource(struct vmw_bo *vbo, struct vmw_resource *res)
+{
+ return xa_err(xa_store(&vbo->detached_resources, (unsigned long)res, res, GFP_KERNEL));
+}
+
+void vmw_bo_del_detached_resource(struct vmw_bo *vbo, struct vmw_resource *res)
+{
+ xa_erase(&vbo->detached_resources, (unsigned long)res);
+}
+
+struct vmw_surface *vmw_bo_surface(struct vmw_bo *vbo)
+{
+ unsigned long index;
+ struct vmw_resource *res = NULL;
+ struct vmw_surface *surf = NULL;
+ struct rb_node *rb_itr = vbo->res_tree.rb_node;
+
+ if (vbo->is_dumb && vbo->dumb_surface) {
+ res = &vbo->dumb_surface->res;
+ goto out;
+ }
+
+ xa_for_each(&vbo->detached_resources, index, res) {
+ if (res->func->res_type == vmw_res_surface)
+ goto out;
+ }
+
+ for (rb_itr = rb_first(&vbo->res_tree); rb_itr;
+ rb_itr = rb_next(rb_itr)) {
+ res = rb_entry(rb_itr, struct vmw_resource, mob_node);
+ if (res->func->res_type == vmw_res_surface)
+ goto out;
+ }
+
+out:
+ if (res)
+ surf = vmw_res_to_srf(res);
+ return surf;
+}
+
+s32 vmw_bo_mobid(struct vmw_bo *vbo)
+{
+ WARN_ON(vbo->tbo.resource->mem_type != VMW_PL_MOB);
+ return (s32)vbo->tbo.resource->start;
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h
new file mode 100644
index 000000000..cf84a163b
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.h
@@ -0,0 +1,238 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/**************************************************************************
+ *
+ * Copyright (c) 2023-2024 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ * 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>
+#include <linux/xarray.h>
+
+struct vmw_bo_dirty;
+struct vmw_fence_obj;
+struct vmw_private;
+struct vmw_resource;
+struct vmw_surface;
+
+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;
+ bool pin;
+ bool keep_resv;
+ size_t size;
+ struct dma_resv *resv;
+ struct sg_table *sg;
+};
+
+/**
+ * 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
+ * @map_count: The number of currently active maps. Will differ from the
+ * cpu_writers because it includes kernel maps.
+ * @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];
+
+ /* Protected by reservation */
+ struct ttm_bo_kmap_obj map;
+
+ struct rb_root res_tree;
+ u32 res_prios[TTM_MAX_BO_PRIORITY];
+ struct xarray detached_resources;
+
+ atomic_t map_count;
+ atomic_t cpu_writers;
+ /* Not ref-counted. Protected by binding_mutex */
+ struct vmw_resource *dx_query_ctx;
+ struct vmw_bo_dirty *dirty;
+
+ bool is_dumb;
+ struct vmw_surface *dumb_surface;
+};
+
+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_map_and_cache_size(struct vmw_bo *vbo, size_t size);
+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_bo_add_detached_resource(struct vmw_bo *vbo, struct vmw_resource *res);
+void vmw_bo_del_detached_resource(struct vmw_bo *vbo, struct vmw_resource *res);
+struct vmw_surface *vmw_bo_surface(struct vmw_bo *vbo);
+
+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)
+ drm_gem_object_put(&tmp_buf->tbo.base);
+}
+
+static inline struct vmw_bo *vmw_bo_reference(struct vmw_bo *buf)
+{
+ drm_gem_object_get(&buf->tbo.base);
+ 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);
+}
+
+s32 vmw_bo_mobid(struct vmw_bo *vbo);
+
+#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 000000000..d932e38d7
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c
@@ -0,0 +1,689 @@
+// 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>
+#include <linux/vmalloc.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_obj(*fifo);
+ 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_fences_update(dev_priv->fman);
+
+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 000000000..f45d93e8c
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c
@@ -0,0 +1,1410 @@
+// 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.
+ * @id: Monotonically increasing ID of the last cmdbuf submitted.
+ * @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;
+ u64 id;
+ 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;
+
+ header->cb_header->id = man->id++;
+
+ 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, &notempty);
+
+ 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_obj(*header);
+ 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_obj(*man);
+ 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 000000000..fe1ba1a61
--- /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_obj(*cres);
+ 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_obj(*man);
+ 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 000000000..bd96f4d5e
--- /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_obj(*ctx);
+ 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 000000000..091f1039a
--- /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_bo_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_obj(*vcotbl);
+ 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_cursor_plane.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
new file mode 100644
index 000000000..d1e7df500
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
@@ -0,0 +1,897 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2024-2025 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ **************************************************************************/
+#include "vmwgfx_cursor_plane.h"
+
+#include "vmwgfx_bo.h"
+#include "vmwgfx_drv.h"
+#include "vmwgfx_kms.h"
+#include "vmwgfx_resource_priv.h"
+#include "vmw_surface_cache.h"
+
+#include "drm/drm_atomic.h"
+#include "drm/drm_atomic_helper.h"
+#include "drm/drm_plane.h"
+#include <asm/page.h>
+
+#define VMW_CURSOR_SNOOP_FORMAT SVGA3D_A8R8G8B8
+#define VMW_CURSOR_SNOOP_WIDTH 64
+#define VMW_CURSOR_SNOOP_HEIGHT 64
+
+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);
+}
+
+static void
+vmw_cursor_plane_update_legacy(struct vmw_private *vmw,
+ struct vmw_plane_state *vps)
+{
+ struct vmw_surface *surface = vmw_user_object_surface(&vps->uo);
+ s32 hotspot_x = vps->cursor.legacy.hotspot_x + vps->base.hotspot_x;
+ s32 hotspot_y = vps->cursor.legacy.hotspot_y + vps->base.hotspot_y;
+
+ if (WARN_ON(!surface || !surface->snooper.image))
+ return;
+
+ if (vps->cursor.legacy.id != surface->snooper.id) {
+ vmw_send_define_cursor_cmd(vmw, surface->snooper.image,
+ vps->base.crtc_w, vps->base.crtc_h,
+ hotspot_x, hotspot_y);
+ vps->cursor.legacy.id = surface->snooper.id;
+ }
+}
+
+static enum vmw_cursor_update_type
+vmw_cursor_update_type(struct vmw_private *vmw, struct vmw_plane_state *vps)
+{
+ struct vmw_surface *surface = vmw_user_object_surface(&vps->uo);
+
+ if (surface && surface->snooper.image)
+ return VMW_CURSOR_UPDATE_LEGACY;
+
+ if (vmw->has_mob) {
+ if ((vmw->capabilities2 & SVGA_CAP2_CURSOR_MOB) != 0)
+ return VMW_CURSOR_UPDATE_MOB;
+ else
+ return VMW_CURSOR_UPDATE_GB_ONLY;
+ }
+ drm_warn_once(&vmw->drm, "Unknown Cursor Type!\n");
+ return VMW_CURSOR_UPDATE_NONE;
+}
+
+static void vmw_cursor_update_mob(struct vmw_private *vmw,
+ struct vmw_plane_state *vps)
+{
+ SVGAGBCursorHeader *header;
+ SVGAGBAlphaCursorHeader *alpha_header;
+ struct vmw_bo *bo = vmw_user_object_buffer(&vps->uo);
+ u32 *image = vmw_bo_map_and_cache(bo);
+ const u32 image_size = vps->base.crtc_w * vps->base.crtc_h * sizeof(*image);
+
+ header = vmw_bo_map_and_cache(vps->cursor.mob);
+ alpha_header = &header->header.alphaHeader;
+
+ memset(header, 0, sizeof(*header));
+
+ header->type = SVGA_ALPHA_CURSOR;
+ header->sizeInBytes = image_size;
+
+ alpha_header->hotspotX = vps->cursor.legacy.hotspot_x + vps->base.hotspot_x;
+ alpha_header->hotspotY = vps->cursor.legacy.hotspot_y + vps->base.hotspot_y;
+ alpha_header->width = vps->base.crtc_w;
+ alpha_header->height = vps->base.crtc_h;
+
+ memcpy(header + 1, image, image_size);
+ vmw_write(vmw, SVGA_REG_CURSOR_MOBID, vmw_bo_mobid(vps->cursor.mob));
+
+ vmw_bo_unmap(bo);
+ vmw_bo_unmap(vps->cursor.mob);
+}
+
+static u32 vmw_cursor_mob_size(enum vmw_cursor_update_type update_type,
+ u32 w, u32 h)
+{
+ switch (update_type) {
+ case VMW_CURSOR_UPDATE_LEGACY:
+ case VMW_CURSOR_UPDATE_GB_ONLY:
+ case VMW_CURSOR_UPDATE_NONE:
+ return 0;
+ case VMW_CURSOR_UPDATE_MOB:
+ return w * h * sizeof(u32) + sizeof(SVGAGBCursorHeader);
+ }
+ return 0;
+}
+
+static void vmw_cursor_mob_destroy(struct vmw_bo **vbo)
+{
+ if (!(*vbo))
+ return;
+
+ ttm_bo_unpin(&(*vbo)->tbo);
+ vmw_bo_unreference(vbo);
+}
+
+/**
+ * vmw_cursor_mob_unmap - Unmaps the cursor mobs.
+ *
+ * @vps: state of the cursor plane
+ *
+ * Returns 0 on success
+ */
+
+static int
+vmw_cursor_mob_unmap(struct vmw_plane_state *vps)
+{
+ int ret = 0;
+ struct vmw_bo *vbo = vps->cursor.mob;
+
+ 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;
+}
+
+static void vmw_cursor_mob_put(struct vmw_cursor_plane *vcp,
+ struct vmw_plane_state *vps)
+{
+ u32 i;
+
+ if (!vps->cursor.mob)
+ return;
+
+ vmw_cursor_mob_unmap(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.mob;
+ vps->cursor.mob = 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.mob->tbo.base.size) {
+ vmw_cursor_mob_destroy(&vcp->cursor_mobs[i]);
+ vcp->cursor_mobs[i] = vps->cursor.mob;
+ vps->cursor.mob = NULL;
+ return;
+ }
+ }
+
+ /* Destroy it if it's not worth caching. */
+ vmw_cursor_mob_destroy(&vps->cursor.mob);
+}
+
+static int vmw_cursor_mob_get(struct vmw_cursor_plane *vcp,
+ struct vmw_plane_state *vps)
+{
+ struct vmw_private *dev_priv = vmw_priv(vcp->base.dev);
+ u32 size = vmw_cursor_mob_size(vps->cursor.update_type,
+ vps->base.crtc_w, vps->base.crtc_h);
+ u32 i;
+ u32 cursor_max_dim, mob_max_size;
+ struct vmw_fence_obj *fence = NULL;
+ 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.mob) {
+ if (vps->cursor.mob->tbo.base.size >= size)
+ return 0;
+ vmw_cursor_mob_put(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.mob = 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.mob);
+
+ if (ret != 0)
+ return ret;
+
+ /* Fence the mob creation so we are guarateed to have the mob */
+ ret = ttm_bo_reserve(&vps->cursor.mob->tbo, false, false, NULL);
+ if (ret != 0)
+ goto teardown;
+
+ ret = vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL);
+ if (ret != 0) {
+ ttm_bo_unreserve(&vps->cursor.mob->tbo);
+ goto teardown;
+ }
+
+ dma_fence_wait(&fence->base, false);
+ dma_fence_put(&fence->base);
+
+ ttm_bo_unreserve(&vps->cursor.mob->tbo);
+
+ return 0;
+
+teardown:
+ vmw_cursor_mob_destroy(&vps->cursor.mob);
+ return ret;
+}
+
+static void vmw_cursor_update_position(struct vmw_private *dev_priv,
+ bool show, int x, int y)
+{
+ const u32 svga_cursor_on = show ? SVGA_CURSOR_ON_SHOW
+ : SVGA_CURSOR_ON_HIDE;
+ u32 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;
+ u32 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.id++;
+
+ ttm_bo_kunmap(&map);
+err_unreserve:
+ ttm_bo_unreserve(bo);
+}
+
+void vmw_cursor_plane_destroy(struct drm_plane *plane)
+{
+ struct vmw_cursor_plane *vcp = vmw_plane_to_vcp(plane);
+ u32 i;
+
+ vmw_cursor_update_position(vmw_priv(plane->dev), false, 0, 0);
+
+ for (i = 0; i < ARRAY_SIZE(vcp->cursor_mobs); i++)
+ vmw_cursor_mob_destroy(&vcp->cursor_mobs[i]);
+
+ drm_plane_cleanup(plane);
+}
+
+/**
+ * vmw_cursor_mob_map - Maps the cursor mobs.
+ *
+ * @vps: plane_state
+ *
+ * Returns 0 on success
+ */
+
+static int
+vmw_cursor_mob_map(struct vmw_plane_state *vps)
+{
+ int ret;
+ u32 size = vmw_cursor_mob_size(vps->cursor.update_type,
+ vps->base.crtc_w, vps->base.crtc_h);
+ struct vmw_bo *vbo = vps->cursor.mob;
+ void *map;
+
+ if (!vbo)
+ return -EINVAL;
+
+ if (vbo->tbo.base.size < size)
+ return -EINVAL;
+
+ if (vbo->map.virtual)
+ return 0;
+
+ ret = ttm_bo_reserve(&vbo->tbo, false, false, NULL);
+ if (unlikely(ret != 0))
+ return -ENOMEM;
+
+ map = vmw_bo_map_and_cache(vbo);
+ if (!map) {
+ vmw_bo_unmap(vbo);
+ ret = -ENOMEM;
+ }
+
+ ttm_bo_unreserve(&vbo->tbo);
+
+ return ret;
+}
+
+/**
+ * vmw_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_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);
+
+ if (!vmw_user_object_is_null(&vps->uo))
+ vmw_user_object_unmap(&vps->uo);
+
+ vmw_cursor_mob_unmap(vps);
+ vmw_cursor_mob_put(vcp, vps);
+
+ vmw_du_plane_unpin_surf(vps);
+ vmw_user_object_unref(&vps->uo);
+}
+
+static bool
+vmw_cursor_buffer_changed(struct vmw_plane_state *new_vps,
+ struct vmw_plane_state *old_vps)
+{
+ struct vmw_bo *new_bo = vmw_user_object_buffer(&new_vps->uo);
+ struct vmw_bo *old_bo = vmw_user_object_buffer(&old_vps->uo);
+ struct vmw_surface *surf;
+ bool dirty = false;
+ int ret;
+
+ if (new_bo != old_bo)
+ return true;
+
+ if (new_bo) {
+ if (!old_bo) {
+ return true;
+ } else if (new_bo->dirty) {
+ vmw_bo_dirty_scan(new_bo);
+ dirty = vmw_bo_is_dirty(new_bo);
+ if (dirty) {
+ surf = vmw_user_object_surface(&new_vps->uo);
+ if (surf)
+ vmw_bo_dirty_transfer_to_res(&surf->res);
+ else
+ vmw_bo_dirty_clear(new_bo);
+ }
+ return dirty;
+ } else if (new_bo != old_bo) {
+ /*
+ * Currently unused because the top exits right away.
+ * In most cases buffer being different will mean
+ * that the contents is different. For the few percent
+ * of cases where that's not true the cost of doing
+ * the memcmp on all other seems to outweight the
+ * benefits. Leave the conditional to be able to
+ * trivially validate it by removing the initial
+ * if (new_bo != old_bo) at the start.
+ */
+ void *old_image;
+ void *new_image;
+ bool changed = false;
+ struct ww_acquire_ctx ctx;
+ const u32 size = new_vps->base.crtc_w *
+ new_vps->base.crtc_h * sizeof(u32);
+
+ ww_acquire_init(&ctx, &reservation_ww_class);
+
+ ret = ttm_bo_reserve(&old_bo->tbo, false, false, &ctx);
+ if (ret != 0) {
+ ww_acquire_fini(&ctx);
+ return true;
+ }
+
+ ret = ttm_bo_reserve(&new_bo->tbo, false, false, &ctx);
+ if (ret != 0) {
+ ttm_bo_unreserve(&old_bo->tbo);
+ ww_acquire_fini(&ctx);
+ return true;
+ }
+
+ old_image = vmw_bo_map_and_cache(old_bo);
+ new_image = vmw_bo_map_and_cache(new_bo);
+
+ if (old_image && new_image && old_image != new_image)
+ changed = memcmp(old_image, new_image, size) !=
+ 0;
+
+ ttm_bo_unreserve(&new_bo->tbo);
+ ttm_bo_unreserve(&old_bo->tbo);
+
+ ww_acquire_fini(&ctx);
+
+ return changed;
+ }
+ return false;
+ }
+
+ return false;
+}
+
+static bool
+vmw_cursor_plane_changed(struct vmw_plane_state *new_vps,
+ struct vmw_plane_state *old_vps)
+{
+ 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->base.hotspot_x != new_vps->base.hotspot_x ||
+ old_vps->base.hotspot_y != new_vps->base.hotspot_y)
+ return true;
+
+ if (old_vps->cursor.legacy.hotspot_x !=
+ new_vps->cursor.legacy.hotspot_x ||
+ old_vps->cursor.legacy.hotspot_y !=
+ new_vps->cursor.legacy.hotspot_y)
+ return true;
+
+ if (old_vps->base.fb != new_vps->base.fb)
+ return true;
+
+ return false;
+}
+
+/**
+ * vmw_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_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);
+ struct vmw_plane_state *old_vps = vmw_plane_state_to_vps(plane->state);
+ struct vmw_private *vmw = vmw_priv(plane->dev);
+ struct vmw_bo *bo = NULL;
+ struct vmw_surface *surface;
+ int ret = 0;
+
+ if (!vmw_user_object_is_null(&vps->uo)) {
+ vmw_user_object_unmap(&vps->uo);
+ vmw_user_object_unref(&vps->uo);
+ }
+
+ if (fb) {
+ if (vmw_framebuffer_to_vfb(fb)->bo) {
+ vps->uo.buffer = vmw_framebuffer_to_vfbd(fb)->buffer;
+ vps->uo.surface = NULL;
+ } else {
+ memcpy(&vps->uo, &vmw_framebuffer_to_vfbs(fb)->uo, sizeof(vps->uo));
+ }
+ vmw_user_object_ref(&vps->uo);
+ }
+
+ vps->cursor.update_type = vmw_cursor_update_type(vmw, vps);
+ switch (vps->cursor.update_type) {
+ case VMW_CURSOR_UPDATE_LEGACY:
+ surface = vmw_user_object_surface(&vps->uo);
+ if (!surface || vps->cursor.legacy.id == surface->snooper.id)
+ vps->cursor.update_type = VMW_CURSOR_UPDATE_NONE;
+ break;
+ case VMW_CURSOR_UPDATE_GB_ONLY:
+ case VMW_CURSOR_UPDATE_MOB: {
+ bo = vmw_user_object_buffer(&vps->uo);
+ if (bo) {
+ struct ttm_operation_ctx ctx = { false, false };
+
+ ret = ttm_bo_reserve(&bo->tbo, true, false, NULL);
+ if (ret != 0)
+ return -ENOMEM;
+
+ ret = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx);
+ if (ret != 0)
+ return -ENOMEM;
+
+ /*
+ * vmw_bo_pin_reserved also validates, so to skip
+ * the extra validation use ttm_bo_pin directly
+ */
+ if (!bo->tbo.pin_count)
+ ttm_bo_pin(&bo->tbo);
+
+ if (vmw_framebuffer_to_vfb(fb)->bo) {
+ const u32 size = new_state->crtc_w *
+ new_state->crtc_h *
+ sizeof(u32);
+
+ (void)vmw_bo_map_and_cache_size(bo, size);
+ } else {
+ vmw_bo_map_and_cache(bo);
+ }
+ ttm_bo_unreserve(&bo->tbo);
+ }
+ if (!vmw_user_object_is_null(&vps->uo)) {
+ if (!vmw_cursor_plane_changed(vps, old_vps) &&
+ !vmw_cursor_buffer_changed(vps, old_vps)) {
+ vps->cursor.update_type =
+ VMW_CURSOR_UPDATE_NONE;
+ } else if (vps->cursor.update_type ==
+ VMW_CURSOR_UPDATE_MOB &&
+ (vmw_cursor_mob_get(vcp, vps) ||
+ vmw_cursor_mob_map(vps))) {
+ /*
+ * Reset the cursor to avoid crashes later.
+ */
+ vps->cursor.update_type =
+ VMW_CURSOR_UPDATE_NONE;
+ }
+ }
+ }
+ break;
+ case VMW_CURSOR_UPDATE_NONE:
+ /* do nothing */
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * vmw_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_cursor_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_plane_state *new_state =
+ drm_atomic_get_new_plane_state(state, plane);
+ struct vmw_private *vmw = vmw_priv(plane->dev);
+ int ret = 0;
+ struct drm_crtc_state *crtc_state = NULL;
+ struct vmw_surface *surface = NULL;
+ struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
+ enum vmw_cursor_update_type update_type;
+ 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;
+
+ update_type = vmw_cursor_update_type(vmw, vps);
+ if (update_type == VMW_CURSOR_UPDATE_LEGACY) {
+ if (new_state->crtc_w != VMW_CURSOR_SNOOP_WIDTH ||
+ new_state->crtc_h != VMW_CURSOR_SNOOP_HEIGHT) {
+ drm_warn(&vmw->drm,
+ "Invalid cursor dimensions (%d, %d)\n",
+ new_state->crtc_w, new_state->crtc_h);
+ return -EINVAL;
+ }
+ surface = vmw_user_object_surface(&vps->uo);
+ if (!surface || !surface->snooper.image) {
+ drm_warn(&vmw->drm,
+ "surface not suitable for cursor\n");
+ return -EINVAL;
+ }
+ } else if (update_type == VMW_CURSOR_UPDATE_GB_ONLY ||
+ update_type == VMW_CURSOR_UPDATE_MOB) {
+ u32 cursor_max_dim =
+ vmw_read(vmw, SVGA_REG_CURSOR_MAX_DIMENSION);
+
+ if (new_state->crtc_w > cursor_max_dim ||
+ new_state->crtc_h > cursor_max_dim) {
+ drm_warn(&vmw->drm,
+ "Cursor dimensions (%d, %d) exceed device max %u\n",
+ new_state->crtc_w, new_state->crtc_h,
+ cursor_max_dim);
+ return -EINVAL;
+ }
+
+ if (update_type == VMW_CURSOR_UPDATE_MOB) {
+ u32 mob_max_size =
+ vmw_read(vmw, SVGA_REG_MOB_MAX_SIZE);
+ u64 mob_size = (u64)new_state->crtc_w *
+ new_state->crtc_h * sizeof(u32) +
+ sizeof(SVGAGBCursorHeader);
+
+ if (mob_size > mob_max_size) {
+ drm_warn(&vmw->drm,
+ "Cursor MOB size %llu exceeds device max %u\n",
+ mob_size, mob_max_size);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void
+vmw_cursor_plane_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct vmw_bo *bo;
+ 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(plane->dev);
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+ struct vmw_plane_state *vps = vmw_plane_state_to_vps(new_state);
+ s32 hotspot_x, hotspot_y, cursor_x, cursor_y;
+
+ /*
+ * Hide the cursor if the new bo is null
+ */
+ if (vmw_user_object_is_null(&vps->uo)) {
+ vmw_cursor_update_position(dev_priv, false, 0, 0);
+ return;
+ }
+
+ switch (vps->cursor.update_type) {
+ case VMW_CURSOR_UPDATE_LEGACY:
+ vmw_cursor_plane_update_legacy(dev_priv, vps);
+ break;
+ case VMW_CURSOR_UPDATE_MOB:
+ vmw_cursor_update_mob(dev_priv, vps);
+ break;
+ case VMW_CURSOR_UPDATE_GB_ONLY:
+ bo = vmw_user_object_buffer(&vps->uo);
+ if (bo)
+ vmw_send_define_cursor_cmd(dev_priv, bo->map.virtual,
+ vps->base.crtc_w,
+ vps->base.crtc_h,
+ vps->base.hotspot_x,
+ vps->base.hotspot_y);
+ break;
+ case VMW_CURSOR_UPDATE_NONE:
+ /* do nothing */
+ break;
+ }
+
+ /*
+ * For all update types update the cursor position
+ */
+ cursor_x = new_state->crtc_x + du->set_gui_x;
+ cursor_y = new_state->crtc_y + du->set_gui_y;
+
+ hotspot_x = vps->cursor.legacy.hotspot_x + new_state->hotspot_x;
+ hotspot_y = vps->cursor.legacy.hotspot_y + new_state->hotspot_y;
+
+ vmw_cursor_update_position(dev_priv, true, cursor_x + hotspot_x,
+ cursor_y + hotspot_y);
+}
+
+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 vmw_plane_state *vps;
+ 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);
+ vps = vmw_plane_state_to_vps(du->cursor.base.state);
+ vps->cursor.legacy.hotspot_x = arg->xhot;
+ vps->cursor.legacy.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);
+ vps = vmw_plane_state_to_vps(du->cursor.base.state);
+ vps->cursor.legacy.hotspot_x = arg->xhot;
+ vps->cursor.legacy.hotspot_y = arg->yhot;
+
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return ret;
+}
+
+void *vmw_cursor_snooper_create(struct drm_file *file_priv,
+ struct vmw_surface_metadata *metadata)
+{
+ if (!file_priv->atomic && 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;
+ void *image = kzalloc(cursor_size_bytes, GFP_KERNEL);
+
+ if (!image) {
+ DRM_ERROR("Failed to allocate cursor_image\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ return image;
+ }
+ return NULL;
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h
new file mode 100644
index 000000000..d32cf17d0
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/**************************************************************************
+ *
+ * Copyright (c) 2024-2025 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ **************************************************************************/
+
+#ifndef VMWGFX_CURSOR_PLANE_H
+#define VMWGFX_CURSOR_PLANE_H
+
+#include "device_include/svga3d_cmd.h"
+#include "drm/drm_file.h"
+#include "drm/drm_fourcc.h"
+#include "drm/drm_plane.h"
+
+#include <linux/types.h>
+
+struct SVGA3dCmdHeader;
+struct ttm_buffer_object;
+struct vmw_bo;
+struct vmw_cursor;
+struct vmw_private;
+struct vmw_surface;
+struct vmw_user_object;
+
+#define vmw_plane_to_vcp(x) container_of(x, struct vmw_cursor_plane, base)
+
+static const u32 __maybe_unused vmw_cursor_plane_formats[] = {
+ DRM_FORMAT_ARGB8888,
+};
+
+enum vmw_cursor_update_type {
+ VMW_CURSOR_UPDATE_NONE = 0,
+ VMW_CURSOR_UPDATE_LEGACY,
+ VMW_CURSOR_UPDATE_GB_ONLY,
+ VMW_CURSOR_UPDATE_MOB,
+};
+
+struct vmw_cursor_plane_state {
+ enum vmw_cursor_update_type update_type;
+ bool changed;
+ bool surface_changed;
+ struct vmw_bo *mob;
+ struct {
+ s32 hotspot_x;
+ s32 hotspot_y;
+ u32 id;
+ } legacy;
+};
+
+/**
+ * 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];
+};
+
+struct vmw_surface_metadata;
+void *vmw_cursor_snooper_create(struct drm_file *file_priv,
+ struct vmw_surface_metadata *metadata);
+void vmw_cursor_cmd_dma_snoop(SVGA3dCmdHeader *header,
+ struct vmw_surface *srf,
+ struct ttm_buffer_object *bo);
+
+void vmw_cursor_plane_destroy(struct drm_plane *plane);
+
+int vmw_cursor_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state);
+void vmw_cursor_plane_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *state);
+int vmw_cursor_plane_prepare_fb(struct drm_plane *plane,
+ struct drm_plane_state *new_state);
+void vmw_cursor_plane_cleanup_fb(struct drm_plane *plane,
+ struct drm_plane_state *old_state);
+
+#endif /* VMWGFX_CURSOR_H */
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.c b/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.c
new file mode 100644
index 000000000..6e6beff9e
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.c
@@ -0,0 +1,143 @@
+/* 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 <linux/vmalloc.h>
+#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 000000000..f70e923ac
--- /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 000000000..0f101aedb
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -0,0 +1,1672 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2009-2025 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ **************************************************************************/
+
+#include "vmwgfx_drv.h"
+
+#include "vmwgfx_bo.h"
+#include "vmwgfx_binding.h"
+#include "vmwgfx_devcaps.h"
+#include "vmwgfx_mksstat.h"
+#include "vmwgfx_vkms.h"
+#include "ttm_object.h"
+
+#include <drm/clients/drm_client_setup.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fbdev_ttm.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/aperture.h>
+#include <linux/cc_platform.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+#include <linux/vmalloc.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,
+ .keep_resv = 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_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);
+ }
+
+ u32 seqno = vmw_fence_read(dev_priv);
+
+ atomic_set(&dev_priv->last_read_seqno, seqno);
+ atomic_set(&dev_priv->marker_seq, 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)
+ ;
+
+ atomic_set(&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."};
+
+ /*
+ * When running with SEV we always want dma mappings, because
+ * otherwise ttm tt pool pages will bounce through swiotlb running
+ * out of available space.
+ */
+ if (vmw_force_coherent || cc_platform_has(CC_ATTR_MEM_ENCRYPT))
+ 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 = pcim_request_all_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");
+ 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 | MEMREMAP_DEC);
+
+ if (IS_ERR(dev->fifo_mem)) {
+ drm_err(&dev->drm,
+ "Failed mapping FIFO memory.\n");
+ return PTR_ERR(dev->fifo_mem);
+ }
+ } else {
+ 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;
+
+ 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)
+ return ret;
+
+
+ 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.");
+ }
+
+ vmw_vkms_init(dev_priv);
+
+ 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);
+
+ 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 KiB, FIFO = %llu KiB, surface = %u KiB\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 KiB, 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) ?
+ TTM_ALLOCATION_POOL_USE_DMA_ALLOC : 0);
+ 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);
+
+ return ret;
+}
+
+static void vmw_driver_unload(struct drm_device *dev)
+{
+ struct vmw_private *dev_priv = vmw_priv(dev);
+ enum vmw_res_type i;
+
+ unregister_pm_notifier(&dev_priv->pm_nb);
+
+ vmw_sw_context_fini(dev_priv);
+ vmw_fifo_resource_dec(dev_priv);
+
+ vmw_svga_disable(dev_priv);
+
+ vmw_vkms_cleanup(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);
+}
+
+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_obj(*vmw_fp);
+ 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)
+{
+}
+
+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");
+ if (vmw->has_gmr)
+ ttm_resource_manager_create_debugfs(ttm_manager_type(&vmw->bdev, VMW_PL_GMR),
+ root, "gmr_ttm");
+ if (vmw->has_mob) {
+ 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,
+ .fop_flags = FOP_UNSIGNED_OFFSET,
+};
+
+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,
+ .gem_prime_import_sg_table = vmw_prime_import_sg_table,
+
+ DRM_FBDEV_TTM_DRIVER_OPS,
+
+ .fops = &vmwgfx_driver_fops,
+ .name = VMWGFX_DRIVER_NAME,
+ .desc = VMWGFX_DRIVER_DESC,
+ .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 = aperture_remove_conflicting_pci_devices(pdev, driver.name);
+ 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_client_setup(&vmw->drm, NULL);
+
+ 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 000000000..38bea8aba
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -0,0 +1,1524 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/**************************************************************************
+ *
+ * Copyright (c) 2009-2025 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ **************************************************************************/
+
+#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_print.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_MAJOR 2
+#define VMWGFX_DRIVER_MINOR 21
+#define VMWGFX_DRIVER_PATCHLEVEL 0
+#define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
+#define VMWGFX_NUM_DISPLAY_UNITS 8
+#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_NUM_DISPLAY_UNITS
+#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 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_res_func;
+
+struct vmw_bo;
+struct vmw_bo;
+struct vmw_resource_dirty;
+
+/**
+ * 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.
+ * @used_prio: Priority for this resource.
+ * @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
+ * @dirty: resource's dirty tracker
+ * @res_free: The resource destructor.
+ * @hw_destroy: Callback to destroy the resource on the device, as part of
+ * resource destruction.
+ */
+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 id;
+ 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;
+ /* private: */
+ void *private;
+ /* public: */
+ unsigned short valid_handle;
+ unsigned short valid;
+};
+
+/**
+ * enum vmw_dma_map_mode - indicate how to perform TTM page dma mappings.
+ * @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
+ */
+enum vmw_dma_map_mode {
+ vmw_dma_alloc_coherent,
+ vmw_dma_map_populate,
+ vmw_dma_map_bind,
+ /* private: */
+ vmw_dma_map_max
+};
+
+/**
+ * struct vmw_sg_table - Scatter/gather table for binding, with additional
+ * device-specific information.
+ *
+ * @mode: which page mapping mode to use
+ * @pages: Array of page pointers to the pages.
+ * @addrs: DMA addresses to the pages if coherent pages are used.
+ * @sgt: Pointer to a struct sg_table with binding information
+ * @num_pages: Number of @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
+ * @filp: DRM state for this file
+ * @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_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;
+
+ 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_commit *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 */
+ atomic_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;
+
+ bool vkms_enabled;
+ struct workqueue_struct *crc_workq;
+
+ /*
+ * 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 container_of(dev, struct vmw_private, drm);
+}
+
+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);
+
+/*
+ * User handles
+ */
+struct vmw_user_object {
+ struct vmw_surface *surface;
+ struct vmw_bo *buffer;
+};
+
+int vmw_user_object_lookup(struct vmw_private *dev_priv, struct drm_file *filp,
+ u32 handle, struct vmw_user_object *uo);
+struct vmw_user_object *vmw_user_object_ref(struct vmw_user_object *uo);
+void vmw_user_object_unref(struct vmw_user_object *uo);
+bool vmw_user_object_is_null(struct vmw_user_object *uo);
+struct vmw_surface *vmw_user_object_surface(struct vmw_user_object *uo);
+struct vmw_bo *vmw_user_object_buffer(struct vmw_user_object *uo);
+void *vmw_user_object_map(struct vmw_user_object *uo);
+void *vmw_user_object_map_size(struct vmw_user_object *uo, size_t size);
+void vmw_user_object_unmap(struct vmw_user_object *uo);
+bool vmw_user_object_is_mapped(struct vmw_user_object *uo);
+
+/*
+ * 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_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_resource_clean(struct vmw_resource *res);
+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;
+extern const struct drm_gem_object_funcs vmw_gem_object_funcs;
+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 - Get the capabilities of the FIFO command
+ * queue or 0 if fifo memory isn't present.
+ * @dev_priv: The device private context
+ *
+ * Returns: capabilities of the FIFO command or %0 if fifo memory not present
+ */
+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 - check Cursor Bypass 3 enabled setting
+ * in the FIFO.
+ * @dev_priv: The device private context
+ *
+ * Returns: %true iff Cursor Bypass 3 is enabled in the FIFO
+ */
+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_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);
+bool vmw_seqno_waiter_add(struct vmw_private *dev_priv);
+bool vmw_seqno_waiter_remove(struct vmw_private *dev_priv);
+bool vmw_goal_waiter_add(struct vmw_private *dev_priv);
+bool vmw_goal_waiter_remove(struct vmw_private *dev_priv);
+bool vmw_generic_waiter_add(struct vmw_private *dev_priv, u32 flag,
+ int *waiter_count);
+bool 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_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);
+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);
+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);
+
+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);
+struct drm_gem_object *vmw_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *table);
+
+/*
+ * 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);
+struct vmw_surface *vmw_lookup_surface_for_buffer(struct vmw_private *vmw,
+ struct vmw_bo *bo,
+ u32 handle);
+u32 vmw_lookup_surface_handle_for_buffer(struct vmw_private *vmw,
+ struct vmw_bo *bo,
+ u32 handle);
+int vmw_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+
+/*
+ * 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).
+ * @do_cpy: 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 vmw_bo *dst,
+ u32 dst_offset, u32 dst_stride,
+ struct vmw_bo *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 */
+bool vmw_bo_is_dirty(struct vmw_bo *vbo);
+void vmw_bo_dirty_scan(struct vmw_bo *vbo);
+int vmw_bo_dirty_add(struct vmw_bo *vbo);
+void vmw_bo_dirty_clear(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
+ * @fmt: format string for the args
+ *
+ * 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
+ * @vmw: The device private structure
+ * @fifo_reg: The fifo register to read from
+ *
+ * This function is intended to be equivalent to ioread32() on
+ * memremap'd memory, but without byteswapping.
+ *
+ * Returns: the value read
+ */
+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
+ * @vmw: The device private structure
+ * @fifo_reg: The fifo register to write to
+ * @value: The value to write
+ *
+ * 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 000000000..a9136a652
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -0,0 +1,4531 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2009-2025 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ **************************************************************************/
+
+#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>
+#include <linux/vmalloc.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_OR_NULL(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 visible 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 validation 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 ret;
+ }
+ 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 ret;
+ }
+ 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);
+ if (IS_ERR_OR_NULL(cotable_res))
+ return cotable_res ? PTR_ERR(cotable_res) : -EINVAL;
+ 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_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context);
+ struct vmw_bo *vmw_bo;
+ int ret;
+
+ if (!ctx_node)
+ return -EINVAL;
+
+ 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 = 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_resource *res;
+ 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);
+
+ if (unlikely(header->size < sizeof(cmd->body) + sizeof(*suffix))) {
+ VMW_DEBUG_USER("Illegal SVGA_3D_CMD_SURFACE_DMA size.\n");
+ return -EINVAL;
+ }
+
+ 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;
+ }
+
+ res = sw_context->res_cache[vmw_res_surface].res;
+ if (!res) {
+ VMW_DEBUG_USER("Invalid DMA surface.\n");
+ return -EINVAL;
+ }
+
+ srf = vmw_res_to_srf(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;
+
+ cmd = container_of(header, typeof(*cmd), header);
+
+ if (unlikely(header->size < sizeof(cmd->body))) {
+ VMW_DEBUG_USER("Illegal DRAW_PRIMITIVES header size.\n");
+ return -EINVAL;
+ }
+
+ ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
+ if (unlikely(ret != 0))
+ return ret;
+
+ 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]);
+ if (IS_ERR_OR_NULL(res))
+ return res ? PTR_ERR(res) : -EINVAL;
+ 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_OR_NULL(res))
+ return res ? PTR_ERR(res) : -EINVAL;
+ 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);
+ if (IS_ERR_OR_NULL(res))
+ return res ? PTR_ERR(res) : -EINVAL;
+ 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);
+ if (IS_ERR_OR_NULL(res))
+ return res ? PTR_ERR(res) : -EINVAL;
+ 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),
+ VMW_CMD_DEF(SVGA_3D_CMD_DEFINE_GB_SURFACE_V4,
+ &vmw_cmd_invalid, false, 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;
+ if (header->size > SVGA_CMD_MAX_DATASIZE) {
+ VMW_DEBUG_USER("SVGA3D command: %d is too big.\n",
+ cmd_id + SVGA_3D_CMD_BASE);
+ return -E2BIG;
+ }
+ *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;
+ fence_rep.passed_seqno = vmw_fences_update(dev_priv->fman);
+ }
+
+ /*
+ * 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;
+
+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 000000000..384c6736c
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -0,0 +1,749 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2009-2025 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ **************************************************************************/
+
+#include "vmwgfx_drv.h"
+
+#define VMW_FENCE_WRAP (1 << 31)
+
+struct vmw_fence_manager {
+ struct vmw_private *dev_priv;
+ spinlock_t lock;
+ struct list_head fence_list;
+ bool fifo_down;
+ u64 ctx;
+};
+
+struct vmw_user_fence {
+ struct ttm_base_object base;
+ struct vmw_fence_obj fence;
+};
+
+/**
+ * struct vmw_event_fence_action - fence callback that delivers a DRM event.
+ *
+ * @base: For use with dma_fence_add_callback(...)
+ * @event: A pointer to the pending event.
+ * @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 dma_fence_cb base;
+
+ struct drm_pending_event *event;
+ 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.extern_lock, struct vmw_fence_manager,
+ lock);
+}
+
+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);
+
+ if (!list_empty(&fence->head)) {
+ /* The fence manager still has an implicit reference to this
+ * fence via the fence list if head is set. Because the lock is
+ * required to be held when the fence manager updates the fence
+ * list either the fence will have been removed after we get
+ * the lock below or we can safely remove it and the fence
+ * manager will never see it. This implies the fence is being
+ * deleted without being signaled which is dubious but valid
+ * if there are no callbacks. The dma_fence code that calls
+ * this hook will warn about deleted unsignaled with callbacks
+ * so no need to warn again here.
+ */
+ spin_lock(&fman->lock);
+ list_del_init(&fence->head);
+ if (fence->waiter_added)
+ vmw_seqno_waiter_remove(fman->dev_priv);
+ 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";
+}
+
+/* When we toggle signaling for the SVGA device there is a race period from
+ * the time we first read the fence seqno to the time we enable interrupts.
+ * If we miss the interrupt for a fence during this period its likely the driver
+ * will stall. As a result we need to re-read the seqno after interrupts are
+ * enabled. If interrupts were already enabled we just increment the number of
+ * seqno waiters.
+ */
+static bool vmw_fence_enable_signaling(struct dma_fence *f)
+{
+ u32 seqno;
+ 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;
+check_for_race:
+ seqno = vmw_fence_read(dev_priv);
+ if (seqno - fence->base.seqno < VMW_FENCE_WRAP) {
+ if (fence->waiter_added) {
+ vmw_seqno_waiter_remove(dev_priv);
+ fence->waiter_added = false;
+ }
+ return false;
+ } else if (!fence->waiter_added) {
+ fence->waiter_added = true;
+ if (vmw_seqno_waiter_add(dev_priv))
+ goto check_for_race;
+ }
+ return true;
+}
+
+static u32 __vmw_fences_update(struct vmw_fence_manager *fman);
+
+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,
+ .release = vmw_fence_obj_destroy,
+};
+
+struct vmw_fence_manager *vmw_fence_manager_init(struct vmw_private *dev_priv)
+{
+ struct vmw_fence_manager *fman = kzalloc_obj(*fman);
+
+ if (unlikely(!fman))
+ return NULL;
+
+ fman->dev_priv = dev_priv;
+ spin_lock_init(&fman->lock);
+ INIT_LIST_HEAD(&fman->fence_list);
+ fman->fifo_down = true;
+ fman->ctx = dma_fence_context_alloc(1);
+
+ return fman;
+}
+
+void vmw_fence_manager_takedown(struct vmw_fence_manager *fman)
+{
+ bool lists_empty;
+
+ spin_lock(&fman->lock);
+ lists_empty = list_empty(&fman->fence_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);
+ fence->destroy = destroy;
+
+ spin_lock(&fman->lock);
+ if (unlikely(fman->fifo_down)) {
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+ /* This creates an implicit reference to the fence from the fence
+ * manager. It will be dropped when the fence is signaled which is
+ * expected to happen before deletion. The dtor has code to catch
+ * the rare deletion before signaling case.
+ */
+ list_add_tail(&fence->head, &fman->fence_list);
+
+out_unlock:
+ spin_unlock(&fman->lock);
+ return ret;
+
+}
+
+static u32 __vmw_fences_update(struct vmw_fence_manager *fman)
+{
+ struct vmw_fence_obj *fence, *next_fence;
+ const bool cookie = dma_fence_begin_signalling();
+ const u32 seqno = vmw_fence_read(fman->dev_priv);
+
+ 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);
+ if (fence->waiter_added) {
+ vmw_seqno_waiter_remove(fman->dev_priv);
+ fence->waiter_added = false;
+ }
+ dma_fence_signal_locked(&fence->base);
+ } else
+ break;
+ }
+ dma_fence_end_signalling(cookie);
+ atomic_set_release(&fman->dev_priv->last_read_seqno, seqno);
+ return seqno;
+}
+
+u32 vmw_fences_update(struct vmw_fence_manager *fman)
+{
+ u32 seqno;
+ spin_lock(&fman->lock);
+ seqno = __vmw_fences_update(fman);
+ spin_unlock(&fman->lock);
+ return seqno;
+}
+
+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_obj(*fence);
+ 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_obj(*ufence);
+ 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)
+{
+ 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);
+
+ spin_lock(&fman->lock);
+ if (unlikely(ret != 0)) {
+ bool cookie = dma_fence_begin_signalling();
+
+ list_del_init(&fence->head);
+ if (fence->waiter_added) {
+ vmw_seqno_waiter_remove(fman->dev_priv);
+ fence->waiter_added = false;
+ }
+ dma_fence_signal_locked(&fence->base);
+ dma_fence_end_signalling(cookie);
+ }
+
+ BUG_ON(!list_empty(&fence->head));
+ spin_unlock(&fman->lock);
+
+ 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 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);
+
+ arg->signaled = vmw_fence_obj_signaled(fence);
+
+ arg->signaled_flags = arg->flags;
+ arg->passed_seqno = atomic_read_acquire(&dev_priv->last_read_seqno);
+
+ 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
+ *
+ * @f: The struct dma_fence which provides timestamp for the action event
+ * @cb: The struct dma_fence_cb callback for the action event.
+ *
+ * This function is called when the seqno of the fence has passed
+ * and it is always called from atomic context.
+ * It queues the event on the submitter's event list.
+ */
+static void vmw_event_fence_action_seq_passed(struct dma_fence *f,
+ struct dma_fence_cb *cb)
+{
+ struct vmw_event_fence_action *eaction =
+ container_of(cb, struct vmw_event_fence_action, base);
+ 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;
+
+ ts = ktime_to_timespec64(f->timestamp);
+ /* 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);
+ dma_fence_put(f);
+ kfree(eaction);
+}
+
+/**
+ * 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_obj(*eaction);
+ if (unlikely(!eaction))
+ return -ENOMEM;
+
+ eaction->event = event;
+ eaction->dev = &fman->dev_priv->drm;
+ eaction->tv_sec = tv_sec;
+ eaction->tv_usec = tv_usec;
+
+ vmw_fence_obj_reference(fence); // Dropped in CB
+ if (dma_fence_add_callback(&fence->base, &eaction->base,
+ vmw_event_fence_action_seq_passed) < 0)
+ vmw_event_fence_action_seq_passed(&fence->base, &eaction->base);
+ 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_obj(*event);
+ 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->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 000000000..e897cccae
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
@@ -0,0 +1,110 @@
+/* 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;
+
+struct vmw_fence_obj {
+ struct dma_fence base;
+ bool waiter_added;
+ struct list_head head;
+ 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;
+}
+
+u32 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 000000000..39f8c4655
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c
@@ -0,0 +1,350 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/*
+ * Copyright (c) 2021-2024 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ * 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"
+
+#include <linux/debugfs.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_fini(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_object_pin(struct drm_gem_object *obj)
+{
+ struct vmw_bo *vbo = to_vmw_bo(obj);
+
+ vmw_bo_pin_reserved(vbo, true);
+
+ return 0;
+}
+
+static void vmw_gem_object_unpin(struct drm_gem_object *obj)
+{
+ struct vmw_bo *vbo = to_vmw_bo(obj);
+
+ vmw_bo_pin_reserved(vbo, 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 int vmw_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map)
+{
+ struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(obj);
+ int ret;
+
+ if (drm_gem_is_imported(obj)) {
+ ret = dma_buf_vmap(obj->import_attach->dmabuf, map);
+ if (!ret) {
+ if (drm_WARN_ON(obj->dev, map->is_iomem)) {
+ dma_buf_vunmap(obj->import_attach->dmabuf, map);
+ return -EIO;
+ }
+ }
+ } else {
+ ret = ttm_bo_vmap(bo, map);
+ }
+
+ return ret;
+}
+
+static void vmw_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
+{
+ if (drm_gem_is_imported(obj))
+ dma_buf_vunmap(obj->import_attach->dmabuf, map);
+ else
+ drm_gem_ttm_vunmap(obj, map);
+}
+
+static int vmw_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+ int ret;
+
+ if (drm_gem_is_imported(obj)) {
+ /*
+ * Reset both vm_ops and vm_private_data, so we don't end up with
+ * vm_ops pointing to our implementation if the dma-buf backend
+ * doesn't set those fields.
+ */
+ vma->vm_private_data = NULL;
+ vma->vm_ops = NULL;
+
+ ret = dma_buf_mmap(obj->dma_buf, vma, 0);
+
+ /* Drop the reference drm_gem_mmap_obj() acquired.*/
+ if (!ret)
+ drm_gem_object_put(obj);
+
+ return ret;
+ }
+
+ return drm_gem_ttm_mmap(obj, 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,
+};
+
+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 = vmw_gem_vmap,
+ .vunmap = vmw_gem_vunmap,
+ .mmap = vmw_gem_mmap,
+ .vm_ops = &vmw_vm_ops,
+};
+
+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_bo_create(dev_priv, &params, 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;
+}
+
+struct drm_gem_object *vmw_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *table)
+{
+ int ret;
+ struct vmw_private *dev_priv = vmw_priv(dev);
+ struct drm_gem_object *gem = NULL;
+ struct vmw_bo *vbo;
+ 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_sg,
+ .size = attach->dmabuf->size,
+ .pin = false,
+ .keep_resv = true,
+ .resv = attach->dmabuf->resv,
+ .sg = table,
+
+ };
+
+ dma_resv_lock(params.resv, NULL);
+
+ ret = vmw_bo_create(dev_priv, &params, &vbo);
+ if (ret != 0)
+ goto out_no_bo;
+
+ vbo->tbo.base.funcs = &vmw_gem_object_funcs;
+
+ gem = &vbo->tbo.base;
+out_no_bo:
+ dma_resv_unlock(params.resv);
+ return gem;
+}
+
+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",
+ bo->tbo.priority,
+ bo->tbo.pin_count,
+ kref_read(&bo->tbo.base.refcount));
+ 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 000000000..20158a92a
--- /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 000000000..d607452df
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c
@@ -0,0 +1,210 @@
+// 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_obj(**res);
+ 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) {
+ ttm_resource_fini(man, *res);
+ kfree(*res);
+ 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 KiB.\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: %u KiB).\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_obj(*gman);
+
+ 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 000000000..d962ef2ef
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
@@ -0,0 +1,318 @@
+// 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>
+#include <linux/vmalloc.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 = kzalloc_objs(*clips, num_clips);
+ 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 = kzalloc_objs(*clips, num_clips);
+ 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 000000000..05773eb39
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c
@@ -0,0 +1,370 @@
+// 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);
+}
+
+bool vmw_seqno_passed(struct vmw_private *dev_priv,
+ uint32_t seqno)
+{
+ bool ret;
+ u32 last_read_seqno = atomic_read_acquire(&dev_priv->last_read_seqno);
+
+ if (last_read_seqno - seqno < VMW_FENCE_WRAP)
+ return true;
+
+ last_read_seqno = vmw_fences_update(dev_priv->fman);
+ if (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;
+}
+
+bool vmw_generic_waiter_add(struct vmw_private *dev_priv,
+ u32 flag, int *waiter_count)
+{
+ bool hw_programmed = false;
+
+ spin_lock(&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);
+ hw_programmed = true;
+ }
+ spin_unlock(&dev_priv->waiter_lock);
+ return hw_programmed;
+}
+
+bool vmw_generic_waiter_remove(struct vmw_private *dev_priv,
+ u32 flag, int *waiter_count)
+{
+ bool hw_programmed = false;
+
+ spin_lock(&dev_priv->waiter_lock);
+ if (--(*waiter_count) == 0) {
+ dev_priv->irq_mask &= ~flag;
+ vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask);
+ hw_programmed = true;
+ }
+ spin_unlock(&dev_priv->waiter_lock);
+ return hw_programmed;
+}
+
+bool vmw_seqno_waiter_add(struct vmw_private *dev_priv)
+{
+ return vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_ANY_FENCE,
+ &dev_priv->fence_queue_waiters);
+}
+
+bool vmw_seqno_waiter_remove(struct vmw_private *dev_priv)
+{
+ return vmw_generic_waiter_remove(dev_priv, SVGA_IRQFLAG_ANY_FENCE,
+ &dev_priv->fence_queue_waiters);
+}
+
+bool vmw_goal_waiter_add(struct vmw_private *dev_priv)
+{
+ return vmw_generic_waiter_add(dev_priv, vmw_irqflag_fence_goal(dev_priv),
+ &dev_priv->goal_queue_waiters);
+}
+
+bool vmw_goal_waiter_remove(struct vmw_private *dev_priv)
+{
+ return 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 000000000..1b407b61f
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -0,0 +1,2028 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2009-2025 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ **************************************************************************/
+
+#include "vmwgfx_kms.h"
+
+#include "vmwgfx_bo.h"
+#include "vmwgfx_resource_priv.h"
+#include "vmwgfx_vkms.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>
+#include <drm/drm_edid.h>
+
+void vmw_du_init(struct vmw_display_unit *du)
+{
+ vmw_vkms_crtc_init(&du->crtc);
+}
+
+void vmw_du_cleanup(struct vmw_display_unit *du)
+{
+ struct vmw_private *dev_priv = vmw_priv(du->primary.dev);
+
+ vmw_vkms_crtc_cleanup(&du->crtc);
+ 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);
+}
+
+
+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
+ */
+void vmw_du_plane_unpin_surf(struct vmw_plane_state *vps)
+{
+ struct vmw_surface *surf = vmw_user_object_surface(&vps->uo);
+
+ if (surf) {
+ if (vps->pinned) {
+ vmw_resource_unpin(&surf->res);
+ vps->pinned--;
+ }
+ }
+}
+
+
+/**
+ * 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);
+}
+
+
+/**
+ * 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_commit *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_state *crtc_state = NULL;
+ struct drm_framebuffer *new_fb = new_state->fb;
+ struct drm_framebuffer *old_fb = old_state->fb;
+ int ret;
+
+ /*
+ * Ignore damage clips if the framebuffer attached to the plane's state
+ * has changed since the last plane update (page-flip). In this case, a
+ * full plane update should happen because uploads are done per-buffer.
+ */
+ if (old_fb != new_fb)
+ new_state->ignore_damage_clips = true;
+
+ 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);
+ return ret;
+}
+
+int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+ 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);
+
+ /*
+ * This is fine in general, but broken userspace might expect
+ * some actual rendering so give a clue as why it's blank.
+ */
+ if (new_state->enable && !has_primary)
+ drm_dbg_driver(&vmw->drm,
+ "CRTC without a primary plane will be blank.\n");
+
+
+ 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_commit *state)
+{
+ vmw_vkms_crtc_atomic_begin(crtc, 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_obj(*vcs);
+
+ 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;
+
+ vps->cursor.mob = NULL;
+
+ /* Each ref counted resource needs to be acquired again */
+ vmw_user_object_ref(&vps->uo);
+ 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_obj(*vps);
+
+ 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 */
+ vmw_user_object_unref(&vps->uo);
+
+ 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_obj(*vcs);
+
+ 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);
+ struct vmw_bo *bo = vmw_user_object_buffer(&vfbs->uo);
+ struct vmw_surface *surf = vmw_user_object_surface(&vfbs->uo);
+
+ if (bo) {
+ vmw_bo_dirty_release(bo);
+ /*
+ * bo->dirty is reference counted so it being NULL
+ * means that the surface wasn't coherent to begin
+ * with and so we have to free the dirty tracker
+ * in the vmw_resource
+ */
+ if (!bo->dirty && surf && surf->res.dirty)
+ surf->res.func->dirty_free(&surf->res);
+ }
+ drm_framebuffer_cleanup(framebuffer);
+ vmw_user_object_unref(&vfbs->uo);
+
+ 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 int vmw_framebuffer_surface_create_handle(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int *handle)
+{
+ struct vmw_framebuffer_surface *vfbs = vmw_framebuffer_to_vfbs(fb);
+ struct vmw_bo *bo = vmw_user_object_buffer(&vfbs->uo);
+
+ if (WARN_ON(!bo))
+ return -EINVAL;
+ return drm_gem_handle_create(file_priv, &bo->tbo.base, handle);
+}
+
+static const struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = {
+ .create_handle = vmw_framebuffer_surface_create_handle,
+ .destroy = vmw_framebuffer_surface_destroy,
+ .dirty = drm_atomic_helper_dirtyfb,
+};
+
+static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
+ struct vmw_user_object *uo,
+ struct vmw_framebuffer **out,
+ const struct drm_format_info *info,
+ const struct drm_mode_fb_cmd2
+ *mode_cmd)
+
+{
+ struct drm_device *dev = &dev_priv->drm;
+ struct vmw_framebuffer_surface *vfbs;
+ struct vmw_surface *surface;
+ int ret;
+
+ /* 3D is only supported on HWv8 and newer hosts */
+ if (dev_priv->active_display_unit == vmw_du_legacy)
+ return -ENOSYS;
+
+ surface = vmw_user_object_surface(uo);
+
+ /*
+ * 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;
+ }
+
+ vfbs = kzalloc_obj(*vfbs);
+ if (!vfbs) {
+ ret = -ENOMEM;
+ goto out_err1;
+ }
+
+ drm_helper_mode_fill_fb_struct(dev, &vfbs->base.base, info, mode_cmd);
+ memcpy(&vfbs->uo, uo, sizeof(vfbs->uo));
+ vmw_user_object_ref(&vfbs->uo);
+
+ if (vfbs->uo.buffer)
+ vfbs->base.base.obj[0] = &vfbs->uo.buffer->tbo.base;
+
+ *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_user_object_unref(&vfbs->uo);
+ 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);
+
+ vmw_bo_dirty_release(vfbd->buffer);
+ 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,
+};
+
+static int vmw_kms_new_framebuffer_bo(struct vmw_private *dev_priv,
+ struct vmw_bo *bo,
+ struct vmw_framebuffer **out,
+ const struct drm_format_info *info,
+ 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_obj(*vfbd);
+ 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, info, mode_cmd);
+ vfbd->base.bo = true;
+ vfbd->buffer = vmw_bo_reference(bo);
+ *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.
+ * @uo: Pointer to user object to wrap the kms framebuffer around.
+ * Either the buffer or surface inside the user object must be NULL.
+ * @info: pixel format information.
+ * @mode_cmd: Frame-buffer metadata.
+ */
+struct vmw_framebuffer *
+vmw_kms_new_framebuffer(struct vmw_private *dev_priv,
+ struct vmw_user_object *uo,
+ const struct drm_format_info *info,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct vmw_framebuffer *vfb = NULL;
+ int ret;
+
+ /* Create the new framebuffer depending one what we have */
+ if (vmw_user_object_surface(uo)) {
+ ret = vmw_kms_new_framebuffer_surface(dev_priv, uo, &vfb,
+ info, mode_cmd);
+ } else if (uo->buffer) {
+ ret = vmw_kms_new_framebuffer_bo(dev_priv, uo->buffer, &vfb,
+ info, 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_format_info *info,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct vmw_private *dev_priv = vmw_priv(dev);
+ struct vmw_framebuffer *vfb = NULL;
+ struct vmw_user_object uo = {0};
+ struct vmw_bo *bo;
+ struct vmw_surface *surface;
+ int ret;
+
+ /* returns either a bo or surface */
+ ret = vmw_user_object_lookup(dev_priv, file_priv, mode_cmd->handles[0],
+ &uo);
+ if (ret) {
+ DRM_ERROR("Invalid buffer object handle %u (0x%x).\n",
+ mode_cmd->handles[0], mode_cmd->handles[0]);
+ goto err_out;
+ }
+
+
+ if (vmw_user_object_surface(&uo) &&
+ !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);
+ ret = -EINVAL;
+ goto err_out;
+ }
+
+
+ vfb = vmw_kms_new_framebuffer(dev_priv, &uo, info, mode_cmd);
+ if (IS_ERR(vfb)) {
+ ret = PTR_ERR(vfb);
+ goto err_out;
+ }
+
+err_out:
+ bo = vmw_user_object_buffer(&uo);
+ surface = vmw_user_object_surface(&uo);
+ /* vmw_user_object_lookup takes one ref so does new_fb */
+ vmw_user_object_unref(&uo);
+
+ if (ret) {
+ DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ if (bo) {
+ ttm_bo_reserve(&bo->tbo, false, false, NULL);
+ ret = vmw_bo_dirty_add(bo);
+ if (!ret && surface && surface->res.func->dirty_alloc) {
+ surface->res.coherent = true;
+ if (surface->res.dirty == NULL)
+ ret = surface->res.func->dirty_alloc(&surface->res);
+ }
+ ttm_bo_unreserve(&bo->tbo);
+ }
+
+ 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_commit *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_commit *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_commit
+ * @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_commit *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 = kzalloc_objs(struct drm_rect, dev->mode_config.num_crtc);
+ 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_commit *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);
+}
+
+static void
+vmw_atomic_commit_tail(struct drm_atomic_commit *old_state)
+{
+ struct vmw_private *vmw = vmw_priv(old_state->dev);
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *old_crtc_state;
+ int i;
+
+ drm_atomic_helper_commit_tail(old_state);
+
+ if (vmw->vkms_enabled) {
+ for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+ (void)old_crtc_state;
+ flush_work(&du->vkms.crc_generator_work);
+ }
+ }
+}
+
+static const struct drm_mode_config_helper_funcs vmw_mode_config_helpers = {
+ .atomic_commit_tail = vmw_atomic_commit_tail,
+};
+
+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;
+ dev->mode_config.helper_private = &vmw_mode_config_helpers;
+
+ 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_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;
+}
+
+static
+bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv,
+ u64 pitch,
+ u64 height)
+{
+ return (pitch * height) < (u64)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);
+}
+
+/**
+ * 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;
+}
+
+
+/**
+ * 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;
+ const 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;
+ } else if (arg->num_outputs > VMWGFX_NUM_DISPLAY_UNITS) {
+ return -E2BIG;
+ }
+
+ rects_size = arg->num_outputs * sizeof(struct drm_vmw_rect);
+ rects = kzalloc_objs(struct drm_vmw_rect, arg->num_outputs);
+ 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_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);
+ struct vmw_surface *surf = vmw_user_object_surface(&vfbs->uo);
+
+ ret = vmw_validation_add_resource(&val_ctx, &surf->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;
+}
+
+/**
+ * vmw_connector_mode_valid - implements drm_connector_helper_funcs.mode_valid callback
+ *
+ * @connector: the drm connector, part of a DU container
+ * @mode: drm mode to check
+ *
+ * Returns MODE_OK on success, or a drm_mode_status error code.
+ */
+enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector,
+ const struct drm_display_mode *mode)
+{
+ enum drm_mode_status ret;
+ struct drm_device *dev = connector->dev;
+ struct vmw_private *dev_priv = vmw_priv(dev);
+ u32 assumed_cpp = 4;
+
+ if (dev_priv->assume_16bpp)
+ assumed_cpp = 2;
+
+ ret = drm_mode_validate_size(mode, dev_priv->texture_max_width,
+ dev_priv->texture_max_height);
+ if (ret != MODE_OK)
+ return ret;
+
+ if (!vmw_kms_validate_mode_vram(dev_priv,
+ mode->hdisplay * assumed_cpp,
+ mode->vdisplay))
+ return MODE_MEM;
+
+ return MODE_OK;
+}
+
+/**
+ * vmw_connector_get_modes - implements drm_connector_helper_funcs.get_modes callback
+ *
+ * @connector: the drm connector, part of a DU container
+ *
+ * Returns the number of added modes.
+ */
+int vmw_connector_get_modes(struct drm_connector *connector)
+{
+ 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 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)
+ };
+ u32 max_width;
+ u32 max_height;
+ u32 num_modes;
+
+ /* 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);
+
+ drm_mode_probed_add(connector, mode);
+ drm_dbg_kms(dev, "preferred mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
+
+ /* Probe connector for all modes not exceeding our geom limits */
+ max_width = dev_priv->texture_max_width;
+ max_height = dev_priv->texture_max_height;
+
+ if (dev_priv->active_display_unit == vmw_du_screen_target) {
+ max_width = min(dev_priv->stdu_max_width, max_width);
+ max_height = min(dev_priv->stdu_max_height, max_height);
+ }
+
+ num_modes = 1 + drm_add_modes_noedid(connector, max_width, max_height);
+
+ return num_modes;
+}
+
+struct vmw_user_object *vmw_user_object_ref(struct vmw_user_object *uo)
+{
+ if (uo->buffer)
+ vmw_user_bo_ref(uo->buffer);
+ else if (uo->surface)
+ vmw_surface_reference(uo->surface);
+ return uo;
+}
+
+void vmw_user_object_unref(struct vmw_user_object *uo)
+{
+ if (uo->buffer)
+ vmw_user_bo_unref(&uo->buffer);
+ else if (uo->surface)
+ vmw_surface_unreference(&uo->surface);
+}
+
+struct vmw_bo *
+vmw_user_object_buffer(struct vmw_user_object *uo)
+{
+ if (uo->buffer)
+ return uo->buffer;
+ else if (uo->surface)
+ return uo->surface->res.guest_memory_bo;
+ return NULL;
+}
+
+struct vmw_surface *
+vmw_user_object_surface(struct vmw_user_object *uo)
+{
+ if (uo->buffer)
+ return uo->buffer->dumb_surface;
+ return uo->surface;
+}
+
+void *vmw_user_object_map(struct vmw_user_object *uo)
+{
+ struct vmw_bo *bo = vmw_user_object_buffer(uo);
+
+ WARN_ON(!bo);
+ return vmw_bo_map_and_cache(bo);
+}
+
+void *vmw_user_object_map_size(struct vmw_user_object *uo, size_t size)
+{
+ struct vmw_bo *bo = vmw_user_object_buffer(uo);
+
+ WARN_ON(!bo);
+ return vmw_bo_map_and_cache_size(bo, size);
+}
+
+void vmw_user_object_unmap(struct vmw_user_object *uo)
+{
+ struct vmw_bo *bo = vmw_user_object_buffer(uo);
+ int ret;
+
+ WARN_ON(!bo);
+
+ /* Fence the mob creation so we are guarateed to have the mob */
+ ret = ttm_bo_reserve(&bo->tbo, false, false, NULL);
+ if (ret != 0)
+ return;
+
+ vmw_bo_unmap(bo);
+ vmw_bo_pin_reserved(bo, false);
+
+ ttm_bo_unreserve(&bo->tbo);
+}
+
+bool vmw_user_object_is_mapped(struct vmw_user_object *uo)
+{
+ struct vmw_bo *bo;
+
+ if (!uo || vmw_user_object_is_null(uo))
+ return false;
+
+ bo = vmw_user_object_buffer(uo);
+
+ if (WARN_ON(!bo))
+ return false;
+
+ WARN_ON(bo->map.bo && !bo->map.virtual);
+ return bo->map.virtual;
+}
+
+bool vmw_user_object_is_null(struct vmw_user_object *uo)
+{
+ return !uo->buffer && !uo->surface;
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
new file mode 100644
index 000000000..2224d7d91
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
@@ -0,0 +1,515 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/**************************************************************************
+ *
+ * Copyright (c) 2009-2025 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ **************************************************************************/
+
+#ifndef VMWGFX_KMS_H_
+#define VMWGFX_KMS_H_
+
+#include "vmwgfx_cursor_plane.h"
+#include "vmwgfx_drv.h"
+
+#include <drm/drm_encoder.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_probe_helper.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 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;
+};
+
+struct vmw_framebuffer_surface {
+ struct vmw_framebuffer base;
+ struct vmw_user_object uo;
+};
+
+struct vmw_framebuffer_bo {
+ struct vmw_framebuffer base;
+ struct vmw_bo *buffer;
+};
+
+
+static const uint32_t __maybe_unused vmw_primary_plane_formats[] = {
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_XRGB1555,
+};
+
+
+#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)
+
+/**
+ * Derived class for crtc state object
+ *
+ * @base DRM crtc object
+ */
+struct vmw_crtc_state {
+ struct drm_crtc_state base;
+};
+
+
+/**
+ * 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_user_object uo;
+
+ int content_fb_type;
+ unsigned long bo_size;
+
+ int pinned;
+
+ /* For CPU Blit */
+ unsigned int cpp;
+
+ 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;
+};
+
+
+/**
+ * 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;
+
+ unsigned unit;
+
+ /*
+ * Prefered mode tracking.
+ */
+ unsigned pref_width;
+ unsigned pref_height;
+ bool pref_active;
+
+ /*
+ * Gui positioning
+ */
+ int gui_x;
+ int gui_y;
+ bool is_implicit;
+ int set_gui_x;
+ int set_gui_y;
+
+ struct {
+ struct work_struct crc_generator_work;
+
+ /* protects concurrent access to the vblank handler */
+ atomic_t atomic_lock;
+ /* protected by @atomic_lock */
+ bool crc_enabled;
+ struct vmw_surface *surface;
+
+ /* protects concurrent access to the crc worker */
+ spinlock_t crc_state_lock;
+ /* protected by @crc_state_lock */
+ bool crc_pending;
+ u64 frame_start;
+ u64 frame_end;
+ } vkms;
+};
+
+#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_init(struct vmw_display_unit *du);
+void vmw_du_cleanup(struct vmw_display_unit *du);
+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_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);
+enum drm_mode_status vmw_connector_mode_valid(struct drm_connector *connector,
+ const struct drm_display_mode *mode);
+int vmw_connector_get_modes(struct drm_connector *connector);
+
+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_user_object *uo,
+ const struct drm_format_info *info,
+ 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);
+
+/* Atomic Helpers */
+int vmw_du_primary_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *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);
+
+int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state);
+void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_atomic_commit *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 000000000..af3e32174
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2009-2024 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ * 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 "vmwgfx_vkms.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_user_object_buffer(&vmw_framebuffer_to_vfbs(&vfb->base)->uo);
+
+ 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_user_object_buffer(&vmw_framebuffer_to_vfbs(&vfb->base)->uo);
+
+
+ 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)
+{
+}
+
+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,
+ .enable_vblank = vmw_vkms_enable_vblank,
+ .disable_vblank = vmw_vkms_disable_vblank,
+ .get_vblank_timestamp = vmw_vkms_get_vblank_timestamp,
+};
+
+
+/*
+ * 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 = drm_helper_probe_single_connector_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 = {
+ .get_modes = vmw_connector_get_modes,
+ .mode_valid = vmw_connector_mode_valid
+};
+
+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_commit *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_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_cursor_plane_atomic_check,
+ .atomic_update = vmw_cursor_plane_atomic_update,
+ .prepare_fb = vmw_cursor_plane_prepare_fb,
+ .cleanup_fb = vmw_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_vkms_crtc_atomic_flush,
+ .atomic_enable = vmw_vkms_crtc_atomic_enable,
+ .atomic_disable = vmw_vkms_crtc_atomic_disable,
+ .handle_vblank_timeout = vmw_vkms_handle_vblank_timeout,
+};
+
+
+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_obj(*ldu);
+ 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;
+
+ /*
+ * 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);
+
+ 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);
+
+ vmw_du_init(&ldu->base);
+
+ 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_obj(*dev_priv->ldu_priv);
+ if (!dev_priv->ldu_priv)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&dev_priv->ldu_priv->active);
+ dev_priv->ldu_priv->num_active = 0;
+ dev_priv->ldu_priv->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 000000000..ede74c7fd
--- /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 000000000..de7a504de
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c
@@ -0,0 +1,655 @@
+// 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);
+ vmw_bo_unreference(&batch->otable_bo);
+ 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_obj(*mob);
+
+ 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 000000000..fd77ab656
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c
@@ -0,0 +1,1137 @@
+// 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_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)
+{
+ u32 ecx, edx, esi, edi;
+
+ vmware_hypercall6(VMW_PORT_CMD_OPEN_CHANNEL,
+ (protocol | GUESTMSG_FLAG_COOKIE), 0,
+ &ecx, &edx, &esi, &edi);
+
+ if ((HIGH_WORD(ecx) & MESSAGE_STATUS_SUCCESS) == 0)
+ return -EINVAL;
+
+ channel->channel_id = HIGH_WORD(edx);
+ channel->cookie_high = esi;
+ channel->cookie_low = edi;
+
+ return 0;
+}
+
+
+
+/**
+ * vmw_close_channel
+ *
+ * @channel: RPC channel
+ *
+ * Returns: 0 on success
+ */
+static int vmw_close_channel(struct rpc_channel *channel)
+{
+ u32 ecx;
+
+ vmware_hypercall5(VMW_PORT_CMD_CLOSE_CHANNEL,
+ 0, channel->channel_id << 16,
+ channel->cookie_high,
+ channel->cookie_low,
+ &ecx);
+
+ 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)
+{
+ u32 ebx, ecx;
+ unsigned long msg_len = strlen(msg);
+
+ /* HB port can't access encrypted memory. */
+ if (hb && !cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {
+ vmware_hypercall_hb_out(
+ (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
+ msg_len,
+ channel->channel_id << 16,
+ (uintptr_t) msg, channel->cookie_low,
+ channel->cookie_high,
+ &ebx);
+
+ 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;
+
+ vmware_hypercall5(VMW_PORT_CMD_MSG |
+ (MSG_TYPE_SENDPAYLOAD << 16),
+ word, channel->channel_id << 16,
+ channel->cookie_high,
+ channel->cookie_low,
+ &ecx);
+ }
+
+ 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)
+{
+ u32 ebx, ecx, edx;
+
+ /* HB port can't access encrypted memory */
+ if (hb && !cc_platform_has(CC_ATTR_MEM_ENCRYPT)) {
+ vmware_hypercall_hb_in(
+ (MESSAGE_STATUS_SUCCESS << 16) | VMW_PORT_CMD_HB_MSG,
+ reply_len,
+ channel->channel_id << 16,
+ channel->cookie_high,
+ (uintptr_t) reply, channel->cookie_low,
+ &ebx);
+
+ 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);
+
+ vmware_hypercall7(VMW_PORT_CMD_MSG |
+ (MSG_TYPE_RECVPAYLOAD << 16),
+ MESSAGE_STATUS_SUCCESS,
+ channel->channel_id << 16,
+ channel->cookie_high,
+ channel->cookie_low,
+ &ebx, &ecx, &edx);
+
+ 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)
+{
+ u32 ebx, ecx;
+ size_t msg_len = strlen(msg);
+ int retries = 0;
+
+ while (retries < RETRIES) {
+ retries++;
+
+ vmware_hypercall5(VMW_PORT_CMD_SENDSIZE,
+ msg_len, channel->channel_id << 16,
+ channel->cookie_high,
+ channel->cookie_low,
+ &ecx);
+
+ 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_FP(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)
+{
+ u32 ebx, ecx, edx;
+ char *reply;
+ size_t reply_len;
+ int retries = 0;
+
+
+ *msg_len = 0;
+ *msg = NULL;
+
+ while (retries < RETRIES) {
+ retries++;
+
+ vmware_hypercall7(VMW_PORT_CMD_RECVSIZE,
+ 0, channel->channel_id << 16,
+ channel->cookie_high,
+ channel->cookie_low,
+ &ebx, &ecx, &edx);
+
+ 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';
+
+ vmware_hypercall5(VMW_PORT_CMD_RECVSTATUS,
+ MESSAGE_STATUS_SUCCESS,
+ channel->channel_id << 16,
+ channel->cookie_high,
+ channel->cookie_low,
+ &ecx);
+
+ 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)
+{
+ vmware_hypercall1(VMW_PORT_CMD_MKSGS_RESET, 0);
+}
+
+/**
+ * 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)
+{
+ vmware_hypercall1(VMW_PORT_CMD_MKSGS_ADD_PPN, (unsigned long)pfn);
+}
+
+/**
+ * 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)
+{
+ vmware_hypercall1(VMW_PORT_CMD_MKSGS_REMOVE_PPN, (unsigned long)pfn);
+}
+
+#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_objs(*pages_stat,
+ ARRAY_SIZE(pdesc->statPPNs) + ARRAY_SIZE(pdesc->infoPPNs) + ARRAY_SIZE(pdesc->strsPPNs));
+
+ 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 000000000..3c78e9338
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg_arm64.h
@@ -0,0 +1,204 @@
+// 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 VMWARE_HYPERVISOR_MAGIC 0x564D5868
+
+#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
+unsigned long vmware_hypercall1(unsigned long cmd, unsigned long in1)
+{
+ register u64 x0 asm("x0") = VMWARE_HYPERVISOR_MAGIC;
+ register u64 x1 asm("x1") = in1;
+ register u64 x2 asm("x2") = cmd;
+ register u64 x3 asm("x3") = VMWARE_HYPERVISOR_PORT;
+ 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_inline volatile (
+ "mrs xzr, mdccsr_el0; "
+ : "+r" (x0)
+ : "r" (x1), "r" (x2), "r" (x3), "r" (x7)
+ : "memory");
+
+ return x0;
+}
+
+static inline
+unsigned long vmware_hypercall5(unsigned long cmd, unsigned long in1,
+ unsigned long in3, unsigned long in4,
+ unsigned long in5, u32 *out2)
+{
+ register u64 x0 asm("x0") = VMWARE_HYPERVISOR_MAGIC;
+ register u64 x1 asm("x1") = in1;
+ register u64 x2 asm("x2") = cmd;
+ register u64 x3 asm("x3") = in3 | VMWARE_HYPERVISOR_PORT;
+ register u64 x4 asm("x4") = in4;
+ register u64 x5 asm("x5") = in5;
+ 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_inline volatile (
+ "mrs xzr, mdccsr_el0; "
+ : "+r" (x0), "+r" (x2)
+ : "r" (x1), "r" (x3), "r" (x4), "r" (x5), "r" (x7)
+ : "memory");
+
+ *out2 = x2;
+ return x0;
+}
+
+static inline
+unsigned long vmware_hypercall6(unsigned long cmd, unsigned long in1,
+ unsigned long in3, u32 *out2,
+ u32 *out3, u32 *out4, u32 *out5)
+{
+ register u64 x0 asm("x0") = VMWARE_HYPERVISOR_MAGIC;
+ register u64 x1 asm("x1") = in1;
+ register u64 x2 asm("x2") = cmd;
+ register u64 x3 asm("x3") = in3 | VMWARE_HYPERVISOR_PORT;
+ register u64 x4 asm("x4");
+ register u64 x5 asm("x5");
+ 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_inline volatile (
+ "mrs xzr, mdccsr_el0; "
+ : "+r" (x0), "+r" (x2), "+r" (x3), "=r" (x4), "=r" (x5)
+ : "r" (x1), "r" (x7)
+ : "memory");
+
+ *out2 = x2;
+ *out3 = x3;
+ *out4 = x4;
+ *out5 = x5;
+ return x0;
+}
+
+static inline
+unsigned long vmware_hypercall7(unsigned long cmd, unsigned long in1,
+ unsigned long in3, unsigned long in4,
+ unsigned long in5, u32 *out1,
+ u32 *out2, u32 *out3)
+{
+ register u64 x0 asm("x0") = VMWARE_HYPERVISOR_MAGIC;
+ register u64 x1 asm("x1") = in1;
+ register u64 x2 asm("x2") = cmd;
+ register u64 x3 asm("x3") = in3 | VMWARE_HYPERVISOR_PORT;
+ register u64 x4 asm("x4") = in4;
+ register u64 x5 asm("x5") = in5;
+ 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_inline volatile (
+ "mrs xzr, mdccsr_el0; "
+ : "+r" (x0), "+r" (x1), "+r" (x2), "+r" (x3)
+ : "r" (x4), "r" (x5), "r" (x7)
+ : "memory");
+
+ *out1 = x1;
+ *out2 = x2;
+ *out3 = x3;
+ return x0;
+}
+
+static inline
+unsigned long vmware_hypercall_hb(unsigned long cmd, unsigned long in2,
+ unsigned long in3, unsigned long in4,
+ unsigned long in5, unsigned long in6,
+ u32 *out1, int dir)
+{
+ register u64 x0 asm("x0") = VMWARE_HYPERVISOR_MAGIC;
+ register u64 x1 asm("x1") = cmd;
+ register u64 x2 asm("x2") = in2;
+ register u64 x3 asm("x3") = in3 | VMWARE_HYPERVISOR_PORT_HB;
+ register u64 x4 asm("x4") = in4;
+ register u64 x5 asm("x5") = in5;
+ register u64 x6 asm("x6") = in6;
+ register u64 x7 asm("x7") = ((u64)X86_IO_MAGIC << 32) |
+ X86_IO_W7_STR |
+ X86_IO_W7_WITH |
+ dir;
+
+ asm_inline volatile (
+ "mrs xzr, mdccsr_el0; "
+ : "+r" (x0), "+r" (x1)
+ : "r" (x2), "r" (x3), "r" (x4), "r" (x5),
+ "r" (x6), "r" (x7)
+ : "memory");
+
+ *out1 = x1;
+ return x0;
+}
+
+static inline
+unsigned long vmware_hypercall_hb_out(unsigned long cmd, unsigned long in2,
+ unsigned long in3, unsigned long in4,
+ unsigned long in5, unsigned long in6,
+ u32 *out1)
+{
+ return vmware_hypercall_hb(cmd, in2, in3, in4, in5, in6, out1, 0);
+}
+
+static inline
+unsigned long vmware_hypercall_hb_in(unsigned long cmd, unsigned long in2,
+ unsigned long in3, unsigned long in4,
+ unsigned long in5, unsigned long in6,
+ u32 *out1)
+{
+ return vmware_hypercall_hb(cmd, in2, in3, in4, in5, in6, out1,
+ X86_IO_W7_DIR);
+}
+#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 000000000..13304d34c
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg_x86.h
@@ -0,0 +1,42 @@
+/* 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>
+
+#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 000000000..679adf7c7
--- /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_legacy);
+ 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_obj(*overlay);
+ 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 000000000..d42561b63
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2019-2025 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ **************************************************************************/
+#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
+ * @ref_count: Reference count for this structure. Must be first member!
+ * @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
+ * @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 {
+ struct kref ref_count;
+ unsigned long start;
+ unsigned long end;
+ enum vmw_bo_dirty_method method;
+ unsigned int change_count;
+ unsigned long bitmap_size;
+ unsigned long bitmap[];
+};
+
+bool vmw_bo_is_dirty(struct vmw_bo *vbo)
+{
+ return vbo->dirty && (vbo->dirty->start < vbo->dirty->end);
+}
+
+/**
+ * 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) {
+ kref_get(&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;
+ kref_init(&dirty->ref_count);
+ 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;
+}
+
+static void vmw_bo_dirty_free(struct kref *kref)
+{
+ struct vmw_bo_dirty *dirty = container_of(kref, struct vmw_bo_dirty, ref_count);
+
+ kvfree(dirty);
+}
+
+/**
+ * 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 && kref_put(&dirty->ref_count, vmw_bo_dirty_free))
+ 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 = min(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;
+}
+
+void vmw_bo_dirty_clear(struct vmw_bo *vbo)
+{
+ struct vmw_bo_dirty *dirty = vbo->dirty;
+ pgoff_t start, cur, end;
+ unsigned long res_start = 0;
+ unsigned long res_end = vbo->tbo.base.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 = min(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);
+ }
+
+ 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 = vma_get_page_prot(vma);
+
+ 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 000000000..598b90ac7
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_prime.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2013-2024 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ * 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 "vmwgfx_bo.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;
+ int ret = ttm_prime_fd_to_handle(tfile, fd, handle);
+
+ if (ret)
+ ret = drm_gem_prime_fd_to_handle(dev, file_priv, fd, handle);
+
+ return ret;
+}
+
+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 vmw_private *vmw = vmw_priv(dev);
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+ struct vmw_bo *vbo;
+ int ret;
+ int surf_handle;
+
+ if (handle > VMWGFX_NUM_MOB) {
+ ret = ttm_prime_handle_to_fd(tfile, handle, flags, prime_fd);
+ } else {
+ ret = vmw_user_bo_lookup(file_priv, handle, &vbo);
+ if (ret)
+ return ret;
+ if (vbo && vbo->is_dumb) {
+ ret = drm_gem_prime_handle_to_fd(dev, file_priv, handle,
+ flags, prime_fd);
+ } else {
+ surf_handle = vmw_lookup_surface_handle_for_buffer(vmw,
+ vbo,
+ handle);
+ if (surf_handle > 0)
+ ret = ttm_prime_handle_to_fd(tfile, surf_handle,
+ flags, prime_fd);
+ else
+ ret = drm_gem_prime_handle_to_fd(dev, file_priv,
+ handle, flags,
+ prime_fd);
+ }
+ vmw_user_bo_unref(&vbo);
+ }
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h b/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h
new file mode 100644
index 000000000..cf585dfe5
--- /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 000000000..e3a187a2c
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -0,0 +1,1161 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2009-2024 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ * 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_del_detached_resource(gbo, res);
+
+ 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_dirty = 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);
+ vmw_resource_reference(res);
+
+ *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_object_lookup(struct vmw_private *dev_priv,
+ struct drm_file *filp,
+ u32 handle,
+ struct vmw_user_object *uo)
+{
+ struct ttm_object_file *tfile = vmw_fpriv(filp)->tfile;
+ struct vmw_resource *res;
+ int ret;
+
+ WARN_ON(uo->surface || uo->buffer);
+
+ ret = vmw_user_resource_lookup_handle(dev_priv, tfile, handle,
+ user_surface_converter,
+ &res);
+ if (!ret) {
+ uo->surface = vmw_res_to_srf(res);
+ return 0;
+ }
+
+ uo->surface = NULL;
+ ret = vmw_user_bo_lookup(filp, handle, &uo->buffer);
+ if (!ret && !uo->buffer->is_dumb) {
+ uo->surface = vmw_lookup_surface_for_buffer(dev_priv,
+ uo->buffer,
+ handle);
+ if (uo->surface)
+ vmw_user_bo_unref(&uo->buffer);
+ }
+
+ 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_bo_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);
+ val_buf->bo = &res->guest_memory_bo->tbo;
+ val_buf->num_shared = 0;
+ drm_gem_object_get(&val_buf->bo->base);
+ 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:
+ drm_gem_object_put(&val_buf->bo->base);
+ 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);
+ drm_gem_object_put(&val_buf->bo->base);
+ 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_dirty = 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);
+}
+
+int vmw_resource_clean(struct vmw_resource *res)
+{
+ int ret = 0;
+
+ if (res->res_dirty) {
+ if (!res->func->clean)
+ return -EINVAL;
+
+ ret = res->func->clean(res);
+ if (ret)
+ return ret;
+ res->res_dirty = false;
+ }
+ return ret;
+}
+
+/**
+ * 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;
+ int ret;
+
+ /*
+ * 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) {
+ ret = vmw_resource_clean(found);
+ if (ret)
+ return ret;
+ 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 000000000..aa7cbd396
--- /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 000000000..c83061cf7
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -0,0 +1,1366 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2011-2024 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ * 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 "vmwgfx_vkms.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_vblank.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;
+
+ 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 = vmw_user_object_buffer(&vps->uo);
+
+ 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;
+ }
+}
+
+/**
+ * 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_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_commit *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 (dev_priv->vkms_enabled)
+ drm_crtc_vblank_off(crtc);
+
+ 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,
+ .enable_vblank = vmw_vkms_enable_vblank,
+ .disable_vblank = vmw_vkms_disable_vblank,
+ .get_vblank_timestamp = vmw_vkms_get_vblank_timestamp,
+};
+
+/*
+ * 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 = drm_helper_probe_single_connector_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 = {
+ .get_modes = vmw_connector_get_modes,
+ .mode_valid = vmw_connector_mode_valid
+};
+
+
+
+/*
+ * 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;
+ struct vmw_bo *bo = vmw_user_object_buffer(&vps->uo);
+
+ if (bo)
+ vmw_bo_unpin(vmw_priv(crtc->dev), bo, false);
+ vmw_user_object_unref(&vps->uo);
+ 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
+ };
+ struct vmw_bo *bo = NULL;
+
+ if (!new_fb) {
+ vmw_user_object_unref(&vps->uo);
+ vps->bo_size = 0;
+
+ return 0;
+ }
+
+ bo_params.size = new_state->crtc_w * new_state->crtc_h * 4;
+ dev_priv = vmw_priv(crtc->dev);
+
+ bo = vmw_user_object_buffer(&vps->uo);
+ if (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, bo, true);
+ }
+
+ vmw_user_object_unref(&vps->uo);
+ 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->uo.buffer);
+ 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->uo.buffer, 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;
+ struct vmw_surface *surf = NULL;
+
+ 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;
+
+ surf = vmw_user_object_surface(&vfbs->uo);
+ blit->body.srcImage.sid = surf->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_commit *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_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_cursor_plane_atomic_check,
+ .atomic_update = vmw_cursor_plane_atomic_update,
+ .prepare_fb = vmw_cursor_plane_prepare_fb,
+ .cleanup_fb = vmw_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_vkms_crtc_atomic_flush,
+ .atomic_enable = vmw_vkms_crtc_atomic_enable,
+ .atomic_disable = vmw_sou_crtc_atomic_disable,
+ .handle_vblank_timeout = vmw_vkms_handle_vblank_timeout,
+};
+
+
+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_obj(*sou);
+ 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;
+
+ /*
+ * 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);
+
+ 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);
+
+ vmw_du_init(&sou->base);
+
+ 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 = &vmw_user_object_surface(&vfbs->uo)->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 000000000..39811cf19
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
@@ -0,0 +1,980 @@
+// 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 <linux/overflow.h>
+
+#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_obj(*shader);
+ 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,
+ u64 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_obj(*ushader);
+ 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,
+ u64 offset,
+ SVGA3dShaderType shader_type)
+{
+ struct vmw_shader *shader;
+ struct vmw_resource *res;
+ int ret;
+
+ shader = kzalloc_obj(*shader);
+ 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, u64 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) {
+ u64 end;
+
+ 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 (check_add_overflow((u64)size, (u64)offset, &end) ||
+ end > buffer->tbo.base.size) {
+ 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 = false,
+ .keep_resv = 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;
+
+ /* 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 (IS_ERR(res)) {
+ ret = PTR_ERR(res);
+ 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 000000000..0d51b4542
--- /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 000000000..d199e718c
--- /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 000000000..01c701e74
--- /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 000000000..4139837f4
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
@@ -0,0 +1,1714 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/******************************************************************************
+ *
+ * Copyright (c) 2014-2024 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ * 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 "vmwgfx_vkms.h"
+#include "vmw_surface_cache.h"
+#include <linux/fsnotify.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_vblank.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)
+
+/*
+ * Some renderers such as llvmpipe will align the width and height of their
+ * buffers to match their tile size. We need to keep this in mind when exposing
+ * modes to userspace so that this possible over-allocation will not exceed
+ * graphics memory. 64x64 pixels seems to be a reasonable upper bound for the
+ * tile size of current renderers.
+ */
+#define GPU_TILE_SIZE 64
+
+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().
+ * @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_surface_copy {
+ SVGA3dCmdHeader header;
+ SVGA3dCmdSurfaceCopy body;
+};
+
+struct vmw_stdu_update_gb_image {
+ SVGA3dCmdHeader header;
+ SVGA3dCmdUpdateGBImage body;
+};
+
+/**
+ * struct vmw_screen_target_display_unit - conglomerated STDU structure
+ *
+ * @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
+ *
+ * Returns: %0 on success or -errno code on failure
+ */
+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
+ *
+ * Returns: %0 on success, negative error code on failure. -ERESTARTSYS if
+ * interrupted.
+ */
+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_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct vmw_private *dev_priv;
+ struct vmw_screen_target_display_unit *stdu;
+ struct drm_crtc_state *new_crtc_state;
+ int ret;
+
+ if (!crtc) {
+ DRM_ERROR("CRTC is NULL\n");
+ return;
+ }
+
+ stdu = vmw_crtc_to_stdu(crtc);
+ dev_priv = vmw_priv(crtc->dev);
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+ if (dev_priv->vkms_enabled)
+ drm_crtc_vblank_off(crtc);
+
+ 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);
+
+ /* Don't destroy the Screen Target if we are only setting the
+ * display as inactive
+ */
+ if (new_crtc_state->enable &&
+ !new_crtc_state->active &&
+ !new_crtc_state->mode_changed)
+ return;
+
+ 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 vmw_bo *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;
+ src_offset = ddirty->top * src_pitch + ddirty->left * stdu->cpp;
+
+ dst_pitch = ddirty->pitch;
+ dst_bo = ddirty->buf;
+ 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 = &vmw_user_object_surface(&vfbs->uo)->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_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);
+
+ 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,
+ .enable_vblank = vmw_vkms_enable_vblank,
+ .disable_vblank = vmw_vkms_disable_vblank,
+ .get_vblank_timestamp = vmw_vkms_get_vblank_timestamp,
+ .get_crc_sources = vmw_vkms_get_crc_sources,
+ .set_crc_source = vmw_vkms_set_crc_source,
+ .verify_crc_source = vmw_vkms_verify_crc_source,
+};
+
+
+
+/******************************************************************************
+ * 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 enum drm_mode_status
+vmw_stdu_connector_mode_valid(struct drm_connector *connector,
+ const struct drm_display_mode *mode)
+{
+ enum drm_mode_status ret;
+ struct drm_device *dev = connector->dev;
+ struct vmw_private *dev_priv = vmw_priv(dev);
+ u64 assumed_cpp = dev_priv->assume_16bpp ? 2 : 4;
+ /* Align width and height to account for GPU tile over-alignment */
+ u64 required_mem = ALIGN(mode->hdisplay, GPU_TILE_SIZE) *
+ ALIGN(mode->vdisplay, GPU_TILE_SIZE) *
+ assumed_cpp;
+ required_mem = ALIGN(required_mem, PAGE_SIZE);
+
+ ret = drm_mode_validate_size(mode, dev_priv->stdu_max_width,
+ dev_priv->stdu_max_height);
+ if (ret != MODE_OK)
+ return ret;
+
+ ret = drm_mode_validate_size(mode, dev_priv->texture_max_width,
+ dev_priv->texture_max_height);
+ if (ret != MODE_OK)
+ return ret;
+
+ if (required_mem > dev_priv->max_primary_mem)
+ return MODE_MEM;
+
+ if (required_mem > dev_priv->max_mob_pages * PAGE_SIZE)
+ return MODE_MEM;
+
+ if (required_mem > dev_priv->max_mob_size)
+ return MODE_MEM;
+
+ return MODE_OK;
+}
+
+/*
+ * Trigger a modeset if the X,Y position of the Screen Target changes.
+ * This is needed when multi-mon is cycled. The original Screen Target will have
+ * the same mode but its relative X,Y position in the topology will change.
+ */
+static int vmw_stdu_connector_atomic_check(struct drm_connector *conn,
+ struct drm_atomic_commit *state)
+{
+ struct drm_connector_state *conn_state;
+ struct vmw_screen_target_display_unit *du;
+ struct drm_crtc_state *new_crtc_state;
+
+ conn_state = drm_atomic_get_connector_state(state, conn);
+
+ if (IS_ERR(conn_state))
+ return PTR_ERR(conn_state);
+
+ du = vmw_connector_to_stdu(conn);
+
+ if (!conn_state->crtc)
+ return 0;
+
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+ if (du->base.gui_x != du->base.set_gui_x ||
+ du->base.gui_y != du->base.set_gui_y)
+ new_crtc_state->mode_changed = true;
+
+ return 0;
+}
+
+static const struct drm_connector_funcs vmw_stdu_connector_funcs = {
+ .dpms = vmw_du_connector_dpms,
+ .detect = vmw_du_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_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 = {
+ .get_modes = vmw_connector_get_modes,
+ .mode_valid = vmw_stdu_connector_mode_valid,
+ .atomic_check = vmw_stdu_connector_atomic_check,
+};
+
+
+
+/******************************************************************************
+ * 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 (vmw_user_object_surface(&vps->uo))
+ 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;
+ struct drm_plane_state *old_state = plane->state;
+ struct drm_rect rect;
+ int ret;
+
+ /* No FB to prepare */
+ if (!new_fb) {
+ if (vmw_user_object_surface(&vps->uo)) {
+ WARN_ON(vps->pinned != 0);
+ vmw_user_object_unref(&vps->uo);
+ }
+
+ return 0;
+ }
+
+ vfb = vmw_framebuffer_to_vfb(new_fb);
+ new_vfbs = (vfb->bo) ? NULL : vmw_framebuffer_to_vfbs(new_fb);
+
+ if (new_vfbs &&
+ vmw_user_object_surface(&new_vfbs->uo)->metadata.base_size.width == hdisplay &&
+ vmw_user_object_surface(&new_vfbs->uo)->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 = vmw_user_object_surface(&new_vfbs->uo)->metadata;
+ }
+
+ metadata.base_size.width = hdisplay;
+ metadata.base_size.height = vdisplay;
+ metadata.base_size.depth = 1;
+
+ if (vmw_user_object_surface(&vps->uo)) {
+ struct drm_vmw_size cur_base_size =
+ vmw_user_object_surface(&vps->uo)->metadata.base_size;
+
+ if (cur_base_size.width != metadata.base_size.width ||
+ cur_base_size.height != metadata.base_size.height ||
+ vmw_user_object_surface(&vps->uo)->metadata.format != metadata.format) {
+ WARN_ON(vps->pinned != 0);
+ vmw_user_object_unref(&vps->uo);
+ }
+
+ }
+
+ if (!vmw_user_object_surface(&vps->uo)) {
+ ret = vmw_gb_surface_define(dev_priv, &metadata,
+ &vps->uo.surface);
+ 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 (vmw_user_object_surface(&vps->uo)) {
+ WARN_ON(vps->pinned != 0);
+ vmw_user_object_unref(&vps->uo);
+ }
+
+ memcpy(&vps->uo, &new_vfbs->uo, sizeof(vps->uo));
+ vmw_user_object_ref(&vps->uo);
+ }
+
+ if (vmw_user_object_surface(&vps->uo)) {
+
+ /* Pin new surface before flipping */
+ ret = vmw_resource_pin(&vmw_user_object_surface(&vps->uo)->res, false);
+ if (ret)
+ goto out_srf_unref;
+
+ vps->pinned++;
+ }
+
+ vps->content_fb_type = new_content_type;
+
+ /*
+ * The drm fb code will do blit's via the vmap interface, which doesn't
+ * trigger vmw_bo page dirty tracking due to being kernel side (and thus
+ * doesn't require mmap'ing) so we have to update the surface's dirty
+ * regions by hand but we want to be careful to not overwrite the
+ * resource if it has been written to by the gpu (res_dirty).
+ */
+ if (vps->uo.buffer && vps->uo.buffer->is_dumb) {
+ struct vmw_surface *surf = vmw_user_object_surface(&vps->uo);
+ struct vmw_resource *res = &surf->res;
+
+ if (!res->res_dirty && drm_atomic_helper_damage_merged(old_state,
+ new_state,
+ &rect)) {
+ /*
+ * At some point it might be useful to actually translate
+ * (rect.x1, rect.y1) => start, and (rect.x2, rect.y2) => end,
+ * but currently the fb code will just report the entire fb
+ * dirty so in practice it doesn't matter.
+ */
+ 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_resource_dirty_update(res, start, end);
+ }
+ }
+
+ /*
+ * 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_user_object_unref(&vps->uo);
+ 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 vmw_bo *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;
+ 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;
+ 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)
+{
+ uint32_t size = 0;
+
+ 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)
+{
+ uint32_t size = 0;
+
+ size += sizeof(struct vmw_stdu_surface_copy) + sizeof(SVGA3dCopyBox) *
+ num_hits + sizeof(struct vmw_stdu_update);
+
+ return 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 = vmw_user_object_surface(&vfbs->uo)->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 (vmw_user_object_surface(&vfbs->uo)->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_commit *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 = vmw_user_object_surface(&vps->uo);
+ 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 void
+vmw_stdu_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+ struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc);
+
+ if (vmw->vkms_enabled)
+ vmw_vkms_set_crc_surface(crtc, stdu->display_srf);
+ vmw_vkms_crtc_atomic_flush(crtc, state);
+}
+
+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_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_cursor_plane_atomic_check,
+ .atomic_update = vmw_cursor_plane_atomic_update,
+ .prepare_fb = vmw_cursor_plane_prepare_fb,
+ .cleanup_fb = vmw_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 = {
+ .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_stdu_crtc_atomic_flush,
+ .atomic_enable = vmw_vkms_crtc_atomic_enable,
+ .atomic_disable = vmw_stdu_crtc_atomic_disable,
+ .handle_vblank_timeout = vmw_vkms_handle_vblank_timeout,
+};
+
+
+/**
+ * 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.
+ *
+ * Returns: %0 on success or -errno code on failure
+ */
+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_obj(*stdu);
+ 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);
+ drm_plane_enable_fb_damage_clips(&cursor->base);
+
+ 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);
+
+ 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);
+
+ vmw_du_init(&stdu->base);
+
+ 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 000000000..52c6cfa16
--- /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_obj(*so);
+ 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 000000000..bd0563741
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -0,0 +1,2350 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2009-2025 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ **************************************************************************/
+
+#include "vmwgfx_bo.h"
+#include "vmwgfx_cursor_plane.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/drm_dumb_buffers.h>
+#include <drm/ttm/ttm_placement.h>
+
+#define SVGA3D_FLAGS_64(upper32, lower32) (((uint64_t)upper32 << 32) | lower32)
+
+/**
+ * struct vmw_user_surface - User-space visible surface resource
+ *
+ * @prime: The TTM prime object.
+ * @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[] __counted_by(num_subres);
+};
+
+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,
+ const 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(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;
+
+ /*
+ * Dumb buffers own the resource and they'll unref the
+ * resource themselves
+ */
+ WARN_ON(res && res->guest_memory_bo && res->guest_memory_bo->is_dumb);
+
+ 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_obj(*user_srf);
+ 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_objs(*srf->offsets, metadata->num_sizes);
+ 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;
+
+ srf->snooper.image = vmw_cursor_snooper_create(file_priv, metadata);
+ if (IS_ERR(srf->snooper.image)) {
+ ret = PTR_ERR(srf->snooper.image);
+ goto out_no_copy;
+ }
+
+ 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 surface will expect a backup
+ * buffer to be present.
+ */
+ if (dev_priv->has_mob) {
+ 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_bo_create(dev_priv, &params, &res->guest_memory_bo);
+ if (unlikely(ret != 0)) {
+ vmw_resource_unreference(&res);
+ goto out_unlock;
+ }
+
+ ret = vmw_bo_add_detached_resource(res->guest_memory_bo, res);
+ 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,
+ 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 struct vmw_user_surface *
+vmw_lookup_user_surface_for_buffer(struct vmw_private *vmw, struct vmw_bo *bo,
+ u32 handle)
+{
+ struct vmw_user_surface *user_srf = NULL;
+ struct vmw_surface *surf;
+ struct ttm_base_object *base;
+
+ surf = vmw_bo_surface(bo);
+ if (surf) {
+ rcu_read_lock();
+ user_srf = container_of(surf, struct vmw_user_surface, srf);
+ base = &user_srf->prime.base;
+ if (base && !kref_get_unless_zero(&base->refcount)) {
+ drm_dbg_driver(&vmw->drm,
+ "%s: referencing a stale surface handle %d\n",
+ __func__, handle);
+ base = NULL;
+ user_srf = NULL;
+ }
+ rcu_read_unlock();
+ }
+
+ return user_srf;
+}
+
+struct vmw_surface *vmw_lookup_surface_for_buffer(struct vmw_private *vmw,
+ struct vmw_bo *bo,
+ u32 handle)
+{
+ struct vmw_user_surface *user_srf =
+ vmw_lookup_user_surface_for_buffer(vmw, bo, handle);
+ struct vmw_surface *surf = NULL;
+ struct ttm_base_object *base;
+
+ if (user_srf) {
+ surf = vmw_surface_reference(&user_srf->srf);
+ base = &user_srf->prime.base;
+ ttm_base_object_unref(&base);
+ }
+ return surf;
+}
+
+u32 vmw_lookup_surface_handle_for_buffer(struct vmw_private *vmw,
+ struct vmw_bo *bo,
+ u32 handle)
+{
+ struct vmw_user_surface *user_srf =
+ vmw_lookup_user_surface_for_buffer(vmw, bo, handle);
+ int surf_handle = 0;
+ struct ttm_base_object *base;
+
+ if (user_srf) {
+ base = &user_srf->prime.base;
+ surf_handle = (u32)base->handle;
+ ttm_base_object_unref(&base);
+ }
+ return surf_handle;
+}
+
+static int vmw_buffer_prime_to_surface_base(struct vmw_private *dev_priv,
+ struct drm_file *file_priv,
+ u32 fd, u32 *handle,
+ struct ttm_base_object **base_p)
+{
+ struct ttm_base_object *base;
+ struct vmw_bo *bo;
+ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+ struct vmw_user_surface *user_srf;
+ int ret;
+
+ ret = drm_gem_prime_fd_to_handle(&dev_priv->drm, file_priv, fd, handle);
+ if (ret) {
+ drm_warn(&dev_priv->drm,
+ "Wasn't able to find user buffer for fd = %u.\n", fd);
+ return ret;
+ }
+
+ ret = vmw_user_bo_lookup(file_priv, *handle, &bo);
+ if (ret) {
+ drm_warn(&dev_priv->drm,
+ "Wasn't able to lookup user buffer for handle = %u.\n", *handle);
+ return ret;
+ }
+
+ user_srf = vmw_lookup_user_surface_for_buffer(dev_priv, bo, *handle);
+ if (WARN_ON(!user_srf)) {
+ drm_warn(&dev_priv->drm,
+ "User surface fd %d (handle %d) is null.\n", fd, *handle);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ base = &user_srf->prime.base;
+ ret = ttm_ref_object_add(tfile, base, NULL, false);
+ if (ret) {
+ drm_warn(&dev_priv->drm,
+ "Couldn't add an object ref for the buffer (%d).\n", *handle);
+ goto out;
+ }
+
+ *base_p = base;
+out:
+ vmw_user_bo_unref(&bo);
+
+ 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 = NULL;
+ 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 (ret)
+ return vmw_buffer_prime_to_surface_base(dev_priv,
+ file_priv,
+ u_handle,
+ &handle,
+ base_p);
+ } 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,
+ const 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) {
+ if (has_sm5_context(dev_priv)) {
+ if (req->base.array_size > SVGA3D_SM5_MAX_SURFACE_ARRAYSIZE) {
+ VMW_DEBUG_USER("Invalid Surface Array Size.\n");
+ return -EINVAL;
+ }
+ } else if (has_sm4_context(dev_priv)) {
+ if (req->base.array_size > SVGA3D_SM4_MAX_SURFACE_ARRAYSIZE) {
+ VMW_DEBUG_USER("Invalid Surface Array Size.\n");
+ return -EINVAL;
+ }
+ } else {
+ 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->is_dumb) {
+ VMW_DEBUG_USER("Can't backup surface with a dumb buffer.\n");
+ vmw_user_bo_unref(&res->guest_memory_bo);
+ ret = -EINVAL;
+ goto out_unlock;
+ } else 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;
+ }
+
+ }
+
+ if (res->guest_memory_bo) {
+ ret = vmw_bo_add_detached_resource(res->guest_memory_bo, res);
+ if (unlikely(ret != 0)) {
+ 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,
+ 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_obj(*user_srf);
+ if (unlikely(!user_srf)) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
+
+ *srf_out = &user_srf->srf;
+
+ 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;
+}
+
+static SVGA3dSurfaceFormat vmw_format_bpp_to_svga(struct vmw_private *vmw,
+ int bpp)
+{
+ switch (bpp) {
+ case 8: /* DRM_FORMAT_C8 */
+ return SVGA3D_P8;
+ case 16: /* DRM_FORMAT_RGB565 */
+ return SVGA3D_R5G6B5;
+ case 32: /* DRM_FORMAT_XRGB8888 */
+ if (has_sm4_context(vmw))
+ return SVGA3D_B8G8R8X8_UNORM;
+ return SVGA3D_X8R8G8B8;
+ default:
+ drm_warn(&vmw->drm, "Unsupported format bpp: %d\n", bpp);
+ return SVGA3D_X8R8G8B8;
+ }
+}
+
+/**
+ * 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 ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
+ struct vmw_bo *vbo = NULL;
+ struct vmw_resource *res = NULL;
+ union drm_vmw_gb_surface_create_ext_arg arg = { 0 };
+ struct drm_vmw_gb_surface_create_ext_req *req = &arg.req;
+ int ret;
+ struct drm_vmw_size drm_size = {
+ .width = args->width,
+ .height = args->height,
+ .depth = 1,
+ };
+ SVGA3dSurfaceFormat format = vmw_format_bpp_to_svga(dev_priv, args->bpp);
+ const struct SVGA3dSurfaceDesc *desc = vmw_surface_get_desc(format);
+ SVGA3dSurfaceAllFlags flags = SVGA3D_SURFACE_HINT_TEXTURE |
+ SVGA3D_SURFACE_HINT_RENDERTARGET |
+ SVGA3D_SURFACE_SCREENTARGET;
+
+ if (vmw_surface_is_dx_screen_target_format(format)) {
+ flags |= SVGA3D_SURFACE_BIND_SHADER_RESOURCE |
+ SVGA3D_SURFACE_BIND_RENDER_TARGET;
+ }
+
+ /*
+ * Without mob support we're just going to use raw memory buffer
+ * because we wouldn't be able to support full surface coherency
+ * without mobs. There also no reason to support surface coherency
+ * without 3d (i.e. gpu usage on the host) because then all the
+ * contents is going to be rendered guest side.
+ */
+ if (!dev_priv->has_mob || !vmw_supports_3d(dev_priv)) {
+ ret = drm_mode_size_dumb(dev, args, 0, 0);
+ if (ret)
+ return ret;
+
+ 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;
+ }
+
+ req->version = drm_vmw_gb_surface_v1;
+ req->multisample_pattern = SVGA3D_MS_PATTERN_NONE;
+ req->quality_level = SVGA3D_MS_QUALITY_NONE;
+ req->buffer_byte_stride = 0;
+ req->must_be_zero = 0;
+ req->base.svga3d_flags = SVGA3D_FLAGS_LOWER_32(flags);
+ req->svga3d_flags_upper_32_bits = SVGA3D_FLAGS_UPPER_32(flags);
+ req->base.format = (uint32_t)format;
+ req->base.drm_surface_flags = drm_vmw_surface_flag_scanout;
+ req->base.drm_surface_flags |= drm_vmw_surface_flag_shareable;
+ req->base.drm_surface_flags |= drm_vmw_surface_flag_create_buffer;
+ req->base.drm_surface_flags |= drm_vmw_surface_flag_coherent;
+ req->base.base_size.width = args->width;
+ req->base.base_size.height = args->height;
+ req->base.base_size.depth = 1;
+ req->base.array_size = 0;
+ req->base.mip_levels = 1;
+ req->base.multisample_count = 0;
+ req->base.buffer_handle = SVGA3D_INVALID_ID;
+ req->base.autogen_filter = SVGA3D_TEX_FILTER_NONE;
+ ret = vmw_gb_surface_define_ext_ioctl(dev, &arg, file_priv);
+ if (ret) {
+ drm_warn(dev, "Unable to create a dumb buffer\n");
+ return ret;
+ }
+
+ args->handle = arg.rep.buffer_handle;
+ args->size = arg.rep.buffer_size;
+ args->pitch = vmw_surface_calculate_pitch(desc, &drm_size);
+
+ ret = vmw_user_resource_lookup_handle(dev_priv, tfile, arg.rep.handle,
+ user_surface_converter,
+ &res);
+ if (ret) {
+ drm_err(dev, "Created resource handle doesn't exist!\n");
+ goto err;
+ }
+
+ vbo = res->guest_memory_bo;
+ vbo->is_dumb = true;
+ vbo->dumb_surface = vmw_res_to_srf(res);
+ drm_gem_object_put(&vbo->tbo.base);
+ /*
+ * Unset the user surface dtor since this in not actually exposed
+ * to userspace. The suface is owned via the dumb_buffer's GEM handle
+ */
+ struct vmw_user_surface *usurf = container_of(vbo->dumb_surface,
+ struct vmw_user_surface, srf);
+ usurf->prime.base.refcount_release = NULL;
+err:
+ if (res)
+ vmw_resource_unreference(&res);
+
+ ttm_ref_object_base_unref(tfile, arg.rep.handle);
+
+ 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 000000000..585996b6c
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_system_manager.c
@@ -0,0 +1,89 @@
+/* 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_obj(**res);
+ 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_obj(*man);
+
+ 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 000000000..dfd08ee19
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c
@@ -0,0 +1,595 @@
+// 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
+};
+
+struct ttm_placement vmw_vram_placement = {
+ .num_placement = 1,
+ .placement = &vram_placement_flags,
+};
+
+struct ttm_placement vmw_sys_placement = {
+ .num_placement = 1,
+ .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:
+ if (vmw_tt->dma_ttm.page_flags & TTM_TT_FLAG_EXTERNAL) {
+ vsgt->sgt = vmw_tt->dma_ttm.sg;
+ } else {
+ 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:
+ drm_warn(&dev_priv->drm, "VSG table map failed!");
+ sg_free_table(vsgt->sgt);
+ 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)
+{
+ bool external = (ttm->page_flags & TTM_TT_FLAG_EXTERNAL) != 0;
+
+ if (ttm_tt_is_populated(ttm))
+ return 0;
+
+ if (external && ttm->sg)
+ return drm_prime_sg_to_dma_addr_array(ttm->sg,
+ ttm->dma_address,
+ ttm->num_pages);
+
+ return ttm_pool_alloc(&bdev->pool, ttm, ctx);
+}
+
+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);
+ bool external = (ttm->page_flags & TTM_TT_FLAG_EXTERNAL) != 0;
+
+ if (external)
+ return;
+
+ 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;
+ bool external = bo->type == ttm_bo_type_sg;
+
+ vmw_be = kzalloc_obj(*vmw_be);
+ if (!vmw_be)
+ return NULL;
+
+ vmw_be->dev_priv = vmw_priv_from_ttm(bo->bdev);
+ vmw_be->mob = NULL;
+
+ if (external)
+ page_flags |= TTM_TT_FLAG_EXTERNAL | TTM_TT_FLAG_EXTERNAL_MAPPABLE;
+
+ if (vmw_be->dev_priv->map_mode == vmw_dma_alloc_coherent || external)
+ 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,
+ .keep_resv = true,
+ };
+
+ ret = vmw_bo_create(dev_priv, &bo_params, &vbo);
+ if (unlikely(ret != 0))
+ return ret;
+
+ 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_va.c b/drivers/gpu/drm/vmwgfx/vmwgfx_va.c
new file mode 100644
index 000000000..d140089e5
--- /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 000000000..45fde7ec5
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
@@ -0,0 +1,849 @@
+// 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>
+
+/**
+ * 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 = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!page)
+ return NULL;
+
+ 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;
+}
+
+/**
+ * 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;
+ vmw_bo_reference(vbo);
+ val_buf->bo = &vbo->tbo;
+ 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) {
+ if (ctx->sw_context)
+ hash_del_rcu(&node->hash.head);
+ 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(&val->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) {
+ drm_gem_object_put(&entry->base.bo->base);
+ 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 000000000..353d83790
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h
@@ -0,0 +1,190 @@
+/* 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
+ */
+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_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
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
new file mode 100644
index 000000000..3d0d5dfa8
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
@@ -0,0 +1,604 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/**************************************************************************
+ *
+ * Copyright (c) 2024 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ * 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_vkms.h"
+
+#include "vmwgfx_bo.h"
+#include "vmwgfx_drv.h"
+#include "vmwgfx_kms.h"
+
+#include "vmw_surface_cache.h"
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_debugfs_crc.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+
+#define GUESTINFO_VBLANK "guestinfo.vmwgfx.vkms_enable"
+
+static int
+vmw_surface_sync(struct vmw_private *vmw,
+ struct vmw_surface *surf)
+{
+ int ret;
+ struct vmw_fence_obj *fence = NULL;
+ struct vmw_bo *bo = surf->res.guest_memory_bo;
+
+ vmw_resource_clean(&surf->res);
+
+ ret = ttm_bo_reserve(&bo->tbo, false, false, NULL);
+ if (ret != 0) {
+ drm_warn(&vmw->drm, "%s: failed reserve\n", __func__);
+ goto done;
+ }
+
+ ret = vmw_execbuf_fence_commands(NULL, vmw, &fence, NULL);
+ if (ret != 0) {
+ drm_warn(&vmw->drm, "%s: failed execbuf\n", __func__);
+ ttm_bo_unreserve(&bo->tbo);
+ goto done;
+ }
+
+ dma_fence_wait(&fence->base, false);
+ dma_fence_put(&fence->base);
+
+ ttm_bo_unreserve(&bo->tbo);
+done:
+ return ret;
+}
+
+static void
+compute_crc(struct drm_crtc *crtc,
+ struct vmw_surface *surf,
+ u32 *crc)
+{
+ u8 *mapped_surface;
+ struct vmw_bo *bo = surf->res.guest_memory_bo;
+ const struct SVGA3dSurfaceDesc *desc =
+ vmw_surface_get_desc(surf->metadata.format);
+ u32 row_pitch_bytes;
+ SVGA3dSize blocks;
+ u32 y;
+
+ *crc = 0;
+
+ vmw_surface_get_size_in_blocks(desc, &surf->metadata.base_size, &blocks);
+ row_pitch_bytes = blocks.width * desc->pitchBytesPerBlock;
+ WARN_ON(!bo);
+ mapped_surface = vmw_bo_map_and_cache(bo);
+
+ for (y = 0; y < blocks.height; y++) {
+ *crc = crc32_le(*crc, mapped_surface, row_pitch_bytes);
+ mapped_surface += row_pitch_bytes;
+ }
+
+ vmw_bo_unmap(bo);
+}
+
+static void
+crc_generate_worker(struct work_struct *work)
+{
+ struct vmw_display_unit *du =
+ container_of(work, struct vmw_display_unit, vkms.crc_generator_work);
+ struct drm_crtc *crtc = &du->crtc;
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+ bool crc_pending;
+ u64 frame_start, frame_end;
+ u32 crc32 = 0;
+ struct vmw_surface *surf = 0;
+
+ spin_lock_irq(&du->vkms.crc_state_lock);
+ crc_pending = du->vkms.crc_pending;
+ spin_unlock_irq(&du->vkms.crc_state_lock);
+
+ /*
+ * We raced with the vblank hrtimer and previous work already computed
+ * the crc, nothing to do.
+ */
+ if (!crc_pending)
+ return;
+
+ spin_lock_irq(&du->vkms.crc_state_lock);
+ surf = vmw_surface_reference(du->vkms.surface);
+ spin_unlock_irq(&du->vkms.crc_state_lock);
+
+ if (surf) {
+ if (vmw_surface_sync(vmw, surf)) {
+ drm_warn(
+ crtc->dev,
+ "CRC worker wasn't able to sync the crc surface!\n");
+ return;
+ }
+
+ compute_crc(crtc, surf, &crc32);
+ vmw_surface_unreference(&surf);
+ }
+
+ spin_lock_irq(&du->vkms.crc_state_lock);
+ frame_start = du->vkms.frame_start;
+ frame_end = du->vkms.frame_end;
+ du->vkms.frame_start = 0;
+ du->vkms.frame_end = 0;
+ du->vkms.crc_pending = false;
+ spin_unlock_irq(&du->vkms.crc_state_lock);
+
+ /*
+ * The worker can fall behind the vblank hrtimer, make sure we catch up.
+ */
+ while (frame_start <= frame_end)
+ drm_crtc_add_crc_entry(crtc, true, frame_start++, &crc32);
+}
+
+bool
+vmw_vkms_handle_vblank_timeout(struct drm_crtc *crtc)
+{
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+ bool has_surface = false;
+ bool locked, ret;
+
+ locked = vmw_vkms_vblank_trylock(crtc);
+ ret = drm_crtc_handle_vblank(crtc);
+ WARN_ON(!ret);
+ if (!locked)
+ return true;
+ has_surface = du->vkms.surface != NULL;
+ vmw_vkms_unlock(crtc);
+
+ if (du->vkms.crc_enabled && has_surface) {
+ u64 frame = drm_crtc_accurate_vblank_count(crtc);
+
+ spin_lock(&du->vkms.crc_state_lock);
+ if (!du->vkms.crc_pending)
+ du->vkms.frame_start = frame;
+ else
+ drm_dbg_driver(crtc->dev,
+ "crc worker falling behind, frame_start: %llu, frame_end: %llu\n",
+ du->vkms.frame_start, frame);
+ du->vkms.frame_end = frame;
+ du->vkms.crc_pending = true;
+ spin_unlock(&du->vkms.crc_state_lock);
+
+ ret = queue_work(vmw->crc_workq, &du->vkms.crc_generator_work);
+ if (!ret)
+ drm_dbg_driver(crtc->dev, "Composer worker already queued\n");
+ }
+
+ return true;
+}
+
+void
+vmw_vkms_init(struct vmw_private *vmw)
+{
+ char buffer[64];
+ const size_t max_buf_len = sizeof(buffer) - 1;
+ size_t buf_len = max_buf_len;
+ int ret;
+
+ vmw->vkms_enabled = false;
+
+ ret = vmw_host_get_guestinfo(GUESTINFO_VBLANK, buffer, &buf_len);
+ if (!ret && buf_len <= max_buf_len) {
+ buffer[buf_len] = '\0';
+
+ ret = kstrtobool(buffer, &vmw->vkms_enabled);
+ if (!ret && vmw->vkms_enabled) {
+ ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS);
+ vmw->vkms_enabled = (ret == 0);
+ }
+ }
+
+ vmw->crc_workq = alloc_ordered_workqueue("vmwgfx_crc_generator", 0);
+ if (!vmw->crc_workq) {
+ drm_warn(&vmw->drm, "crc workqueue allocation failed. Disabling vkms.");
+ vmw->vkms_enabled = false;
+ }
+ if (vmw->vkms_enabled)
+ drm_info(&vmw->drm, "VKMS enabled\n");
+}
+
+void
+vmw_vkms_cleanup(struct vmw_private *vmw)
+{
+ if (vmw->crc_workq)
+ destroy_workqueue(vmw->crc_workq);
+}
+
+bool
+vmw_vkms_get_vblank_timestamp(struct drm_crtc *crtc,
+ int *max_error,
+ ktime_t *vblank_time,
+ bool in_vblank_irq)
+{
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+
+ if (!vmw->vkms_enabled)
+ return false;
+
+ drm_crtc_vblank_get_vblank_timeout(crtc, vblank_time);
+
+ return true;
+}
+
+int
+vmw_vkms_enable_vblank(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct vmw_private *vmw = vmw_priv(dev);
+
+ if (!vmw->vkms_enabled)
+ return -EINVAL;
+
+ return drm_crtc_vblank_start_timer(crtc);
+}
+
+void
+vmw_vkms_disable_vblank(struct drm_crtc *crtc)
+{
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+
+ if (!vmw->vkms_enabled)
+ return;
+
+ drm_crtc_vblank_cancel_timer(crtc);
+
+ du->vkms.surface = NULL;
+}
+
+enum vmw_vkms_lock_state {
+ VMW_VKMS_LOCK_UNLOCKED = 0,
+ VMW_VKMS_LOCK_MODESET = 1,
+ VMW_VKMS_LOCK_VBLANK = 2
+};
+
+void
+vmw_vkms_crtc_init(struct drm_crtc *crtc)
+{
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+
+ atomic_set(&du->vkms.atomic_lock, VMW_VKMS_LOCK_UNLOCKED);
+ spin_lock_init(&du->vkms.crc_state_lock);
+
+ INIT_WORK(&du->vkms.crc_generator_work, crc_generate_worker);
+ du->vkms.surface = NULL;
+}
+
+void
+vmw_vkms_crtc_cleanup(struct drm_crtc *crtc)
+{
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+
+ if (vmw->vkms_enabled)
+ drm_crtc_vblank_cancel_timer(crtc);
+
+ if (du->vkms.surface)
+ vmw_surface_unreference(&du->vkms.surface);
+ WARN_ON(work_pending(&du->vkms.crc_generator_work));
+}
+
+void
+vmw_vkms_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+
+ if (vmw->vkms_enabled)
+ vmw_vkms_modeset_lock(crtc);
+}
+
+void
+vmw_vkms_crtc_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ unsigned long flags;
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+
+ if (!vmw->vkms_enabled)
+ return;
+
+ if (crtc->state->event) {
+ spin_lock_irqsave(&crtc->dev->event_lock, flags);
+
+ if (drm_crtc_vblank_get(crtc) != 0)
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ else
+ drm_crtc_arm_vblank_event(crtc, crtc->state->event);
+
+ spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+
+ crtc->state->event = NULL;
+ }
+
+ vmw_vkms_unlock(crtc);
+}
+
+void
+vmw_vkms_crtc_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+
+ if (vmw->vkms_enabled)
+ drm_crtc_vblank_on(crtc);
+}
+
+void
+vmw_vkms_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state)
+{
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+
+ if (vmw->vkms_enabled)
+ drm_crtc_vblank_off(crtc);
+}
+
+static bool
+is_crc_supported(struct drm_crtc *crtc)
+{
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+
+ if (!vmw->vkms_enabled)
+ return false;
+
+ if (vmw->active_display_unit != vmw_du_screen_target)
+ return false;
+
+ return true;
+}
+
+static const char * const pipe_crc_sources[] = {"auto"};
+
+static int
+crc_parse_source(const char *src_name,
+ bool *enabled)
+{
+ int ret = 0;
+
+ if (!src_name) {
+ *enabled = false;
+ } else if (strcmp(src_name, "auto") == 0) {
+ *enabled = true;
+ } else {
+ *enabled = false;
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+const char *const *
+vmw_vkms_get_crc_sources(struct drm_crtc *crtc,
+ size_t *count)
+{
+ *count = 0;
+ if (!is_crc_supported(crtc))
+ return NULL;
+
+ *count = ARRAY_SIZE(pipe_crc_sources);
+ return pipe_crc_sources;
+}
+
+int
+vmw_vkms_verify_crc_source(struct drm_crtc *crtc,
+ const char *src_name,
+ size_t *values_cnt)
+{
+ bool enabled;
+
+ if (!is_crc_supported(crtc))
+ return -EINVAL;
+
+ if (crc_parse_source(src_name, &enabled) < 0) {
+ drm_dbg_driver(crtc->dev, "unknown source '%s'\n", src_name);
+ return -EINVAL;
+ }
+
+ *values_cnt = 1;
+
+ return 0;
+}
+
+int
+vmw_vkms_set_crc_source(struct drm_crtc *crtc,
+ const char *src_name)
+{
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+ bool enabled, prev_enabled, locked;
+ int ret;
+
+ if (!is_crc_supported(crtc))
+ return -EINVAL;
+
+ ret = crc_parse_source(src_name, &enabled);
+
+ if (enabled)
+ drm_crtc_vblank_get(crtc);
+
+ locked = vmw_vkms_modeset_lock_relaxed(crtc);
+ prev_enabled = du->vkms.crc_enabled;
+ du->vkms.crc_enabled = enabled;
+ if (locked)
+ vmw_vkms_unlock(crtc);
+
+ if (prev_enabled)
+ drm_crtc_vblank_put(crtc);
+
+ return ret;
+}
+
+void
+vmw_vkms_set_crc_surface(struct drm_crtc *crtc,
+ struct vmw_surface *surf)
+{
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+ struct vmw_private *vmw = vmw_priv(crtc->dev);
+
+ if (vmw->vkms_enabled && du->vkms.surface != surf) {
+ WARN_ON(atomic_read(&du->vkms.atomic_lock) != VMW_VKMS_LOCK_MODESET);
+ if (du->vkms.surface)
+ vmw_surface_unreference(&du->vkms.surface);
+ if (surf)
+ du->vkms.surface = vmw_surface_reference(surf);
+ }
+}
+
+/**
+ * vmw_vkms_lock_max_wait_ns - Return the max wait for the vkms lock
+ * @du: The vmw_display_unit from which to grab the vblank timings
+ *
+ * Returns the maximum wait time used to acquire the vkms lock. By
+ * default uses a time of a single frame and in case where vblank
+ * was not initialized for the display unit 1/60th of a second.
+ */
+static inline u64
+vmw_vkms_lock_max_wait_ns(struct vmw_display_unit *du)
+{
+ struct drm_crtc *crtc = &du->crtc;
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+
+ if (!vblank || !vblank->framedur_ns)
+ return NSEC_PER_SEC / 60; /* disabled; assume 60 Hz */
+
+ return vblank->framedur_ns;
+}
+
+/**
+ * vmw_vkms_modeset_lock - Protects access to crtc during modeset
+ * @crtc: The crtc to lock for vkms
+ *
+ * This function prevents the VKMS timers/callbacks from being called
+ * while a modeset operation is in process. We don't want the callbacks
+ * e.g. the vblank simulator to be trying to access incomplete state
+ * so we need to make sure they execute only when the modeset has
+ * finished.
+ *
+ * Normally this would have been done with a spinlock but locking the
+ * entire atomic modeset with vmwgfx is impossible because kms prepare
+ * executes non-atomic ops (e.g. vmw_validation_prepare holds a mutex to
+ * guard various bits of state). Which means that we need to synchronize
+ * atomic context (the vblank handler) with the non-atomic entirity
+ * of kms - so use an atomic_t to track which part of vkms has access
+ * to the basic vkms state.
+ */
+void
+vmw_vkms_modeset_lock(struct drm_crtc *crtc)
+{
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+ const u64 nsecs_delay = 10;
+ const u64 MAX_NSECS_DELAY = vmw_vkms_lock_max_wait_ns(du);
+ u64 total_delay = 0;
+ int ret;
+
+ do {
+ ret = atomic_cmpxchg(&du->vkms.atomic_lock,
+ VMW_VKMS_LOCK_UNLOCKED,
+ VMW_VKMS_LOCK_MODESET);
+ if (ret == VMW_VKMS_LOCK_UNLOCKED || total_delay >= MAX_NSECS_DELAY)
+ break;
+ ndelay(nsecs_delay);
+ total_delay += nsecs_delay;
+ } while (1);
+
+ if (total_delay >= MAX_NSECS_DELAY) {
+ drm_warn(crtc->dev, "VKMS lock expired! total_delay = %lld, ret = %d, cur = %d\n",
+ total_delay, ret, atomic_read(&du->vkms.atomic_lock));
+ }
+}
+
+/**
+ * vmw_vkms_modeset_lock_relaxed - Protects access to crtc during modeset
+ * @crtc: The crtc to lock for vkms
+ *
+ * Much like vmw_vkms_modeset_lock except that when the crtc is currently
+ * in a modeset it will return immediately.
+ *
+ * Returns true if actually locked vkms to modeset or false otherwise.
+ */
+bool
+vmw_vkms_modeset_lock_relaxed(struct drm_crtc *crtc)
+{
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+ const u64 nsecs_delay = 10;
+ const u64 MAX_NSECS_DELAY = vmw_vkms_lock_max_wait_ns(du);
+ u64 total_delay = 0;
+ int ret;
+
+ do {
+ ret = atomic_cmpxchg(&du->vkms.atomic_lock,
+ VMW_VKMS_LOCK_UNLOCKED,
+ VMW_VKMS_LOCK_MODESET);
+ if (ret == VMW_VKMS_LOCK_UNLOCKED ||
+ ret == VMW_VKMS_LOCK_MODESET ||
+ total_delay >= MAX_NSECS_DELAY)
+ break;
+ ndelay(nsecs_delay);
+ total_delay += nsecs_delay;
+ } while (1);
+
+ if (total_delay >= MAX_NSECS_DELAY) {
+ drm_warn(crtc->dev, "VKMS relaxed lock expired!\n");
+ return false;
+ }
+
+ return ret == VMW_VKMS_LOCK_UNLOCKED;
+}
+
+/**
+ * vmw_vkms_vblank_trylock - Protects access to crtc during vblank
+ * @crtc: The crtc to lock for vkms
+ *
+ * Tries to lock vkms for vblank, returns immediately.
+ *
+ * Returns true if locked vkms to vblank or false otherwise.
+ */
+bool
+vmw_vkms_vblank_trylock(struct drm_crtc *crtc)
+{
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+ u32 ret;
+
+ ret = atomic_cmpxchg(&du->vkms.atomic_lock,
+ VMW_VKMS_LOCK_UNLOCKED,
+ VMW_VKMS_LOCK_VBLANK);
+
+ return ret == VMW_VKMS_LOCK_UNLOCKED;
+}
+
+void
+vmw_vkms_unlock(struct drm_crtc *crtc)
+{
+ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
+
+ /* Release flag; mark it as unlocked. */
+ atomic_set(&du->vkms.atomic_lock, VMW_VKMS_LOCK_UNLOCKED);
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.h
new file mode 100644
index 000000000..27bc84631
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/**************************************************************************
+ *
+ * Copyright (c) 2024 Broadcom. All Rights Reserved. The term
+ * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
+ *
+ * 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_VKMS_H_
+#define VMWGFX_VKMS_H_
+
+#include <linux/types.h>
+
+struct drm_atomic_commit;
+struct drm_crtc;
+struct vmw_private;
+struct vmw_surface;
+
+void vmw_vkms_init(struct vmw_private *vmw);
+void vmw_vkms_cleanup(struct vmw_private *vmw);
+
+void vmw_vkms_modeset_lock(struct drm_crtc *crtc);
+bool vmw_vkms_modeset_lock_relaxed(struct drm_crtc *crtc);
+bool vmw_vkms_vblank_trylock(struct drm_crtc *crtc);
+void vmw_vkms_unlock(struct drm_crtc *crtc);
+
+bool vmw_vkms_handle_vblank_timeout(struct drm_crtc *crtc);
+bool vmw_vkms_get_vblank_timestamp(struct drm_crtc *crtc,
+ int *max_error,
+ ktime_t *vblank_time,
+ bool in_vblank_irq);
+int vmw_vkms_enable_vblank(struct drm_crtc *crtc);
+void vmw_vkms_disable_vblank(struct drm_crtc *crtc);
+
+void vmw_vkms_crtc_init(struct drm_crtc *crtc);
+void vmw_vkms_crtc_cleanup(struct drm_crtc *crtc);
+void vmw_vkms_crtc_atomic_begin(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state);
+void vmw_vkms_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_commit *state);
+void vmw_vkms_crtc_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state);
+void vmw_vkms_crtc_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_commit *state);
+
+const char *const *vmw_vkms_get_crc_sources(struct drm_crtc *crtc,
+ size_t *count);
+int vmw_vkms_verify_crc_source(struct drm_crtc *crtc,
+ const char *src_name,
+ size_t *values_cnt);
+int vmw_vkms_set_crc_source(struct drm_crtc *crtc,
+ const char *src_name);
+void vmw_vkms_set_crc_surface(struct drm_crtc *crtc,
+ struct vmw_surface *surf);
+
+#endif