Commit | Line | Data |
---|---|---|
543831cf TH |
1 | /************************************************************************** |
2 | * | |
3 | * Copyright © 2009-2012 VMware, Inc., Palo Alto, CA., USA | |
4 | * All Rights Reserved. | |
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_drv.h" | |
29 | #include "vmwgfx_resource_priv.h" | |
30 | #include "ttm/ttm_placement.h" | |
31 | ||
32 | struct vmw_user_context { | |
33 | struct ttm_base_object base; | |
34 | struct vmw_resource res; | |
173fb7d4 | 35 | struct vmw_ctx_binding_state cbs; |
543831cf TH |
36 | }; |
37 | ||
b5c3b1a6 TH |
38 | |
39 | ||
40 | typedef int (*vmw_scrub_func)(struct vmw_ctx_bindinfo *); | |
41 | ||
543831cf TH |
42 | static void vmw_user_context_free(struct vmw_resource *res); |
43 | static struct vmw_resource * | |
44 | vmw_user_context_base_to_res(struct ttm_base_object *base); | |
45 | ||
58a0c5f0 TH |
46 | static int vmw_gb_context_create(struct vmw_resource *res); |
47 | static int vmw_gb_context_bind(struct vmw_resource *res, | |
48 | struct ttm_validate_buffer *val_buf); | |
49 | static int vmw_gb_context_unbind(struct vmw_resource *res, | |
50 | bool readback, | |
51 | struct ttm_validate_buffer *val_buf); | |
52 | static int vmw_gb_context_destroy(struct vmw_resource *res); | |
b5c3b1a6 TH |
53 | static int vmw_context_scrub_shader(struct vmw_ctx_bindinfo *bi); |
54 | static int vmw_context_scrub_render_target(struct vmw_ctx_bindinfo *bi); | |
55 | static int vmw_context_scrub_texture(struct vmw_ctx_bindinfo *bi); | |
173fb7d4 | 56 | static void vmw_context_binding_state_kill(struct vmw_ctx_binding_state *cbs); |
543831cf TH |
57 | static uint64_t vmw_user_context_size; |
58 | ||
59 | static const struct vmw_user_resource_conv user_context_conv = { | |
60 | .object_type = VMW_RES_CONTEXT, | |
61 | .base_obj_to_res = vmw_user_context_base_to_res, | |
62 | .res_free = vmw_user_context_free | |
63 | }; | |
64 | ||
65 | const struct vmw_user_resource_conv *user_context_converter = | |
66 | &user_context_conv; | |
67 | ||
68 | ||
69 | static const struct vmw_res_func vmw_legacy_context_func = { | |
70 | .res_type = vmw_res_context, | |
71 | .needs_backup = false, | |
72 | .may_evict = false, | |
73 | .type_name = "legacy contexts", | |
74 | .backup_placement = NULL, | |
75 | .create = NULL, | |
76 | .destroy = NULL, | |
77 | .bind = NULL, | |
78 | .unbind = NULL | |
79 | }; | |
80 | ||
58a0c5f0 TH |
81 | static const struct vmw_res_func vmw_gb_context_func = { |
82 | .res_type = vmw_res_context, | |
83 | .needs_backup = true, | |
84 | .may_evict = true, | |
85 | .type_name = "guest backed contexts", | |
86 | .backup_placement = &vmw_mob_placement, | |
87 | .create = vmw_gb_context_create, | |
88 | .destroy = vmw_gb_context_destroy, | |
89 | .bind = vmw_gb_context_bind, | |
90 | .unbind = vmw_gb_context_unbind | |
91 | }; | |
92 | ||
b5c3b1a6 TH |
93 | static const vmw_scrub_func vmw_scrub_funcs[vmw_ctx_binding_max] = { |
94 | [vmw_ctx_binding_shader] = vmw_context_scrub_shader, | |
95 | [vmw_ctx_binding_rt] = vmw_context_scrub_render_target, | |
96 | [vmw_ctx_binding_tex] = vmw_context_scrub_texture }; | |
97 | ||
543831cf TH |
98 | /** |
99 | * Context management: | |
100 | */ | |
101 | ||
102 | static void vmw_hw_context_destroy(struct vmw_resource *res) | |
103 | { | |
104 | ||
105 | struct vmw_private *dev_priv = res->dev_priv; | |
106 | struct { | |
107 | SVGA3dCmdHeader header; | |
108 | SVGA3dCmdDestroyContext body; | |
109 | } *cmd; | |
110 | ||
111 | ||
58a0c5f0 TH |
112 | if (res->func->destroy == vmw_gb_context_destroy) { |
113 | mutex_lock(&dev_priv->cmdbuf_mutex); | |
114 | (void) vmw_gb_context_destroy(res); | |
115 | if (dev_priv->pinned_bo != NULL && | |
116 | !dev_priv->query_cid_valid) | |
117 | __vmw_execbuf_release_pinned_bo(dev_priv, NULL); | |
118 | mutex_unlock(&dev_priv->cmdbuf_mutex); | |
119 | return; | |
120 | } | |
121 | ||
543831cf TH |
122 | vmw_execbuf_release_pinned_bo(dev_priv); |
123 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | |
124 | if (unlikely(cmd == NULL)) { | |
125 | DRM_ERROR("Failed reserving FIFO space for surface " | |
126 | "destruction.\n"); | |
127 | return; | |
128 | } | |
129 | ||
130 | cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DESTROY); | |
131 | cmd->header.size = cpu_to_le32(sizeof(cmd->body)); | |
132 | cmd->body.cid = cpu_to_le32(res->id); | |
133 | ||
134 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | |
135 | vmw_3d_resource_dec(dev_priv, false); | |
136 | } | |
137 | ||
58a0c5f0 TH |
138 | static int vmw_gb_context_init(struct vmw_private *dev_priv, |
139 | struct vmw_resource *res, | |
140 | void (*res_free) (struct vmw_resource *res)) | |
141 | { | |
142 | int ret; | |
173fb7d4 TH |
143 | struct vmw_user_context *uctx = |
144 | container_of(res, struct vmw_user_context, res); | |
58a0c5f0 TH |
145 | |
146 | ret = vmw_resource_init(dev_priv, res, true, | |
147 | res_free, &vmw_gb_context_func); | |
148 | res->backup_size = SVGA3D_CONTEXT_DATA_SIZE; | |
149 | ||
150 | if (unlikely(ret != 0)) { | |
151 | if (res_free) | |
152 | res_free(res); | |
153 | else | |
154 | kfree(res); | |
155 | return ret; | |
156 | } | |
157 | ||
173fb7d4 TH |
158 | memset(&uctx->cbs, 0, sizeof(uctx->cbs)); |
159 | INIT_LIST_HEAD(&uctx->cbs.list); | |
160 | ||
58a0c5f0 TH |
161 | vmw_resource_activate(res, vmw_hw_context_destroy); |
162 | return 0; | |
163 | } | |
164 | ||
543831cf TH |
165 | static int vmw_context_init(struct vmw_private *dev_priv, |
166 | struct vmw_resource *res, | |
167 | void (*res_free) (struct vmw_resource *res)) | |
168 | { | |
169 | int ret; | |
170 | ||
171 | struct { | |
172 | SVGA3dCmdHeader header; | |
173 | SVGA3dCmdDefineContext body; | |
174 | } *cmd; | |
175 | ||
58a0c5f0 TH |
176 | if (dev_priv->has_mob) |
177 | return vmw_gb_context_init(dev_priv, res, res_free); | |
178 | ||
543831cf TH |
179 | ret = vmw_resource_init(dev_priv, res, false, |
180 | res_free, &vmw_legacy_context_func); | |
181 | ||
182 | if (unlikely(ret != 0)) { | |
183 | DRM_ERROR("Failed to allocate a resource id.\n"); | |
184 | goto out_early; | |
185 | } | |
186 | ||
187 | if (unlikely(res->id >= SVGA3D_MAX_CONTEXT_IDS)) { | |
188 | DRM_ERROR("Out of hw context ids.\n"); | |
189 | vmw_resource_unreference(&res); | |
190 | return -ENOMEM; | |
191 | } | |
192 | ||
193 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | |
194 | if (unlikely(cmd == NULL)) { | |
195 | DRM_ERROR("Fifo reserve failed.\n"); | |
196 | vmw_resource_unreference(&res); | |
197 | return -ENOMEM; | |
198 | } | |
199 | ||
200 | cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DEFINE); | |
201 | cmd->header.size = cpu_to_le32(sizeof(cmd->body)); | |
202 | cmd->body.cid = cpu_to_le32(res->id); | |
203 | ||
204 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | |
205 | (void) vmw_3d_resource_inc(dev_priv, false); | |
206 | vmw_resource_activate(res, vmw_hw_context_destroy); | |
207 | return 0; | |
208 | ||
209 | out_early: | |
210 | if (res_free == NULL) | |
211 | kfree(res); | |
212 | else | |
213 | res_free(res); | |
214 | return ret; | |
215 | } | |
216 | ||
217 | struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv) | |
218 | { | |
219 | struct vmw_resource *res = kmalloc(sizeof(*res), GFP_KERNEL); | |
220 | int ret; | |
221 | ||
222 | if (unlikely(res == NULL)) | |
223 | return NULL; | |
224 | ||
225 | ret = vmw_context_init(dev_priv, res, NULL); | |
226 | ||
227 | return (ret == 0) ? res : NULL; | |
228 | } | |
229 | ||
58a0c5f0 TH |
230 | |
231 | static int vmw_gb_context_create(struct vmw_resource *res) | |
232 | { | |
233 | struct vmw_private *dev_priv = res->dev_priv; | |
234 | int ret; | |
235 | struct { | |
236 | SVGA3dCmdHeader header; | |
237 | SVGA3dCmdDefineGBContext body; | |
238 | } *cmd; | |
239 | ||
240 | if (likely(res->id != -1)) | |
241 | return 0; | |
242 | ||
243 | ret = vmw_resource_alloc_id(res); | |
244 | if (unlikely(ret != 0)) { | |
245 | DRM_ERROR("Failed to allocate a context id.\n"); | |
246 | goto out_no_id; | |
247 | } | |
248 | ||
249 | if (unlikely(res->id >= VMWGFX_NUM_GB_CONTEXT)) { | |
250 | ret = -EBUSY; | |
251 | goto out_no_fifo; | |
252 | } | |
253 | ||
254 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | |
255 | if (unlikely(cmd == NULL)) { | |
256 | DRM_ERROR("Failed reserving FIFO space for context " | |
257 | "creation.\n"); | |
258 | ret = -ENOMEM; | |
259 | goto out_no_fifo; | |
260 | } | |
261 | ||
262 | cmd->header.id = SVGA_3D_CMD_DEFINE_GB_CONTEXT; | |
263 | cmd->header.size = sizeof(cmd->body); | |
264 | cmd->body.cid = res->id; | |
265 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | |
266 | (void) vmw_3d_resource_inc(dev_priv, false); | |
267 | ||
268 | return 0; | |
269 | ||
270 | out_no_fifo: | |
271 | vmw_resource_release_id(res); | |
272 | out_no_id: | |
273 | return ret; | |
274 | } | |
275 | ||
276 | static int vmw_gb_context_bind(struct vmw_resource *res, | |
277 | struct ttm_validate_buffer *val_buf) | |
278 | { | |
279 | struct vmw_private *dev_priv = res->dev_priv; | |
280 | struct { | |
281 | SVGA3dCmdHeader header; | |
282 | SVGA3dCmdBindGBContext body; | |
283 | } *cmd; | |
284 | struct ttm_buffer_object *bo = val_buf->bo; | |
285 | ||
286 | BUG_ON(bo->mem.mem_type != VMW_PL_MOB); | |
287 | ||
288 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | |
289 | if (unlikely(cmd == NULL)) { | |
290 | DRM_ERROR("Failed reserving FIFO space for context " | |
291 | "binding.\n"); | |
292 | return -ENOMEM; | |
293 | } | |
294 | ||
295 | cmd->header.id = SVGA_3D_CMD_BIND_GB_CONTEXT; | |
296 | cmd->header.size = sizeof(cmd->body); | |
297 | cmd->body.cid = res->id; | |
298 | cmd->body.mobid = bo->mem.start; | |
299 | cmd->body.validContents = res->backup_dirty; | |
300 | res->backup_dirty = false; | |
301 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | |
302 | ||
303 | return 0; | |
304 | } | |
305 | ||
306 | static int vmw_gb_context_unbind(struct vmw_resource *res, | |
307 | bool readback, | |
308 | struct ttm_validate_buffer *val_buf) | |
309 | { | |
310 | struct vmw_private *dev_priv = res->dev_priv; | |
311 | struct ttm_buffer_object *bo = val_buf->bo; | |
312 | struct vmw_fence_obj *fence; | |
173fb7d4 TH |
313 | struct vmw_user_context *uctx = |
314 | container_of(res, struct vmw_user_context, res); | |
58a0c5f0 TH |
315 | |
316 | struct { | |
317 | SVGA3dCmdHeader header; | |
318 | SVGA3dCmdReadbackGBContext body; | |
319 | } *cmd1; | |
320 | struct { | |
321 | SVGA3dCmdHeader header; | |
322 | SVGA3dCmdBindGBContext body; | |
323 | } *cmd2; | |
324 | uint32_t submit_size; | |
325 | uint8_t *cmd; | |
326 | ||
327 | ||
328 | BUG_ON(bo->mem.mem_type != VMW_PL_MOB); | |
329 | ||
173fb7d4 TH |
330 | mutex_lock(&dev_priv->binding_mutex); |
331 | vmw_context_binding_state_kill(&uctx->cbs); | |
332 | ||
58a0c5f0 TH |
333 | submit_size = sizeof(*cmd2) + (readback ? sizeof(*cmd1) : 0); |
334 | ||
335 | cmd = vmw_fifo_reserve(dev_priv, submit_size); | |
336 | if (unlikely(cmd == NULL)) { | |
337 | DRM_ERROR("Failed reserving FIFO space for context " | |
338 | "unbinding.\n"); | |
173fb7d4 | 339 | mutex_unlock(&dev_priv->binding_mutex); |
58a0c5f0 TH |
340 | return -ENOMEM; |
341 | } | |
342 | ||
343 | cmd2 = (void *) cmd; | |
344 | if (readback) { | |
345 | cmd1 = (void *) cmd; | |
346 | cmd1->header.id = SVGA_3D_CMD_READBACK_GB_CONTEXT; | |
347 | cmd1->header.size = sizeof(cmd1->body); | |
348 | cmd1->body.cid = res->id; | |
349 | cmd2 = (void *) (&cmd1[1]); | |
350 | } | |
351 | cmd2->header.id = SVGA_3D_CMD_BIND_GB_CONTEXT; | |
352 | cmd2->header.size = sizeof(cmd2->body); | |
353 | cmd2->body.cid = res->id; | |
354 | cmd2->body.mobid = SVGA3D_INVALID_ID; | |
355 | ||
356 | vmw_fifo_commit(dev_priv, submit_size); | |
173fb7d4 | 357 | mutex_unlock(&dev_priv->binding_mutex); |
58a0c5f0 TH |
358 | |
359 | /* | |
360 | * Create a fence object and fence the backup buffer. | |
361 | */ | |
362 | ||
363 | (void) vmw_execbuf_fence_commands(NULL, dev_priv, | |
364 | &fence, NULL); | |
365 | ||
366 | vmw_fence_single_bo(bo, fence); | |
367 | ||
368 | if (likely(fence != NULL)) | |
369 | vmw_fence_obj_unreference(&fence); | |
370 | ||
371 | return 0; | |
372 | } | |
373 | ||
374 | static int vmw_gb_context_destroy(struct vmw_resource *res) | |
375 | { | |
376 | struct vmw_private *dev_priv = res->dev_priv; | |
377 | struct { | |
378 | SVGA3dCmdHeader header; | |
379 | SVGA3dCmdDestroyGBContext body; | |
380 | } *cmd; | |
173fb7d4 TH |
381 | struct vmw_user_context *uctx = |
382 | container_of(res, struct vmw_user_context, res); | |
383 | ||
384 | BUG_ON(!list_empty(&uctx->cbs.list)); | |
58a0c5f0 TH |
385 | |
386 | if (likely(res->id == -1)) | |
387 | return 0; | |
388 | ||
389 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | |
390 | if (unlikely(cmd == NULL)) { | |
391 | DRM_ERROR("Failed reserving FIFO space for context " | |
392 | "destruction.\n"); | |
393 | return -ENOMEM; | |
394 | } | |
395 | ||
396 | cmd->header.id = SVGA_3D_CMD_DESTROY_GB_CONTEXT; | |
397 | cmd->header.size = sizeof(cmd->body); | |
398 | cmd->body.cid = res->id; | |
399 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | |
400 | if (dev_priv->query_cid == res->id) | |
401 | dev_priv->query_cid_valid = false; | |
402 | vmw_resource_release_id(res); | |
403 | vmw_3d_resource_dec(dev_priv, false); | |
404 | ||
405 | return 0; | |
406 | } | |
407 | ||
543831cf TH |
408 | /** |
409 | * User-space context management: | |
410 | */ | |
411 | ||
412 | static struct vmw_resource * | |
413 | vmw_user_context_base_to_res(struct ttm_base_object *base) | |
414 | { | |
415 | return &(container_of(base, struct vmw_user_context, base)->res); | |
416 | } | |
417 | ||
418 | static void vmw_user_context_free(struct vmw_resource *res) | |
419 | { | |
420 | struct vmw_user_context *ctx = | |
421 | container_of(res, struct vmw_user_context, res); | |
422 | struct vmw_private *dev_priv = res->dev_priv; | |
423 | ||
424 | ttm_base_object_kfree(ctx, base); | |
425 | ttm_mem_global_free(vmw_mem_glob(dev_priv), | |
426 | vmw_user_context_size); | |
427 | } | |
428 | ||
429 | /** | |
430 | * This function is called when user space has no more references on the | |
431 | * base object. It releases the base-object's reference on the resource object. | |
432 | */ | |
433 | ||
434 | static void vmw_user_context_base_release(struct ttm_base_object **p_base) | |
435 | { | |
436 | struct ttm_base_object *base = *p_base; | |
437 | struct vmw_user_context *ctx = | |
438 | container_of(base, struct vmw_user_context, base); | |
439 | struct vmw_resource *res = &ctx->res; | |
440 | ||
441 | *p_base = NULL; | |
442 | vmw_resource_unreference(&res); | |
443 | } | |
444 | ||
445 | int vmw_context_destroy_ioctl(struct drm_device *dev, void *data, | |
446 | struct drm_file *file_priv) | |
447 | { | |
448 | struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; | |
449 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | |
450 | ||
451 | return ttm_ref_object_base_unref(tfile, arg->cid, TTM_REF_USAGE); | |
452 | } | |
453 | ||
454 | int vmw_context_define_ioctl(struct drm_device *dev, void *data, | |
455 | struct drm_file *file_priv) | |
456 | { | |
457 | struct vmw_private *dev_priv = vmw_priv(dev); | |
458 | struct vmw_user_context *ctx; | |
459 | struct vmw_resource *res; | |
460 | struct vmw_resource *tmp; | |
461 | struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; | |
462 | struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; | |
463 | struct vmw_master *vmaster = vmw_master(file_priv->master); | |
464 | int ret; | |
465 | ||
466 | ||
467 | /* | |
468 | * Approximate idr memory usage with 128 bytes. It will be limited | |
469 | * by maximum number_of contexts anyway. | |
470 | */ | |
471 | ||
472 | if (unlikely(vmw_user_context_size == 0)) | |
473 | vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128; | |
474 | ||
475 | ret = ttm_read_lock(&vmaster->lock, true); | |
476 | if (unlikely(ret != 0)) | |
477 | return ret; | |
478 | ||
479 | ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), | |
480 | vmw_user_context_size, | |
481 | false, true); | |
482 | if (unlikely(ret != 0)) { | |
483 | if (ret != -ERESTARTSYS) | |
484 | DRM_ERROR("Out of graphics memory for context" | |
485 | " creation.\n"); | |
486 | goto out_unlock; | |
487 | } | |
488 | ||
489 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | |
490 | if (unlikely(ctx == NULL)) { | |
491 | ttm_mem_global_free(vmw_mem_glob(dev_priv), | |
492 | vmw_user_context_size); | |
493 | ret = -ENOMEM; | |
494 | goto out_unlock; | |
495 | } | |
496 | ||
497 | res = &ctx->res; | |
498 | ctx->base.shareable = false; | |
499 | ctx->base.tfile = NULL; | |
500 | ||
501 | /* | |
502 | * From here on, the destructor takes over resource freeing. | |
503 | */ | |
504 | ||
505 | ret = vmw_context_init(dev_priv, res, vmw_user_context_free); | |
506 | if (unlikely(ret != 0)) | |
507 | goto out_unlock; | |
508 | ||
509 | tmp = vmw_resource_reference(&ctx->res); | |
510 | ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT, | |
511 | &vmw_user_context_base_release, NULL); | |
512 | ||
513 | if (unlikely(ret != 0)) { | |
514 | vmw_resource_unreference(&tmp); | |
515 | goto out_err; | |
516 | } | |
517 | ||
518 | arg->cid = ctx->base.hash.key; | |
519 | out_err: | |
520 | vmw_resource_unreference(&res); | |
521 | out_unlock: | |
522 | ttm_read_unlock(&vmaster->lock); | |
523 | return ret; | |
524 | ||
525 | } | |
b5c3b1a6 TH |
526 | |
527 | /** | |
528 | * vmw_context_scrub_shader - scrub a shader binding from a context. | |
529 | * | |
530 | * @bi: single binding information. | |
531 | */ | |
532 | static int vmw_context_scrub_shader(struct vmw_ctx_bindinfo *bi) | |
533 | { | |
534 | struct vmw_private *dev_priv = bi->ctx->dev_priv; | |
535 | struct { | |
536 | SVGA3dCmdHeader header; | |
537 | SVGA3dCmdSetShader body; | |
538 | } *cmd; | |
539 | ||
540 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | |
541 | if (unlikely(cmd == NULL)) { | |
542 | DRM_ERROR("Failed reserving FIFO space for shader " | |
543 | "unbinding.\n"); | |
544 | return -ENOMEM; | |
545 | } | |
546 | ||
547 | cmd->header.id = SVGA_3D_CMD_SET_SHADER; | |
548 | cmd->header.size = sizeof(cmd->body); | |
549 | cmd->body.cid = bi->ctx->id; | |
550 | cmd->body.type = bi->i1.shader_type; | |
551 | cmd->body.shid = SVGA3D_INVALID_ID; | |
552 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | |
553 | ||
554 | return 0; | |
555 | } | |
556 | ||
557 | /** | |
558 | * vmw_context_scrub_render_target - scrub a render target binding | |
559 | * from a context. | |
560 | * | |
561 | * @bi: single binding information. | |
562 | */ | |
563 | static int vmw_context_scrub_render_target(struct vmw_ctx_bindinfo *bi) | |
564 | { | |
565 | struct vmw_private *dev_priv = bi->ctx->dev_priv; | |
566 | struct { | |
567 | SVGA3dCmdHeader header; | |
568 | SVGA3dCmdSetRenderTarget body; | |
569 | } *cmd; | |
570 | ||
571 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | |
572 | if (unlikely(cmd == NULL)) { | |
573 | DRM_ERROR("Failed reserving FIFO space for render target " | |
574 | "unbinding.\n"); | |
575 | return -ENOMEM; | |
576 | } | |
577 | ||
578 | cmd->header.id = SVGA_3D_CMD_SETRENDERTARGET; | |
579 | cmd->header.size = sizeof(cmd->body); | |
580 | cmd->body.cid = bi->ctx->id; | |
581 | cmd->body.type = bi->i1.rt_type; | |
582 | cmd->body.target.sid = SVGA3D_INVALID_ID; | |
583 | cmd->body.target.face = 0; | |
584 | cmd->body.target.mipmap = 0; | |
585 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | |
586 | ||
587 | return 0; | |
588 | } | |
589 | ||
590 | /** | |
591 | * vmw_context_scrub_texture - scrub a texture binding from a context. | |
592 | * | |
593 | * @bi: single binding information. | |
594 | * | |
595 | * TODO: Possibly complement this function with a function that takes | |
596 | * a list of texture bindings and combines them to a single command. | |
597 | */ | |
598 | static int vmw_context_scrub_texture(struct vmw_ctx_bindinfo *bi) | |
599 | { | |
600 | struct vmw_private *dev_priv = bi->ctx->dev_priv; | |
601 | struct { | |
602 | SVGA3dCmdHeader header; | |
603 | struct { | |
604 | SVGA3dCmdSetTextureState c; | |
605 | SVGA3dTextureState s1; | |
606 | } body; | |
607 | } *cmd; | |
608 | ||
609 | cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); | |
610 | if (unlikely(cmd == NULL)) { | |
611 | DRM_ERROR("Failed reserving FIFO space for texture " | |
612 | "unbinding.\n"); | |
613 | return -ENOMEM; | |
614 | } | |
615 | ||
616 | ||
617 | cmd->header.id = SVGA_3D_CMD_SETTEXTURESTATE; | |
618 | cmd->header.size = sizeof(cmd->body); | |
619 | cmd->body.c.cid = bi->ctx->id; | |
620 | cmd->body.s1.stage = bi->i1.texture_stage; | |
621 | cmd->body.s1.name = SVGA3D_TS_BIND_TEXTURE; | |
622 | cmd->body.s1.value = (uint32) SVGA3D_INVALID_ID; | |
623 | vmw_fifo_commit(dev_priv, sizeof(*cmd)); | |
624 | ||
625 | return 0; | |
626 | } | |
627 | ||
628 | /** | |
629 | * vmw_context_binding_drop: Stop tracking a context binding | |
630 | * | |
631 | * @cb: Pointer to binding tracker storage. | |
632 | * | |
633 | * Stops tracking a context binding, and re-initializes its storage. | |
634 | * Typically used when the context binding is replaced with a binding to | |
635 | * another (or the same, for that matter) resource. | |
636 | */ | |
637 | static void vmw_context_binding_drop(struct vmw_ctx_binding *cb) | |
638 | { | |
639 | list_del(&cb->ctx_list); | |
173fb7d4 TH |
640 | if (!list_empty(&cb->res_list)) |
641 | list_del(&cb->res_list); | |
b5c3b1a6 TH |
642 | cb->bi.ctx = NULL; |
643 | } | |
644 | ||
645 | /** | |
646 | * vmw_context_binding_add: Start tracking a context binding | |
647 | * | |
648 | * @cbs: Pointer to the context binding state tracker. | |
649 | * @bi: Information about the binding to track. | |
650 | * | |
651 | * Performs basic checks on the binding to make sure arguments are within | |
652 | * bounds and then starts tracking the binding in the context binding | |
653 | * state structure @cbs. | |
654 | */ | |
655 | int vmw_context_binding_add(struct vmw_ctx_binding_state *cbs, | |
656 | const struct vmw_ctx_bindinfo *bi) | |
657 | { | |
658 | struct vmw_ctx_binding *loc; | |
659 | ||
660 | switch (bi->bt) { | |
661 | case vmw_ctx_binding_rt: | |
662 | if (unlikely((unsigned)bi->i1.rt_type >= SVGA3D_RT_MAX)) { | |
663 | DRM_ERROR("Illegal render target type %u.\n", | |
664 | (unsigned) bi->i1.rt_type); | |
665 | return -EINVAL; | |
666 | } | |
667 | loc = &cbs->render_targets[bi->i1.rt_type]; | |
668 | break; | |
669 | case vmw_ctx_binding_tex: | |
670 | if (unlikely((unsigned)bi->i1.texture_stage >= | |
671 | SVGA3D_NUM_TEXTURE_UNITS)) { | |
672 | DRM_ERROR("Illegal texture/sampler unit %u.\n", | |
673 | (unsigned) bi->i1.texture_stage); | |
674 | return -EINVAL; | |
675 | } | |
676 | loc = &cbs->texture_units[bi->i1.texture_stage]; | |
677 | break; | |
678 | case vmw_ctx_binding_shader: | |
679 | if (unlikely((unsigned)bi->i1.shader_type >= | |
680 | SVGA3D_SHADERTYPE_MAX)) { | |
681 | DRM_ERROR("Illegal shader type %u.\n", | |
682 | (unsigned) bi->i1.shader_type); | |
683 | return -EINVAL; | |
684 | } | |
685 | loc = &cbs->shaders[bi->i1.shader_type]; | |
686 | break; | |
687 | default: | |
688 | BUG(); | |
689 | } | |
690 | ||
691 | if (loc->bi.ctx != NULL) | |
692 | vmw_context_binding_drop(loc); | |
693 | ||
694 | loc->bi = *bi; | |
695 | list_add_tail(&loc->ctx_list, &cbs->list); | |
173fb7d4 | 696 | INIT_LIST_HEAD(&loc->res_list); |
b5c3b1a6 TH |
697 | |
698 | return 0; | |
699 | } | |
700 | ||
173fb7d4 TH |
701 | /** |
702 | * vmw_context_binding_transfer: Transfer a context binding tracking entry. | |
703 | * | |
704 | * @cbs: Pointer to the persistent context binding state tracker. | |
705 | * @bi: Information about the binding to track. | |
706 | * | |
707 | */ | |
708 | static void vmw_context_binding_transfer(struct vmw_ctx_binding_state *cbs, | |
709 | const struct vmw_ctx_bindinfo *bi) | |
710 | { | |
711 | struct vmw_ctx_binding *loc; | |
712 | ||
713 | switch (bi->bt) { | |
714 | case vmw_ctx_binding_rt: | |
715 | loc = &cbs->render_targets[bi->i1.rt_type]; | |
716 | break; | |
717 | case vmw_ctx_binding_tex: | |
718 | loc = &cbs->texture_units[bi->i1.texture_stage]; | |
719 | break; | |
720 | case vmw_ctx_binding_shader: | |
721 | loc = &cbs->shaders[bi->i1.shader_type]; | |
722 | break; | |
723 | default: | |
724 | BUG(); | |
725 | } | |
726 | ||
727 | if (loc->bi.ctx != NULL) | |
728 | vmw_context_binding_drop(loc); | |
729 | ||
730 | loc->bi = *bi; | |
731 | list_add_tail(&loc->ctx_list, &cbs->list); | |
732 | if (bi->res != NULL) | |
733 | list_add_tail(&loc->res_list, &bi->res->binding_head); | |
734 | else | |
735 | INIT_LIST_HEAD(&loc->res_list); | |
736 | } | |
737 | ||
b5c3b1a6 TH |
738 | /** |
739 | * vmw_context_binding_kill - Kill a binding on the device | |
740 | * and stop tracking it. | |
741 | * | |
742 | * @cb: Pointer to binding tracker storage. | |
743 | * | |
744 | * Emits FIFO commands to scrub a binding represented by @cb. | |
745 | * Then stops tracking the binding and re-initializes its storage. | |
746 | */ | |
3e894a62 | 747 | static void vmw_context_binding_kill(struct vmw_ctx_binding *cb) |
b5c3b1a6 TH |
748 | { |
749 | (void) vmw_scrub_funcs[cb->bi.bt](&cb->bi); | |
750 | vmw_context_binding_drop(cb); | |
751 | } | |
752 | ||
753 | /** | |
754 | * vmw_context_binding_state_kill - Kill all bindings associated with a | |
755 | * struct vmw_ctx_binding state structure, and re-initialize the structure. | |
756 | * | |
757 | * @cbs: Pointer to the context binding state tracker. | |
758 | * | |
759 | * Emits commands to scrub all bindings associated with the | |
760 | * context binding state tracker. Then re-initializes the whole structure. | |
761 | */ | |
173fb7d4 | 762 | static void vmw_context_binding_state_kill(struct vmw_ctx_binding_state *cbs) |
b5c3b1a6 TH |
763 | { |
764 | struct vmw_ctx_binding *entry, *next; | |
765 | ||
173fb7d4 | 766 | list_for_each_entry_safe(entry, next, &cbs->list, ctx_list) |
b5c3b1a6 | 767 | vmw_context_binding_kill(entry); |
173fb7d4 TH |
768 | } |
769 | ||
770 | /** | |
771 | * vmw_context_binding_res_list_kill - Kill all bindings on a | |
772 | * resource binding list | |
773 | * | |
774 | * @head: list head of resource binding list | |
775 | * | |
776 | * Kills all bindings associated with a specific resource. Typically | |
777 | * called before the resource is destroyed. | |
778 | */ | |
779 | void vmw_context_binding_res_list_kill(struct list_head *head) | |
780 | { | |
781 | struct vmw_ctx_binding *entry, *next; | |
782 | ||
783 | list_for_each_entry_safe(entry, next, head, res_list) | |
784 | vmw_context_binding_kill(entry); | |
785 | } | |
786 | ||
787 | /** | |
788 | * vmw_context_binding_state_transfer - Commit staged binding info | |
789 | * | |
790 | * @ctx: Pointer to context to commit the staged binding info to. | |
791 | * @from: Staged binding info built during execbuf. | |
792 | * | |
793 | * Transfers binding info from a temporary structure to the persistent | |
794 | * structure in the context. This can be done once commands | |
795 | */ | |
796 | void vmw_context_binding_state_transfer(struct vmw_resource *ctx, | |
797 | struct vmw_ctx_binding_state *from) | |
798 | { | |
799 | struct vmw_user_context *uctx = | |
800 | container_of(ctx, struct vmw_user_context, res); | |
801 | struct vmw_ctx_binding *entry, *next; | |
802 | ||
803 | list_for_each_entry_safe(entry, next, &from->list, ctx_list) | |
804 | vmw_context_binding_transfer(&uctx->cbs, &entry->bi); | |
b5c3b1a6 | 805 | } |