1 /*
2 * Copyright (c) 2009-2024 Broadcom. All Rights Reserved.
3 * The term “Broadcom” refers to Broadcom Inc.
4 * and/or its subsidiaries.
5 * SPDX-License-Identifier: MIT
6 */
7
8 #include "util/compiler.h"
9 #include "util/u_inlines.h"
10 #include "util/u_memory.h"
11 #include "util/format/u_format.h"
12
13 #include "vmw_context.h"
14 #include "vmw_screen.h"
15 #include "vmw_surface.h"
16 #include "vmw_buffer.h"
17 #include "svga_drm_public.h"
18 #include "svga3d_surfacedefs.h"
19
20 #include "frontend/drm_driver.h"
21
22 #include "vmwgfx_drm.h"
23 #include <xf86drm.h>
24
25 #include <stdio.h>
26 #include <fcntl.h>
27
28 struct dri1_api_version {
29 int major;
30 int minor;
31 int patch_level;
32 };
33
34 static struct svga_winsys_surface *
35 vmw_drm_surface_from_handle(struct svga_winsys_screen *sws,
36 struct winsys_handle *whandle,
37 SVGA3dSurfaceFormat *format);
38
39 static struct svga_winsys_surface *
40 vmw_drm_gb_surface_from_handle(struct svga_winsys_screen *sws,
41 struct winsys_handle *whandle,
42 SVGA3dSurfaceFormat *format);
43 static bool
44 vmw_drm_surface_get_handle(struct svga_winsys_screen *sws,
45 struct svga_winsys_surface *surface,
46 unsigned stride,
47 struct winsys_handle *whandle);
48
49 static struct dri1_api_version drm_required = { 2, 1, 0 };
50 static struct dri1_api_version drm_compat = { 2, 0, 0 };
51
52 static bool
vmw_dri1_check_version(const struct dri1_api_version * cur,const struct dri1_api_version * required,const struct dri1_api_version * compat,const char component[])53 vmw_dri1_check_version(const struct dri1_api_version *cur,
54 const struct dri1_api_version *required,
55 const struct dri1_api_version *compat,
56 const char component[])
57 {
58 if (cur->major > required->major && cur->major <= compat->major)
59 return true;
60 if (cur->major == required->major && cur->minor >= required->minor)
61 return true;
62
63 vmw_error("%s version failure.\n", component);
64 vmw_error("%s version is %d.%d.%d and this driver can only work\n"
65 "with versions %d.%d.x through %d.x.x.\n",
66 component,
67 cur->major, cur->minor, cur->patch_level,
68 required->major, required->minor, compat->major);
69 return false;
70 }
71
72 /* This is actually the entrypoint to the entire driver,
73 * called by the target bootstrap code.
74 */
75 struct svga_winsys_screen *
svga_drm_winsys_screen_create(int fd)76 svga_drm_winsys_screen_create(int fd)
77 {
78 struct vmw_winsys_screen *vws;
79 struct dri1_api_version drm_ver;
80 drmVersionPtr ver;
81
82 ver = drmGetVersion(fd);
83 if (ver == NULL)
84 return NULL;
85
86 drm_ver.major = ver->version_major;
87 drm_ver.minor = ver->version_minor;
88 drm_ver.patch_level = 0; /* ??? */
89
90 drmFreeVersion(ver);
91 if (!vmw_dri1_check_version(&drm_ver, &drm_required,
92 &drm_compat, "vmwgfx drm driver"))
93 return NULL;
94
95 vws = vmw_winsys_create(fd);
96 if (!vws)
97 goto out_no_vws;
98
99 /* XXX do this properly */
100 vws->base.surface_from_handle = vws->base.have_gb_objects ?
101 vmw_drm_gb_surface_from_handle : vmw_drm_surface_from_handle;
102 vws->base.surface_get_handle = vmw_drm_surface_get_handle;
103
104 return &vws->base;
105
106 out_no_vws:
107 return NULL;
108 }
109
110 /**
111 * vmw_drm_gb_surface_from_handle - Create a shared surface
112 *
113 * @sws: Screen to register the surface with.
114 * @whandle: struct winsys_handle identifying the kernel surface object
115 * @format: On successful return points to a value describing the
116 * surface format.
117 *
118 * Returns a refcounted pointer to a struct svga_winsys_surface
119 * embedded in a struct vmw_svga_winsys_surface on success or NULL
120 * on failure.
121 */
122 static struct svga_winsys_surface *
vmw_drm_gb_surface_from_handle(struct svga_winsys_screen * sws,struct winsys_handle * whandle,SVGA3dSurfaceFormat * format)123 vmw_drm_gb_surface_from_handle(struct svga_winsys_screen *sws,
124 struct winsys_handle *whandle,
125 SVGA3dSurfaceFormat *format)
126 {
127 struct vmw_svga_winsys_surface *vsrf;
128 struct svga_winsys_surface *ssrf;
129 struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
130 SVGA3dSurfaceAllFlags flags;
131 uint32_t mip_levels;
132 struct vmw_buffer_desc desc;
133 struct pb_manager *provider = vws->pools.dma_base;
134 struct pb_buffer *pb_buf;
135 uint32_t handle;
136 int ret;
137
138 if (whandle->offset != 0) {
139 fprintf(stderr, "Attempt to import unsupported winsys offset %u\n",
140 whandle->offset);
141 return NULL;
142 }
143
144 ret = vmw_ioctl_gb_surface_ref(vws, whandle, &flags, format,
145 &mip_levels, &handle, &desc.region);
146
147 if (ret) {
148 fprintf(stderr, "Failed referencing shared surface. SID %d.\n"
149 "Error %d (%s).\n",
150 whandle->handle, ret, strerror(-ret));
151 return NULL;
152 }
153
154 if (mip_levels != 1) {
155 fprintf(stderr, "Incorrect number of mipmap levels on shared surface."
156 " SID %d, levels %d\n",
157 whandle->handle, mip_levels);
158 goto out_mip;
159 }
160
161 vsrf = CALLOC_STRUCT(vmw_svga_winsys_surface);
162 if (!vsrf)
163 goto out_mip;
164
165 pipe_reference_init(&vsrf->refcnt, 1);
166 p_atomic_set(&vsrf->validated, 0);
167 vsrf->screen = vws;
168 vsrf->sid = handle;
169 vsrf->size = vmw_region_size(desc.region);
170
171 /*
172 * Synchronize backing buffers of shared surfaces using the
173 * kernel, since we don't pass fence objects around between
174 * processes.
175 */
176 desc.pb_desc.alignment = 4096;
177 desc.pb_desc.usage = VMW_BUFFER_USAGE_SHARED | VMW_BUFFER_USAGE_SYNC;
178 pb_buf = provider->create_buffer(provider, vsrf->size, &desc.pb_desc);
179 vsrf->buf = vmw_svga_winsys_buffer_wrap(pb_buf);
180 if (!vsrf->buf)
181 goto out_no_buf;
182 ssrf = svga_winsys_surface(vsrf);
183
184 return ssrf;
185
186 out_no_buf:
187 FREE(vsrf);
188 out_mip:
189 vmw_ioctl_region_destroy(desc.region);
190 vmw_ioctl_surface_destroy(vws, whandle->handle);
191 return NULL;
192 }
193
194 static struct svga_winsys_surface *
vmw_drm_surface_from_handle(struct svga_winsys_screen * sws,struct winsys_handle * whandle,SVGA3dSurfaceFormat * format)195 vmw_drm_surface_from_handle(struct svga_winsys_screen *sws,
196 struct winsys_handle *whandle,
197 SVGA3dSurfaceFormat *format)
198 {
199 struct vmw_svga_winsys_surface *vsrf;
200 struct svga_winsys_surface *ssrf;
201 struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
202 union drm_vmw_surface_reference_arg arg;
203 struct drm_vmw_surface_arg *req = &arg.req;
204 struct drm_vmw_surface_create_req *rep = &arg.rep;
205 uint32_t handle = 0;
206 struct drm_vmw_size size;
207 SVGA3dSize base_size;
208 int ret;
209 int i;
210
211 if (whandle->offset != 0) {
212 fprintf(stderr, "Attempt to import unsupported winsys offset %u\n",
213 whandle->offset);
214 return NULL;
215 }
216
217 switch (whandle->type) {
218 case WINSYS_HANDLE_TYPE_SHARED:
219 case WINSYS_HANDLE_TYPE_KMS:
220 handle = whandle->handle;
221 break;
222 case WINSYS_HANDLE_TYPE_FD:
223 ret = drmPrimeFDToHandle(vws->ioctl.drm_fd, whandle->handle,
224 &handle);
225 if (ret) {
226 vmw_error("Failed to get handle from prime fd %d.\n",
227 (int) whandle->handle);
228 return NULL;
229 }
230 break;
231 default:
232 vmw_error("Attempt to import unsupported handle type %d.\n",
233 whandle->type);
234 return NULL;
235 }
236
237 memset(&arg, 0, sizeof(arg));
238 req->sid = handle;
239 rep->size_addr = (unsigned long)&size;
240
241 ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_REF_SURFACE,
242 &arg, sizeof(arg));
243
244 /*
245 * Need to close the handle we got from prime.
246 */
247 if (whandle->type == WINSYS_HANDLE_TYPE_FD)
248 vmw_ioctl_surface_destroy(vws, handle);
249
250 if (ret) {
251 /*
252 * Any attempt to share something other than a surface, like a dumb
253 * kms buffer, should fail here.
254 */
255 vmw_error("Failed referencing shared surface. SID %d.\n"
256 "Error %d (%s).\n",
257 handle, ret, strerror(-ret));
258 return NULL;
259 }
260
261 if (rep->mip_levels[0] != 1) {
262 vmw_error("Incorrect number of mipmap levels on shared surface."
263 " SID %d, levels %d\n",
264 handle, rep->mip_levels[0]);
265 goto out_mip;
266 }
267
268 for (i=1; i < DRM_VMW_MAX_SURFACE_FACES; ++i) {
269 if (rep->mip_levels[i] != 0) {
270 vmw_error("Incorrect number of faces levels on shared surface."
271 " SID %d, face %d present.\n",
272 handle, i);
273 goto out_mip;
274 }
275 }
276
277 vsrf = CALLOC_STRUCT(vmw_svga_winsys_surface);
278 if (!vsrf)
279 goto out_mip;
280
281 pipe_reference_init(&vsrf->refcnt, 1);
282 p_atomic_set(&vsrf->validated, 0);
283 vsrf->screen = vws;
284 vsrf->sid = handle;
285 ssrf = svga_winsys_surface(vsrf);
286 *format = rep->format;
287
288 /* Estimate usage, for early flushing. */
289
290 base_size.width = size.width;
291 base_size.height = size.height;
292 base_size.depth = size.depth;
293 vsrf->size = svga3dsurface_get_serialized_size(rep->format, base_size,
294 rep->mip_levels[0],
295 false);
296
297 return ssrf;
298
299 out_mip:
300 vmw_ioctl_surface_destroy(vws, handle);
301
302 return NULL;
303 }
304
305 static bool
vmw_drm_surface_get_handle(struct svga_winsys_screen * sws,struct svga_winsys_surface * surface,unsigned stride,struct winsys_handle * whandle)306 vmw_drm_surface_get_handle(struct svga_winsys_screen *sws,
307 struct svga_winsys_surface *surface,
308 unsigned stride,
309 struct winsys_handle *whandle)
310 {
311 struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
312 struct vmw_svga_winsys_surface *vsrf;
313 int ret;
314
315 if (!surface)
316 return false;
317
318 vsrf = vmw_svga_winsys_surface(surface);
319 whandle->handle = vsrf->sid;
320 whandle->stride = stride;
321 whandle->offset = 0;
322
323 switch (whandle->type) {
324 case WINSYS_HANDLE_TYPE_SHARED:
325 case WINSYS_HANDLE_TYPE_KMS:
326 whandle->handle = vsrf->sid;
327 break;
328 case WINSYS_HANDLE_TYPE_FD:
329 ret = drmPrimeHandleToFD(vws->ioctl.drm_fd, vsrf->sid, DRM_CLOEXEC,
330 (int *)&whandle->handle);
331 if (ret) {
332 vmw_error("Failed to get file descriptor from prime.\n");
333 return false;
334 }
335 break;
336 default:
337 vmw_error("Attempt to export unsupported handle type %d.\n",
338 whandle->type);
339 return false;
340 }
341
342 return true;
343 }
344