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 35int 36nouveau_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; 55fail: 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 65int 66nouveau_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; 88fail: 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 99int 100nouveau_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 153void 154nouveau_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 174struct nouveau_object * 175nouveau_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 192struct nouveau_handle * 193nouveau_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 201struct nouveau_handle * 202nouveau_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 210struct nouveau_handle * 211nouveau_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 219void 220nouveau_handle_put(struct nouveau_handle *handle) 221{ 222 if (handle) 223 nouveau_namedb_put(handle); 224} 225 226int 227nouveau_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 300fail_handle: 301 nouveau_object_dec(object, false); 302fail_init: 303 nouveau_object_ref(NULL, &object); 304fail_ctor: 305 nouveau_object_ref(NULL, &engctx); 306fail_engctx: 307 if (engine) 308 nouveau_object_dec(engine, false); 309fail_class: 310 nouveau_object_ref(NULL, &parent); 311 return ret; 312} 313 314int 315nouveau_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