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