1/* $NetBSD: nouveau_core_engctx.c,v 1.1.1.1 2014/08/06 12:36:23 riastradh Exp $ */
2
3/*
4 * Copyright 2012 Red Hat Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 * Authors: Ben Skeggs
25 */
26
27#include <sys/cdefs.h>
28__KERNEL_RCSID(0, "$NetBSD: nouveau_core_engctx.c,v 1.1.1.1 2014/08/06 12:36:23 riastradh Exp $");
29
30#include <core/object.h>
31#include <core/namedb.h>
32#include <core/handle.h>
33#include <core/client.h>
34#include <core/engctx.h>
35
36#include <subdev/vm.h>
37
38static inline int
39nouveau_engctx_exists(struct nouveau_object *parent,
40 struct nouveau_engine *engine, void **pobject)
41{
42 struct nouveau_engctx *engctx;
43 struct nouveau_object *parctx;
44
45 list_for_each_entry(engctx, &engine->contexts, head) {
46 parctx = nv_pclass(nv_object(engctx), NV_PARENT_CLASS);
47 if (parctx == parent) {
48 atomic_inc(&nv_object(engctx)->refcount);
49 *pobject = engctx;
50 return 1;
51 }
52 }
53
54 return 0;
55}
56
57int
58nouveau_engctx_create_(struct nouveau_object *parent,
59 struct nouveau_object *engobj,
60 struct nouveau_oclass *oclass,
61 struct nouveau_object *pargpu,
62 u32 size, u32 align, u32 flags,
63 int length, void **pobject)
64{
65 struct nouveau_client *client = nouveau_client(parent);
66 struct nouveau_engine *engine = nv_engine(engobj);
67 struct nouveau_object *engctx;
68 unsigned long save;
69 int ret;
70
71 /* check if this engine already has a context for the parent object,
72 * and reference it instead of creating a new one
73 */
74 spin_lock_irqsave(&engine->lock, save);
75 ret = nouveau_engctx_exists(parent, engine, pobject);
76 spin_unlock_irqrestore(&engine->lock, save);
77 if (ret)
78 return ret;
79
80 /* create the new context, supports creating both raw objects and
81 * objects backed by instance memory
82 */
83 if (size) {
84 ret = nouveau_gpuobj_create_(parent, engobj, oclass,
85 NV_ENGCTX_CLASS,
86 pargpu, size, align, flags,
87 length, pobject);
88 } else {
89 ret = nouveau_object_create_(parent, engobj, oclass,
90 NV_ENGCTX_CLASS, length, pobject);
91 }
92
93 engctx = *pobject;
94 if (ret)
95 return ret;
96
97 /* must take the lock again and re-check a context doesn't already
98 * exist (in case of a race) - the lock had to be dropped before as
99 * it's not possible to allocate the object with it held.
100 */
101 spin_lock_irqsave(&engine->lock, save);
102 ret = nouveau_engctx_exists(parent, engine, pobject);
103 if (ret) {
104 spin_unlock_irqrestore(&engine->lock, save);
105 nouveau_object_ref(NULL, &engctx);
106 return ret;
107 }
108
109 if (client->vm)
110 atomic_inc(&client->vm->engref[nv_engidx(engobj)]);
111 list_add(&nv_engctx(engctx)->head, &engine->contexts);
112 nv_engctx(engctx)->addr = ~0ULL;
113 spin_unlock_irqrestore(&engine->lock, save);
114 return 0;
115}
116
117void
118nouveau_engctx_destroy(struct nouveau_engctx *engctx)
119{
120 struct nouveau_object *engobj = nv_object(engctx)->engine;
121 struct nouveau_engine *engine = nv_engine(engobj);
122 struct nouveau_client *client = nouveau_client(engctx);
123 unsigned long save;
124
125 nouveau_gpuobj_unmap(&engctx->vma);
126 spin_lock_irqsave(&engine->lock, save);
127 list_del(&engctx->head);
128 spin_unlock_irqrestore(&engine->lock, save);
129
130 if (client->vm)
131 atomic_dec(&client->vm->engref[nv_engidx(engobj)]);
132
133 if (engctx->base.size)
134 nouveau_gpuobj_destroy(&engctx->base);
135 else
136 nouveau_object_destroy(&engctx->base.base);
137}
138
139int
140nouveau_engctx_init(struct nouveau_engctx *engctx)
141{
142 struct nouveau_object *object = nv_object(engctx);
143 struct nouveau_subdev *subdev = nv_subdev(object->engine);
144 struct nouveau_object *parent;
145 struct nouveau_subdev *pardev;
146 int ret;
147
148 ret = nouveau_gpuobj_init(&engctx->base);
149 if (ret)
150 return ret;
151
152 parent = nv_pclass(object->parent, NV_PARENT_CLASS);
153 pardev = nv_subdev(parent->engine);
154 if (nv_parent(parent)->context_attach) {
155 mutex_lock(&pardev->mutex);
156 ret = nv_parent(parent)->context_attach(parent, object);
157 mutex_unlock(&pardev->mutex);
158 }
159
160 if (ret) {
161 nv_error(parent, "failed to attach %s context, %d\n",
162 subdev->name, ret);
163 return ret;
164 }
165
166 nv_debug(parent, "attached %s context\n", subdev->name);
167 return 0;
168}
169
170int
171nouveau_engctx_fini(struct nouveau_engctx *engctx, bool suspend)
172{
173 struct nouveau_object *object = nv_object(engctx);
174 struct nouveau_subdev *subdev = nv_subdev(object->engine);
175 struct nouveau_object *parent;
176 struct nouveau_subdev *pardev;
177 int ret = 0;
178
179 parent = nv_pclass(object->parent, NV_PARENT_CLASS);
180 pardev = nv_subdev(parent->engine);
181 if (nv_parent(parent)->context_detach) {
182 mutex_lock(&pardev->mutex);
183 ret = nv_parent(parent)->context_detach(parent, suspend, object);
184 mutex_unlock(&pardev->mutex);
185 }
186
187 if (ret) {
188 nv_error(parent, "failed to detach %s context, %d\n",
189 subdev->name, ret);
190 return ret;
191 }
192
193 nv_debug(parent, "detached %s context\n", subdev->name);
194 return nouveau_gpuobj_fini(&engctx->base, suspend);
195}
196
197int
198_nouveau_engctx_ctor(struct nouveau_object *parent,
199 struct nouveau_object *engine,
200 struct nouveau_oclass *oclass, void *data, u32 size,
201 struct nouveau_object **pobject)
202{
203 struct nouveau_engctx *engctx;
204 int ret;
205
206 ret = nouveau_engctx_create(parent, engine, oclass, NULL, 256, 256,
207 NVOBJ_FLAG_ZERO_ALLOC, &engctx);
208 *pobject = nv_object(engctx);
209 return ret;
210}
211
212void
213_nouveau_engctx_dtor(struct nouveau_object *object)
214{
215 nouveau_engctx_destroy(nv_engctx(object));
216}
217
218int
219_nouveau_engctx_init(struct nouveau_object *object)
220{
221 return nouveau_engctx_init(nv_engctx(object));
222}
223
224
225int
226_nouveau_engctx_fini(struct nouveau_object *object, bool suspend)
227{
228 return nouveau_engctx_fini(nv_engctx(object), suspend);
229}
230
231struct nouveau_object *
232nouveau_engctx_get(struct nouveau_engine *engine, u64 addr)
233{
234 struct nouveau_engctx *engctx;
235 unsigned long flags;
236
237 spin_lock_irqsave(&engine->lock, flags);
238 list_for_each_entry(engctx, &engine->contexts, head) {
239 if (engctx->addr == addr) {
240 engctx->save = flags;
241 return nv_object(engctx);
242 }
243 }
244 spin_unlock_irqrestore(&engine->lock, flags);
245 return NULL;
246}
247
248void
249nouveau_engctx_put(struct nouveau_object *object)
250{
251 if (object) {
252 struct nouveau_engine *engine = nv_engine(object->engine);
253 struct nouveau_engctx *engctx = nv_engctx(object);
254 spin_unlock_irqrestore(&engine->lock, engctx->save);
255 }
256}
257