1 /*
2 * Copyright 2012 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
24
25 #include <core/object.h>
26 #include <core/handle.h>
27 #include <core/client.h>
28
29 #define hprintk(h,l,f,a...) do { \
30 struct nouveau_client *c = nouveau_client((h)->object); \
31 struct nouveau_handle *p = (h)->parent; u32 n = p ? p->name : ~0; \
32 nv_printk((c), l, "0x%08x:0x%08x "f, n, (h)->name, ##a); \
33 } while(0)
34
35 int
nouveau_handle_init(struct nouveau_handle * handle)36 nouveau_handle_init(struct nouveau_handle *handle)
37 {
38 struct nouveau_handle *item;
39 int ret;
40
41 hprintk(handle, TRACE, "init running\n");
42 ret = nouveau_object_inc(handle->object);
43 if (ret)
44 return ret;
45
46 hprintk(handle, TRACE, "init children\n");
47 list_for_each_entry(item, &handle->tree, head) {
48 ret = nouveau_handle_init(item);
49 if (ret)
50 goto fail;
51 }
52
53 hprintk(handle, TRACE, "init completed\n");
54 return 0;
55 fail:
56 hprintk(handle, ERROR, "init failed with %d\n", ret);
57 list_for_each_entry_continue_reverse(item, &handle->tree, head) {
58 nouveau_handle_fini(item, false);
59 }
60
61 nouveau_object_dec(handle->object, false);
62 return ret;
63 }
64
65 int
nouveau_handle_fini(struct nouveau_handle * handle,bool suspend)66 nouveau_handle_fini(struct nouveau_handle *handle, bool suspend)
67 {
68 static char *name[2] = { "fini", "suspend" };
69 struct nouveau_handle *item;
70 int ret;
71
72 hprintk(handle, TRACE, "%s children\n", name[suspend]);
73 list_for_each_entry(item, &handle->tree, head) {
74 ret = nouveau_handle_fini(item, suspend);
75 if (ret && suspend)
76 goto fail;
77 }
78
79 hprintk(handle, TRACE, "%s running\n", name[suspend]);
80 if (handle->object) {
81 ret = nouveau_object_dec(handle->object, suspend);
82 if (ret && suspend)
83 goto fail;
84 }
85
86 hprintk(handle, TRACE, "%s completed\n", name[suspend]);
87 return 0;
88 fail:
89 hprintk(handle, ERROR, "%s failed with %d\n", name[suspend], ret);
90 list_for_each_entry_continue_reverse(item, &handle->tree, head) {
91 int rret = nouveau_handle_init(item);
92 if (rret)
93 hprintk(handle, FATAL, "failed to restart, %d\n", rret);
94 }
95
96 return ret;
97 }
98
99 int
nouveau_handle_create(struct nouveau_object * parent,u32 _parent,u32 _handle,struct nouveau_object * object,struct nouveau_handle ** phandle)100 nouveau_handle_create(struct nouveau_object *parent, u32 _parent, u32 _handle,
101 struct nouveau_object *object,
102 struct nouveau_handle **phandle)
103 {
104 struct nouveau_object *namedb;
105 struct nouveau_handle *handle;
106 int ret;
107
108 namedb = parent;
109 while (!nv_iclass(namedb, NV_NAMEDB_CLASS))
110 namedb = namedb->parent;
111
112 handle = kzalloc(sizeof(*handle), GFP_KERNEL);
113 if (!handle)
114 return -ENOMEM;
115
116 INIT_LIST_HEAD(&handle->head);
117 INIT_LIST_HEAD(&handle->tree);
118 handle->name = _handle;
119 handle->priv = ~0;
120
121 ret = nouveau_namedb_insert(nv_namedb(namedb), _handle, object, handle);
122 if (ret) {
123 kfree(handle);
124 return ret;
125 }
126
127 if (nv_parent(parent)->object_attach) {
128 ret = nv_parent(parent)->object_attach(parent, object, _handle);
129 if (ret < 0) {
130 nouveau_handle_destroy(handle);
131 return ret;
132 }
133
134 handle->priv = ret;
135 }
136
137 if (object != namedb) {
138 while (!nv_iclass(namedb, NV_CLIENT_CLASS))
139 namedb = namedb->parent;
140
141 handle->parent = nouveau_namedb_get(nv_namedb(namedb), _parent);
142 if (handle->parent) {
143 list_add(&handle->head, &handle->parent->tree);
144 nouveau_namedb_put(handle->parent);
145 }
146 }
147
148 hprintk(handle, TRACE, "created\n");
149 *phandle = handle;
150 return 0;
151 }
152
153 void
nouveau_handle_destroy(struct nouveau_handle * handle)154 nouveau_handle_destroy(struct nouveau_handle *handle)
155 {
156 struct nouveau_handle *item, *temp;
157
158 hprintk(handle, TRACE, "destroy running\n");
159 list_for_each_entry_safe(item, temp, &handle->tree, head) {
160 nouveau_handle_destroy(item);
161 }
162 list_del(&handle->head);
163
164 if (handle->priv != ~0) {
165 struct nouveau_object *parent = handle->parent->object;
166 nv_parent(parent)->object_detach(parent, handle->priv);
167 }
168
169 hprintk(handle, TRACE, "destroy completed\n");
170 nouveau_namedb_remove(handle);
171 kfree(handle);
172 }
173
174 struct nouveau_object *
nouveau_handle_ref(struct nouveau_object * parent,u32 name)175 nouveau_handle_ref(struct nouveau_object *parent, u32 name)
176 {
177 struct nouveau_object *object = NULL;
178 struct nouveau_handle *handle;
179
180 while (!nv_iclass(parent, NV_NAMEDB_CLASS))
181 parent = parent->parent;
182
183 handle = nouveau_namedb_get(nv_namedb(parent), name);
184 if (handle) {
185 nouveau_object_ref(handle->object, &object);
186 nouveau_namedb_put(handle);
187 }
188
189 return object;
190 }
191
192 struct nouveau_handle *
nouveau_handle_get_class(struct nouveau_object * engctx,u16 oclass)193 nouveau_handle_get_class(struct nouveau_object *engctx, u16 oclass)
194 {
195 struct nouveau_namedb *namedb;
196 if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS)))
197 return nouveau_namedb_get_class(namedb, oclass);
198 return NULL;
199 }
200
201 struct nouveau_handle *
nouveau_handle_get_vinst(struct nouveau_object * engctx,u64 vinst)202 nouveau_handle_get_vinst(struct nouveau_object *engctx, u64 vinst)
203 {
204 struct nouveau_namedb *namedb;
205 if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS)))
206 return nouveau_namedb_get_vinst(namedb, vinst);
207 return NULL;
208 }
209
210 struct nouveau_handle *
nouveau_handle_get_cinst(struct nouveau_object * engctx,u32 cinst)211 nouveau_handle_get_cinst(struct nouveau_object *engctx, u32 cinst)
212 {
213 struct nouveau_namedb *namedb;
214 if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS)))
215 return nouveau_namedb_get_cinst(namedb, cinst);
216 return NULL;
217 }
218
219 void
nouveau_handle_put(struct nouveau_handle * handle)220 nouveau_handle_put(struct nouveau_handle *handle)
221 {
222 if (handle)
223 nouveau_namedb_put(handle);
224 }
225
226 int
nouveau_handle_new(struct nouveau_object * client,u32 _parent,u32 _handle,u16 _oclass,void * data,u32 size,struct nouveau_object ** pobject)227 nouveau_handle_new(struct nouveau_object *client, u32 _parent, u32 _handle,
228 u16 _oclass, void *data, u32 size,
229 struct nouveau_object **pobject)
230 {
231 struct nouveau_object *parent = NULL;
232 struct nouveau_object *engctx = NULL;
233 struct nouveau_object *object = NULL;
234 struct nouveau_object *engine;
235 struct nouveau_oclass *oclass;
236 struct nouveau_handle *handle;
237 int ret;
238
239 /* lookup parent object and ensure it *is* a parent */
240 parent = nouveau_handle_ref(client, _parent);
241 if (!parent) {
242 nv_error(client, "parent 0x%08x not found\n", _parent);
243 return -ENOENT;
244 }
245
246 if (!nv_iclass(parent, NV_PARENT_CLASS)) {
247 nv_error(parent, "cannot have children\n");
248 ret = -EINVAL;
249 goto fail_class;
250 }
251
252 /* check that parent supports the requested subclass */
253 ret = nouveau_parent_sclass(parent, _oclass, &engine, &oclass);
254 if (ret) {
255 nv_debug(parent, "illegal class 0x%04x\n", _oclass);
256 goto fail_class;
257 }
258
259 /* make sure engine init has been completed *before* any objects
260 * it controls are created - the constructors may depend on
261 * state calculated at init (ie. default context construction)
262 */
263 if (engine) {
264 ret = nouveau_object_inc(engine);
265 if (ret)
266 goto fail_class;
267 }
268
269 /* if engine requires it, create a context object to insert
270 * between the parent and its children (eg. PGRAPH context)
271 */
272 if (engine && nv_engine(engine)->cclass) {
273 ret = nouveau_object_ctor(parent, engine,
274 nv_engine(engine)->cclass,
275 data, size, &engctx);
276 if (ret)
277 goto fail_engctx;
278 } else {
279 nouveau_object_ref(parent, &engctx);
280 }
281
282 /* finally, create new object and bind it to its handle */
283 ret = nouveau_object_ctor(engctx, engine, oclass, data, size, &object);
284 *pobject = object;
285 if (ret)
286 goto fail_ctor;
287
288 ret = nouveau_object_inc(object);
289 if (ret)
290 goto fail_init;
291
292 ret = nouveau_handle_create(parent, _parent, _handle, object, &handle);
293 if (ret)
294 goto fail_handle;
295
296 ret = nouveau_handle_init(handle);
297 if (ret)
298 nouveau_handle_destroy(handle);
299
300 fail_handle:
301 nouveau_object_dec(object, false);
302 fail_init:
303 nouveau_object_ref(NULL, &object);
304 fail_ctor:
305 nouveau_object_ref(NULL, &engctx);
306 fail_engctx:
307 if (engine)
308 nouveau_object_dec(engine, false);
309 fail_class:
310 nouveau_object_ref(NULL, &parent);
311 return ret;
312 }
313
314 int
nouveau_handle_del(struct nouveau_object * client,u32 _parent,u32 _handle)315 nouveau_handle_del(struct nouveau_object *client, u32 _parent, u32 _handle)
316 {
317 struct nouveau_object *parent = NULL;
318 struct nouveau_object *namedb = NULL;
319 struct nouveau_handle *handle = NULL;
320
321 parent = nouveau_handle_ref(client, _parent);
322 if (!parent)
323 return -ENOENT;
324
325 namedb = nv_pclass(parent, NV_NAMEDB_CLASS);
326 if (namedb) {
327 handle = nouveau_namedb_get(nv_namedb(namedb), _handle);
328 if (handle) {
329 nouveau_namedb_put(handle);
330 nouveau_handle_fini(handle, false);
331 nouveau_handle_destroy(handle);
332 }
333 }
334
335 nouveau_object_ref(NULL, &parent);
336 return handle ? 0 : -EINVAL;
337 }
338