1 | // SPDX-License-Identifier: GPL-2.0 OR MIT |
2 | /************************************************************************** |
3 | * |
4 | * Copyright 2009-2023 VMware, Inc., Palo Alto, CA., USA |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the |
8 | * "Software"), to deal in the Software without restriction, including |
9 | * without limitation the rights to use, copy, modify, merge, publish, |
10 | * distribute, sub license, and/or sell copies of the Software, and to |
11 | * permit persons to whom the Software is furnished to do so, subject to |
12 | * the following conditions: |
13 | * |
14 | * The above copyright notice and this permission notice (including the |
15 | * next paragraph) shall be included in all copies or substantial portions |
16 | * of the Software. |
17 | * |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
21 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, |
22 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
23 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
24 | * USE OR OTHER DEALINGS IN THE SOFTWARE. |
25 | * |
26 | **************************************************************************/ |
27 | |
28 | #include "vmwgfx_bo.h" |
29 | #include "vmwgfx_drv.h" |
30 | #include "vmwgfx_resource_priv.h" |
31 | #include "vmwgfx_so.h" |
32 | #include "vmwgfx_binding.h" |
33 | #include "vmw_surface_cache.h" |
34 | #include "device_include/svga3d_surfacedefs.h" |
35 | |
36 | #include <drm/ttm/ttm_placement.h> |
37 | |
38 | #define SVGA3D_FLAGS_64(upper32, lower32) (((uint64_t)upper32 << 32) | lower32) |
39 | #define SVGA3D_FLAGS_UPPER_32(svga3d_flags) (svga3d_flags >> 32) |
40 | #define SVGA3D_FLAGS_LOWER_32(svga3d_flags) \ |
41 | (svga3d_flags & ((uint64_t)U32_MAX)) |
42 | |
43 | /** |
44 | * struct vmw_user_surface - User-space visible surface resource |
45 | * |
46 | * @prime: The TTM prime object. |
47 | * @srf: The surface metadata. |
48 | * @master: Master of the creating client. Used for security check. |
49 | */ |
50 | struct vmw_user_surface { |
51 | struct ttm_prime_object prime; |
52 | struct vmw_surface srf; |
53 | struct drm_master *master; |
54 | }; |
55 | |
56 | /** |
57 | * struct vmw_surface_offset - Backing store mip level offset info |
58 | * |
59 | * @face: Surface face. |
60 | * @mip: Mip level. |
61 | * @bo_offset: Offset into backing store of this mip level. |
62 | * |
63 | */ |
64 | struct vmw_surface_offset { |
65 | uint32_t face; |
66 | uint32_t mip; |
67 | uint32_t bo_offset; |
68 | }; |
69 | |
70 | /** |
71 | * struct vmw_surface_dirty - Surface dirty-tracker |
72 | * @cache: Cached layout information of the surface. |
73 | * @num_subres: Number of subresources. |
74 | * @boxes: Array of SVGA3dBoxes indicating dirty regions. One per subresource. |
75 | */ |
76 | struct vmw_surface_dirty { |
77 | struct vmw_surface_cache cache; |
78 | u32 num_subres; |
79 | SVGA3dBox boxes[] __counted_by(num_subres); |
80 | }; |
81 | |
82 | static void vmw_user_surface_free(struct vmw_resource *res); |
83 | static struct vmw_resource * |
84 | vmw_user_surface_base_to_res(struct ttm_base_object *base); |
85 | static int vmw_legacy_srf_bind(struct vmw_resource *res, |
86 | struct ttm_validate_buffer *val_buf); |
87 | static int vmw_legacy_srf_unbind(struct vmw_resource *res, |
88 | bool readback, |
89 | struct ttm_validate_buffer *val_buf); |
90 | static int vmw_legacy_srf_create(struct vmw_resource *res); |
91 | static int vmw_legacy_srf_destroy(struct vmw_resource *res); |
92 | static int vmw_gb_surface_create(struct vmw_resource *res); |
93 | static int vmw_gb_surface_bind(struct vmw_resource *res, |
94 | struct ttm_validate_buffer *val_buf); |
95 | static int vmw_gb_surface_unbind(struct vmw_resource *res, |
96 | bool readback, |
97 | struct ttm_validate_buffer *val_buf); |
98 | static int vmw_gb_surface_destroy(struct vmw_resource *res); |
99 | static int |
100 | vmw_gb_surface_define_internal(struct drm_device *dev, |
101 | struct drm_vmw_gb_surface_create_ext_req *req, |
102 | struct drm_vmw_gb_surface_create_rep *rep, |
103 | struct drm_file *file_priv); |
104 | static int |
105 | vmw_gb_surface_reference_internal(struct drm_device *dev, |
106 | struct drm_vmw_surface_arg *req, |
107 | struct drm_vmw_gb_surface_ref_ext_rep *rep, |
108 | struct drm_file *file_priv); |
109 | |
110 | static void vmw_surface_dirty_free(struct vmw_resource *res); |
111 | static int vmw_surface_dirty_alloc(struct vmw_resource *res); |
112 | static int vmw_surface_dirty_sync(struct vmw_resource *res); |
113 | static void vmw_surface_dirty_range_add(struct vmw_resource *res, size_t start, |
114 | size_t end); |
115 | static int vmw_surface_clean(struct vmw_resource *res); |
116 | |
117 | static const struct vmw_user_resource_conv user_surface_conv = { |
118 | .object_type = VMW_RES_SURFACE, |
119 | .base_obj_to_res = vmw_user_surface_base_to_res, |
120 | .res_free = vmw_user_surface_free |
121 | }; |
122 | |
123 | const struct vmw_user_resource_conv *user_surface_converter = |
124 | &user_surface_conv; |
125 | |
126 | static const struct vmw_res_func vmw_legacy_surface_func = { |
127 | .res_type = vmw_res_surface, |
128 | .needs_guest_memory = false, |
129 | .may_evict = true, |
130 | .prio = 1, |
131 | .dirty_prio = 1, |
132 | .type_name = "legacy surfaces" , |
133 | .domain = VMW_BO_DOMAIN_GMR, |
134 | .busy_domain = VMW_BO_DOMAIN_GMR | VMW_BO_DOMAIN_VRAM, |
135 | .create = &vmw_legacy_srf_create, |
136 | .destroy = &vmw_legacy_srf_destroy, |
137 | .bind = &vmw_legacy_srf_bind, |
138 | .unbind = &vmw_legacy_srf_unbind |
139 | }; |
140 | |
141 | static const struct vmw_res_func vmw_gb_surface_func = { |
142 | .res_type = vmw_res_surface, |
143 | .needs_guest_memory = true, |
144 | .may_evict = true, |
145 | .prio = 1, |
146 | .dirty_prio = 2, |
147 | .type_name = "guest backed surfaces" , |
148 | .domain = VMW_BO_DOMAIN_MOB, |
149 | .busy_domain = VMW_BO_DOMAIN_MOB, |
150 | .create = vmw_gb_surface_create, |
151 | .destroy = vmw_gb_surface_destroy, |
152 | .bind = vmw_gb_surface_bind, |
153 | .unbind = vmw_gb_surface_unbind, |
154 | .dirty_alloc = vmw_surface_dirty_alloc, |
155 | .dirty_free = vmw_surface_dirty_free, |
156 | .dirty_sync = vmw_surface_dirty_sync, |
157 | .dirty_range_add = vmw_surface_dirty_range_add, |
158 | .clean = vmw_surface_clean, |
159 | }; |
160 | |
161 | /* |
162 | * struct vmw_surface_dma - SVGA3D DMA command |
163 | */ |
164 | struct vmw_surface_dma { |
165 | SVGA3dCmdHeader ; |
166 | SVGA3dCmdSurfaceDMA body; |
167 | SVGA3dCopyBox cb; |
168 | SVGA3dCmdSurfaceDMASuffix suffix; |
169 | }; |
170 | |
171 | /* |
172 | * struct vmw_surface_define - SVGA3D Surface Define command |
173 | */ |
174 | struct vmw_surface_define { |
175 | SVGA3dCmdHeader ; |
176 | SVGA3dCmdDefineSurface body; |
177 | }; |
178 | |
179 | /* |
180 | * struct vmw_surface_destroy - SVGA3D Surface Destroy command |
181 | */ |
182 | struct vmw_surface_destroy { |
183 | SVGA3dCmdHeader ; |
184 | SVGA3dCmdDestroySurface body; |
185 | }; |
186 | |
187 | |
188 | /** |
189 | * vmw_surface_dma_size - Compute fifo size for a dma command. |
190 | * |
191 | * @srf: Pointer to a struct vmw_surface |
192 | * |
193 | * Computes the required size for a surface dma command for backup or |
194 | * restoration of the surface represented by @srf. |
195 | */ |
196 | static inline uint32_t vmw_surface_dma_size(const struct vmw_surface *srf) |
197 | { |
198 | return srf->metadata.num_sizes * sizeof(struct vmw_surface_dma); |
199 | } |
200 | |
201 | |
202 | /** |
203 | * vmw_surface_define_size - Compute fifo size for a surface define command. |
204 | * |
205 | * @srf: Pointer to a struct vmw_surface |
206 | * |
207 | * Computes the required size for a surface define command for the definition |
208 | * of the surface represented by @srf. |
209 | */ |
210 | static inline uint32_t vmw_surface_define_size(const struct vmw_surface *srf) |
211 | { |
212 | return sizeof(struct vmw_surface_define) + srf->metadata.num_sizes * |
213 | sizeof(SVGA3dSize); |
214 | } |
215 | |
216 | |
217 | /** |
218 | * vmw_surface_destroy_size - Compute fifo size for a surface destroy command. |
219 | * |
220 | * Computes the required size for a surface destroy command for the destruction |
221 | * of a hw surface. |
222 | */ |
223 | static inline uint32_t vmw_surface_destroy_size(void) |
224 | { |
225 | return sizeof(struct vmw_surface_destroy); |
226 | } |
227 | |
228 | /** |
229 | * vmw_surface_destroy_encode - Encode a surface_destroy command. |
230 | * |
231 | * @id: The surface id |
232 | * @cmd_space: Pointer to memory area in which the commands should be encoded. |
233 | */ |
234 | static void vmw_surface_destroy_encode(uint32_t id, |
235 | void *cmd_space) |
236 | { |
237 | struct vmw_surface_destroy *cmd = (struct vmw_surface_destroy *) |
238 | cmd_space; |
239 | |
240 | cmd->header.id = SVGA_3D_CMD_SURFACE_DESTROY; |
241 | cmd->header.size = sizeof(cmd->body); |
242 | cmd->body.sid = id; |
243 | } |
244 | |
245 | /** |
246 | * vmw_surface_define_encode - Encode a surface_define command. |
247 | * |
248 | * @srf: Pointer to a struct vmw_surface object. |
249 | * @cmd_space: Pointer to memory area in which the commands should be encoded. |
250 | */ |
251 | static void vmw_surface_define_encode(const struct vmw_surface *srf, |
252 | void *cmd_space) |
253 | { |
254 | struct vmw_surface_define *cmd = (struct vmw_surface_define *) |
255 | cmd_space; |
256 | struct drm_vmw_size *src_size; |
257 | SVGA3dSize *cmd_size; |
258 | uint32_t cmd_len; |
259 | int i; |
260 | |
261 | cmd_len = sizeof(cmd->body) + srf->metadata.num_sizes * |
262 | sizeof(SVGA3dSize); |
263 | |
264 | cmd->header.id = SVGA_3D_CMD_SURFACE_DEFINE; |
265 | cmd->header.size = cmd_len; |
266 | cmd->body.sid = srf->res.id; |
267 | /* |
268 | * Downcast of surfaceFlags, was upcasted when received from user-space, |
269 | * since driver internally stores as 64 bit. |
270 | * For legacy surface define only 32 bit flag is supported. |
271 | */ |
272 | cmd->body.surfaceFlags = (SVGA3dSurface1Flags)srf->metadata.flags; |
273 | cmd->body.format = srf->metadata.format; |
274 | for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) |
275 | cmd->body.face[i].numMipLevels = srf->metadata.mip_levels[i]; |
276 | |
277 | cmd += 1; |
278 | cmd_size = (SVGA3dSize *) cmd; |
279 | src_size = srf->metadata.sizes; |
280 | |
281 | for (i = 0; i < srf->metadata.num_sizes; ++i, cmd_size++, src_size++) { |
282 | cmd_size->width = src_size->width; |
283 | cmd_size->height = src_size->height; |
284 | cmd_size->depth = src_size->depth; |
285 | } |
286 | } |
287 | |
288 | /** |
289 | * vmw_surface_dma_encode - Encode a surface_dma command. |
290 | * |
291 | * @srf: Pointer to a struct vmw_surface object. |
292 | * @cmd_space: Pointer to memory area in which the commands should be encoded. |
293 | * @ptr: Pointer to an SVGAGuestPtr indicating where the surface contents |
294 | * should be placed or read from. |
295 | * @to_surface: Boolean whether to DMA to the surface or from the surface. |
296 | */ |
297 | static void vmw_surface_dma_encode(struct vmw_surface *srf, |
298 | void *cmd_space, |
299 | const SVGAGuestPtr *ptr, |
300 | bool to_surface) |
301 | { |
302 | uint32_t i; |
303 | struct vmw_surface_dma *cmd = (struct vmw_surface_dma *)cmd_space; |
304 | const struct SVGA3dSurfaceDesc *desc = |
305 | vmw_surface_get_desc(format: srf->metadata.format); |
306 | |
307 | for (i = 0; i < srf->metadata.num_sizes; ++i) { |
308 | SVGA3dCmdHeader * = &cmd->header; |
309 | SVGA3dCmdSurfaceDMA *body = &cmd->body; |
310 | SVGA3dCopyBox *cb = &cmd->cb; |
311 | SVGA3dCmdSurfaceDMASuffix *suffix = &cmd->suffix; |
312 | const struct vmw_surface_offset *cur_offset = &srf->offsets[i]; |
313 | const struct drm_vmw_size *cur_size = &srf->metadata.sizes[i]; |
314 | |
315 | header->id = SVGA_3D_CMD_SURFACE_DMA; |
316 | header->size = sizeof(*body) + sizeof(*cb) + sizeof(*suffix); |
317 | |
318 | body->guest.ptr = *ptr; |
319 | body->guest.ptr.offset += cur_offset->bo_offset; |
320 | body->guest.pitch = vmw_surface_calculate_pitch(desc, size: cur_size); |
321 | body->host.sid = srf->res.id; |
322 | body->host.face = cur_offset->face; |
323 | body->host.mipmap = cur_offset->mip; |
324 | body->transfer = ((to_surface) ? SVGA3D_WRITE_HOST_VRAM : |
325 | SVGA3D_READ_HOST_VRAM); |
326 | cb->x = 0; |
327 | cb->y = 0; |
328 | cb->z = 0; |
329 | cb->srcx = 0; |
330 | cb->srcy = 0; |
331 | cb->srcz = 0; |
332 | cb->w = cur_size->width; |
333 | cb->h = cur_size->height; |
334 | cb->d = cur_size->depth; |
335 | |
336 | suffix->suffixSize = sizeof(*suffix); |
337 | suffix->maximumOffset = |
338 | vmw_surface_get_image_buffer_size(desc, size: cur_size, |
339 | pitch: body->guest.pitch); |
340 | suffix->flags.discard = 0; |
341 | suffix->flags.unsynchronized = 0; |
342 | suffix->flags.reserved = 0; |
343 | ++cmd; |
344 | } |
345 | }; |
346 | |
347 | |
348 | /** |
349 | * vmw_hw_surface_destroy - destroy a Device surface |
350 | * |
351 | * @res: Pointer to a struct vmw_resource embedded in a struct |
352 | * vmw_surface. |
353 | * |
354 | * Destroys a the device surface associated with a struct vmw_surface if |
355 | * any, and adjusts resource count accordingly. |
356 | */ |
357 | static void vmw_hw_surface_destroy(struct vmw_resource *res) |
358 | { |
359 | |
360 | struct vmw_private *dev_priv = res->dev_priv; |
361 | void *cmd; |
362 | |
363 | if (res->func->destroy == vmw_gb_surface_destroy) { |
364 | (void) vmw_gb_surface_destroy(res); |
365 | return; |
366 | } |
367 | |
368 | if (res->id != -1) { |
369 | |
370 | cmd = VMW_CMD_RESERVE(dev_priv, vmw_surface_destroy_size()); |
371 | if (unlikely(!cmd)) |
372 | return; |
373 | |
374 | vmw_surface_destroy_encode(id: res->id, cmd_space: cmd); |
375 | vmw_cmd_commit(dev_priv, bytes: vmw_surface_destroy_size()); |
376 | |
377 | /* |
378 | * used_memory_size_atomic, or separate lock |
379 | * to avoid taking dev_priv::cmdbuf_mutex in |
380 | * the destroy path. |
381 | */ |
382 | |
383 | mutex_lock(&dev_priv->cmdbuf_mutex); |
384 | dev_priv->used_memory_size -= res->guest_memory_size; |
385 | mutex_unlock(lock: &dev_priv->cmdbuf_mutex); |
386 | } |
387 | } |
388 | |
389 | /** |
390 | * vmw_legacy_srf_create - Create a device surface as part of the |
391 | * resource validation process. |
392 | * |
393 | * @res: Pointer to a struct vmw_surface. |
394 | * |
395 | * If the surface doesn't have a hw id. |
396 | * |
397 | * Returns -EBUSY if there wasn't sufficient device resources to |
398 | * complete the validation. Retry after freeing up resources. |
399 | * |
400 | * May return other errors if the kernel is out of guest resources. |
401 | */ |
402 | static int vmw_legacy_srf_create(struct vmw_resource *res) |
403 | { |
404 | struct vmw_private *dev_priv = res->dev_priv; |
405 | struct vmw_surface *srf; |
406 | uint32_t submit_size; |
407 | uint8_t *cmd; |
408 | int ret; |
409 | |
410 | if (likely(res->id != -1)) |
411 | return 0; |
412 | |
413 | srf = vmw_res_to_srf(res); |
414 | if (unlikely(dev_priv->used_memory_size + res->guest_memory_size >= |
415 | dev_priv->memory_size)) |
416 | return -EBUSY; |
417 | |
418 | /* |
419 | * Alloc id for the resource. |
420 | */ |
421 | |
422 | ret = vmw_resource_alloc_id(res); |
423 | if (unlikely(ret != 0)) { |
424 | DRM_ERROR("Failed to allocate a surface id.\n" ); |
425 | goto out_no_id; |
426 | } |
427 | |
428 | if (unlikely(res->id >= SVGA3D_HB_MAX_SURFACE_IDS)) { |
429 | ret = -EBUSY; |
430 | goto out_no_fifo; |
431 | } |
432 | |
433 | /* |
434 | * Encode surface define- commands. |
435 | */ |
436 | |
437 | submit_size = vmw_surface_define_size(srf); |
438 | cmd = VMW_CMD_RESERVE(dev_priv, submit_size); |
439 | if (unlikely(!cmd)) { |
440 | ret = -ENOMEM; |
441 | goto out_no_fifo; |
442 | } |
443 | |
444 | vmw_surface_define_encode(srf, cmd_space: cmd); |
445 | vmw_cmd_commit(dev_priv, bytes: submit_size); |
446 | vmw_fifo_resource_inc(dev_priv); |
447 | |
448 | /* |
449 | * Surface memory usage accounting. |
450 | */ |
451 | |
452 | dev_priv->used_memory_size += res->guest_memory_size; |
453 | return 0; |
454 | |
455 | out_no_fifo: |
456 | vmw_resource_release_id(res); |
457 | out_no_id: |
458 | return ret; |
459 | } |
460 | |
461 | /** |
462 | * vmw_legacy_srf_dma - Copy backup data to or from a legacy surface. |
463 | * |
464 | * @res: Pointer to a struct vmw_res embedded in a struct |
465 | * vmw_surface. |
466 | * @val_buf: Pointer to a struct ttm_validate_buffer containing |
467 | * information about the backup buffer. |
468 | * @bind: Boolean wether to DMA to the surface. |
469 | * |
470 | * Transfer backup data to or from a legacy surface as part of the |
471 | * validation process. |
472 | * May return other errors if the kernel is out of guest resources. |
473 | * The backup buffer will be fenced or idle upon successful completion, |
474 | * and if the surface needs persistent backup storage, the backup buffer |
475 | * will also be returned reserved iff @bind is true. |
476 | */ |
477 | static int vmw_legacy_srf_dma(struct vmw_resource *res, |
478 | struct ttm_validate_buffer *val_buf, |
479 | bool bind) |
480 | { |
481 | SVGAGuestPtr ptr; |
482 | struct vmw_fence_obj *fence; |
483 | uint32_t submit_size; |
484 | struct vmw_surface *srf = vmw_res_to_srf(res); |
485 | uint8_t *cmd; |
486 | struct vmw_private *dev_priv = res->dev_priv; |
487 | |
488 | BUG_ON(!val_buf->bo); |
489 | submit_size = vmw_surface_dma_size(srf); |
490 | cmd = VMW_CMD_RESERVE(dev_priv, submit_size); |
491 | if (unlikely(!cmd)) |
492 | return -ENOMEM; |
493 | |
494 | vmw_bo_get_guest_ptr(buf: val_buf->bo, ptr: &ptr); |
495 | vmw_surface_dma_encode(srf, cmd_space: cmd, ptr: &ptr, to_surface: bind); |
496 | |
497 | vmw_cmd_commit(dev_priv, bytes: submit_size); |
498 | |
499 | /* |
500 | * Create a fence object and fence the backup buffer. |
501 | */ |
502 | |
503 | (void) vmw_execbuf_fence_commands(NULL, dev_priv, |
504 | p_fence: &fence, NULL); |
505 | |
506 | vmw_bo_fence_single(bo: val_buf->bo, fence); |
507 | |
508 | if (likely(fence != NULL)) |
509 | vmw_fence_obj_unreference(fence_p: &fence); |
510 | |
511 | return 0; |
512 | } |
513 | |
514 | /** |
515 | * vmw_legacy_srf_bind - Perform a legacy surface bind as part of the |
516 | * surface validation process. |
517 | * |
518 | * @res: Pointer to a struct vmw_res embedded in a struct |
519 | * vmw_surface. |
520 | * @val_buf: Pointer to a struct ttm_validate_buffer containing |
521 | * information about the backup buffer. |
522 | * |
523 | * This function will copy backup data to the surface if the |
524 | * backup buffer is dirty. |
525 | */ |
526 | static int vmw_legacy_srf_bind(struct vmw_resource *res, |
527 | struct ttm_validate_buffer *val_buf) |
528 | { |
529 | if (!res->guest_memory_dirty) |
530 | return 0; |
531 | |
532 | return vmw_legacy_srf_dma(res, val_buf, bind: true); |
533 | } |
534 | |
535 | |
536 | /** |
537 | * vmw_legacy_srf_unbind - Perform a legacy surface unbind as part of the |
538 | * surface eviction process. |
539 | * |
540 | * @res: Pointer to a struct vmw_res embedded in a struct |
541 | * vmw_surface. |
542 | * @readback: Readback - only true if dirty |
543 | * @val_buf: Pointer to a struct ttm_validate_buffer containing |
544 | * information about the backup buffer. |
545 | * |
546 | * This function will copy backup data from the surface. |
547 | */ |
548 | static int vmw_legacy_srf_unbind(struct vmw_resource *res, |
549 | bool readback, |
550 | struct ttm_validate_buffer *val_buf) |
551 | { |
552 | if (unlikely(readback)) |
553 | return vmw_legacy_srf_dma(res, val_buf, bind: false); |
554 | return 0; |
555 | } |
556 | |
557 | /** |
558 | * vmw_legacy_srf_destroy - Destroy a device surface as part of a |
559 | * resource eviction process. |
560 | * |
561 | * @res: Pointer to a struct vmw_res embedded in a struct |
562 | * vmw_surface. |
563 | */ |
564 | static int vmw_legacy_srf_destroy(struct vmw_resource *res) |
565 | { |
566 | struct vmw_private *dev_priv = res->dev_priv; |
567 | uint32_t submit_size; |
568 | uint8_t *cmd; |
569 | |
570 | BUG_ON(res->id == -1); |
571 | |
572 | /* |
573 | * Encode the dma- and surface destroy commands. |
574 | */ |
575 | |
576 | submit_size = vmw_surface_destroy_size(); |
577 | cmd = VMW_CMD_RESERVE(dev_priv, submit_size); |
578 | if (unlikely(!cmd)) |
579 | return -ENOMEM; |
580 | |
581 | vmw_surface_destroy_encode(id: res->id, cmd_space: cmd); |
582 | vmw_cmd_commit(dev_priv, bytes: submit_size); |
583 | |
584 | /* |
585 | * Surface memory usage accounting. |
586 | */ |
587 | |
588 | dev_priv->used_memory_size -= res->guest_memory_size; |
589 | |
590 | /* |
591 | * Release the surface ID. |
592 | */ |
593 | |
594 | vmw_resource_release_id(res); |
595 | vmw_fifo_resource_dec(dev_priv); |
596 | |
597 | return 0; |
598 | } |
599 | |
600 | |
601 | /** |
602 | * vmw_surface_init - initialize a struct vmw_surface |
603 | * |
604 | * @dev_priv: Pointer to a device private struct. |
605 | * @srf: Pointer to the struct vmw_surface to initialize. |
606 | * @res_free: Pointer to a resource destructor used to free |
607 | * the object. |
608 | */ |
609 | static int vmw_surface_init(struct vmw_private *dev_priv, |
610 | struct vmw_surface *srf, |
611 | void (*res_free) (struct vmw_resource *res)) |
612 | { |
613 | int ret; |
614 | struct vmw_resource *res = &srf->res; |
615 | |
616 | BUG_ON(!res_free); |
617 | ret = vmw_resource_init(dev_priv, res, delay_id: true, res_free, |
618 | func: (dev_priv->has_mob) ? &vmw_gb_surface_func : |
619 | &vmw_legacy_surface_func); |
620 | |
621 | if (unlikely(ret != 0)) { |
622 | res_free(res); |
623 | return ret; |
624 | } |
625 | |
626 | /* |
627 | * The surface won't be visible to hardware until a |
628 | * surface validate. |
629 | */ |
630 | |
631 | INIT_LIST_HEAD(list: &srf->view_list); |
632 | res->hw_destroy = vmw_hw_surface_destroy; |
633 | return ret; |
634 | } |
635 | |
636 | /** |
637 | * vmw_user_surface_base_to_res - TTM base object to resource converter for |
638 | * user visible surfaces |
639 | * |
640 | * @base: Pointer to a TTM base object |
641 | * |
642 | * Returns the struct vmw_resource embedded in a struct vmw_surface |
643 | * for the user-visible object identified by the TTM base object @base. |
644 | */ |
645 | static struct vmw_resource * |
646 | vmw_user_surface_base_to_res(struct ttm_base_object *base) |
647 | { |
648 | return &(container_of(base, struct vmw_user_surface, |
649 | prime.base)->srf.res); |
650 | } |
651 | |
652 | /** |
653 | * vmw_user_surface_free - User visible surface resource destructor |
654 | * |
655 | * @res: A struct vmw_resource embedded in a struct vmw_surface. |
656 | */ |
657 | static void vmw_user_surface_free(struct vmw_resource *res) |
658 | { |
659 | struct vmw_surface *srf = vmw_res_to_srf(res); |
660 | struct vmw_user_surface *user_srf = |
661 | container_of(srf, struct vmw_user_surface, srf); |
662 | |
663 | WARN_ON_ONCE(res->dirty); |
664 | if (user_srf->master) |
665 | drm_master_put(master: &user_srf->master); |
666 | kfree(objp: srf->offsets); |
667 | kfree(objp: srf->metadata.sizes); |
668 | kfree(objp: srf->snooper.image); |
669 | ttm_prime_object_kfree(user_srf, prime); |
670 | } |
671 | |
672 | /** |
673 | * vmw_user_surface_base_release - User visible surface TTM base object destructor |
674 | * |
675 | * @p_base: Pointer to a pointer to a TTM base object |
676 | * embedded in a struct vmw_user_surface. |
677 | * |
678 | * Drops the base object's reference on its resource, and the |
679 | * pointer pointed to by *p_base is set to NULL. |
680 | */ |
681 | static void vmw_user_surface_base_release(struct ttm_base_object **p_base) |
682 | { |
683 | struct ttm_base_object *base = *p_base; |
684 | struct vmw_user_surface *user_srf = |
685 | container_of(base, struct vmw_user_surface, prime.base); |
686 | struct vmw_resource *res = &user_srf->srf.res; |
687 | |
688 | *p_base = NULL; |
689 | vmw_resource_unreference(p_res: &res); |
690 | } |
691 | |
692 | /** |
693 | * vmw_surface_destroy_ioctl - Ioctl function implementing |
694 | * the user surface destroy functionality. |
695 | * |
696 | * @dev: Pointer to a struct drm_device. |
697 | * @data: Pointer to data copied from / to user-space. |
698 | * @file_priv: Pointer to a drm file private structure. |
699 | */ |
700 | int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data, |
701 | struct drm_file *file_priv) |
702 | { |
703 | struct drm_vmw_surface_arg *arg = (struct drm_vmw_surface_arg *)data; |
704 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; |
705 | |
706 | return ttm_ref_object_base_unref(tfile, key: arg->sid); |
707 | } |
708 | |
709 | /** |
710 | * vmw_surface_define_ioctl - Ioctl function implementing |
711 | * the user surface define functionality. |
712 | * |
713 | * @dev: Pointer to a struct drm_device. |
714 | * @data: Pointer to data copied from / to user-space. |
715 | * @file_priv: Pointer to a drm file private structure. |
716 | */ |
717 | int vmw_surface_define_ioctl(struct drm_device *dev, void *data, |
718 | struct drm_file *file_priv) |
719 | { |
720 | struct vmw_private *dev_priv = vmw_priv(dev); |
721 | struct vmw_user_surface *user_srf; |
722 | struct vmw_surface *srf; |
723 | struct vmw_surface_metadata *metadata; |
724 | struct vmw_resource *res; |
725 | struct vmw_resource *tmp; |
726 | union drm_vmw_surface_create_arg *arg = |
727 | (union drm_vmw_surface_create_arg *)data; |
728 | struct drm_vmw_surface_create_req *req = &arg->req; |
729 | struct drm_vmw_surface_arg *rep = &arg->rep; |
730 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; |
731 | int ret; |
732 | int i, j; |
733 | uint32_t cur_bo_offset; |
734 | struct drm_vmw_size *cur_size; |
735 | struct vmw_surface_offset *cur_offset; |
736 | uint32_t num_sizes; |
737 | const SVGA3dSurfaceDesc *desc; |
738 | |
739 | num_sizes = 0; |
740 | for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { |
741 | if (req->mip_levels[i] > DRM_VMW_MAX_MIP_LEVELS) |
742 | return -EINVAL; |
743 | num_sizes += req->mip_levels[i]; |
744 | } |
745 | |
746 | if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * DRM_VMW_MAX_MIP_LEVELS || |
747 | num_sizes == 0) |
748 | return -EINVAL; |
749 | |
750 | desc = vmw_surface_get_desc(format: req->format); |
751 | if (unlikely(desc->blockDesc == SVGA3DBLOCKDESC_NONE)) { |
752 | VMW_DEBUG_USER("Invalid format %d for surface creation.\n" , |
753 | req->format); |
754 | return -EINVAL; |
755 | } |
756 | |
757 | user_srf = kzalloc(size: sizeof(*user_srf), GFP_KERNEL); |
758 | if (unlikely(!user_srf)) { |
759 | ret = -ENOMEM; |
760 | goto out_unlock; |
761 | } |
762 | |
763 | srf = &user_srf->srf; |
764 | metadata = &srf->metadata; |
765 | res = &srf->res; |
766 | |
767 | /* Driver internally stores as 64-bit flags */ |
768 | metadata->flags = (SVGA3dSurfaceAllFlags)req->flags; |
769 | metadata->format = req->format; |
770 | metadata->scanout = req->scanout; |
771 | |
772 | memcpy(metadata->mip_levels, req->mip_levels, |
773 | sizeof(metadata->mip_levels)); |
774 | metadata->num_sizes = num_sizes; |
775 | metadata->sizes = |
776 | memdup_array_user(src: (struct drm_vmw_size __user *)(unsigned long) |
777 | req->size_addr, |
778 | n: metadata->num_sizes, size: sizeof(*metadata->sizes)); |
779 | if (IS_ERR(ptr: metadata->sizes)) { |
780 | ret = PTR_ERR(ptr: metadata->sizes); |
781 | goto out_no_sizes; |
782 | } |
783 | srf->offsets = kmalloc_array(n: metadata->num_sizes, size: sizeof(*srf->offsets), |
784 | GFP_KERNEL); |
785 | if (unlikely(!srf->offsets)) { |
786 | ret = -ENOMEM; |
787 | goto out_no_offsets; |
788 | } |
789 | |
790 | metadata->base_size = *srf->metadata.sizes; |
791 | metadata->autogen_filter = SVGA3D_TEX_FILTER_NONE; |
792 | metadata->multisample_count = 0; |
793 | metadata->multisample_pattern = SVGA3D_MS_PATTERN_NONE; |
794 | metadata->quality_level = SVGA3D_MS_QUALITY_NONE; |
795 | |
796 | cur_bo_offset = 0; |
797 | cur_offset = srf->offsets; |
798 | cur_size = metadata->sizes; |
799 | |
800 | for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { |
801 | for (j = 0; j < metadata->mip_levels[i]; ++j) { |
802 | uint32_t stride = vmw_surface_calculate_pitch( |
803 | desc, size: cur_size); |
804 | |
805 | cur_offset->face = i; |
806 | cur_offset->mip = j; |
807 | cur_offset->bo_offset = cur_bo_offset; |
808 | cur_bo_offset += vmw_surface_get_image_buffer_size |
809 | (desc, size: cur_size, pitch: stride); |
810 | ++cur_offset; |
811 | ++cur_size; |
812 | } |
813 | } |
814 | res->guest_memory_size = cur_bo_offset; |
815 | if (metadata->scanout && |
816 | metadata->num_sizes == 1 && |
817 | metadata->sizes[0].width == VMW_CURSOR_SNOOP_WIDTH && |
818 | metadata->sizes[0].height == VMW_CURSOR_SNOOP_HEIGHT && |
819 | metadata->format == VMW_CURSOR_SNOOP_FORMAT) { |
820 | const struct SVGA3dSurfaceDesc *desc = |
821 | vmw_surface_get_desc(VMW_CURSOR_SNOOP_FORMAT); |
822 | const u32 cursor_size_bytes = VMW_CURSOR_SNOOP_WIDTH * |
823 | VMW_CURSOR_SNOOP_HEIGHT * |
824 | desc->pitchBytesPerBlock; |
825 | srf->snooper.image = kzalloc(size: cursor_size_bytes, GFP_KERNEL); |
826 | if (!srf->snooper.image) { |
827 | DRM_ERROR("Failed to allocate cursor_image\n" ); |
828 | ret = -ENOMEM; |
829 | goto out_no_copy; |
830 | } |
831 | } else { |
832 | srf->snooper.image = NULL; |
833 | } |
834 | |
835 | if (drm_is_primary_client(file_priv)) |
836 | user_srf->master = drm_file_get_master(file_priv); |
837 | |
838 | /** |
839 | * From this point, the generic resource management functions |
840 | * destroy the object on failure. |
841 | */ |
842 | |
843 | ret = vmw_surface_init(dev_priv, srf, res_free: vmw_user_surface_free); |
844 | if (unlikely(ret != 0)) |
845 | goto out_unlock; |
846 | |
847 | /* |
848 | * A gb-aware client referencing a surface will expect a backup |
849 | * buffer to be present. |
850 | */ |
851 | if (dev_priv->has_mob) { |
852 | struct vmw_bo_params params = { |
853 | .domain = VMW_BO_DOMAIN_SYS, |
854 | .busy_domain = VMW_BO_DOMAIN_SYS, |
855 | .bo_type = ttm_bo_type_device, |
856 | .size = res->guest_memory_size, |
857 | .pin = false |
858 | }; |
859 | |
860 | ret = vmw_gem_object_create(vmw: dev_priv, |
861 | params: ¶ms, |
862 | p_vbo: &res->guest_memory_bo); |
863 | if (unlikely(ret != 0)) { |
864 | vmw_resource_unreference(p_res: &res); |
865 | goto out_unlock; |
866 | } |
867 | } |
868 | |
869 | tmp = vmw_resource_reference(res: &srf->res); |
870 | ret = ttm_prime_object_init(tfile, size: res->guest_memory_size, |
871 | prime: &user_srf->prime, |
872 | VMW_RES_SURFACE, |
873 | refcount_release: &vmw_user_surface_base_release); |
874 | |
875 | if (unlikely(ret != 0)) { |
876 | vmw_resource_unreference(p_res: &tmp); |
877 | vmw_resource_unreference(p_res: &res); |
878 | goto out_unlock; |
879 | } |
880 | |
881 | rep->sid = user_srf->prime.base.handle; |
882 | vmw_resource_unreference(p_res: &res); |
883 | |
884 | return 0; |
885 | out_no_copy: |
886 | kfree(objp: srf->offsets); |
887 | out_no_offsets: |
888 | kfree(objp: metadata->sizes); |
889 | out_no_sizes: |
890 | ttm_prime_object_kfree(user_srf, prime); |
891 | out_unlock: |
892 | return ret; |
893 | } |
894 | |
895 | |
896 | static int |
897 | vmw_surface_handle_reference(struct vmw_private *dev_priv, |
898 | struct drm_file *file_priv, |
899 | uint32_t u_handle, |
900 | enum drm_vmw_handle_type handle_type, |
901 | struct ttm_base_object **base_p) |
902 | { |
903 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; |
904 | struct vmw_user_surface *user_srf; |
905 | uint32_t handle; |
906 | struct ttm_base_object *base; |
907 | int ret; |
908 | |
909 | if (handle_type == DRM_VMW_HANDLE_PRIME) { |
910 | ret = ttm_prime_fd_to_handle(tfile, fd: u_handle, handle: &handle); |
911 | if (unlikely(ret != 0)) |
912 | return ret; |
913 | } else { |
914 | handle = u_handle; |
915 | } |
916 | |
917 | ret = -EINVAL; |
918 | base = ttm_base_object_lookup_for_ref(tdev: dev_priv->tdev, key: handle); |
919 | if (unlikely(!base)) { |
920 | VMW_DEBUG_USER("Could not find surface to reference.\n" ); |
921 | goto out_no_lookup; |
922 | } |
923 | |
924 | if (unlikely(ttm_base_object_type(base) != VMW_RES_SURFACE)) { |
925 | VMW_DEBUG_USER("Referenced object is not a surface.\n" ); |
926 | goto out_bad_resource; |
927 | } |
928 | if (handle_type != DRM_VMW_HANDLE_PRIME) { |
929 | bool require_exist = false; |
930 | |
931 | user_srf = container_of(base, struct vmw_user_surface, |
932 | prime.base); |
933 | |
934 | /* Error out if we are unauthenticated primary */ |
935 | if (drm_is_primary_client(file_priv) && |
936 | !file_priv->authenticated) { |
937 | ret = -EACCES; |
938 | goto out_bad_resource; |
939 | } |
940 | |
941 | /* |
942 | * Make sure the surface creator has the same |
943 | * authenticating master, or is already registered with us. |
944 | */ |
945 | if (drm_is_primary_client(file_priv) && |
946 | user_srf->master != file_priv->master) |
947 | require_exist = true; |
948 | |
949 | if (unlikely(drm_is_render_client(file_priv))) |
950 | require_exist = true; |
951 | |
952 | ret = ttm_ref_object_add(tfile, base, NULL, require_existed: require_exist); |
953 | if (unlikely(ret != 0)) { |
954 | DRM_ERROR("Could not add a reference to a surface.\n" ); |
955 | goto out_bad_resource; |
956 | } |
957 | } |
958 | |
959 | *base_p = base; |
960 | return 0; |
961 | |
962 | out_bad_resource: |
963 | ttm_base_object_unref(p_base: &base); |
964 | out_no_lookup: |
965 | if (handle_type == DRM_VMW_HANDLE_PRIME) |
966 | (void) ttm_ref_object_base_unref(tfile, key: handle); |
967 | |
968 | return ret; |
969 | } |
970 | |
971 | /** |
972 | * vmw_surface_reference_ioctl - Ioctl function implementing |
973 | * the user surface reference functionality. |
974 | * |
975 | * @dev: Pointer to a struct drm_device. |
976 | * @data: Pointer to data copied from / to user-space. |
977 | * @file_priv: Pointer to a drm file private structure. |
978 | */ |
979 | int vmw_surface_reference_ioctl(struct drm_device *dev, void *data, |
980 | struct drm_file *file_priv) |
981 | { |
982 | struct vmw_private *dev_priv = vmw_priv(dev); |
983 | union drm_vmw_surface_reference_arg *arg = |
984 | (union drm_vmw_surface_reference_arg *)data; |
985 | struct drm_vmw_surface_arg *req = &arg->req; |
986 | struct drm_vmw_surface_create_req *rep = &arg->rep; |
987 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; |
988 | struct vmw_surface *srf; |
989 | struct vmw_user_surface *user_srf; |
990 | struct drm_vmw_size __user *user_sizes; |
991 | struct ttm_base_object *base; |
992 | int ret; |
993 | |
994 | ret = vmw_surface_handle_reference(dev_priv, file_priv, u_handle: req->sid, |
995 | handle_type: req->handle_type, base_p: &base); |
996 | if (unlikely(ret != 0)) |
997 | return ret; |
998 | |
999 | user_srf = container_of(base, struct vmw_user_surface, prime.base); |
1000 | srf = &user_srf->srf; |
1001 | |
1002 | /* Downcast of flags when sending back to user space */ |
1003 | rep->flags = (uint32_t)srf->metadata.flags; |
1004 | rep->format = srf->metadata.format; |
1005 | memcpy(rep->mip_levels, srf->metadata.mip_levels, |
1006 | sizeof(srf->metadata.mip_levels)); |
1007 | user_sizes = (struct drm_vmw_size __user *)(unsigned long) |
1008 | rep->size_addr; |
1009 | |
1010 | if (user_sizes) |
1011 | ret = copy_to_user(to: user_sizes, from: &srf->metadata.base_size, |
1012 | n: sizeof(srf->metadata.base_size)); |
1013 | if (unlikely(ret != 0)) { |
1014 | VMW_DEBUG_USER("copy_to_user failed %p %u\n" , user_sizes, |
1015 | srf->metadata.num_sizes); |
1016 | ttm_ref_object_base_unref(tfile, key: base->handle); |
1017 | ret = -EFAULT; |
1018 | } |
1019 | |
1020 | ttm_base_object_unref(p_base: &base); |
1021 | |
1022 | return ret; |
1023 | } |
1024 | |
1025 | /** |
1026 | * vmw_gb_surface_create - Encode a surface_define command. |
1027 | * |
1028 | * @res: Pointer to a struct vmw_resource embedded in a struct |
1029 | * vmw_surface. |
1030 | */ |
1031 | static int vmw_gb_surface_create(struct vmw_resource *res) |
1032 | { |
1033 | struct vmw_private *dev_priv = res->dev_priv; |
1034 | struct vmw_surface *srf = vmw_res_to_srf(res); |
1035 | struct vmw_surface_metadata *metadata = &srf->metadata; |
1036 | uint32_t cmd_len, cmd_id, submit_len; |
1037 | int ret; |
1038 | struct { |
1039 | SVGA3dCmdHeader ; |
1040 | SVGA3dCmdDefineGBSurface body; |
1041 | } *cmd; |
1042 | struct { |
1043 | SVGA3dCmdHeader ; |
1044 | SVGA3dCmdDefineGBSurface_v2 body; |
1045 | } *cmd2; |
1046 | struct { |
1047 | SVGA3dCmdHeader ; |
1048 | SVGA3dCmdDefineGBSurface_v3 body; |
1049 | } *cmd3; |
1050 | struct { |
1051 | SVGA3dCmdHeader ; |
1052 | SVGA3dCmdDefineGBSurface_v4 body; |
1053 | } *cmd4; |
1054 | |
1055 | if (likely(res->id != -1)) |
1056 | return 0; |
1057 | |
1058 | vmw_fifo_resource_inc(dev_priv); |
1059 | ret = vmw_resource_alloc_id(res); |
1060 | if (unlikely(ret != 0)) { |
1061 | DRM_ERROR("Failed to allocate a surface id.\n" ); |
1062 | goto out_no_id; |
1063 | } |
1064 | |
1065 | if (unlikely(res->id >= VMWGFX_NUM_GB_SURFACE)) { |
1066 | ret = -EBUSY; |
1067 | goto out_no_fifo; |
1068 | } |
1069 | |
1070 | if (has_sm5_context(dev_priv) && metadata->array_size > 0) { |
1071 | cmd_id = SVGA_3D_CMD_DEFINE_GB_SURFACE_V4; |
1072 | cmd_len = sizeof(cmd4->body); |
1073 | submit_len = sizeof(*cmd4); |
1074 | } else if (has_sm4_1_context(dev_priv) && metadata->array_size > 0) { |
1075 | cmd_id = SVGA_3D_CMD_DEFINE_GB_SURFACE_V3; |
1076 | cmd_len = sizeof(cmd3->body); |
1077 | submit_len = sizeof(*cmd3); |
1078 | } else if (metadata->array_size > 0) { |
1079 | /* VMW_SM_4 support verified at creation time. */ |
1080 | cmd_id = SVGA_3D_CMD_DEFINE_GB_SURFACE_V2; |
1081 | cmd_len = sizeof(cmd2->body); |
1082 | submit_len = sizeof(*cmd2); |
1083 | } else { |
1084 | cmd_id = SVGA_3D_CMD_DEFINE_GB_SURFACE; |
1085 | cmd_len = sizeof(cmd->body); |
1086 | submit_len = sizeof(*cmd); |
1087 | } |
1088 | |
1089 | cmd = VMW_CMD_RESERVE(dev_priv, submit_len); |
1090 | cmd2 = (typeof(cmd2))cmd; |
1091 | cmd3 = (typeof(cmd3))cmd; |
1092 | cmd4 = (typeof(cmd4))cmd; |
1093 | if (unlikely(!cmd)) { |
1094 | ret = -ENOMEM; |
1095 | goto out_no_fifo; |
1096 | } |
1097 | |
1098 | if (has_sm5_context(dev_priv) && metadata->array_size > 0) { |
1099 | cmd4->header.id = cmd_id; |
1100 | cmd4->header.size = cmd_len; |
1101 | cmd4->body.sid = srf->res.id; |
1102 | cmd4->body.surfaceFlags = metadata->flags; |
1103 | cmd4->body.format = metadata->format; |
1104 | cmd4->body.numMipLevels = metadata->mip_levels[0]; |
1105 | cmd4->body.multisampleCount = metadata->multisample_count; |
1106 | cmd4->body.multisamplePattern = metadata->multisample_pattern; |
1107 | cmd4->body.qualityLevel = metadata->quality_level; |
1108 | cmd4->body.autogenFilter = metadata->autogen_filter; |
1109 | cmd4->body.size.width = metadata->base_size.width; |
1110 | cmd4->body.size.height = metadata->base_size.height; |
1111 | cmd4->body.size.depth = metadata->base_size.depth; |
1112 | cmd4->body.arraySize = metadata->array_size; |
1113 | cmd4->body.bufferByteStride = metadata->buffer_byte_stride; |
1114 | } else if (has_sm4_1_context(dev_priv) && metadata->array_size > 0) { |
1115 | cmd3->header.id = cmd_id; |
1116 | cmd3->header.size = cmd_len; |
1117 | cmd3->body.sid = srf->res.id; |
1118 | cmd3->body.surfaceFlags = metadata->flags; |
1119 | cmd3->body.format = metadata->format; |
1120 | cmd3->body.numMipLevels = metadata->mip_levels[0]; |
1121 | cmd3->body.multisampleCount = metadata->multisample_count; |
1122 | cmd3->body.multisamplePattern = metadata->multisample_pattern; |
1123 | cmd3->body.qualityLevel = metadata->quality_level; |
1124 | cmd3->body.autogenFilter = metadata->autogen_filter; |
1125 | cmd3->body.size.width = metadata->base_size.width; |
1126 | cmd3->body.size.height = metadata->base_size.height; |
1127 | cmd3->body.size.depth = metadata->base_size.depth; |
1128 | cmd3->body.arraySize = metadata->array_size; |
1129 | } else if (metadata->array_size > 0) { |
1130 | cmd2->header.id = cmd_id; |
1131 | cmd2->header.size = cmd_len; |
1132 | cmd2->body.sid = srf->res.id; |
1133 | cmd2->body.surfaceFlags = metadata->flags; |
1134 | cmd2->body.format = metadata->format; |
1135 | cmd2->body.numMipLevels = metadata->mip_levels[0]; |
1136 | cmd2->body.multisampleCount = metadata->multisample_count; |
1137 | cmd2->body.autogenFilter = metadata->autogen_filter; |
1138 | cmd2->body.size.width = metadata->base_size.width; |
1139 | cmd2->body.size.height = metadata->base_size.height; |
1140 | cmd2->body.size.depth = metadata->base_size.depth; |
1141 | cmd2->body.arraySize = metadata->array_size; |
1142 | } else { |
1143 | cmd->header.id = cmd_id; |
1144 | cmd->header.size = cmd_len; |
1145 | cmd->body.sid = srf->res.id; |
1146 | cmd->body.surfaceFlags = metadata->flags; |
1147 | cmd->body.format = metadata->format; |
1148 | cmd->body.numMipLevels = metadata->mip_levels[0]; |
1149 | cmd->body.multisampleCount = metadata->multisample_count; |
1150 | cmd->body.autogenFilter = metadata->autogen_filter; |
1151 | cmd->body.size.width = metadata->base_size.width; |
1152 | cmd->body.size.height = metadata->base_size.height; |
1153 | cmd->body.size.depth = metadata->base_size.depth; |
1154 | } |
1155 | |
1156 | vmw_cmd_commit(dev_priv, bytes: submit_len); |
1157 | |
1158 | return 0; |
1159 | |
1160 | out_no_fifo: |
1161 | vmw_resource_release_id(res); |
1162 | out_no_id: |
1163 | vmw_fifo_resource_dec(dev_priv); |
1164 | return ret; |
1165 | } |
1166 | |
1167 | |
1168 | static int vmw_gb_surface_bind(struct vmw_resource *res, |
1169 | struct ttm_validate_buffer *val_buf) |
1170 | { |
1171 | struct vmw_private *dev_priv = res->dev_priv; |
1172 | struct { |
1173 | SVGA3dCmdHeader ; |
1174 | SVGA3dCmdBindGBSurface body; |
1175 | } *cmd1; |
1176 | struct { |
1177 | SVGA3dCmdHeader ; |
1178 | SVGA3dCmdUpdateGBSurface body; |
1179 | } *cmd2; |
1180 | uint32_t submit_size; |
1181 | struct ttm_buffer_object *bo = val_buf->bo; |
1182 | |
1183 | BUG_ON(bo->resource->mem_type != VMW_PL_MOB); |
1184 | |
1185 | submit_size = sizeof(*cmd1) + (res->guest_memory_dirty ? sizeof(*cmd2) : 0); |
1186 | |
1187 | cmd1 = VMW_CMD_RESERVE(dev_priv, submit_size); |
1188 | if (unlikely(!cmd1)) |
1189 | return -ENOMEM; |
1190 | |
1191 | cmd1->header.id = SVGA_3D_CMD_BIND_GB_SURFACE; |
1192 | cmd1->header.size = sizeof(cmd1->body); |
1193 | cmd1->body.sid = res->id; |
1194 | cmd1->body.mobid = bo->resource->start; |
1195 | if (res->guest_memory_dirty) { |
1196 | cmd2 = (void *) &cmd1[1]; |
1197 | cmd2->header.id = SVGA_3D_CMD_UPDATE_GB_SURFACE; |
1198 | cmd2->header.size = sizeof(cmd2->body); |
1199 | cmd2->body.sid = res->id; |
1200 | } |
1201 | vmw_cmd_commit(dev_priv, bytes: submit_size); |
1202 | |
1203 | if (res->guest_memory_bo->dirty && res->guest_memory_dirty) { |
1204 | /* We've just made a full upload. Cear dirty regions. */ |
1205 | vmw_bo_dirty_clear_res(res); |
1206 | } |
1207 | |
1208 | res->guest_memory_dirty = false; |
1209 | |
1210 | return 0; |
1211 | } |
1212 | |
1213 | static int vmw_gb_surface_unbind(struct vmw_resource *res, |
1214 | bool readback, |
1215 | struct ttm_validate_buffer *val_buf) |
1216 | { |
1217 | struct vmw_private *dev_priv = res->dev_priv; |
1218 | struct ttm_buffer_object *bo = val_buf->bo; |
1219 | struct vmw_fence_obj *fence; |
1220 | |
1221 | struct { |
1222 | SVGA3dCmdHeader ; |
1223 | SVGA3dCmdReadbackGBSurface body; |
1224 | } *cmd1; |
1225 | struct { |
1226 | SVGA3dCmdHeader ; |
1227 | SVGA3dCmdInvalidateGBSurface body; |
1228 | } *cmd2; |
1229 | struct { |
1230 | SVGA3dCmdHeader ; |
1231 | SVGA3dCmdBindGBSurface body; |
1232 | } *cmd3; |
1233 | uint32_t submit_size; |
1234 | uint8_t *cmd; |
1235 | |
1236 | |
1237 | BUG_ON(bo->resource->mem_type != VMW_PL_MOB); |
1238 | |
1239 | submit_size = sizeof(*cmd3) + (readback ? sizeof(*cmd1) : sizeof(*cmd2)); |
1240 | cmd = VMW_CMD_RESERVE(dev_priv, submit_size); |
1241 | if (unlikely(!cmd)) |
1242 | return -ENOMEM; |
1243 | |
1244 | if (readback) { |
1245 | cmd1 = (void *) cmd; |
1246 | cmd1->header.id = SVGA_3D_CMD_READBACK_GB_SURFACE; |
1247 | cmd1->header.size = sizeof(cmd1->body); |
1248 | cmd1->body.sid = res->id; |
1249 | cmd3 = (void *) &cmd1[1]; |
1250 | } else { |
1251 | cmd2 = (void *) cmd; |
1252 | cmd2->header.id = SVGA_3D_CMD_INVALIDATE_GB_SURFACE; |
1253 | cmd2->header.size = sizeof(cmd2->body); |
1254 | cmd2->body.sid = res->id; |
1255 | cmd3 = (void *) &cmd2[1]; |
1256 | } |
1257 | |
1258 | cmd3->header.id = SVGA_3D_CMD_BIND_GB_SURFACE; |
1259 | cmd3->header.size = sizeof(cmd3->body); |
1260 | cmd3->body.sid = res->id; |
1261 | cmd3->body.mobid = SVGA3D_INVALID_ID; |
1262 | |
1263 | vmw_cmd_commit(dev_priv, bytes: submit_size); |
1264 | |
1265 | /* |
1266 | * Create a fence object and fence the backup buffer. |
1267 | */ |
1268 | |
1269 | (void) vmw_execbuf_fence_commands(NULL, dev_priv, |
1270 | p_fence: &fence, NULL); |
1271 | |
1272 | vmw_bo_fence_single(bo: val_buf->bo, fence); |
1273 | |
1274 | if (likely(fence != NULL)) |
1275 | vmw_fence_obj_unreference(fence_p: &fence); |
1276 | |
1277 | return 0; |
1278 | } |
1279 | |
1280 | static int vmw_gb_surface_destroy(struct vmw_resource *res) |
1281 | { |
1282 | struct vmw_private *dev_priv = res->dev_priv; |
1283 | struct vmw_surface *srf = vmw_res_to_srf(res); |
1284 | struct { |
1285 | SVGA3dCmdHeader ; |
1286 | SVGA3dCmdDestroyGBSurface body; |
1287 | } *cmd; |
1288 | |
1289 | if (likely(res->id == -1)) |
1290 | return 0; |
1291 | |
1292 | mutex_lock(&dev_priv->binding_mutex); |
1293 | vmw_view_surface_list_destroy(dev_priv, view_list: &srf->view_list); |
1294 | vmw_binding_res_list_scrub(head: &res->binding_head); |
1295 | |
1296 | cmd = VMW_CMD_RESERVE(dev_priv, sizeof(*cmd)); |
1297 | if (unlikely(!cmd)) { |
1298 | mutex_unlock(lock: &dev_priv->binding_mutex); |
1299 | return -ENOMEM; |
1300 | } |
1301 | |
1302 | cmd->header.id = SVGA_3D_CMD_DESTROY_GB_SURFACE; |
1303 | cmd->header.size = sizeof(cmd->body); |
1304 | cmd->body.sid = res->id; |
1305 | vmw_cmd_commit(dev_priv, bytes: sizeof(*cmd)); |
1306 | mutex_unlock(lock: &dev_priv->binding_mutex); |
1307 | vmw_resource_release_id(res); |
1308 | vmw_fifo_resource_dec(dev_priv); |
1309 | |
1310 | return 0; |
1311 | } |
1312 | |
1313 | /** |
1314 | * vmw_gb_surface_define_ioctl - Ioctl function implementing |
1315 | * the user surface define functionality. |
1316 | * |
1317 | * @dev: Pointer to a struct drm_device. |
1318 | * @data: Pointer to data copied from / to user-space. |
1319 | * @file_priv: Pointer to a drm file private structure. |
1320 | */ |
1321 | int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data, |
1322 | struct drm_file *file_priv) |
1323 | { |
1324 | union drm_vmw_gb_surface_create_arg *arg = |
1325 | (union drm_vmw_gb_surface_create_arg *)data; |
1326 | struct drm_vmw_gb_surface_create_rep *rep = &arg->rep; |
1327 | struct drm_vmw_gb_surface_create_ext_req req_ext; |
1328 | |
1329 | req_ext.base = arg->req; |
1330 | req_ext.version = drm_vmw_gb_surface_v1; |
1331 | req_ext.svga3d_flags_upper_32_bits = 0; |
1332 | req_ext.multisample_pattern = SVGA3D_MS_PATTERN_NONE; |
1333 | req_ext.quality_level = SVGA3D_MS_QUALITY_NONE; |
1334 | req_ext.buffer_byte_stride = 0; |
1335 | req_ext.must_be_zero = 0; |
1336 | |
1337 | return vmw_gb_surface_define_internal(dev, req: &req_ext, rep, file_priv); |
1338 | } |
1339 | |
1340 | /** |
1341 | * vmw_gb_surface_reference_ioctl - Ioctl function implementing |
1342 | * the user surface reference functionality. |
1343 | * |
1344 | * @dev: Pointer to a struct drm_device. |
1345 | * @data: Pointer to data copied from / to user-space. |
1346 | * @file_priv: Pointer to a drm file private structure. |
1347 | */ |
1348 | int vmw_gb_surface_reference_ioctl(struct drm_device *dev, void *data, |
1349 | struct drm_file *file_priv) |
1350 | { |
1351 | union drm_vmw_gb_surface_reference_arg *arg = |
1352 | (union drm_vmw_gb_surface_reference_arg *)data; |
1353 | struct drm_vmw_surface_arg *req = &arg->req; |
1354 | struct drm_vmw_gb_surface_ref_rep *rep = &arg->rep; |
1355 | struct drm_vmw_gb_surface_ref_ext_rep rep_ext; |
1356 | int ret; |
1357 | |
1358 | ret = vmw_gb_surface_reference_internal(dev, req, rep: &rep_ext, file_priv); |
1359 | |
1360 | if (unlikely(ret != 0)) |
1361 | return ret; |
1362 | |
1363 | rep->creq = rep_ext.creq.base; |
1364 | rep->crep = rep_ext.crep; |
1365 | |
1366 | return ret; |
1367 | } |
1368 | |
1369 | /** |
1370 | * vmw_gb_surface_define_ext_ioctl - Ioctl function implementing |
1371 | * the user surface define functionality. |
1372 | * |
1373 | * @dev: Pointer to a struct drm_device. |
1374 | * @data: Pointer to data copied from / to user-space. |
1375 | * @file_priv: Pointer to a drm file private structure. |
1376 | */ |
1377 | int vmw_gb_surface_define_ext_ioctl(struct drm_device *dev, void *data, |
1378 | struct drm_file *file_priv) |
1379 | { |
1380 | union drm_vmw_gb_surface_create_ext_arg *arg = |
1381 | (union drm_vmw_gb_surface_create_ext_arg *)data; |
1382 | struct drm_vmw_gb_surface_create_ext_req *req = &arg->req; |
1383 | struct drm_vmw_gb_surface_create_rep *rep = &arg->rep; |
1384 | |
1385 | return vmw_gb_surface_define_internal(dev, req, rep, file_priv); |
1386 | } |
1387 | |
1388 | /** |
1389 | * vmw_gb_surface_reference_ext_ioctl - Ioctl function implementing |
1390 | * the user surface reference functionality. |
1391 | * |
1392 | * @dev: Pointer to a struct drm_device. |
1393 | * @data: Pointer to data copied from / to user-space. |
1394 | * @file_priv: Pointer to a drm file private structure. |
1395 | */ |
1396 | int vmw_gb_surface_reference_ext_ioctl(struct drm_device *dev, void *data, |
1397 | struct drm_file *file_priv) |
1398 | { |
1399 | union drm_vmw_gb_surface_reference_ext_arg *arg = |
1400 | (union drm_vmw_gb_surface_reference_ext_arg *)data; |
1401 | struct drm_vmw_surface_arg *req = &arg->req; |
1402 | struct drm_vmw_gb_surface_ref_ext_rep *rep = &arg->rep; |
1403 | |
1404 | return vmw_gb_surface_reference_internal(dev, req, rep, file_priv); |
1405 | } |
1406 | |
1407 | /** |
1408 | * vmw_gb_surface_define_internal - Ioctl function implementing |
1409 | * the user surface define functionality. |
1410 | * |
1411 | * @dev: Pointer to a struct drm_device. |
1412 | * @req: Request argument from user-space. |
1413 | * @rep: Response argument to user-space. |
1414 | * @file_priv: Pointer to a drm file private structure. |
1415 | */ |
1416 | static int |
1417 | vmw_gb_surface_define_internal(struct drm_device *dev, |
1418 | struct drm_vmw_gb_surface_create_ext_req *req, |
1419 | struct drm_vmw_gb_surface_create_rep *rep, |
1420 | struct drm_file *file_priv) |
1421 | { |
1422 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; |
1423 | struct vmw_private *dev_priv = vmw_priv(dev); |
1424 | struct vmw_user_surface *user_srf; |
1425 | struct vmw_surface_metadata metadata = {0}; |
1426 | struct vmw_surface *srf; |
1427 | struct vmw_resource *res; |
1428 | struct vmw_resource *tmp; |
1429 | int ret = 0; |
1430 | uint32_t backup_handle = 0; |
1431 | SVGA3dSurfaceAllFlags svga3d_flags_64 = |
1432 | SVGA3D_FLAGS_64(req->svga3d_flags_upper_32_bits, |
1433 | req->base.svga3d_flags); |
1434 | |
1435 | /* array_size must be null for non-GL3 host. */ |
1436 | if (req->base.array_size > 0 && !has_sm4_context(dev_priv)) { |
1437 | VMW_DEBUG_USER("SM4 surface not supported.\n" ); |
1438 | return -EINVAL; |
1439 | } |
1440 | |
1441 | if (!has_sm4_1_context(dev_priv)) { |
1442 | if (req->svga3d_flags_upper_32_bits != 0) |
1443 | ret = -EINVAL; |
1444 | |
1445 | if (req->base.multisample_count != 0) |
1446 | ret = -EINVAL; |
1447 | |
1448 | if (req->multisample_pattern != SVGA3D_MS_PATTERN_NONE) |
1449 | ret = -EINVAL; |
1450 | |
1451 | if (req->quality_level != SVGA3D_MS_QUALITY_NONE) |
1452 | ret = -EINVAL; |
1453 | |
1454 | if (ret) { |
1455 | VMW_DEBUG_USER("SM4.1 surface not supported.\n" ); |
1456 | return ret; |
1457 | } |
1458 | } |
1459 | |
1460 | if (req->buffer_byte_stride > 0 && !has_sm5_context(dev_priv)) { |
1461 | VMW_DEBUG_USER("SM5 surface not supported.\n" ); |
1462 | return -EINVAL; |
1463 | } |
1464 | |
1465 | if ((svga3d_flags_64 & SVGA3D_SURFACE_MULTISAMPLE) && |
1466 | req->base.multisample_count == 0) { |
1467 | VMW_DEBUG_USER("Invalid sample count.\n" ); |
1468 | return -EINVAL; |
1469 | } |
1470 | |
1471 | if (req->base.mip_levels > DRM_VMW_MAX_MIP_LEVELS) { |
1472 | VMW_DEBUG_USER("Invalid mip level.\n" ); |
1473 | return -EINVAL; |
1474 | } |
1475 | |
1476 | metadata.flags = svga3d_flags_64; |
1477 | metadata.format = req->base.format; |
1478 | metadata.mip_levels[0] = req->base.mip_levels; |
1479 | metadata.multisample_count = req->base.multisample_count; |
1480 | metadata.multisample_pattern = req->multisample_pattern; |
1481 | metadata.quality_level = req->quality_level; |
1482 | metadata.array_size = req->base.array_size; |
1483 | metadata.buffer_byte_stride = req->buffer_byte_stride; |
1484 | metadata.num_sizes = 1; |
1485 | metadata.base_size = req->base.base_size; |
1486 | metadata.scanout = req->base.drm_surface_flags & |
1487 | drm_vmw_surface_flag_scanout; |
1488 | |
1489 | /* Define a surface based on the parameters. */ |
1490 | ret = vmw_gb_surface_define(dev_priv, req: &metadata, srf_out: &srf); |
1491 | if (ret != 0) { |
1492 | VMW_DEBUG_USER("Failed to define surface.\n" ); |
1493 | return ret; |
1494 | } |
1495 | |
1496 | user_srf = container_of(srf, struct vmw_user_surface, srf); |
1497 | if (drm_is_primary_client(file_priv)) |
1498 | user_srf->master = drm_file_get_master(file_priv); |
1499 | |
1500 | res = &user_srf->srf.res; |
1501 | |
1502 | if (req->base.buffer_handle != SVGA3D_INVALID_ID) { |
1503 | ret = vmw_user_bo_lookup(filp: file_priv, handle: req->base.buffer_handle, |
1504 | out: &res->guest_memory_bo); |
1505 | if (ret == 0) { |
1506 | if (res->guest_memory_bo->tbo.base.size < res->guest_memory_size) { |
1507 | VMW_DEBUG_USER("Surface backup buffer too small.\n" ); |
1508 | vmw_user_bo_unref(buf: &res->guest_memory_bo); |
1509 | ret = -EINVAL; |
1510 | goto out_unlock; |
1511 | } else { |
1512 | backup_handle = req->base.buffer_handle; |
1513 | } |
1514 | } |
1515 | } else if (req->base.drm_surface_flags & |
1516 | (drm_vmw_surface_flag_create_buffer | |
1517 | drm_vmw_surface_flag_coherent)) { |
1518 | ret = vmw_gem_object_create_with_handle(dev_priv, filp: file_priv, |
1519 | size: res->guest_memory_size, |
1520 | handle: &backup_handle, |
1521 | p_vbo: &res->guest_memory_bo); |
1522 | } |
1523 | |
1524 | if (unlikely(ret != 0)) { |
1525 | vmw_resource_unreference(p_res: &res); |
1526 | goto out_unlock; |
1527 | } |
1528 | |
1529 | if (req->base.drm_surface_flags & drm_vmw_surface_flag_coherent) { |
1530 | struct vmw_bo *backup = res->guest_memory_bo; |
1531 | |
1532 | ttm_bo_reserve(bo: &backup->tbo, interruptible: false, no_wait: false, NULL); |
1533 | if (!res->func->dirty_alloc) |
1534 | ret = -EINVAL; |
1535 | if (!ret) |
1536 | ret = vmw_bo_dirty_add(vbo: backup); |
1537 | if (!ret) { |
1538 | res->coherent = true; |
1539 | ret = res->func->dirty_alloc(res); |
1540 | } |
1541 | ttm_bo_unreserve(bo: &backup->tbo); |
1542 | if (ret) { |
1543 | vmw_resource_unreference(p_res: &res); |
1544 | goto out_unlock; |
1545 | } |
1546 | |
1547 | } |
1548 | |
1549 | tmp = vmw_resource_reference(res); |
1550 | ret = ttm_prime_object_init(tfile, size: res->guest_memory_size, prime: &user_srf->prime, |
1551 | VMW_RES_SURFACE, |
1552 | refcount_release: &vmw_user_surface_base_release); |
1553 | |
1554 | if (unlikely(ret != 0)) { |
1555 | vmw_resource_unreference(p_res: &tmp); |
1556 | vmw_resource_unreference(p_res: &res); |
1557 | goto out_unlock; |
1558 | } |
1559 | |
1560 | rep->handle = user_srf->prime.base.handle; |
1561 | rep->backup_size = res->guest_memory_size; |
1562 | if (res->guest_memory_bo) { |
1563 | rep->buffer_map_handle = |
1564 | drm_vma_node_offset_addr(node: &res->guest_memory_bo->tbo.base.vma_node); |
1565 | rep->buffer_size = res->guest_memory_bo->tbo.base.size; |
1566 | rep->buffer_handle = backup_handle; |
1567 | } else { |
1568 | rep->buffer_map_handle = 0; |
1569 | rep->buffer_size = 0; |
1570 | rep->buffer_handle = SVGA3D_INVALID_ID; |
1571 | } |
1572 | vmw_resource_unreference(p_res: &res); |
1573 | |
1574 | out_unlock: |
1575 | return ret; |
1576 | } |
1577 | |
1578 | /** |
1579 | * vmw_gb_surface_reference_internal - Ioctl function implementing |
1580 | * the user surface reference functionality. |
1581 | * |
1582 | * @dev: Pointer to a struct drm_device. |
1583 | * @req: Pointer to user-space request surface arg. |
1584 | * @rep: Pointer to response to user-space. |
1585 | * @file_priv: Pointer to a drm file private structure. |
1586 | */ |
1587 | static int |
1588 | vmw_gb_surface_reference_internal(struct drm_device *dev, |
1589 | struct drm_vmw_surface_arg *req, |
1590 | struct drm_vmw_gb_surface_ref_ext_rep *rep, |
1591 | struct drm_file *file_priv) |
1592 | { |
1593 | struct vmw_private *dev_priv = vmw_priv(dev); |
1594 | struct vmw_surface *srf; |
1595 | struct vmw_user_surface *user_srf; |
1596 | struct vmw_surface_metadata *metadata; |
1597 | struct ttm_base_object *base; |
1598 | u32 backup_handle; |
1599 | int ret; |
1600 | |
1601 | ret = vmw_surface_handle_reference(dev_priv, file_priv, u_handle: req->sid, |
1602 | handle_type: req->handle_type, base_p: &base); |
1603 | if (unlikely(ret != 0)) |
1604 | return ret; |
1605 | |
1606 | user_srf = container_of(base, struct vmw_user_surface, prime.base); |
1607 | srf = &user_srf->srf; |
1608 | if (!srf->res.guest_memory_bo) { |
1609 | DRM_ERROR("Shared GB surface is missing a backup buffer.\n" ); |
1610 | goto out_bad_resource; |
1611 | } |
1612 | metadata = &srf->metadata; |
1613 | |
1614 | mutex_lock(&dev_priv->cmdbuf_mutex); /* Protect res->backup */ |
1615 | ret = drm_gem_handle_create(file_priv, obj: &srf->res.guest_memory_bo->tbo.base, |
1616 | handlep: &backup_handle); |
1617 | mutex_unlock(lock: &dev_priv->cmdbuf_mutex); |
1618 | if (ret != 0) { |
1619 | drm_err(dev, "Wasn't able to create a backing handle for surface sid = %u.\n" , |
1620 | req->sid); |
1621 | goto out_bad_resource; |
1622 | } |
1623 | |
1624 | rep->creq.base.svga3d_flags = SVGA3D_FLAGS_LOWER_32(metadata->flags); |
1625 | rep->creq.base.format = metadata->format; |
1626 | rep->creq.base.mip_levels = metadata->mip_levels[0]; |
1627 | rep->creq.base.drm_surface_flags = 0; |
1628 | rep->creq.base.multisample_count = metadata->multisample_count; |
1629 | rep->creq.base.autogen_filter = metadata->autogen_filter; |
1630 | rep->creq.base.array_size = metadata->array_size; |
1631 | rep->creq.base.buffer_handle = backup_handle; |
1632 | rep->creq.base.base_size = metadata->base_size; |
1633 | rep->crep.handle = user_srf->prime.base.handle; |
1634 | rep->crep.backup_size = srf->res.guest_memory_size; |
1635 | rep->crep.buffer_handle = backup_handle; |
1636 | rep->crep.buffer_map_handle = |
1637 | drm_vma_node_offset_addr(node: &srf->res.guest_memory_bo->tbo.base.vma_node); |
1638 | rep->crep.buffer_size = srf->res.guest_memory_bo->tbo.base.size; |
1639 | |
1640 | rep->creq.version = drm_vmw_gb_surface_v1; |
1641 | rep->creq.svga3d_flags_upper_32_bits = |
1642 | SVGA3D_FLAGS_UPPER_32(metadata->flags); |
1643 | rep->creq.multisample_pattern = metadata->multisample_pattern; |
1644 | rep->creq.quality_level = metadata->quality_level; |
1645 | rep->creq.must_be_zero = 0; |
1646 | |
1647 | out_bad_resource: |
1648 | ttm_base_object_unref(p_base: &base); |
1649 | |
1650 | return ret; |
1651 | } |
1652 | |
1653 | /** |
1654 | * vmw_subres_dirty_add - Add a dirty region to a subresource |
1655 | * @dirty: The surfaces's dirty tracker. |
1656 | * @loc_start: The location corresponding to the start of the region. |
1657 | * @loc_end: The location corresponding to the end of the region. |
1658 | * |
1659 | * As we are assuming that @loc_start and @loc_end represent a sequential |
1660 | * range of backing store memory, if the region spans multiple lines then |
1661 | * regardless of the x coordinate, the full lines are dirtied. |
1662 | * Correspondingly if the region spans multiple z slices, then full rather |
1663 | * than partial z slices are dirtied. |
1664 | */ |
1665 | static void vmw_subres_dirty_add(struct vmw_surface_dirty *dirty, |
1666 | const struct vmw_surface_loc *loc_start, |
1667 | const struct vmw_surface_loc *loc_end) |
1668 | { |
1669 | const struct vmw_surface_cache *cache = &dirty->cache; |
1670 | SVGA3dBox *box = &dirty->boxes[loc_start->sub_resource]; |
1671 | u32 mip = loc_start->sub_resource % cache->num_mip_levels; |
1672 | const struct drm_vmw_size *size = &cache->mip[mip].size; |
1673 | u32 box_c2 = box->z + box->d; |
1674 | |
1675 | if (WARN_ON(loc_start->sub_resource >= dirty->num_subres)) |
1676 | return; |
1677 | |
1678 | if (box->d == 0 || box->z > loc_start->z) |
1679 | box->z = loc_start->z; |
1680 | if (box_c2 < loc_end->z) |
1681 | box->d = loc_end->z - box->z; |
1682 | |
1683 | if (loc_start->z + 1 == loc_end->z) { |
1684 | box_c2 = box->y + box->h; |
1685 | if (box->h == 0 || box->y > loc_start->y) |
1686 | box->y = loc_start->y; |
1687 | if (box_c2 < loc_end->y) |
1688 | box->h = loc_end->y - box->y; |
1689 | |
1690 | if (loc_start->y + 1 == loc_end->y) { |
1691 | box_c2 = box->x + box->w; |
1692 | if (box->w == 0 || box->x > loc_start->x) |
1693 | box->x = loc_start->x; |
1694 | if (box_c2 < loc_end->x) |
1695 | box->w = loc_end->x - box->x; |
1696 | } else { |
1697 | box->x = 0; |
1698 | box->w = size->width; |
1699 | } |
1700 | } else { |
1701 | box->y = 0; |
1702 | box->h = size->height; |
1703 | box->x = 0; |
1704 | box->w = size->width; |
1705 | } |
1706 | } |
1707 | |
1708 | /** |
1709 | * vmw_subres_dirty_full - Mark a full subresource as dirty |
1710 | * @dirty: The surface's dirty tracker. |
1711 | * @subres: The subresource |
1712 | */ |
1713 | static void vmw_subres_dirty_full(struct vmw_surface_dirty *dirty, u32 subres) |
1714 | { |
1715 | const struct vmw_surface_cache *cache = &dirty->cache; |
1716 | u32 mip = subres % cache->num_mip_levels; |
1717 | const struct drm_vmw_size *size = &cache->mip[mip].size; |
1718 | SVGA3dBox *box = &dirty->boxes[subres]; |
1719 | |
1720 | box->x = 0; |
1721 | box->y = 0; |
1722 | box->z = 0; |
1723 | box->w = size->width; |
1724 | box->h = size->height; |
1725 | box->d = size->depth; |
1726 | } |
1727 | |
1728 | /* |
1729 | * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for texture |
1730 | * surfaces. |
1731 | */ |
1732 | static void vmw_surface_tex_dirty_range_add(struct vmw_resource *res, |
1733 | size_t start, size_t end) |
1734 | { |
1735 | struct vmw_surface_dirty *dirty = |
1736 | (struct vmw_surface_dirty *) res->dirty; |
1737 | size_t backup_end = res->guest_memory_offset + res->guest_memory_size; |
1738 | struct vmw_surface_loc loc1, loc2; |
1739 | const struct vmw_surface_cache *cache; |
1740 | |
1741 | start = max_t(size_t, start, res->guest_memory_offset) - res->guest_memory_offset; |
1742 | end = min(end, backup_end) - res->guest_memory_offset; |
1743 | cache = &dirty->cache; |
1744 | vmw_surface_get_loc(cache, loc: &loc1, offset: start); |
1745 | vmw_surface_get_loc(cache, loc: &loc2, offset: end - 1); |
1746 | vmw_surface_inc_loc(cache, loc: &loc2); |
1747 | |
1748 | if (loc1.sheet != loc2.sheet) { |
1749 | u32 sub_res; |
1750 | |
1751 | /* |
1752 | * Multiple multisample sheets. To do this in an optimized |
1753 | * fashion, compute the dirty region for each sheet and the |
1754 | * resulting union. Since this is not a common case, just dirty |
1755 | * the whole surface. |
1756 | */ |
1757 | for (sub_res = 0; sub_res < dirty->num_subres; ++sub_res) |
1758 | vmw_subres_dirty_full(dirty, subres: sub_res); |
1759 | return; |
1760 | } |
1761 | if (loc1.sub_resource + 1 == loc2.sub_resource) { |
1762 | /* Dirty range covers a single sub-resource */ |
1763 | vmw_subres_dirty_add(dirty, loc_start: &loc1, loc_end: &loc2); |
1764 | } else { |
1765 | /* Dirty range covers multiple sub-resources */ |
1766 | struct vmw_surface_loc loc_min, loc_max; |
1767 | u32 sub_res; |
1768 | |
1769 | vmw_surface_max_loc(cache, sub_resource: loc1.sub_resource, loc: &loc_max); |
1770 | vmw_subres_dirty_add(dirty, loc_start: &loc1, loc_end: &loc_max); |
1771 | vmw_surface_min_loc(cache, sub_resource: loc2.sub_resource - 1, loc: &loc_min); |
1772 | vmw_subres_dirty_add(dirty, loc_start: &loc_min, loc_end: &loc2); |
1773 | for (sub_res = loc1.sub_resource + 1; |
1774 | sub_res < loc2.sub_resource - 1; ++sub_res) |
1775 | vmw_subres_dirty_full(dirty, subres: sub_res); |
1776 | } |
1777 | } |
1778 | |
1779 | /* |
1780 | * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for buffer |
1781 | * surfaces. |
1782 | */ |
1783 | static void vmw_surface_buf_dirty_range_add(struct vmw_resource *res, |
1784 | size_t start, size_t end) |
1785 | { |
1786 | struct vmw_surface_dirty *dirty = |
1787 | (struct vmw_surface_dirty *) res->dirty; |
1788 | const struct vmw_surface_cache *cache = &dirty->cache; |
1789 | size_t backup_end = res->guest_memory_offset + cache->mip_chain_bytes; |
1790 | SVGA3dBox *box = &dirty->boxes[0]; |
1791 | u32 box_c2; |
1792 | |
1793 | box->h = box->d = 1; |
1794 | start = max_t(size_t, start, res->guest_memory_offset) - res->guest_memory_offset; |
1795 | end = min(end, backup_end) - res->guest_memory_offset; |
1796 | box_c2 = box->x + box->w; |
1797 | if (box->w == 0 || box->x > start) |
1798 | box->x = start; |
1799 | if (box_c2 < end) |
1800 | box->w = end - box->x; |
1801 | } |
1802 | |
1803 | /* |
1804 | * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for surfaces |
1805 | */ |
1806 | static void vmw_surface_dirty_range_add(struct vmw_resource *res, size_t start, |
1807 | size_t end) |
1808 | { |
1809 | struct vmw_surface *srf = vmw_res_to_srf(res); |
1810 | |
1811 | if (WARN_ON(end <= res->guest_memory_offset || |
1812 | start >= res->guest_memory_offset + res->guest_memory_size)) |
1813 | return; |
1814 | |
1815 | if (srf->metadata.format == SVGA3D_BUFFER) |
1816 | vmw_surface_buf_dirty_range_add(res, start, end); |
1817 | else |
1818 | vmw_surface_tex_dirty_range_add(res, start, end); |
1819 | } |
1820 | |
1821 | /* |
1822 | * vmw_surface_dirty_sync - The surface's dirty_sync callback. |
1823 | */ |
1824 | static int vmw_surface_dirty_sync(struct vmw_resource *res) |
1825 | { |
1826 | struct vmw_private *dev_priv = res->dev_priv; |
1827 | u32 i, num_dirty; |
1828 | struct vmw_surface_dirty *dirty = |
1829 | (struct vmw_surface_dirty *) res->dirty; |
1830 | size_t alloc_size; |
1831 | const struct vmw_surface_cache *cache = &dirty->cache; |
1832 | struct { |
1833 | SVGA3dCmdHeader ; |
1834 | SVGA3dCmdDXUpdateSubResource body; |
1835 | } *cmd1; |
1836 | struct { |
1837 | SVGA3dCmdHeader ; |
1838 | SVGA3dCmdUpdateGBImage body; |
1839 | } *cmd2; |
1840 | void *cmd; |
1841 | |
1842 | num_dirty = 0; |
1843 | for (i = 0; i < dirty->num_subres; ++i) { |
1844 | const SVGA3dBox *box = &dirty->boxes[i]; |
1845 | |
1846 | if (box->d) |
1847 | num_dirty++; |
1848 | } |
1849 | |
1850 | if (!num_dirty) |
1851 | goto out; |
1852 | |
1853 | alloc_size = num_dirty * ((has_sm4_context(dev_priv)) ? sizeof(*cmd1) : sizeof(*cmd2)); |
1854 | cmd = VMW_CMD_RESERVE(dev_priv, alloc_size); |
1855 | if (!cmd) |
1856 | return -ENOMEM; |
1857 | |
1858 | cmd1 = cmd; |
1859 | cmd2 = cmd; |
1860 | |
1861 | for (i = 0; i < dirty->num_subres; ++i) { |
1862 | const SVGA3dBox *box = &dirty->boxes[i]; |
1863 | |
1864 | if (!box->d) |
1865 | continue; |
1866 | |
1867 | /* |
1868 | * DX_UPDATE_SUBRESOURCE is aware of array surfaces. |
1869 | * UPDATE_GB_IMAGE is not. |
1870 | */ |
1871 | if (has_sm4_context(dev_priv)) { |
1872 | cmd1->header.id = SVGA_3D_CMD_DX_UPDATE_SUBRESOURCE; |
1873 | cmd1->header.size = sizeof(cmd1->body); |
1874 | cmd1->body.sid = res->id; |
1875 | cmd1->body.subResource = i; |
1876 | cmd1->body.box = *box; |
1877 | cmd1++; |
1878 | } else { |
1879 | cmd2->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE; |
1880 | cmd2->header.size = sizeof(cmd2->body); |
1881 | cmd2->body.image.sid = res->id; |
1882 | cmd2->body.image.face = i / cache->num_mip_levels; |
1883 | cmd2->body.image.mipmap = i - |
1884 | (cache->num_mip_levels * cmd2->body.image.face); |
1885 | cmd2->body.box = *box; |
1886 | cmd2++; |
1887 | } |
1888 | |
1889 | } |
1890 | vmw_cmd_commit(dev_priv, bytes: alloc_size); |
1891 | out: |
1892 | memset(&dirty->boxes[0], 0, sizeof(dirty->boxes[0]) * |
1893 | dirty->num_subres); |
1894 | |
1895 | return 0; |
1896 | } |
1897 | |
1898 | /* |
1899 | * vmw_surface_dirty_alloc - The surface's dirty_alloc callback. |
1900 | */ |
1901 | static int vmw_surface_dirty_alloc(struct vmw_resource *res) |
1902 | { |
1903 | struct vmw_surface *srf = vmw_res_to_srf(res); |
1904 | const struct vmw_surface_metadata *metadata = &srf->metadata; |
1905 | struct vmw_surface_dirty *dirty; |
1906 | u32 num_layers = 1; |
1907 | u32 num_mip; |
1908 | u32 num_subres; |
1909 | u32 num_samples; |
1910 | size_t dirty_size; |
1911 | int ret; |
1912 | |
1913 | if (metadata->array_size) |
1914 | num_layers = metadata->array_size; |
1915 | else if (metadata->flags & SVGA3D_SURFACE_CUBEMAP) |
1916 | num_layers *= SVGA3D_MAX_SURFACE_FACES; |
1917 | |
1918 | num_mip = metadata->mip_levels[0]; |
1919 | if (!num_mip) |
1920 | num_mip = 1; |
1921 | |
1922 | num_subres = num_layers * num_mip; |
1923 | dirty_size = struct_size(dirty, boxes, num_subres); |
1924 | |
1925 | dirty = kvzalloc(size: dirty_size, GFP_KERNEL); |
1926 | if (!dirty) { |
1927 | ret = -ENOMEM; |
1928 | goto out_no_dirty; |
1929 | } |
1930 | |
1931 | num_samples = max_t(u32, 1, metadata->multisample_count); |
1932 | ret = vmw_surface_setup_cache(size: &metadata->base_size, format: metadata->format, |
1933 | num_mip_levels: num_mip, num_layers, num_samples, |
1934 | cache: &dirty->cache); |
1935 | if (ret) |
1936 | goto out_no_cache; |
1937 | |
1938 | dirty->num_subres = num_subres; |
1939 | res->dirty = (struct vmw_resource_dirty *) dirty; |
1940 | |
1941 | return 0; |
1942 | |
1943 | out_no_cache: |
1944 | kvfree(addr: dirty); |
1945 | out_no_dirty: |
1946 | return ret; |
1947 | } |
1948 | |
1949 | /* |
1950 | * vmw_surface_dirty_free - The surface's dirty_free callback |
1951 | */ |
1952 | static void vmw_surface_dirty_free(struct vmw_resource *res) |
1953 | { |
1954 | struct vmw_surface_dirty *dirty = |
1955 | (struct vmw_surface_dirty *) res->dirty; |
1956 | |
1957 | kvfree(addr: dirty); |
1958 | res->dirty = NULL; |
1959 | } |
1960 | |
1961 | /* |
1962 | * vmw_surface_clean - The surface's clean callback |
1963 | */ |
1964 | static int vmw_surface_clean(struct vmw_resource *res) |
1965 | { |
1966 | struct vmw_private *dev_priv = res->dev_priv; |
1967 | size_t alloc_size; |
1968 | struct { |
1969 | SVGA3dCmdHeader ; |
1970 | SVGA3dCmdReadbackGBSurface body; |
1971 | } *cmd; |
1972 | |
1973 | alloc_size = sizeof(*cmd); |
1974 | cmd = VMW_CMD_RESERVE(dev_priv, alloc_size); |
1975 | if (!cmd) |
1976 | return -ENOMEM; |
1977 | |
1978 | cmd->header.id = SVGA_3D_CMD_READBACK_GB_SURFACE; |
1979 | cmd->header.size = sizeof(cmd->body); |
1980 | cmd->body.sid = res->id; |
1981 | vmw_cmd_commit(dev_priv, bytes: alloc_size); |
1982 | |
1983 | return 0; |
1984 | } |
1985 | |
1986 | /* |
1987 | * vmw_gb_surface_define - Define a private GB surface |
1988 | * |
1989 | * @dev_priv: Pointer to a device private. |
1990 | * @metadata: Metadata representing the surface to create. |
1991 | * @user_srf_out: allocated user_srf. Set to NULL on failure. |
1992 | * |
1993 | * GB surfaces allocated by this function will not have a user mode handle, and |
1994 | * thus will only be visible to vmwgfx. For optimization reasons the |
1995 | * surface may later be given a user mode handle by another function to make |
1996 | * it available to user mode drivers. |
1997 | */ |
1998 | int vmw_gb_surface_define(struct vmw_private *dev_priv, |
1999 | const struct vmw_surface_metadata *req, |
2000 | struct vmw_surface **srf_out) |
2001 | { |
2002 | struct vmw_surface_metadata *metadata; |
2003 | struct vmw_user_surface *user_srf; |
2004 | struct vmw_surface *srf; |
2005 | u32 sample_count = 1; |
2006 | u32 num_layers = 1; |
2007 | int ret; |
2008 | |
2009 | *srf_out = NULL; |
2010 | |
2011 | if (req->scanout) { |
2012 | if (!vmw_surface_is_screen_target_format(format: req->format)) { |
2013 | VMW_DEBUG_USER("Invalid Screen Target surface format." ); |
2014 | return -EINVAL; |
2015 | } |
2016 | |
2017 | if (req->base_size.width > dev_priv->texture_max_width || |
2018 | req->base_size.height > dev_priv->texture_max_height) { |
2019 | VMW_DEBUG_USER("%ux%u\n, exceed max surface size %ux%u" , |
2020 | req->base_size.width, |
2021 | req->base_size.height, |
2022 | dev_priv->texture_max_width, |
2023 | dev_priv->texture_max_height); |
2024 | return -EINVAL; |
2025 | } |
2026 | } else { |
2027 | const SVGA3dSurfaceDesc *desc = |
2028 | vmw_surface_get_desc(format: req->format); |
2029 | |
2030 | if (desc->blockDesc == SVGA3DBLOCKDESC_NONE) { |
2031 | VMW_DEBUG_USER("Invalid surface format.\n" ); |
2032 | return -EINVAL; |
2033 | } |
2034 | } |
2035 | |
2036 | if (req->autogen_filter != SVGA3D_TEX_FILTER_NONE) |
2037 | return -EINVAL; |
2038 | |
2039 | if (req->num_sizes != 1) |
2040 | return -EINVAL; |
2041 | |
2042 | if (req->sizes != NULL) |
2043 | return -EINVAL; |
2044 | |
2045 | user_srf = kzalloc(size: sizeof(*user_srf), GFP_KERNEL); |
2046 | if (unlikely(!user_srf)) { |
2047 | ret = -ENOMEM; |
2048 | goto out_unlock; |
2049 | } |
2050 | |
2051 | *srf_out = &user_srf->srf; |
2052 | |
2053 | srf = &user_srf->srf; |
2054 | srf->metadata = *req; |
2055 | srf->offsets = NULL; |
2056 | |
2057 | metadata = &srf->metadata; |
2058 | |
2059 | if (metadata->array_size) |
2060 | num_layers = req->array_size; |
2061 | else if (metadata->flags & SVGA3D_SURFACE_CUBEMAP) |
2062 | num_layers = SVGA3D_MAX_SURFACE_FACES; |
2063 | |
2064 | if (metadata->flags & SVGA3D_SURFACE_MULTISAMPLE) |
2065 | sample_count = metadata->multisample_count; |
2066 | |
2067 | srf->res.guest_memory_size = |
2068 | vmw_surface_get_serialized_size_extended( |
2069 | format: metadata->format, |
2070 | base_level_size: metadata->base_size, |
2071 | num_mip_levels: metadata->mip_levels[0], |
2072 | num_layers, |
2073 | num_samples: sample_count); |
2074 | |
2075 | if (metadata->flags & SVGA3D_SURFACE_BIND_STREAM_OUTPUT) |
2076 | srf->res.guest_memory_size += sizeof(SVGA3dDXSOState); |
2077 | |
2078 | /* |
2079 | * Don't set SVGA3D_SURFACE_SCREENTARGET flag for a scanout surface with |
2080 | * size greater than STDU max width/height. This is really a workaround |
2081 | * to support creation of big framebuffer requested by some user-space |
2082 | * for whole topology. That big framebuffer won't really be used for |
2083 | * binding with screen target as during prepare_fb a separate surface is |
2084 | * created so it's safe to ignore SVGA3D_SURFACE_SCREENTARGET flag. |
2085 | */ |
2086 | if (dev_priv->active_display_unit == vmw_du_screen_target && |
2087 | metadata->scanout && |
2088 | metadata->base_size.width <= dev_priv->stdu_max_width && |
2089 | metadata->base_size.height <= dev_priv->stdu_max_height) |
2090 | metadata->flags |= SVGA3D_SURFACE_SCREENTARGET; |
2091 | |
2092 | /* |
2093 | * From this point, the generic resource management functions |
2094 | * destroy the object on failure. |
2095 | */ |
2096 | ret = vmw_surface_init(dev_priv, srf, res_free: vmw_user_surface_free); |
2097 | |
2098 | return ret; |
2099 | |
2100 | out_unlock: |
2101 | return ret; |
2102 | } |
2103 | |