]> Pileus Git - ~andy/linux/blob - drivers/gpu/drm/vmwgfx/vmwgfx_context.c
Merge branch 'for-linus' of git://git.infradead.org/users/vkoul/slave-dma
[~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 };
36
37 static void vmw_user_context_free(struct vmw_resource *res);
38 static struct vmw_resource *
39 vmw_user_context_base_to_res(struct ttm_base_object *base);
40
41 static uint64_t vmw_user_context_size;
42
43 static const struct vmw_user_resource_conv user_context_conv = {
44         .object_type = VMW_RES_CONTEXT,
45         .base_obj_to_res = vmw_user_context_base_to_res,
46         .res_free = vmw_user_context_free
47 };
48
49 const struct vmw_user_resource_conv *user_context_converter =
50         &user_context_conv;
51
52
53 static const struct vmw_res_func vmw_legacy_context_func = {
54         .res_type = vmw_res_context,
55         .needs_backup = false,
56         .may_evict = false,
57         .type_name = "legacy contexts",
58         .backup_placement = NULL,
59         .create = NULL,
60         .destroy = NULL,
61         .bind = NULL,
62         .unbind = NULL
63 };
64
65 /**
66  * Context management:
67  */
68
69 static void vmw_hw_context_destroy(struct vmw_resource *res)
70 {
71
72         struct vmw_private *dev_priv = res->dev_priv;
73         struct {
74                 SVGA3dCmdHeader header;
75                 SVGA3dCmdDestroyContext body;
76         } *cmd;
77
78
79         vmw_execbuf_release_pinned_bo(dev_priv);
80         cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
81         if (unlikely(cmd == NULL)) {
82                 DRM_ERROR("Failed reserving FIFO space for surface "
83                           "destruction.\n");
84                 return;
85         }
86
87         cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DESTROY);
88         cmd->header.size = cpu_to_le32(sizeof(cmd->body));
89         cmd->body.cid = cpu_to_le32(res->id);
90
91         vmw_fifo_commit(dev_priv, sizeof(*cmd));
92         vmw_3d_resource_dec(dev_priv, false);
93 }
94
95 static int vmw_context_init(struct vmw_private *dev_priv,
96                             struct vmw_resource *res,
97                             void (*res_free) (struct vmw_resource *res))
98 {
99         int ret;
100
101         struct {
102                 SVGA3dCmdHeader header;
103                 SVGA3dCmdDefineContext body;
104         } *cmd;
105
106         ret = vmw_resource_init(dev_priv, res, false,
107                                 res_free, &vmw_legacy_context_func);
108
109         if (unlikely(ret != 0)) {
110                 DRM_ERROR("Failed to allocate a resource id.\n");
111                 goto out_early;
112         }
113
114         if (unlikely(res->id >= SVGA3D_MAX_CONTEXT_IDS)) {
115                 DRM_ERROR("Out of hw context ids.\n");
116                 vmw_resource_unreference(&res);
117                 return -ENOMEM;
118         }
119
120         cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
121         if (unlikely(cmd == NULL)) {
122                 DRM_ERROR("Fifo reserve failed.\n");
123                 vmw_resource_unreference(&res);
124                 return -ENOMEM;
125         }
126
127         cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DEFINE);
128         cmd->header.size = cpu_to_le32(sizeof(cmd->body));
129         cmd->body.cid = cpu_to_le32(res->id);
130
131         vmw_fifo_commit(dev_priv, sizeof(*cmd));
132         (void) vmw_3d_resource_inc(dev_priv, false);
133         vmw_resource_activate(res, vmw_hw_context_destroy);
134         return 0;
135
136 out_early:
137         if (res_free == NULL)
138                 kfree(res);
139         else
140                 res_free(res);
141         return ret;
142 }
143
144 struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv)
145 {
146         struct vmw_resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
147         int ret;
148
149         if (unlikely(res == NULL))
150                 return NULL;
151
152         ret = vmw_context_init(dev_priv, res, NULL);
153
154         return (ret == 0) ? res : NULL;
155 }
156
157 /**
158  * User-space context management:
159  */
160
161 static struct vmw_resource *
162 vmw_user_context_base_to_res(struct ttm_base_object *base)
163 {
164         return &(container_of(base, struct vmw_user_context, base)->res);
165 }
166
167 static void vmw_user_context_free(struct vmw_resource *res)
168 {
169         struct vmw_user_context *ctx =
170             container_of(res, struct vmw_user_context, res);
171         struct vmw_private *dev_priv = res->dev_priv;
172
173         ttm_base_object_kfree(ctx, base);
174         ttm_mem_global_free(vmw_mem_glob(dev_priv),
175                             vmw_user_context_size);
176 }
177
178 /**
179  * This function is called when user space has no more references on the
180  * base object. It releases the base-object's reference on the resource object.
181  */
182
183 static void vmw_user_context_base_release(struct ttm_base_object **p_base)
184 {
185         struct ttm_base_object *base = *p_base;
186         struct vmw_user_context *ctx =
187             container_of(base, struct vmw_user_context, base);
188         struct vmw_resource *res = &ctx->res;
189
190         *p_base = NULL;
191         vmw_resource_unreference(&res);
192 }
193
194 int vmw_context_destroy_ioctl(struct drm_device *dev, void *data,
195                               struct drm_file *file_priv)
196 {
197         struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
198         struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
199
200         return ttm_ref_object_base_unref(tfile, arg->cid, TTM_REF_USAGE);
201 }
202
203 int vmw_context_define_ioctl(struct drm_device *dev, void *data,
204                              struct drm_file *file_priv)
205 {
206         struct vmw_private *dev_priv = vmw_priv(dev);
207         struct vmw_user_context *ctx;
208         struct vmw_resource *res;
209         struct vmw_resource *tmp;
210         struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
211         struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
212         struct vmw_master *vmaster = vmw_master(file_priv->master);
213         int ret;
214
215
216         /*
217          * Approximate idr memory usage with 128 bytes. It will be limited
218          * by maximum number_of contexts anyway.
219          */
220
221         if (unlikely(vmw_user_context_size == 0))
222                 vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128;
223
224         ret = ttm_read_lock(&vmaster->lock, true);
225         if (unlikely(ret != 0))
226                 return ret;
227
228         ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv),
229                                    vmw_user_context_size,
230                                    false, true);
231         if (unlikely(ret != 0)) {
232                 if (ret != -ERESTARTSYS)
233                         DRM_ERROR("Out of graphics memory for context"
234                                   " creation.\n");
235                 goto out_unlock;
236         }
237
238         ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
239         if (unlikely(ctx == NULL)) {
240                 ttm_mem_global_free(vmw_mem_glob(dev_priv),
241                                     vmw_user_context_size);
242                 ret = -ENOMEM;
243                 goto out_unlock;
244         }
245
246         res = &ctx->res;
247         ctx->base.shareable = false;
248         ctx->base.tfile = NULL;
249
250         /*
251          * From here on, the destructor takes over resource freeing.
252          */
253
254         ret = vmw_context_init(dev_priv, res, vmw_user_context_free);
255         if (unlikely(ret != 0))
256                 goto out_unlock;
257
258         tmp = vmw_resource_reference(&ctx->res);
259         ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT,
260                                    &vmw_user_context_base_release, NULL);
261
262         if (unlikely(ret != 0)) {
263                 vmw_resource_unreference(&tmp);
264                 goto out_err;
265         }
266
267         arg->cid = ctx->base.hash.key;
268 out_err:
269         vmw_resource_unreference(&res);
270 out_unlock:
271         ttm_read_unlock(&vmaster->lock);
272         return ret;
273
274 }