# Makefile for the drm device driver.  This driver provides support for the
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
-ccflags-y := -Iinclude/drm
+ccflags-y := -Iinclude/drm -DCONFIG_NOUVEAU_DEBUG=7 -DCONFIG_NOUVEAU_DEBUG_DEFAULT=3
 ccflags-y += -I$(src)/core/include
 ccflags-y += -I$(src)
 
-nouveau-y := core/core/mm.o
+nouveau-y := core/core/client.o
+nouveau-y += core/core/engine.o
+nouveau-y += core/core/handle.o
+nouveau-y += core/core/mm.o
+nouveau-y += core/core/namedb.o
+nouveau-y += core/core/object.o
+nouveau-y += core/core/option.o
+nouveau-y += core/core/parent.o
+nouveau-y += core/core/printk.o
 nouveau-y += core/core/ramht.o
+nouveau-y += core/core/subdev.o
 
+nouveau-y += core/subdev/device/base.o
+nouveau-y += core/subdev/device/nv04.o
+nouveau-y += core/subdev/device/nv10.o
+nouveau-y += core/subdev/device/nv20.o
+nouveau-y += core/subdev/device/nv30.o
+nouveau-y += core/subdev/device/nv40.o
+nouveau-y += core/subdev/device/nv50.o
+nouveau-y += core/subdev/device/nvc0.o
+nouveau-y += core/subdev/device/nve0.o
 nouveau-y += core/subdev/fb/nv04.o
 nouveau-y += core/subdev/fb/nv10.o
 nouveau-y += core/subdev/fb/nv20.o
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/object.h>
+#include <core/client.h>
+#include <core/handle.h>
+#include <core/option.h>
+
+#include <subdev/device.h>
+
+static void
+nouveau_client_dtor(struct nouveau_object *object)
+{
+       struct nouveau_client *client = (void *)object;
+       nouveau_object_ref(NULL, &client->device);
+       nouveau_handle_destroy(client->root);
+       nouveau_namedb_destroy(&client->base);
+}
+
+static struct nouveau_oclass
+nouveau_client_oclass = {
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .dtor = nouveau_client_dtor,
+       },
+};
+
+int
+nouveau_client_create_(u32 name, u64 devname, const char *cfg, const char *dbg,
+                      int length, void **pobject)
+{
+       struct nouveau_object *device;
+       struct nouveau_client *client;
+       int ret;
+
+       device = (void *)nouveau_device_find(devname);
+       if (!device)
+               return -ENODEV;
+
+       ret = nouveau_namedb_create_(NULL, NULL, &nouveau_client_oclass,
+                                    NV_CLIENT_CLASS, nouveau_device_sclass,
+                                    0, length, pobject);
+       client = *pobject;
+       if (ret)
+               return ret;
+
+       ret = nouveau_handle_create(nv_object(client), ~0, ~0,
+                                   nv_object(client), &client->root);
+       if (ret) {
+               nouveau_namedb_destroy(&client->base);
+               return ret;
+       }
+
+       /* prevent init/fini being called, os in in charge of this */
+       atomic_set(&nv_object(client)->usecount, 2);
+
+       nouveau_object_ref(device, &client->device);
+       client->handle = name;
+       client->debug = nouveau_dbgopt(dbg, "CLIENT");
+       return 0;
+}
+
+int
+nouveau_client_init(struct nouveau_client *client)
+{
+       int ret;
+       nv_debug(client, "init running\n");
+       ret = nouveau_handle_init(client->root);
+       nv_debug(client, "init completed with %d\n", ret);
+       return ret;
+}
+
+int
+nouveau_client_fini(struct nouveau_client *client, bool suspend)
+{
+       const char *name[2] = { "fini", "suspend" };
+       int ret;
+
+       nv_debug(client, "%s running\n", name[suspend]);
+       ret = nouveau_handle_fini(client->root, suspend);
+       nv_debug(client, "%s completed with %d\n", name[suspend], ret);
+       return ret;
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/object.h>
+#include <core/namedb.h>
+#include <core/handle.h>
+#include <core/client.h>
+#include <core/engctx.h>
+
+#include <subdev/vm.h>
+
+int
+nouveau_engctx_create_(struct nouveau_object *parent,
+                      struct nouveau_object *engobj,
+                      struct nouveau_oclass *oclass,
+                      struct nouveau_object *pargpu,
+                      u32 size, u32 align, u32 flags,
+                      int length, void **pobject)
+{
+       struct nouveau_client *client = nouveau_client(parent);
+       struct nouveau_engine *engine = nv_engine(engobj);
+       struct nouveau_subdev *subdev = nv_subdev(engine);
+       struct nouveau_engctx *engctx;
+       struct nouveau_object *ctxpar;
+       int ret;
+
+       /* use existing context for the engine if one is available */
+       mutex_lock(&subdev->mutex);
+       list_for_each_entry(engctx, &engine->contexts, head) {
+               ctxpar = nv_pclass(nv_object(engctx), NV_PARENT_CLASS);
+               if (ctxpar == parent) {
+                       atomic_inc(&nv_object(engctx)->refcount);
+                       *pobject = engctx;
+                       mutex_unlock(&subdev->mutex);
+                       return 1;
+               }
+       }
+       mutex_unlock(&subdev->mutex);
+
+       if (size) {
+               ret = nouveau_gpuobj_create_(parent, engobj, oclass,
+                                            NV_ENGCTX_CLASS,
+                                            pargpu, size, align, flags,
+                                            length, pobject);
+       } else {
+               ret = nouveau_object_create_(parent, engobj, oclass,
+                                            NV_ENGCTX_CLASS, length, pobject);
+       }
+
+       engctx = *pobject;
+       if (engctx && client->vm)
+               atomic_inc(&client->vm->engref[nv_engidx(engobj)]);
+       if (ret)
+               return ret;
+
+       list_add(&engctx->head, &engine->contexts);
+       return 0;
+}
+
+void
+nouveau_engctx_destroy(struct nouveau_engctx *engctx)
+{
+       struct nouveau_object *engine = nv_object(engctx)->engine;
+       struct nouveau_client *client = nouveau_client(engctx);
+
+       nouveau_gpuobj_unmap(&engctx->vma);
+       list_del(&engctx->head);
+       if (client->vm)
+               atomic_dec(&client->vm->engref[nv_engidx(engine)]);
+
+       if (engctx->base.size)
+               nouveau_gpuobj_destroy(&engctx->base);
+       else
+               nouveau_object_destroy(&engctx->base.base);
+}
+
+int
+nouveau_engctx_init(struct nouveau_engctx *engctx)
+{
+       struct nouveau_object *object = nv_object(engctx);
+       struct nouveau_subdev *subdev = nv_subdev(object->engine);
+       struct nouveau_object *parent;
+       struct nouveau_subdev *pardev;
+       int ret;
+
+       ret = nouveau_gpuobj_init(&engctx->base);
+       if (ret)
+               return ret;
+
+       parent = nv_pclass(object->parent, NV_PARENT_CLASS);
+       pardev = nv_subdev(parent->engine);
+       if (nv_parent(parent)->context_attach) {
+               mutex_lock(&pardev->mutex);
+               ret = nv_parent(parent)->context_attach(parent, object);
+               mutex_unlock(&pardev->mutex);
+       }
+
+       if (ret) {
+               nv_error(parent, "failed to attach %s context, %d\n",
+                        subdev->name, ret);
+               return ret;
+       }
+
+       nv_debug(parent, "attached %s context\n", subdev->name);
+       return 0;
+}
+
+int
+nouveau_engctx_fini(struct nouveau_engctx *engctx, bool suspend)
+{
+       struct nouveau_object *object = nv_object(engctx);
+       struct nouveau_subdev *subdev = nv_subdev(object->engine);
+       struct nouveau_object *parent;
+       struct nouveau_subdev *pardev;
+       int ret = 0;
+
+       parent = nv_pclass(object->parent, NV_PARENT_CLASS);
+       pardev = nv_subdev(parent->engine);
+       if (nv_parent(parent)->context_detach) {
+               mutex_lock(&pardev->mutex);
+               ret = nv_parent(parent)->context_detach(parent, suspend, object);
+               mutex_unlock(&pardev->mutex);
+       }
+
+       if (ret) {
+               nv_error(parent, "failed to detach %s context, %d\n",
+                        subdev->name, ret);
+               return ret;
+       }
+
+       nv_debug(parent, "detached %s context\n", subdev->name);
+       return nouveau_gpuobj_fini(&engctx->base, suspend);
+}
+
+void
+_nouveau_engctx_dtor(struct nouveau_object *object)
+{
+       nouveau_engctx_destroy(nv_engctx(object));
+}
+
+int
+_nouveau_engctx_init(struct nouveau_object *object)
+{
+       return nouveau_engctx_init(nv_engctx(object));
+}
+
+
+int
+_nouveau_engctx_fini(struct nouveau_object *object, bool suspend)
+{
+       return nouveau_engctx_fini(nv_engctx(object), suspend);
+}
+
+struct nouveau_object *
+nouveau_engctx_lookup(struct nouveau_engine *engine, u64 addr)
+{
+       struct nouveau_engctx *engctx;
+
+       list_for_each_entry(engctx, &engine->contexts, head) {
+               if (engctx->base.size &&
+                   nv_gpuobj(engctx)->addr == addr)
+                       return nv_object(engctx);
+       }
+
+       return NULL;
+}
+
+struct nouveau_handle *
+nouveau_engctx_lookup_class(struct nouveau_engine *engine, u64 addr, u16 oclass)
+{
+       struct nouveau_object *engctx = nouveau_engctx_lookup(engine, addr);
+       struct nouveau_namedb *namedb;
+
+       if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS)))
+               return nouveau_namedb_get_class(namedb, oclass);
+
+       return NULL;
+}
+
+struct nouveau_handle *
+nouveau_engctx_lookup_vinst(struct nouveau_engine *engine, u64 addr, u64 vinst)
+{
+       struct nouveau_object *engctx = nouveau_engctx_lookup(engine, addr);
+       struct nouveau_namedb *namedb;
+
+       if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS)))
+               return nouveau_namedb_get_vinst(namedb, vinst);
+
+       return NULL;
+}
+
+struct nouveau_handle *
+nouveau_engctx_lookup_cinst(struct nouveau_engine *engine, u64 addr, u32 cinst)
+{
+       struct nouveau_object *engctx = nouveau_engctx_lookup(engine, addr);
+       struct nouveau_namedb *namedb;
+
+       if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS)))
+               return nouveau_namedb_get_cinst(namedb, cinst);
+
+       return NULL;
+}
+
+void
+nouveau_engctx_handle_put(struct nouveau_handle *handle)
+{
+       if (handle)
+               nouveau_namedb_put(handle);
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/device.h>
+#include <core/engine.h>
+#include <core/option.h>
+
+int
+nouveau_engine_create_(struct nouveau_object *parent,
+                      struct nouveau_object *engobj,
+                      struct nouveau_oclass *oclass, bool enable,
+                      const char *iname, const char *fname,
+                      int length, void **pobject)
+{
+       struct nouveau_device *device = nv_device(parent);
+       struct nouveau_engine *engine;
+       int ret;
+
+       ret = nouveau_subdev_create_(parent, engobj, oclass, NV_ENGINE_CLASS,
+                                    iname, fname, length, pobject);
+       engine = *pobject;
+       if (ret)
+               return ret;
+
+       if (!nouveau_boolopt(device->cfgopt, iname, enable)) {
+               if (!enable)
+                       nv_warn(engine, "disabled, %s=1 to enable\n", iname);
+               return -ENODEV;
+       }
+
+       INIT_LIST_HEAD(&engine->contexts);
+       return 0;
+}
 
--- /dev/null
+/*
+ * Copyright (C) 2010 Nouveau Project
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <core/os.h>
+#include <core/enum.h>
+
+const struct nouveau_enum *
+nouveau_enum_find(const struct nouveau_enum *en, u32 value)
+{
+       while (en->name) {
+               if (en->value == value)
+                       return en;
+               en++;
+       }
+
+       return NULL;
+}
+
+void
+nouveau_enum_print(const struct nouveau_enum *en, u32 value)
+{
+       en = nouveau_enum_find(en, value);
+       if (en)
+               printk("%s", en->name);
+       else
+               printk("(unknown enum 0x%08x)", value);
+}
+
+void
+nouveau_bitfield_print(const struct nouveau_bitfield *bf, u32 value)
+{
+       while (bf->name) {
+               if (value & bf->mask) {
+                       printk(" %s", bf->name);
+                       value &= ~bf->mask;
+               }
+
+               bf++;
+       }
+
+       if (value)
+               printk(" (unknown bits 0x%08x)", value);
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/object.h>
+#include <core/gpuobj.h>
+
+#include <subdev/instmem.h>
+#include <subdev/bar.h>
+#include <subdev/vm.h>
+
+void
+nouveau_gpuobj_destroy(struct nouveau_gpuobj *gpuobj)
+{
+       int i;
+
+       if (gpuobj->flags & NVOBJ_FLAG_ZERO_FREE) {
+               for (i = 0; i < gpuobj->size; i += 4)
+                       nv_wo32(gpuobj, i, 0x00000000);
+       }
+
+       if (gpuobj->heap.block_size)
+               nouveau_mm_fini(&gpuobj->heap);
+
+       nouveau_object_destroy(&gpuobj->base);
+}
+
+int
+nouveau_gpuobj_create_(struct nouveau_object *parent,
+                      struct nouveau_object *engine,
+                      struct nouveau_oclass *oclass, u32 pclass,
+                      struct nouveau_object *pargpu,
+                      u32 size, u32 align, u32 flags,
+                      int length, void **pobject)
+{
+       struct nouveau_instmem *imem = nouveau_instmem(parent);
+       struct nouveau_bar *bar = nouveau_bar(parent);
+       struct nouveau_gpuobj *gpuobj;
+       struct nouveau_mm *heap = NULL;
+       int ret, i;
+       u64 addr;
+
+       *pobject = NULL;
+
+       if (pargpu) {
+               while ((pargpu = nv_pclass(pargpu, NV_GPUOBJ_CLASS))) {
+                       if (nv_gpuobj(pargpu)->heap.block_size)
+                               break;
+                       pargpu = pargpu->parent;
+               }
+
+               if (unlikely(pargpu == NULL)) {
+                       nv_error(parent, "no gpuobj heap\n");
+                       return -EINVAL;
+               }
+
+               addr =  nv_gpuobj(pargpu)->addr;
+               heap = &nv_gpuobj(pargpu)->heap;
+               atomic_inc(&parent->refcount);
+       } else {
+               ret = imem->alloc(imem, parent, size, align, &parent);
+               pargpu = parent;
+               if (ret)
+                       return ret;
+
+               addr = nv_memobj(pargpu)->addr;
+               size = nv_memobj(pargpu)->size;
+
+               if (bar && bar->alloc) {
+                       struct nouveau_instobj *iobj = (void *)parent;
+                       struct nouveau_mem **mem = (void *)(iobj + 1);
+                       struct nouveau_mem *node = *mem;
+                       if (!bar->alloc(bar, parent, node, &pargpu)) {
+                               nouveau_object_ref(NULL, &parent);
+                               parent = pargpu;
+                       }
+               }
+       }
+
+       ret = nouveau_object_create_(parent, engine, oclass, pclass |
+                                    NV_GPUOBJ_CLASS, length, pobject);
+       nouveau_object_ref(NULL, &parent);
+       gpuobj = *pobject;
+       if (ret)
+               return ret;
+
+       gpuobj->parent = pargpu;
+       gpuobj->flags = flags;
+       gpuobj->addr = addr;
+       gpuobj->size = size;
+
+       if (heap) {
+               ret = nouveau_mm_head(heap, 1, size, size,
+                                     max(align, (u32)1), &gpuobj->node);
+               if (ret)
+                       return ret;
+
+               gpuobj->addr += gpuobj->node->offset;
+       }
+
+       if (gpuobj->flags & NVOBJ_FLAG_HEAP) {
+               ret = nouveau_mm_init(&gpuobj->heap, 0, gpuobj->size, 1);
+               if (ret)
+                       return ret;
+       }
+
+       if (flags & NVOBJ_FLAG_ZERO_ALLOC) {
+               for (i = 0; i < gpuobj->size; i += 4)
+                       nv_wo32(gpuobj, i, 0x00000000);
+       }
+
+       return ret;
+}
+
+struct nouveau_gpuobj_class {
+       struct nouveau_object *pargpu;
+       u64 size;
+       u32 align;
+       u32 flags;
+};
+
+int
+_nouveau_gpuobj_ctor(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, void *data, u32 size,
+                    struct nouveau_object **pobject)
+{
+       struct nouveau_gpuobj_class *args = data;
+       struct nouveau_gpuobj *object;
+       int ret;
+
+       ret = nouveau_gpuobj_create(parent, engine, oclass, 0, args->pargpu,
+                                   args->size, args->align, args->flags,
+                                   &object);
+       *pobject = nv_object(object);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+void
+_nouveau_gpuobj_dtor(struct nouveau_object *object)
+{
+       nouveau_gpuobj_destroy(nv_gpuobj(object));
+}
+
+int
+_nouveau_gpuobj_init(struct nouveau_object *object)
+{
+       return nouveau_gpuobj_init(nv_gpuobj(object));
+}
+
+int
+_nouveau_gpuobj_fini(struct nouveau_object *object, bool suspend)
+{
+       return nouveau_gpuobj_fini(nv_gpuobj(object), suspend);
+}
+
+u32
+_nouveau_gpuobj_rd32(struct nouveau_object *object, u32 addr)
+{
+       struct nouveau_gpuobj *gpuobj = nv_gpuobj(object);
+       struct nouveau_ofuncs *pfuncs = nv_ofuncs(gpuobj->parent);
+       if (gpuobj->node)
+               addr += gpuobj->node->offset;
+       return pfuncs->rd32(gpuobj->parent, addr);
+}
+
+void
+_nouveau_gpuobj_wr32(struct nouveau_object *object, u32 addr, u32 data)
+{
+       struct nouveau_gpuobj *gpuobj = nv_gpuobj(object);
+       struct nouveau_ofuncs *pfuncs = nv_ofuncs(gpuobj->parent);
+       if (gpuobj->node)
+               addr += gpuobj->node->offset;
+       pfuncs->wr32(gpuobj->parent, addr, data);
+}
+
+static struct nouveau_oclass
+_nouveau_gpuobj_oclass = {
+       .handle = 0x00000000,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .ctor = _nouveau_gpuobj_ctor,
+               .dtor = _nouveau_gpuobj_dtor,
+               .init = _nouveau_gpuobj_init,
+               .fini = _nouveau_gpuobj_fini,
+               .rd32 = _nouveau_gpuobj_rd32,
+               .wr32 = _nouveau_gpuobj_wr32,
+       },
+};
+
+int
+nouveau_gpuobj_new(struct nouveau_object *parent, struct nouveau_object *pargpu,
+                  u32 size, u32 align, u32 flags,
+                  struct nouveau_gpuobj **pgpuobj)
+{
+       struct nouveau_object *engine = parent;
+       struct nouveau_gpuobj_class args = {
+               .pargpu = pargpu,
+               .size = size,
+               .align = align,
+               .flags = flags,
+       };
+
+       if (!nv_iclass(engine, NV_SUBDEV_CLASS))
+               engine = engine->engine;
+       BUG_ON(engine == NULL);
+
+       return nouveau_object_ctor(parent, engine, &_nouveau_gpuobj_oclass,
+                                  &args, sizeof(args),
+                                  (struct nouveau_object **)pgpuobj);
+}
+
+int
+nouveau_gpuobj_map(struct nouveau_gpuobj *gpuobj, u32 access,
+                  struct nouveau_vma *vma)
+{
+       struct nouveau_bar *bar = nouveau_bar(gpuobj);
+       int ret = -EINVAL;
+
+       if (bar && bar->umap) {
+               struct nouveau_instobj *iobj = (void *)
+                       nv_pclass(nv_object(gpuobj), NV_MEMOBJ_CLASS);
+               struct nouveau_mem **mem = (void *)(iobj + 1);
+               ret = bar->umap(bar, *mem, access, vma);
+       }
+
+       return ret;
+}
+
+int
+nouveau_gpuobj_map_vm(struct nouveau_gpuobj *gpuobj, struct nouveau_vm *vm,
+                     u32 access, struct nouveau_vma *vma)
+{
+       struct nouveau_instobj *iobj = (void *)
+               nv_pclass(nv_object(gpuobj), NV_MEMOBJ_CLASS);
+       struct nouveau_mem **mem = (void *)(iobj + 1);
+       int ret;
+
+       ret = nouveau_vm_get(vm, gpuobj->size, 12, access, vma);
+       if (ret)
+               return ret;
+
+       nouveau_vm_map(vma, *mem);
+       return 0;
+}
+
+void
+nouveau_gpuobj_unmap(struct nouveau_vma *vma)
+{
+       if (vma->node) {
+               nouveau_vm_unmap(vma);
+               nouveau_vm_put(vma);
+       }
+}
+
+/* the below is basically only here to support sharing the paged dma object
+ * for PCI(E)GART on <=nv4x chipsets, and should *not* be expected to work
+ * anywhere else.
+ */
+
+static void
+nouveau_gpudup_dtor(struct nouveau_object *object)
+{
+       struct nouveau_gpuobj *gpuobj = (void *)object;
+       nouveau_object_ref(NULL, &gpuobj->parent);
+       nouveau_object_destroy(&gpuobj->base);
+}
+
+static struct nouveau_oclass
+nouveau_gpudup_oclass = {
+       .handle = NV_GPUOBJ_CLASS,
+       .ofuncs = &(struct nouveau_ofuncs) {
+               .dtor = nouveau_gpudup_dtor,
+               .init = nouveau_object_init,
+               .fini = nouveau_object_fini,
+       },
+};
+
+int
+nouveau_gpuobj_dup(struct nouveau_object *parent, struct nouveau_gpuobj *base,
+                  struct nouveau_gpuobj **pgpuobj)
+{
+       struct nouveau_gpuobj *gpuobj;
+       int ret;
+
+       ret = nouveau_object_create(parent, parent->engine,
+                                  &nouveau_gpudup_oclass, 0, &gpuobj);
+       *pgpuobj = gpuobj;
+       if (ret)
+               return ret;
+
+       nouveau_object_ref(nv_object(base), &gpuobj->parent);
+       gpuobj->addr = base->addr;
+       gpuobj->size = base->size;
+       return 0;
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/object.h>
+#include <core/handle.h>
+#include <core/client.h>
+
+#define hprintk(h,l,f,a...) do {                                               \
+       struct nouveau_client *c = nouveau_client((h)->object);                \
+       struct nouveau_handle *p = (h)->parent; u32 n = p ? p->name : ~0;      \
+       nv_printk((c), l, "0x%08x:0x%08x "f, n, (h)->name, ##a);               \
+} while(0)
+
+int
+nouveau_handle_init(struct nouveau_handle *handle)
+{
+       struct nouveau_handle *item;
+       int ret;
+
+       hprintk(handle, TRACE, "init running\n");
+       ret = nouveau_object_inc(handle->object);
+       if (ret)
+               return ret;
+
+       hprintk(handle, TRACE, "init children\n");
+       list_for_each_entry(item, &handle->tree, head) {
+               ret = nouveau_handle_init(item);
+               if (ret)
+                       goto fail;
+       }
+
+       hprintk(handle, TRACE, "init completed\n");
+       return 0;
+fail:
+       hprintk(handle, ERROR, "init failed with %d\n", ret);
+       list_for_each_entry_continue_reverse(item, &handle->tree, head) {
+               nouveau_handle_fini(item, false);
+       }
+
+       nouveau_object_dec(handle->object, false);
+       return ret;
+}
+
+int
+nouveau_handle_fini(struct nouveau_handle *handle, bool suspend)
+{
+       static char *name[2] = { "fini", "suspend" };
+       struct nouveau_handle *item;
+       int ret;
+
+       hprintk(handle, TRACE, "%s children\n", name[suspend]);
+       list_for_each_entry(item, &handle->tree, head) {
+               ret = nouveau_handle_fini(item, suspend);
+               if (ret && suspend)
+                       goto fail;
+       }
+
+       hprintk(handle, TRACE, "%s running\n", name[suspend]);
+       if (handle->object) {
+               ret = nouveau_object_dec(handle->object, suspend);
+               if (ret && suspend)
+                       goto fail;
+       }
+
+       hprintk(handle, TRACE, "%s completed\n", name[suspend]);
+       return 0;
+fail:
+       hprintk(handle, ERROR, "%s failed with %d\n", name[suspend], ret);
+       list_for_each_entry_continue_reverse(item, &handle->tree, head) {
+               int rret = nouveau_handle_init(item);
+               if (rret)
+                       hprintk(handle, FATAL, "failed to restart, %d\n", rret);
+       }
+
+       return ret;
+}
+
+int
+nouveau_handle_create(struct nouveau_object *parent, u32 _parent, u32 _handle,
+                     struct nouveau_object *object,
+                     struct nouveau_handle **phandle)
+{
+       struct nouveau_object *namedb;
+       struct nouveau_handle *handle;
+       int ret;
+
+       namedb = parent;
+       while (!nv_iclass(namedb, NV_NAMEDB_CLASS))
+               namedb = namedb->parent;
+
+       handle = *phandle = kzalloc(sizeof(*handle), GFP_KERNEL);
+       if (!handle)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&handle->head);
+       INIT_LIST_HEAD(&handle->tree);
+       handle->name = _handle;
+       handle->priv = ~0;
+
+       ret = nouveau_namedb_insert(nv_namedb(namedb), _handle, object, handle);
+       if (ret) {
+               kfree(handle);
+               return ret;
+       }
+
+       if (nv_parent(parent)->object_attach) {
+               ret = nv_parent(parent)->object_attach(parent, object, _handle);
+               if (ret < 0) {
+                       nouveau_handle_destroy(handle);
+                       return ret;
+               }
+
+               handle->priv = ret;
+       }
+
+       if (object != namedb) {
+               while (!nv_iclass(namedb, NV_CLIENT_CLASS))
+                       namedb = namedb->parent;
+
+               handle->parent = nouveau_namedb_get(nv_namedb(namedb), _parent);
+               if (handle->parent) {
+                       list_add(&handle->head, &handle->parent->tree);
+                       nouveau_namedb_put(handle->parent);
+               }
+       }
+
+       hprintk(handle, TRACE, "created\n");
+       return 0;
+}
+
+void
+nouveau_handle_destroy(struct nouveau_handle *handle)
+{
+       struct nouveau_handle *item, *temp;
+
+       hprintk(handle, TRACE, "destroy running\n");
+       list_for_each_entry_safe(item, temp, &handle->tree, head) {
+               nouveau_handle_destroy(item);
+       }
+       list_del(&handle->head);
+
+       if (handle->priv != ~0) {
+               struct nouveau_object *parent = handle->parent->object;
+               nv_parent(parent)->object_detach(parent, handle->priv);
+       }
+
+       hprintk(handle, TRACE, "destroy completed\n");
+       nouveau_namedb_remove(handle);
+       kfree(handle);
+}
+
+struct nouveau_object *
+nouveau_handle_ref(struct nouveau_object *parent, u32 name)
+{
+       struct nouveau_object *object = NULL;
+       struct nouveau_handle *handle;
+
+       while (!nv_iclass(parent, NV_NAMEDB_CLASS))
+               parent = parent->parent;
+
+       handle = nouveau_namedb_get(nv_namedb(parent), name);
+       if (handle) {
+               nouveau_object_ref(handle->object, &object);
+               nouveau_namedb_put(handle);
+       }
+
+       return object;
+}
 
  * Authors: Ben Skeggs
  */
 
-#include "drmP.h"
-#include "nouveau_drv.h"
+#include <core/os.h>
 #include <core/mm.h>
 
 static inline void
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/object.h>
+#include <core/namedb.h>
+#include <core/handle.h>
+#include <core/gpuobj.h>
+
+static struct nouveau_handle *
+nouveau_namedb_lookup(struct nouveau_namedb *namedb, u32 name)
+{
+       struct nouveau_handle *handle;
+
+       list_for_each_entry(handle, &namedb->list, node) {
+               if (handle->name == name)
+                       return handle;
+       }
+
+       return NULL;
+}
+
+static struct nouveau_handle *
+nouveau_namedb_lookup_class(struct nouveau_namedb *namedb, u16 oclass)
+{
+       struct nouveau_handle *handle;
+
+       list_for_each_entry(handle, &namedb->list, node) {
+               if (nv_mclass(handle->object) == oclass)
+                       return handle;
+       }
+
+       return NULL;
+}
+
+static struct nouveau_handle *
+nouveau_namedb_lookup_vinst(struct nouveau_namedb *namedb, u64 vinst)
+{
+       struct nouveau_handle *handle;
+
+       list_for_each_entry(handle, &namedb->list, node) {
+               if (nv_iclass(handle->object, NV_GPUOBJ_CLASS)) {
+                       if (nv_gpuobj(handle->object)->addr == vinst)
+                               return handle;
+               }
+       }
+
+       return NULL;
+}
+
+static struct nouveau_handle *
+nouveau_namedb_lookup_cinst(struct nouveau_namedb *namedb, u32 cinst)
+{
+       struct nouveau_handle *handle;
+
+       list_for_each_entry(handle, &namedb->list, node) {
+               if (nv_iclass(handle->object, NV_GPUOBJ_CLASS)) {
+                       if (nv_gpuobj(handle->object)->node &&
+                           nv_gpuobj(handle->object)->node->offset == cinst)
+                               return handle;
+               }
+       }
+
+       return NULL;
+}
+
+int
+nouveau_namedb_insert(struct nouveau_namedb *namedb, u32 name,
+                     struct nouveau_object *object,
+                     struct nouveau_handle *handle)
+{
+       int ret = -EEXIST;
+       write_lock_irq(&namedb->lock);
+       if (!nouveau_namedb_lookup(namedb, name)) {
+               nouveau_object_ref(object, &handle->object);
+               handle->namedb = namedb;
+               list_add(&handle->node, &namedb->list);
+               ret = 0;
+       }
+       write_unlock_irq(&namedb->lock);
+       return ret;
+}
+
+void
+nouveau_namedb_remove(struct nouveau_handle *handle)
+{
+       struct nouveau_namedb *namedb = handle->namedb;
+       struct nouveau_object *object = handle->object;
+       write_lock_irq(&namedb->lock);
+       list_del(&handle->node);
+       write_unlock_irq(&namedb->lock);
+       nouveau_object_ref(NULL, &object);
+}
+
+struct nouveau_handle *
+nouveau_namedb_get(struct nouveau_namedb *namedb, u32 name)
+{
+       struct nouveau_handle *handle;
+       read_lock(&namedb->lock);
+       handle = nouveau_namedb_lookup(namedb, name);
+       if (handle == NULL)
+               read_unlock(&namedb->lock);
+       return handle;
+}
+
+struct nouveau_handle *
+nouveau_namedb_get_class(struct nouveau_namedb *namedb, u16 oclass)
+{
+       struct nouveau_handle *handle;
+       read_lock(&namedb->lock);
+       handle = nouveau_namedb_lookup_class(namedb, oclass);
+       if (handle == NULL)
+               read_unlock(&namedb->lock);
+       return handle;
+}
+
+struct nouveau_handle *
+nouveau_namedb_get_vinst(struct nouveau_namedb *namedb, u64 vinst)
+{
+       struct nouveau_handle *handle;
+       read_lock(&namedb->lock);
+       handle = nouveau_namedb_lookup_vinst(namedb, vinst);
+       if (handle == NULL)
+               read_unlock(&namedb->lock);
+       return handle;
+}
+
+struct nouveau_handle *
+nouveau_namedb_get_cinst(struct nouveau_namedb *namedb, u32 cinst)
+{
+       struct nouveau_handle *handle;
+       read_lock(&namedb->lock);
+       handle = nouveau_namedb_lookup_cinst(namedb, cinst);
+       if (handle == NULL)
+               read_unlock(&namedb->lock);
+       return handle;
+}
+
+void
+nouveau_namedb_put(struct nouveau_handle *handle)
+{
+       if (handle)
+               read_unlock(&handle->namedb->lock);
+}
+
+int
+nouveau_namedb_create_(struct nouveau_object *parent,
+                      struct nouveau_object *engine,
+                      struct nouveau_oclass *oclass, u32 pclass,
+                      struct nouveau_oclass *sclass, u32 engcls,
+                      int length, void **pobject)
+{
+       struct nouveau_namedb *namedb;
+       int ret;
+
+       ret = nouveau_parent_create_(parent, engine, oclass, pclass |
+                                    NV_NAMEDB_CLASS, sclass, engcls,
+                                    length, pobject);
+       namedb = *pobject;
+       if (ret)
+               return ret;
+
+       rwlock_init(&namedb->lock);
+       INIT_LIST_HEAD(&namedb->list);
+       return 0;
+}
+
+int
+_nouveau_namedb_ctor(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, void *data, u32 size,
+                    struct nouveau_object **pobject)
+{
+       struct nouveau_namedb *object;
+       int ret;
+
+       ret = nouveau_namedb_create(parent, engine, oclass, 0, NULL, 0, &object);
+       *pobject = nv_object(object);
+       if (ret)
+               return ret;
+
+       return 0;
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/object.h>
+#include <core/parent.h>
+#include <core/namedb.h>
+#include <core/handle.h>
+#include <core/engine.h>
+
+#ifdef NOUVEAU_OBJECT_MAGIC
+static struct list_head _objlist = LIST_HEAD_INIT(_objlist);
+static DEFINE_SPINLOCK(_objlist_lock);
+#endif
+
+int
+nouveau_object_create_(struct nouveau_object *parent,
+                      struct nouveau_object *engine,
+                      struct nouveau_oclass *oclass, u32 pclass,
+                      int size, void **pobject)
+{
+       struct nouveau_object *object;
+
+       object = *pobject = kzalloc(size, GFP_KERNEL);
+       if (!object)
+               return -ENOMEM;
+
+       nouveau_object_ref(parent, &object->parent);
+       nouveau_object_ref(engine, &object->engine);
+       object->oclass = oclass;
+       object->oclass->handle |= pclass;
+       atomic_set(&object->refcount, 1);
+       atomic_set(&object->usecount, 0);
+
+#ifdef NOUVEAU_OBJECT_MAGIC
+       object->_magic = NOUVEAU_OBJECT_MAGIC;
+       spin_lock(&_objlist_lock);
+       list_add(&object->list, &_objlist);
+       spin_unlock(&_objlist_lock);
+#endif
+       return 0;
+}
+
+int
+_nouveau_object_ctor(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, void *data, u32 size,
+                    struct nouveau_object **pobject)
+{
+       struct nouveau_object *object;
+       int ret;
+
+       ret = nouveau_object_create(parent, engine, oclass, 0, &object);
+       *pobject = nv_object(object);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+void
+nouveau_object_destroy(struct nouveau_object *object)
+{
+#ifdef NOUVEAU_OBJECT_MAGIC
+       spin_lock(&_objlist_lock);
+       list_del(&object->list);
+       spin_unlock(&_objlist_lock);
+#endif
+       nouveau_object_ref(NULL, &object->engine);
+       nouveau_object_ref(NULL, &object->parent);
+       kfree(object);
+}
+
+void
+_nouveau_object_dtor(struct nouveau_object *object)
+{
+       nouveau_object_destroy(object);
+}
+
+int
+nouveau_object_init(struct nouveau_object *object)
+{
+       return 0;
+}
+
+int
+_nouveau_object_init(struct nouveau_object *object)
+{
+       return nouveau_object_init(object);
+}
+
+int
+nouveau_object_fini(struct nouveau_object *object, bool suspend)
+{
+       return 0;
+}
+
+int
+_nouveau_object_fini(struct nouveau_object *object, bool suspend)
+{
+       return nouveau_object_fini(object, suspend);
+}
+
+struct nouveau_ofuncs
+nouveau_object_ofuncs = {
+       .ctor = _nouveau_object_ctor,
+       .dtor = _nouveau_object_dtor,
+       .init = _nouveau_object_init,
+       .fini = _nouveau_object_fini,
+};
+
+int
+nouveau_object_ctor(struct nouveau_object *parent,
+                   struct nouveau_object *engine,
+                   struct nouveau_oclass *oclass, void *data, u32 size,
+                   struct nouveau_object **pobject)
+{
+       struct nouveau_ofuncs *ofuncs = oclass->ofuncs;
+       int ret;
+
+       *pobject = NULL;
+
+       ret = ofuncs->ctor(parent, engine, oclass, data, size, pobject);
+       if (ret < 0) {
+               if (ret != -ENODEV) {
+                       nv_error(parent, "failed to create 0x%08x, %d\n",
+                                oclass->handle, ret);
+               }
+
+               if (*pobject) {
+                       ofuncs->dtor(*pobject);
+                       *pobject = NULL;
+               }
+
+               return ret;
+       }
+
+       nv_debug(*pobject, "created\n");
+       return 0;
+}
+
+static void
+nouveau_object_dtor(struct nouveau_object *object)
+{
+       nv_debug(object, "destroying\n");
+       nv_ofuncs(object)->dtor(object);
+}
+
+void
+nouveau_object_ref(struct nouveau_object *obj, struct nouveau_object **ref)
+{
+       if (obj) {
+               atomic_inc(&obj->refcount);
+               nv_trace(obj, "inc() == %d\n", atomic_read(&obj->refcount));
+       }
+
+       if (*ref) {
+               int dead = atomic_dec_and_test(&(*ref)->refcount);
+               nv_trace(*ref, "dec() == %d\n", atomic_read(&(*ref)->refcount));
+               if (dead)
+                       nouveau_object_dtor(*ref);
+       }
+
+       *ref = obj;
+}
+
+int
+nouveau_object_new(struct nouveau_object *client, u32 _parent, u32 _handle,
+                  u16 _oclass, void *data, u32 size,
+                  struct nouveau_object **pobject)
+{
+       struct nouveau_object *parent = NULL;
+       struct nouveau_object *engctx = NULL;
+       struct nouveau_object *object = NULL;
+       struct nouveau_object *engine;
+       struct nouveau_oclass *oclass;
+       struct nouveau_handle *handle;
+       int ret;
+
+       /* lookup parent object and ensure it *is* a parent */
+       parent = nouveau_handle_ref(client, _parent);
+       if (!parent) {
+               nv_error(client, "parent 0x%08x not found\n", _parent);
+               return -ENOENT;
+       }
+
+       if (!nv_iclass(parent, NV_PARENT_CLASS)) {
+               nv_error(parent, "cannot have children\n");
+               ret = -EINVAL;
+               goto fail_class;
+       }
+
+       /* check that parent supports the requested subclass */
+       ret = nouveau_parent_sclass(parent, _oclass, &engine, &oclass);
+       if (ret) {
+               nv_debug(parent, "illegal class 0x%04x\n", _oclass);
+               goto fail_class;
+       }
+
+       /* make sure engine init has been completed *before* any objects
+        * it controls are created - the constructors may depend on
+        * state calculated at init (ie. default context construction)
+        */
+       if (engine) {
+               ret = nouveau_object_inc(engine);
+               if (ret)
+                       goto fail_class;
+       }
+
+       /* if engine requires it, create a context object to insert
+        * between the parent and its children (eg. PGRAPH context)
+        */
+       if (engine && nv_engine(engine)->cclass) {
+               ret = nouveau_object_ctor(parent, engine,
+                                         nv_engine(engine)->cclass,
+                                         data, size, &engctx);
+               if (ret)
+                       goto fail_engctx;
+       } else {
+               nouveau_object_ref(parent, &engctx);
+       }
+
+       /* finally, create new object and bind it to its handle */
+       ret = nouveau_object_ctor(engctx, engine, oclass, data, size, &object);
+       *pobject = object;
+       if (ret)
+               goto fail_ctor;
+
+       ret = nouveau_object_inc(object);
+       if (ret)
+               goto fail_init;
+
+       ret = nouveau_handle_create(parent, _parent, _handle, object, &handle);
+       if (ret)
+               goto fail_handle;
+
+       ret = nouveau_handle_init(handle);
+       if (ret)
+               nouveau_handle_destroy(handle);
+
+fail_handle:
+       nouveau_object_dec(object, false);
+fail_init:
+       nouveau_object_ref(NULL, &object);
+fail_ctor:
+       nouveau_object_ref(NULL, &engctx);
+fail_engctx:
+       if (engine)
+               nouveau_object_dec(engine, false);
+fail_class:
+       nouveau_object_ref(NULL, &parent);
+       return ret;
+}
+
+int
+nouveau_object_del(struct nouveau_object *client, u32 _parent, u32 _handle)
+{
+       struct nouveau_object *parent = NULL;
+       struct nouveau_object *namedb = NULL;
+       struct nouveau_handle *handle = NULL;
+       int ret = -EINVAL;
+
+       parent = nouveau_handle_ref(client, _parent);
+       if (!parent)
+               return -ENOENT;
+
+       namedb = nv_pclass(parent, NV_NAMEDB_CLASS);
+       if (namedb) {
+               handle = nouveau_namedb_get(nv_namedb(namedb), _handle);
+               if (handle) {
+                       nouveau_namedb_put(handle);
+                       nouveau_handle_fini(handle, false);
+                       nouveau_handle_destroy(handle);
+               }
+       }
+
+       nouveau_object_ref(NULL, &parent);
+       return ret;
+}
+
+int
+nouveau_object_inc(struct nouveau_object *object)
+{
+       int ref = atomic_add_return(1, &object->usecount);
+       int ret;
+
+       nv_trace(object, "use(+1) == %d\n", atomic_read(&object->usecount));
+       if (ref != 1)
+               return 0;
+
+       nv_trace(object, "initialising...\n");
+       if (object->parent) {
+               ret = nouveau_object_inc(object->parent);
+               if (ret) {
+                       nv_error(object, "parent failed, %d\n", ret);
+                       goto fail_parent;
+               }
+       }
+
+       if (object->engine) {
+               mutex_lock(&nv_subdev(object->engine)->mutex);
+               ret = nouveau_object_inc(object->engine);
+               mutex_unlock(&nv_subdev(object->engine)->mutex);
+               if (ret) {
+                       nv_error(object, "engine failed, %d\n", ret);
+                       goto fail_engine;
+               }
+       }
+
+       ret = nv_ofuncs(object)->init(object);
+       if (ret) {
+               nv_error(object, "init failed, %d\n", ret);
+               goto fail_self;
+       }
+
+       nv_debug(object, "initialised\n");
+       return 0;
+
+fail_self:
+       if (object->engine) {
+               mutex_lock(&nv_subdev(object->engine)->mutex);
+               nouveau_object_dec(object->engine, false);
+               mutex_unlock(&nv_subdev(object->engine)->mutex);
+       }
+fail_engine:
+       if (object->parent)
+                nouveau_object_dec(object->parent, false);
+fail_parent:
+       atomic_dec(&object->usecount);
+       return ret;
+}
+
+static int
+nouveau_object_decf(struct nouveau_object *object)
+{
+       int ret;
+
+       nv_trace(object, "stopping...\n");
+
+       ret = nv_ofuncs(object)->fini(object, false);
+       if (ret)
+               nv_warn(object, "failed fini, %d\n", ret);
+
+       if (object->engine) {
+               mutex_lock(&nv_subdev(object->engine)->mutex);
+               nouveau_object_dec(object->engine, false);
+               mutex_unlock(&nv_subdev(object->engine)->mutex);
+       }
+
+       if (object->parent)
+               nouveau_object_dec(object->parent, false);
+
+       nv_debug(object, "stopped\n");
+       return 0;
+}
+
+static int
+nouveau_object_decs(struct nouveau_object *object)
+{
+       int ret, rret;
+
+       nv_trace(object, "suspending...\n");
+
+       ret = nv_ofuncs(object)->fini(object, true);
+       if (ret) {
+               nv_error(object, "failed suspend, %d\n", ret);
+               return ret;
+       }
+
+       if (object->engine) {
+               mutex_lock(&nv_subdev(object->engine)->mutex);
+               ret = nouveau_object_dec(object->engine, true);
+               mutex_unlock(&nv_subdev(object->engine)->mutex);
+               if (ret) {
+                       nv_warn(object, "engine failed suspend, %d\n", ret);
+                       goto fail_engine;
+               }
+       }
+
+       if (object->parent) {
+               ret = nouveau_object_dec(object->parent, true);
+               if (ret) {
+                       nv_warn(object, "parent failed suspend, %d\n", ret);
+                       goto fail_parent;
+               }
+       }
+
+       nv_debug(object, "suspended\n");
+       return 0;
+
+fail_parent:
+       if (object->engine) {
+               mutex_lock(&nv_subdev(object->engine)->mutex);
+               rret = nouveau_object_inc(object->engine);
+               mutex_unlock(&nv_subdev(object->engine)->mutex);
+               if (rret)
+                       nv_fatal(object, "engine failed to reinit, %d\n", rret);
+       }
+
+fail_engine:
+       rret = nv_ofuncs(object)->init(object);
+       if (rret)
+               nv_fatal(object, "failed to reinit, %d\n", rret);
+
+       return ret;
+}
+
+int
+nouveau_object_dec(struct nouveau_object *object, bool suspend)
+{
+       int ref = atomic_add_return(-1, &object->usecount);
+       int ret;
+
+       nv_trace(object, "use(-1) == %d\n", atomic_read(&object->usecount));
+
+       if (ref == 0) {
+               if (suspend)
+                       ret = nouveau_object_decs(object);
+               else
+                       ret = nouveau_object_decf(object);
+
+               if (ret) {
+                       atomic_inc(&object->usecount);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+void
+nouveau_object_debug(void)
+{
+#ifdef NOUVEAU_OBJECT_MAGIC
+       struct nouveau_object *object;
+       if (!list_empty(&_objlist)) {
+               nv_fatal(NULL, "*******************************************\n");
+               nv_fatal(NULL, "* AIIIII! object(s) still exist!!!\n");
+               nv_fatal(NULL, "*******************************************\n");
+               list_for_each_entry(object, &_objlist, list) {
+                       nv_fatal(object, "%p/%p/%d/%d\n",
+                                object->parent, object->engine,
+                                atomic_read(&object->refcount),
+                                atomic_read(&object->usecount));
+               }
+       }
+#endif
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/os.h>
+#include <core/debug.h>
+
+/* compares unterminated string 'str' with zero-terminated string 'cmp' */
+static inline int
+strncasecmpz(const char *str, const char *cmp, size_t len)
+{
+       if (strlen(cmp) != len)
+               return len;
+       return strncasecmp(str, cmp, len);
+}
+
+const char *
+nouveau_stropt(const char *optstr, const char *opt, int *arglen)
+{
+       while (optstr && *optstr != '\0') {
+               int len = strcspn(optstr, ",=");
+               switch (optstr[len]) {
+               case '=':
+                       if (!strncasecmpz(optstr, opt, len)) {
+                               optstr += len + 1;
+                               *arglen = strcspn(optstr, ",=");
+                               return *arglen ? optstr : NULL;
+                       }
+                       optstr++;
+                       break;
+               case ',':
+                       optstr++;
+                       break;
+               default:
+                       break;
+               }
+               optstr += len;
+       }
+
+       return NULL;
+}
+
+bool
+nouveau_boolopt(const char *optstr, const char *opt, bool value)
+{
+       int arglen;
+
+       optstr = nouveau_stropt(optstr, opt, &arglen);
+       if (optstr) {
+               if (!strncasecmpz(optstr, "0", arglen) ||
+                   !strncasecmpz(optstr, "no", arglen) ||
+                   !strncasecmpz(optstr, "off", arglen) ||
+                   !strncasecmpz(optstr, "false", arglen))
+                       value = false;
+               else
+               if (!strncasecmpz(optstr, "1", arglen) ||
+                   !strncasecmpz(optstr, "yes", arglen) ||
+                   !strncasecmpz(optstr, "on", arglen) ||
+                   !strncasecmpz(optstr, "true", arglen))
+                       value = true;
+       }
+
+       return value;
+}
+
+int
+nouveau_dbgopt(const char *optstr, const char *sub)
+{
+       int mode = 1, level = CONFIG_NOUVEAU_DEBUG_DEFAULT;
+
+       while (optstr) {
+               int len = strcspn(optstr, ",=");
+               switch (optstr[len]) {
+               case '=':
+                       if (strncasecmpz(optstr, sub, len))
+                               mode = 0;
+                       optstr++;
+                       break;
+               default:
+                       if (mode) {
+                               if (!strncasecmpz(optstr, "fatal", len))
+                                       level = NV_DBG_FATAL;
+                               else if (!strncasecmpz(optstr, "error", len))
+                                       level = NV_DBG_ERROR;
+                               else if (!strncasecmpz(optstr, "warn", len))
+                                       level = NV_DBG_WARN;
+                               else if (!strncasecmpz(optstr, "info", len))
+                                       level = NV_DBG_INFO;
+                               else if (!strncasecmpz(optstr, "debug", len))
+                                       level = NV_DBG_DEBUG;
+                               else if (!strncasecmpz(optstr, "trace", len))
+                                       level = NV_DBG_TRACE;
+                               else if (!strncasecmpz(optstr, "paranoia", len))
+                                       level = NV_DBG_PARANOIA;
+                               else if (!strncasecmpz(optstr, "spam", len))
+                                       level = NV_DBG_SPAM;
+                       }
+
+                       if (optstr[len] != '\0') {
+                               optstr++;
+                               mode = 1;
+                               break;
+                       }
+
+                       return level;
+               }
+               optstr += len;
+       }
+
+       return level;
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/object.h>
+#include <core/parent.h>
+
+int
+nouveau_parent_sclass(struct nouveau_object *parent, u16 handle,
+                     struct nouveau_object **pengine,
+                     struct nouveau_oclass **poclass)
+{
+       struct nouveau_sclass *sclass;
+       struct nouveau_engine *engine;
+       struct nouveau_oclass *oclass;
+       u64 mask;
+
+       sclass = nv_parent(parent)->sclass;
+       while (sclass) {
+               if ((sclass->oclass->handle & 0xffff) == handle) {
+                       *pengine = parent->engine;
+                       *poclass = sclass->oclass;
+                       return 0;
+               }
+
+               sclass = sclass->sclass;
+       }
+
+       mask = nv_parent(parent)->engine;
+       while (mask) {
+               int i = ffsll(mask) - 1;
+
+               if ((engine = nouveau_engine(parent, i))) {
+                       oclass = engine->sclass;
+                       while (oclass->ofuncs) {
+                               if ((oclass->handle & 0xffff) == handle) {
+                                       *pengine = nv_object(engine);
+                                       *poclass = oclass;
+                                       return 0;
+                               }
+                               oclass++;
+                       }
+               }
+
+               mask &= ~(1ULL << i);
+       }
+
+       return -EINVAL;
+}
+
+int
+nouveau_parent_create_(struct nouveau_object *parent,
+                      struct nouveau_object *engine,
+                      struct nouveau_oclass *oclass, u32 pclass,
+                      struct nouveau_oclass *sclass, u64 engcls,
+                      int size, void **pobject)
+{
+       struct nouveau_parent *object;
+       struct nouveau_sclass *nclass;
+       int ret;
+
+       ret = nouveau_object_create_(parent, engine, oclass, pclass |
+                                    NV_PARENT_CLASS, size, pobject);
+       object = *pobject;
+       if (ret)
+               return ret;
+
+       while (sclass && sclass->ofuncs) {
+               nclass = kzalloc(sizeof(*nclass), GFP_KERNEL);
+               if (!nclass)
+                       return -ENOMEM;
+
+               nclass->sclass = object->sclass;
+               object->sclass = nclass;
+               nclass->engine = engine ? nv_engine(engine) : NULL;
+               nclass->oclass = sclass;
+               sclass++;
+       }
+
+       object->engine = engcls;
+       return 0;
+}
+
+int
+_nouveau_parent_ctor(struct nouveau_object *parent,
+                    struct nouveau_object *engine,
+                    struct nouveau_oclass *oclass, void *data, u32 size,
+                    struct nouveau_object **pobject)
+{
+       struct nouveau_parent *object;
+       int ret;
+
+       ret = nouveau_parent_create(parent, engine, oclass, 0, NULL, 0, &object);
+       *pobject = nv_object(object);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+void
+nouveau_parent_destroy(struct nouveau_parent *parent)
+{
+       struct nouveau_sclass *sclass;
+
+       while ((sclass = parent->sclass)) {
+               parent->sclass = sclass->sclass;
+               kfree(sclass);
+       }
+
+       nouveau_object_destroy(&parent->base);
+}
+
+
+void
+_nouveau_parent_dtor(struct nouveau_object *object)
+{
+       nouveau_parent_destroy(nv_parent(object));
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/object.h>
+#include <core/client.h>
+#include <core/subdev.h>
+#include <core/printk.h>
+
+void
+nv_printk_(struct nouveau_object *object, const char *pfx, int level,
+          const char *fmt, ...)
+{
+       static const char name[] = { '!', 'E', 'W', ' ', 'D', 'T', 'P', 'S' };
+       char mfmt[256];
+       va_list args;
+
+       if (object && !nv_iclass(object, NV_CLIENT_CLASS)) {
+               struct nouveau_object *device = object;
+               struct nouveau_object *subdev = object;
+               char obuf[64], *ofmt = "";
+
+               if (object->engine) {
+                       snprintf(obuf, sizeof(obuf), "[0x%08x][%p]",
+                                nv_hclass(object), object);
+                       ofmt = obuf;
+                       subdev = object->engine;
+                       device = object->engine;
+               }
+
+               if (subdev->parent)
+                       device = subdev->parent;
+
+               if (level > nv_subdev(subdev)->debug)
+                       return;
+
+               snprintf(mfmt, sizeof(mfmt), "%snouveau %c[%8s][%s]%s %s", pfx,
+                        name[level], nv_subdev(subdev)->name,
+                        nv_device(device)->name, ofmt, fmt);
+       } else
+       if (object && nv_iclass(object, NV_CLIENT_CLASS)) {
+               if (level > nv_client(object)->debug)
+                       return;
+
+               snprintf(mfmt, sizeof(mfmt), "%snouveau %c[%8d] %s", pfx,
+                        name[level], nv_client(object)->handle, fmt);
+       } else {
+               snprintf(mfmt, sizeof(mfmt), "%snouveau: %s", pfx, fmt);
+       }
+
+       va_start(args, fmt);
+       vprintk(mfmt, args);
+       va_end(args);
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/object.h>
+#include <core/subdev.h>
+#include <core/device.h>
+#include <core/option.h>
+
+void
+nouveau_subdev_reset(struct nouveau_object *subdev)
+{
+       nv_trace(subdev, "resetting...\n");
+       nv_ofuncs(subdev)->fini(subdev, false);
+       nv_debug(subdev, "reset\n");
+}
+
+int
+nouveau_subdev_init(struct nouveau_subdev *subdev)
+{
+       int ret = nouveau_object_init(&subdev->base);
+       if (ret)
+               return ret;
+
+       nouveau_subdev_reset(&subdev->base);
+       return 0;
+}
+
+int
+_nouveau_subdev_init(struct nouveau_object *object)
+{
+       return nouveau_subdev_init(nv_subdev(object));
+}
+
+int
+nouveau_subdev_fini(struct nouveau_subdev *subdev, bool suspend)
+{
+       if (subdev->unit) {
+               nv_mask(subdev, 0x000200, subdev->unit, 0x00000000);
+               nv_mask(subdev, 0x000200, subdev->unit, subdev->unit);
+       }
+
+       return nouveau_object_fini(&subdev->base, suspend);
+}
+
+int
+_nouveau_subdev_fini(struct nouveau_object *object, bool suspend)
+{
+       return nouveau_subdev_fini(nv_subdev(object), suspend);
+}
+
+void
+nouveau_subdev_destroy(struct nouveau_subdev *subdev)
+{
+       int subidx = nv_hclass(subdev) & 0xff;
+       nv_device(subdev)->subdev[subidx] = NULL;
+       nouveau_object_destroy(&subdev->base);
+}
+
+void
+_nouveau_subdev_dtor(struct nouveau_object *object)
+{
+       nouveau_subdev_destroy(nv_subdev(object));
+}
+
+int
+nouveau_subdev_create_(struct nouveau_object *parent,
+                      struct nouveau_object *engine,
+                      struct nouveau_oclass *oclass, u32 pclass,
+                      const char *subname, const char *sysname,
+                      int size, void **pobject)
+{
+       struct nouveau_subdev *subdev;
+       int ret;
+
+       ret = nouveau_object_create_(parent, engine, oclass, pclass |
+                                    NV_SUBDEV_CLASS, size, pobject);
+       subdev = *pobject;
+       if (ret)
+               return ret;
+
+       mutex_init(&subdev->mutex);
+       subdev->name = subname;
+
+       if (parent) {
+               struct nouveau_device *device = nv_device(parent);
+               int subidx = nv_hclass(subdev) & 0xff;
+
+               subdev->debug = nouveau_dbgopt(device->dbgopt, subname);
+               subdev->mmio  = nv_subdev(device)->mmio;
+               device->subdev[subidx] = *pobject;
+       }
+
+       return 0;
+}
 
--- /dev/null
+#ifndef __NOUVEAU_CLASS_H__
+#define __NOUVEAU_CLASS_H__
+
+/* 0080: NV_DEVICE
+ */
+
+#define NV_DEVICE_DISABLE_IDENTIFY                        0x0000000000000001ULL
+#define NV_DEVICE_DISABLE_MMIO                            0x0000000000000002ULL
+#define NV_DEVICE_DISABLE_VBIOS                           0x0000000000000004ULL
+#define NV_DEVICE_DISABLE_CORE                            0x0000000000000008ULL
+#define NV_DEVICE_DISABLE_DISP                            0x0000000000010000ULL
+#define NV_DEVICE_DISABLE_FIFO                            0x0000000000020000ULL
+#define NV_DEVICE_DISABLE_GRAPH                           0x0000000100000000ULL
+#define NV_DEVICE_DISABLE_MPEG                            0x0000000200000000ULL
+#define NV_DEVICE_DISABLE_ME                              0x0000000400000000ULL
+#define NV_DEVICE_DISABLE_VP                              0x0000000800000000ULL
+#define NV_DEVICE_DISABLE_CRYPT                           0x0000001000000000ULL
+#define NV_DEVICE_DISABLE_BSP                             0x0000002000000000ULL
+#define NV_DEVICE_DISABLE_PPP                             0x0000004000000000ULL
+#define NV_DEVICE_DISABLE_COPY0                           0x0000008000000000ULL
+#define NV_DEVICE_DISABLE_COPY1                           0x0000010000000000ULL
+#define NV_DEVICE_DISABLE_UNK1C1                          0x0000020000000000ULL
+
+struct nv_device_class {
+       u64 device;     /* device identifier, ~0 for client default */
+       u64 disable;    /* disable particular subsystems */
+       u64 debug0;     /* as above, but *internal* ids, and *NOT* ABI */
+};
+
+/* 0002: NV_DMA_FROM_MEMORY
+ * 0003: NV_DMA_TO_MEMORY
+ * 003d: NV_DMA_IN_MEMORY
+ */
+
+#define NV_DMA_TARGET_MASK                                           0x000000ff
+#define NV_DMA_TARGET_VM                                             0x00000000
+#define NV_DMA_TARGET_VRAM                                           0x00000001
+#define NV_DMA_TARGET_PCI                                            0x00000002
+#define NV_DMA_TARGET_PCI_US                                         0x00000003
+#define NV_DMA_TARGET_AGP                                            0x00000004
+#define NV_DMA_ACCESS_MASK                                           0x00000f00
+#define NV_DMA_ACCESS_VM                                             0x00000000
+#define NV_DMA_ACCESS_RD                                             0x00000100
+#define NV_DMA_ACCESS_WR                                             0x00000200
+#define NV_DMA_ACCESS_RDWR                                           0x00000300
+
+struct nv_dma_class {
+       u32 flags;
+       u32 pad0;
+       u64 start;
+       u64 limit;
+};
+
+/* 006b: NV03_CHANNEL_DMA
+ * 006e: NV10_CHANNEL_DMA
+ * 406e: NV40_CHANNEL_DMA
+ */
+
+struct nv_channel_dma_class {
+       u32 pushbuf;
+       u32 pad0;
+       u64 offset;
+};
+
+/* 506f: NV50_CHANNEL_IND
+ * 826f: NV84_CHANNEL_IND
+ * 906f: NVC0_CHANNEL_IND
+ * a06f: NVE0_CHANNEL_IND
+ */
+
+struct nv_channel_ind_class {
+       u32 pushbuf;
+       u32 ilength;
+       u64 ioffset;
+};
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_CLIENT_H__
+#define __NOUVEAU_CLIENT_H__
+
+#include <core/namedb.h>
+
+struct nouveau_client {
+       struct nouveau_namedb base;
+       struct nouveau_handle *root;
+       struct nouveau_object *device;
+       u32 handle;
+       u32 debug;
+       struct nouveau_vm *vm;
+};
+
+static inline struct nouveau_client *
+nv_client(void *obj)
+{
+#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA
+       if (unlikely(!nv_iclass(obj, NV_CLIENT_CLASS)))
+               nv_assert("BAD CAST -> NvClient, %08x", nv_hclass(obj));
+#endif
+       return obj;
+}
+
+static inline struct nouveau_client *
+nouveau_client(void *obj)
+{
+       struct nouveau_object *client = nv_object(obj);
+       while (client && !(nv_iclass(client, NV_CLIENT_CLASS)))
+               client = client->parent;
+       return (void *)client;
+}
+
+#define nouveau_client_create(n,c,oc,od,d)                                     \
+       nouveau_client_create_((n), (c), (oc), (od), sizeof(**d), (void **)d)
+
+int  nouveau_client_create_(u32 name, u64 device, const char *cfg,
+                           const char *dbg, int, void **);
+int  nouveau_client_init(struct nouveau_client *);
+int  nouveau_client_fini(struct nouveau_client *, bool suspend);
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_DEBUG_H__
+#define __NOUVEAU_DEBUG_H__
+
+#define NV_DBG_FATAL    0
+#define NV_DBG_ERROR    1
+#define NV_DBG_WARN     2
+#define NV_DBG_INFO     3
+#define NV_DBG_DEBUG    4
+#define NV_DBG_TRACE    5
+#define NV_DBG_PARANOIA 6
+#define NV_DBG_SPAM     7
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_DEVICE_H__
+#define __NOUVEAU_DEVICE_H__
+
+#include <core/object.h>
+#include <core/subdev.h>
+#include <core/engine.h>
+
+enum nv_subdev_type {
+       NVDEV_SUBDEV_DEVICE,
+       NVDEV_SUBDEV_VBIOS,
+       NVDEV_SUBDEV_GPIO,
+       NVDEV_SUBDEV_I2C,
+       NVDEV_SUBDEV_CLOCK,
+       NVDEV_SUBDEV_MXM,
+       NVDEV_SUBDEV_DEVINIT,
+       NVDEV_SUBDEV_MC,
+       NVDEV_SUBDEV_TIMER,
+       NVDEV_SUBDEV_FB,
+       NVDEV_SUBDEV_LTCG,
+       NVDEV_SUBDEV_INSTMEM,
+       NVDEV_SUBDEV_VM,
+       NVDEV_SUBDEV_BAR,
+       NVDEV_SUBDEV_VOLT,
+       NVDEV_SUBDEV_FAN0,
+       NVDEV_SUBDEV_THERM,
+       NVDEV_ENGINE_DMAOBJ,
+       NVDEV_ENGINE_FIFO,
+       NVDEV_ENGINE_SW,
+       NVDEV_ENGINE_GR,
+       NVDEV_ENGINE_MPEG,
+       NVDEV_ENGINE_ME,
+       NVDEV_ENGINE_VP,
+       NVDEV_ENGINE_CRYPT,
+       NVDEV_ENGINE_BSP,
+       NVDEV_ENGINE_PPP,
+       NVDEV_ENGINE_COPY0,
+       NVDEV_ENGINE_COPY1,
+       NVDEV_ENGINE_UNK1C1,
+       NVDEV_ENGINE_FENCE,
+       NVDEV_ENGINE_DISP,
+       NVDEV_SUBDEV_NR,
+};
+
+struct nouveau_device {
+       struct nouveau_subdev base;
+       struct list_head head;
+
+       struct pci_dev *pdev;
+       u64 handle;
+
+       const char *cfgopt;
+       const char *dbgopt;
+       const char *name;
+       const char *cname;
+
+       enum {
+               NV_04    = 0x04,
+               NV_10    = 0x10,
+               NV_20    = 0x20,
+               NV_30    = 0x30,
+               NV_40    = 0x40,
+               NV_50    = 0x50,
+               NV_C0    = 0xc0,
+               NV_D0    = 0xd0,
+               NV_E0    = 0xe0,
+       } card_type;
+       u32 chipset;
+       u32 crystal;
+
+       struct nouveau_oclass *oclass[NVDEV_SUBDEV_NR];
+       struct nouveau_object *subdev[NVDEV_SUBDEV_NR];
+};
+
+static inline struct nouveau_device *
+nv_device(void *obj)
+{
+       struct nouveau_object *object = nv_object(obj);
+       struct nouveau_object *device = object;
+
+       if (device->engine)
+               device = device->engine;
+       if (device->parent)
+               device = device->parent;
+
+#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA
+       if (unlikely(!nv_iclass(device, NV_SUBDEV_CLASS) ||
+                    (nv_hclass(device) & 0xff) != NVDEV_SUBDEV_DEVICE)) {
+               nv_assert("BAD CAST -> NvDevice, 0x%08x 0x%08x",
+                         nv_hclass(object), nv_hclass(device));
+       }
+#endif
+
+       return (void *)device;
+}
+
+static inline struct nouveau_subdev *
+nouveau_subdev(void *obj, int sub)
+{
+       if (nv_device(obj)->subdev[sub])
+               return nv_subdev(nv_device(obj)->subdev[sub]);
+       return NULL;
+}
+
+static inline struct nouveau_engine *
+nouveau_engine(void *obj, int sub)
+{
+       struct nouveau_subdev *subdev = nouveau_subdev(obj, sub);
+       if (subdev && nv_iclass(subdev, NV_ENGINE_CLASS))
+               return nv_engine(subdev);
+       return NULL;
+}
+
+static inline bool
+nv_device_match(struct nouveau_object *object, u16 dev, u16 ven, u16 sub)
+{
+       struct nouveau_device *device = nv_device(object);
+       return device->pdev->device == dev &&
+              device->pdev->subsystem_vendor == ven &&
+              device->pdev->subsystem_device == sub;
+}
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_ENGCTX_H__
+#define __NOUVEAU_ENGCTX_H__
+
+#include <core/object.h>
+#include <core/gpuobj.h>
+
+#include <subdev/vm.h>
+
+#define NV_ENGCTX_(eng,var) (NV_ENGCTX_CLASS | ((var) << 8) | (eng))
+#define NV_ENGCTX(name,var)  NV_ENGCTX_(NVDEV_ENGINE_##name, (var))
+
+struct nouveau_engctx {
+       struct nouveau_gpuobj base;
+       struct nouveau_vma vma;
+       struct list_head head;
+};
+
+static inline void *
+nv_engctx(void *obj)
+{
+#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA
+       if (unlikely(!nv_iclass(obj, NV_ENGCTX_CLASS)))
+               nv_assert("BAD CAST -> NvEngCtx, %08x", nv_hclass(obj));
+#endif
+       return obj;
+}
+
+#define nouveau_engctx_create(p,e,c,g,s,a,f,d)                                 \
+       nouveau_engctx_create_((p), (e), (c), (g), (s), (a), (f),              \
+                              sizeof(**d), (void **)d)
+
+int  nouveau_engctx_create_(struct nouveau_object *, struct nouveau_object *,
+                           struct nouveau_oclass *, struct nouveau_object *,
+                           u32 size, u32 align, u32 flags,
+                           int length, void **data);
+void nouveau_engctx_destroy(struct nouveau_engctx *);
+int  nouveau_engctx_init(struct nouveau_engctx *);
+int  nouveau_engctx_fini(struct nouveau_engctx *, bool suspend);
+
+void _nouveau_engctx_dtor(struct nouveau_object *);
+int  _nouveau_engctx_init(struct nouveau_object *);
+int  _nouveau_engctx_fini(struct nouveau_object *, bool suspend);
+#define _nouveau_engctx_rd32 _nouveau_gpuobj_rd32
+#define _nouveau_engctx_wr32 _nouveau_gpuobj_wr32
+
+struct nouveau_object *
+nouveau_engctx_lookup(struct nouveau_engine *, u64 addr);
+
+struct nouveau_handle *
+nouveau_engctx_lookup_class(struct nouveau_engine *, u64, u16);
+
+struct nouveau_handle *
+nouveau_engctx_lookup_vinst(struct nouveau_engine *, u64, u64);
+
+struct nouveau_handle *
+nouveau_engctx_lookup_cinst(struct nouveau_engine *, u64, u32);
+
+void
+nouveau_engctx_handle_put(struct nouveau_handle *);
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_ENGINE_H__
+#define __NOUVEAU_ENGINE_H__
+
+#include <core/object.h>
+#include <core/subdev.h>
+
+#define NV_ENGINE_(eng,var) (NV_ENGINE_CLASS | ((var) << 8) | (eng))
+#define NV_ENGINE(name,var)  NV_ENGINE_(NVDEV_ENGINE_##name, (var))
+
+struct nouveau_engine {
+       struct nouveau_subdev base;
+       struct nouveau_oclass *cclass;
+       struct nouveau_oclass *sclass;
+       struct list_head contexts;
+       void (*tile_prog)(struct nouveau_engine *, int region);
+       int  (*tlb_flush)(struct nouveau_engine *);
+};
+
+static inline struct nouveau_engine *
+nv_engine(void *obj)
+{
+#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA
+       if (unlikely(!nv_iclass(obj, NV_ENGINE_CLASS)))
+               nv_assert("BAD CAST -> NvEngine, %08x", nv_hclass(obj));
+#endif
+       return obj;
+}
+
+static inline int
+nv_engidx(struct nouveau_object *object)
+{
+       return nv_subidx(object);
+}
+
+#define nouveau_engine_create(p,e,c,d,i,f,r)                                   \
+       nouveau_engine_create_((p), (e), (c), (d), (i), (f),                   \
+                              sizeof(**r),(void **)r)
+
+#define nouveau_engine_destroy(p)                                              \
+       nouveau_subdev_destroy(&(p)->base)
+#define nouveau_engine_init(p)                                                 \
+       nouveau_subdev_init(&(p)->base)
+#define nouveau_engine_fini(p,s)                                               \
+       nouveau_subdev_fini(&(p)->base, (s))
+
+int nouveau_engine_create_(struct nouveau_object *, struct nouveau_object *,
+                          struct nouveau_oclass *, bool, const char *,
+                          const char *, int, void **);
+
+#define _nouveau_engine_dtor _nouveau_subdev_dtor
+#define _nouveau_engine_init _nouveau_subdev_init
+#define _nouveau_engine_fini _nouveau_subdev_fini
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_ENUM_H__
+#define __NOUVEAU_ENUM_H__
+
+struct nouveau_enum {
+       u32 value;
+       const char *name;
+       void *data;
+};
+
+const struct nouveau_enum *
+nouveau_enum_find(const struct nouveau_enum *, u32 value);
+
+void
+nouveau_enum_print(const struct nouveau_enum *en, u32 value);
+
+struct nouveau_bitfield {
+       u32 mask;
+       const char *name;
+};
+
+void nouveau_bitfield_print(const struct nouveau_bitfield *, u32 value);
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_GPUOBJ_H__
+#define __NOUVEAU_GPUOBJ_H__
+
+#include <core/object.h>
+#ifndef XXX_THIS_IS_A_HACK
+#include <core/device.h>
+#include <core/parent.h>
+#include <core/mm.h>
+#endif
+
+struct nouveau_vma;
+struct nouveau_vm;
+
+#define NVOBJ_FLAG_ZERO_ALLOC 0x00000001
+#define NVOBJ_FLAG_ZERO_FREE  0x00000002
+#define NVOBJ_FLAG_HEAP       0x00000004
+
+struct nouveau_gpuobj {
+       struct nouveau_object base;
+       struct nouveau_object *parent;
+       struct nouveau_mm_node *node;
+       struct nouveau_mm heap;
+
+       u32 flags;
+       u64 addr;
+       u32 size;
+
+       /*XXX*/
+       struct drm_device *dev;
+       u32 engine;
+       u32 class;
+};
+
+#ifndef XXX_THIS_IS_A_HACK
+static inline struct nouveau_gpuobj *
+nv_gpuobj(void *obj)
+{
+#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA
+       if (unlikely(!nv_iclass(obj, NV_GPUOBJ_CLASS)))
+               nv_assert("BAD CAST -> NvGpuObj, %08x", nv_hclass(obj));
+#endif
+       return obj;
+}
+
+#define nouveau_gpuobj_create(p,e,c,v,g,s,a,f,d)                               \
+       nouveau_gpuobj_create_((p), (e), (c), (v), (g), (s), (a), (f),         \
+                              sizeof(**d), (void **)d)
+#define nouveau_gpuobj_init(p) nouveau_object_init(&(p)->base)
+#define nouveau_gpuobj_fini(p,s) nouveau_object_fini(&(p)->base, (s))
+int  nouveau_gpuobj_create_(struct nouveau_object *, struct nouveau_object *,
+                           struct nouveau_oclass *, u32 pclass,
+                           struct nouveau_object *, u32 size, u32 align,
+                           u32 flags, int length, void **);
+void nouveau_gpuobj_destroy(struct nouveau_gpuobj *);
+
+int nouveau_gpuobj_new(struct nouveau_object *, struct nouveau_object *,
+                      u32 size, u32 align, u32 flags,
+                      struct nouveau_gpuobj **);
+int nouveau_gpuobj_dup(struct nouveau_object *, struct nouveau_gpuobj *,
+                      struct nouveau_gpuobj **);
+
+int nouveau_gpuobj_map(struct nouveau_gpuobj *, u32 acc, struct nouveau_vma *);
+int nouveau_gpuobj_map_vm(struct nouveau_gpuobj *, struct nouveau_vm *,
+                         u32 access, struct nouveau_vma *);
+void nouveau_gpuobj_unmap(struct nouveau_vma *);
+#endif
+
+static inline void
+nouveau_gpuobj_ref(struct nouveau_gpuobj *obj, struct nouveau_gpuobj **ref)
+{
+       nouveau_object_ref(&obj->base, (struct nouveau_object **)ref);
+}
+
+#ifndef XXX_THIS_IS_A_HACK
+void _nouveau_gpuobj_dtor(struct nouveau_object *);
+int  _nouveau_gpuobj_init(struct nouveau_object *);
+int  _nouveau_gpuobj_fini(struct nouveau_object *, bool);
+u32  _nouveau_gpuobj_rd32(struct nouveau_object *, u32);
+void _nouveau_gpuobj_wr32(struct nouveau_object *, u32, u32);
+#endif
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_HANDLE_H__
+#define __NOUVEAU_HANDLE_H__
+
+struct nouveau_handle {
+       struct nouveau_namedb *namedb;
+       struct list_head node;
+
+       struct list_head head;
+       struct list_head tree;
+       u32 name;
+       u32 priv;
+
+       struct nouveau_handle *parent;
+       struct nouveau_object *object;
+};
+
+int  nouveau_handle_create(struct nouveau_object *, u32 parent, u32 handle,
+                          struct nouveau_object *, struct nouveau_handle **);
+void nouveau_handle_destroy(struct nouveau_handle *);
+int  nouveau_handle_init(struct nouveau_handle *);
+int  nouveau_handle_fini(struct nouveau_handle *, bool suspend);
+
+struct nouveau_object *
+nouveau_handle_ref(struct nouveau_object *, u32 name);
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_MATH_H__
+#define __NOUVEAU_MATH_H__
+
+static inline int
+log2i(u64 base)
+{
+       u64 temp = base >> 1;
+       int log2;
+
+       for (log2 = 0; temp; log2++, temp >>= 1) {
+       }
+
+       return (base & (base - 1)) ? log2 + 1: log2;
+}
+
+#endif
 
  * Authors: Ben Skeggs
  */
 
-#ifndef __NOUVEAU_REGION_H__
-#define __NOUVEAU_REGION_H__
+#ifndef __NOUVEAU_MM_H__
+#define __NOUVEAU_MM_H__
 
 struct nouveau_mm_node {
        struct list_head nl_entry;
 
--- /dev/null
+#ifndef __NOUVEAU_NAMEDB_H__
+#define __NOUVEAU_NAMEDB_H__
+
+#include <core/parent.h>
+
+struct nouveau_handle;
+
+struct nouveau_namedb {
+       struct nouveau_parent base;
+       rwlock_t lock;
+       struct list_head list;
+};
+
+static inline struct nouveau_namedb *
+nv_namedb(void *obj)
+{
+#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA
+       if (unlikely(!nv_iclass(obj, NV_NAMEDB_CLASS)))
+               nv_assert("BAD CAST -> NvNameDB, %08x", nv_hclass(obj));
+#endif
+       return obj;
+}
+
+#define nouveau_namedb_create(p,e,c,v,s,m,d)                                   \
+       nouveau_namedb_create_((p), (e), (c), (v), (s), (m),                   \
+                              sizeof(**d), (void **)d)
+#define nouveau_namedb_init(p)                                                 \
+       nouveau_parent_init(&(p)->base)
+#define nouveau_namedb_fini(p,s)                                               \
+       nouveau_parent_fini(&(p)->base, (s))
+#define nouveau_namedb_destroy(p)                                              \
+       nouveau_parent_destroy(&(p)->base)
+
+int  nouveau_namedb_create_(struct nouveau_object *, struct nouveau_object *,
+                           struct nouveau_oclass *, u32 pclass,
+                           struct nouveau_oclass *, u32 engcls,
+                           int size, void **);
+
+int  _nouveau_namedb_ctor(struct nouveau_object *, struct nouveau_object *,
+                         struct nouveau_oclass *, void *, u32,
+                         struct nouveau_object **);
+#define _nouveau_namedb_dtor _nouveau_parent_dtor
+#define _nouveau_namedb_init _nouveau_parent_init
+#define _nouveau_namedb_fini _nouveau_parent_fini
+
+int  nouveau_namedb_insert(struct nouveau_namedb *, u32 name,
+                          struct nouveau_object *, struct nouveau_handle *);
+void nouveau_namedb_remove(struct nouveau_handle *);
+
+struct nouveau_handle *nouveau_namedb_get(struct nouveau_namedb *, u32);
+struct nouveau_handle *nouveau_namedb_get_class(struct nouveau_namedb *, u16);
+struct nouveau_handle *nouveau_namedb_get_vinst(struct nouveau_namedb *, u64);
+struct nouveau_handle *nouveau_namedb_get_cinst(struct nouveau_namedb *, u32);
+void nouveau_namedb_put(struct nouveau_handle *);
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_OBJECT_H__
+#define __NOUVEAU_OBJECT_H__
+
+#include <core/os.h>
+#include <core/printk.h>
+
+#define NV_PARENT_CLASS 0x80000000
+#define NV_NAMEDB_CLASS 0x40000000
+#define NV_CLIENT_CLASS 0x20000000
+#define NV_SUBDEV_CLASS 0x10000000
+#define NV_ENGINE_CLASS 0x08000000
+#define NV_MEMOBJ_CLASS 0x04000000
+#define NV_GPUOBJ_CLASS 0x02000000
+#define NV_ENGCTX_CLASS 0x01000000
+#define NV_OBJECT_CLASS 0x0000ffff
+
+struct nouveau_object {
+       struct nouveau_oclass *oclass;
+       struct nouveau_object *parent;
+       struct nouveau_object *engine;
+       atomic_t refcount;
+       atomic_t usecount;
+#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA
+#define NOUVEAU_OBJECT_MAGIC 0x75ef0bad
+       struct list_head list;
+       u32 _magic;
+#endif
+};
+
+static inline struct nouveau_object *
+nv_object(void *obj)
+{
+#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA
+       if (likely(obj)) {
+               struct nouveau_object *object = obj;
+               if (unlikely(object->_magic != NOUVEAU_OBJECT_MAGIC))
+                       nv_assert("BAD CAST -> NvObject, invalid magic");
+       }
+#endif
+       return obj;
+}
+
+#define nouveau_object_create(p,e,c,s,d)                                       \
+       nouveau_object_create_((p), (e), (c), (s), sizeof(**d), (void **)d)
+int  nouveau_object_create_(struct nouveau_object *, struct nouveau_object *,
+                           struct nouveau_oclass *, u32, int size, void **);
+void nouveau_object_destroy(struct nouveau_object *);
+int  nouveau_object_init(struct nouveau_object *);
+int  nouveau_object_fini(struct nouveau_object *, bool suspend);
+
+extern struct nouveau_ofuncs nouveau_object_ofuncs;
+
+struct nouveau_oclass {
+       u32 handle;
+       struct nouveau_ofuncs *ofuncs;
+       struct nouveau_omthds *omthds;
+};
+
+#define nv_oclass(o)    nv_object(o)->oclass
+#define nv_hclass(o)    nv_oclass(o)->handle
+#define nv_iclass(o,i) (nv_hclass(o) & (i))
+#define nv_mclass(o)    nv_iclass(o, NV_OBJECT_CLASS)
+
+static inline struct nouveau_object *
+nv_pclass(struct nouveau_object *parent, u32 oclass)
+{
+       while (parent && !nv_iclass(parent, oclass))
+               parent = parent->parent;
+       return parent;
+}
+
+struct nouveau_omthds {
+       u32 method;
+       int (*call)(struct nouveau_object *, u32, void *, u32);
+};
+
+struct nouveau_ofuncs {
+       int  (*ctor)(struct nouveau_object *, struct nouveau_object *,
+                    struct nouveau_oclass *, void *data, u32 size,
+                    struct nouveau_object **);
+       void (*dtor)(struct nouveau_object *);
+       int  (*init)(struct nouveau_object *);
+       int  (*fini)(struct nouveau_object *, bool suspend);
+       u8   (*rd08)(struct nouveau_object *, u32 offset);
+       u16  (*rd16)(struct nouveau_object *, u32 offset);
+       u32  (*rd32)(struct nouveau_object *, u32 offset);
+       void (*wr08)(struct nouveau_object *, u32 offset, u8 data);
+       void (*wr16)(struct nouveau_object *, u32 offset, u16 data);
+       void (*wr32)(struct nouveau_object *, u32 offset, u32 data);
+};
+
+static inline struct nouveau_ofuncs *
+nv_ofuncs(void *obj)
+{
+       return nv_oclass(obj)->ofuncs;
+}
+
+int  nouveau_object_ctor(struct nouveau_object *, struct nouveau_object *,
+                        struct nouveau_oclass *, void *, u32,
+                        struct nouveau_object **);
+void nouveau_object_ref(struct nouveau_object *, struct nouveau_object **);
+int nouveau_object_inc(struct nouveau_object *);
+int nouveau_object_dec(struct nouveau_object *, bool suspend);
+
+int nouveau_object_new(struct nouveau_object *, u32 parent, u32 handle,
+                      u16 oclass, void *data, u32 size,
+                      struct nouveau_object **);
+int nouveau_object_del(struct nouveau_object *, u32 parent, u32 handle);
+void nouveau_object_debug(void);
+
+static inline int
+nv_call(void *obj, u32 mthd, u32 data)
+{
+       struct nouveau_omthds *method = nv_oclass(obj)->omthds;
+
+       while (method && method->call) {
+               if (method->method == mthd)
+                       return method->call(obj, mthd, &data, sizeof(data));
+               method++;
+       }
+
+       return -EINVAL;
+}
+
+static inline u8
+nv_ro08(void *obj, u32 addr)
+{
+       u8 data = nv_ofuncs(obj)->rd08(obj, addr);
+       nv_spam(obj, "nv_ro08 0x%08x 0x%02x\n", addr, data);
+       return data;
+}
+
+static inline u16
+nv_ro16(void *obj, u32 addr)
+{
+       u16 data = nv_ofuncs(obj)->rd16(obj, addr);
+       nv_spam(obj, "nv_ro16 0x%08x 0x%04x\n", addr, data);
+       return data;
+}
+
+static inline u32
+nv_ro32(void *obj, u32 addr)
+{
+       u32 data = nv_ofuncs(obj)->rd32(obj, addr);
+       nv_spam(obj, "nv_ro32 0x%08x 0x%08x\n", addr, data);
+       return data;
+}
+
+static inline void
+nv_wo08(void *obj, u32 addr, u8 data)
+{
+       nv_spam(obj, "nv_wo08 0x%08x 0x%02x\n", addr, data);
+       nv_ofuncs(obj)->wr08(obj, addr, data);
+}
+
+static inline void
+nv_wo16(void *obj, u32 addr, u16 data)
+{
+       nv_spam(obj, "nv_wo16 0x%08x 0x%04x\n", addr, data);
+       nv_ofuncs(obj)->wr16(obj, addr, data);
+}
+
+static inline void
+nv_wo32(void *obj, u32 addr, u32 data)
+{
+       nv_spam(obj, "nv_wo32 0x%08x 0x%08x\n", addr, data);
+       nv_ofuncs(obj)->wr32(obj, addr, data);
+}
+
+static inline u32
+nv_mo32(void *obj, u32 addr, u32 mask, u32 data)
+{
+       u32 temp = nv_ro32(obj, addr);
+       nv_wo32(obj, addr, (temp & ~mask) | data);
+       return temp;
+}
+
+static inline bool
+nv_strncmp(void *obj, u32 addr, u32 len, const char *str)
+{
+       while (len--) {
+               if (nv_ro08(obj, addr++) != *(str++))
+                       return false;
+       }
+       return true;
+}
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_OPTION_H__
+#define __NOUVEAU_OPTION_H__
+
+#include <core/os.h>
+
+const char *nouveau_stropt(const char *optstr, const char *opt, int *len);
+bool nouveau_boolopt(const char *optstr, const char *opt, bool value);
+
+int nouveau_dbgopt(const char *optstr, const char *sub);
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_PARENT_H__
+#define __NOUVEAU_PARENT_H__
+
+#include <core/device.h>
+#include <core/object.h>
+
+struct nouveau_sclass {
+       struct nouveau_sclass *sclass;
+       struct nouveau_engine *engine;
+       struct nouveau_oclass *oclass;
+};
+
+struct nouveau_parent {
+       struct nouveau_object base;
+
+       struct nouveau_sclass *sclass;
+       u32 engine;
+
+       int  (*context_attach)(struct nouveau_object *,
+                              struct nouveau_object *);
+       int  (*context_detach)(struct nouveau_object *, bool suspend,
+                              struct nouveau_object *);
+
+       int  (*object_attach)(struct nouveau_object *parent,
+                             struct nouveau_object *object, u32 name);
+       void (*object_detach)(struct nouveau_object *parent, int cookie);
+};
+
+static inline struct nouveau_parent *
+nv_parent(void *obj)
+{
+#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA
+       if (unlikely(!(nv_iclass(obj, NV_PARENT_CLASS))))
+               nv_assert("BAD CAST -> NvParent, %08x", nv_hclass(obj));
+#endif
+       return obj;
+}
+
+#define nouveau_parent_create(p,e,c,v,s,m,d)                                   \
+       nouveau_parent_create_((p), (e), (c), (v), (s), (m),                   \
+                              sizeof(**d), (void **)d)
+#define nouveau_parent_init(p)                                                 \
+       nouveau_object_init(&(p)->base)
+#define nouveau_parent_fini(p,s)                                               \
+       nouveau_object_fini(&(p)->base, (s))
+
+int  nouveau_parent_create_(struct nouveau_object *, struct nouveau_object *,
+                           struct nouveau_oclass *, u32 pclass,
+                           struct nouveau_oclass *, u64 engcls,
+                           int size, void **);
+void nouveau_parent_destroy(struct nouveau_parent *);
+
+int  _nouveau_parent_ctor(struct nouveau_object *, struct nouveau_object *,
+                         struct nouveau_oclass *, void *, u32,
+                         struct nouveau_object **);
+void _nouveau_parent_dtor(struct nouveau_object *);
+#define _nouveau_parent_init _nouveau_object_init
+#define _nouveau_parent_fini _nouveau_object_fini
+
+int nouveau_parent_sclass(struct nouveau_object *, u16 handle,
+                         struct nouveau_object **pengine,
+                         struct nouveau_oclass **poclass);
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_PRINTK_H__
+#define __NOUVEAU_PRINTK_H__
+
+#include <core/os.h>
+#include <core/debug.h>
+
+struct nouveau_object;
+
+#define NV_PRINTK_FATAL    KERN_CRIT
+#define NV_PRINTK_ERROR    KERN_ERR
+#define NV_PRINTK_WARN     KERN_WARNING
+#define NV_PRINTK_INFO     KERN_INFO
+#define NV_PRINTK_DEBUG    KERN_DEBUG
+#define NV_PRINTK_PARANOIA KERN_DEBUG
+#define NV_PRINTK_TRACE    KERN_DEBUG
+#define NV_PRINTK_SPAM     KERN_DEBUG
+
+void nv_printk_(struct nouveau_object *, const char *, int, const char *, ...);
+
+#define nv_printk(o,l,f,a...) do {                                             \
+       if (NV_DBG_##l <= CONFIG_NOUVEAU_DEBUG)                                \
+               nv_printk_(nv_object(o), NV_PRINTK_##l, NV_DBG_##l, f, ##a);   \
+} while(0)
+
+#define nv_fatal(o,f,a...) nv_printk((o), FATAL, f, ##a)
+#define nv_error(o,f,a...) nv_printk((o), ERROR, f, ##a)
+#define nv_warn(o,f,a...) nv_printk((o), WARN, f, ##a)
+#define nv_info(o,f,a...) nv_printk((o), INFO, f, ##a)
+#define nv_debug(o,f,a...) nv_printk((o), DEBUG, f, ##a)
+#define nv_trace(o,f,a...) nv_printk((o), TRACE, f, ##a)
+#define nv_spam(o,f,a...) nv_printk((o), SPAM, f, ##a)
+
+#define nv_assert(f,a...) do {                                                 \
+       if (NV_DBG_FATAL <= CONFIG_NOUVEAU_DEBUG)                              \
+               nv_printk_(NULL, NV_PRINTK_FATAL, NV_DBG_FATAL, f "\n", ##a);  \
+       BUG_ON(1);                                                             \
+} while(0)
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_SUBDEV_H__
+#define __NOUVEAU_SUBDEV_H__
+
+#include <core/object.h>
+
+#define NV_SUBDEV_(sub,var) (NV_SUBDEV_CLASS | ((var) << 8) | (sub))
+#define NV_SUBDEV(name,var)  NV_SUBDEV_(NVDEV_SUBDEV_##name, (var))
+
+struct nouveau_subdev {
+       struct nouveau_object base;
+       struct mutex mutex;
+       const char *name;
+       void __iomem *mmio;
+       u32 debug;
+       u32 unit;
+
+       void (*intr)(struct nouveau_subdev *);
+};
+
+static inline struct nouveau_subdev *
+nv_subdev(void *obj)
+{
+#if CONFIG_NOUVEAU_DEBUG >= NV_DBG_PARANOIA
+       if (unlikely(!nv_iclass(obj, NV_SUBDEV_CLASS)))
+               nv_assert("BAD CAST -> NvSubDev, %08x", nv_hclass(obj));
+#endif
+       return obj;
+}
+
+static inline int
+nv_subidx(struct nouveau_object *object)
+{
+       return nv_hclass(nv_subdev(object)) & 0xff;
+}
+
+#define nouveau_subdev_create(p,e,o,v,s,f,d)                                   \
+       nouveau_subdev_create_((p), (e), (o), (v), (s), (f),                   \
+                              sizeof(**d),(void **)d)
+
+int  nouveau_subdev_create_(struct nouveau_object *, struct nouveau_object *,
+                           struct nouveau_oclass *, u32 pclass,
+                           const char *sname, const char *fname,
+                           int size, void **);
+void nouveau_subdev_destroy(struct nouveau_subdev *);
+int  nouveau_subdev_init(struct nouveau_subdev *);
+int  nouveau_subdev_fini(struct nouveau_subdev *, bool suspend);
+void nouveau_subdev_reset(struct nouveau_object *);
+
+void _nouveau_subdev_dtor(struct nouveau_object *);
+int  _nouveau_subdev_init(struct nouveau_object *);
+int  _nouveau_subdev_fini(struct nouveau_object *, bool suspend);
+
+#define s_printk(s,l,f,a...) do {                                              \
+       if ((s)->debug >= OS_DBG_##l) {                                        \
+               nv_printk((s)->base.parent, (s)->name, l, f, ##a);             \
+       }                                                                      \
+} while(0)
+
+static inline u8
+nv_rd08(void *obj, u32 addr)
+{
+       struct nouveau_subdev *subdev = nv_subdev(obj);
+       u8 data = ioread8(subdev->mmio + addr);
+       nv_spam(subdev, "nv_rd08 0x%06x 0x%02x\n", addr, data);
+       return data;
+}
+
+static inline u16
+nv_rd16(void *obj, u32 addr)
+{
+       struct nouveau_subdev *subdev = nv_subdev(obj);
+       u16 data = ioread16_native(subdev->mmio + addr);
+       nv_spam(subdev, "nv_rd16 0x%06x 0x%04x\n", addr, data);
+       return data;
+}
+
+static inline u32
+nv_rd32(void *obj, u32 addr)
+{
+       struct nouveau_subdev *subdev = nv_subdev(obj);
+       u32 data = ioread32_native(subdev->mmio + addr);
+       nv_spam(subdev, "nv_rd32 0x%06x 0x%08x\n", addr, data);
+       return data;
+}
+
+static inline void
+nv_wr08(void *obj, u32 addr, u8 data)
+{
+       struct nouveau_subdev *subdev = nv_subdev(obj);
+       nv_spam(subdev, "nv_wr08 0x%06x 0x%02x\n", addr, data);
+       iowrite8(data, subdev->mmio + addr);
+}
+
+static inline void
+nv_wr16(void *obj, u32 addr, u16 data)
+{
+       struct nouveau_subdev *subdev = nv_subdev(obj);
+       nv_spam(subdev, "nv_wr16 0x%06x 0x%04x\n", addr, data);
+       iowrite16_native(data, subdev->mmio + addr);
+}
+
+static inline void
+nv_wr32(void *obj, u32 addr, u32 data)
+{
+       struct nouveau_subdev *subdev = nv_subdev(obj);
+       nv_spam(subdev, "nv_wr32 0x%06x 0x%08x\n", addr, data);
+       iowrite32_native(data, subdev->mmio + addr);
+}
+
+static inline u32
+nv_mask(void *obj, u32 addr, u32 mask, u32 data)
+{
+       u32 temp = nv_rd32(obj, addr);
+       nv_wr32(obj, addr, (temp & ~mask) | data);
+       return temp;
+}
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_SUBDEV_DEVICE_H__
+#define __NOUVEAU_SUBDEV_DEVICE_H__
+
+#include <core/device.h>
+
+#define nouveau_device_create(p,n,s,c,d,u)                                     \
+       nouveau_device_create_((p), (n), (s), (c), (d), sizeof(**u), (void **)u)
+
+int  nouveau_device_create_(struct pci_dev *, u64 name, const char *sname,
+                           const char *cfg, const char *dbg, int, void **);
+void nouveau_device_destroy(struct nouveau_device **);
+
+int nv04_identify(struct nouveau_device *);
+int nv10_identify(struct nouveau_device *);
+int nv20_identify(struct nouveau_device *);
+int nv30_identify(struct nouveau_device *);
+int nv40_identify(struct nouveau_device *);
+int nv50_identify(struct nouveau_device *);
+int nvc0_identify(struct nouveau_device *);
+int nve0_identify(struct nouveau_device *);
+
+extern struct nouveau_oclass nouveau_device_sclass[];
+struct nouveau_device *nouveau_device_find(u64 name);
+
+#endif
 
--- /dev/null
+#ifndef __NOUVEAU_OS_H__
+#define __NOUVEAU_OS_H__
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/printk.h>
+#include <linux/bitops.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/delay.h>
+#include <linux/io-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/acpi.h>
+
+#include <asm/unaligned.h>
+
+static inline int
+ffsll(u64 mask)
+{
+       int i;
+       for (i = 0; i < 64; i++) {
+               if (mask & (1ULL << i))
+                       return i + 1;
+       }
+       return 0;
+}
+
+#ifndef ioread32_native
+#ifdef __BIG_ENDIAN
+#define ioread16_native ioread16be
+#define iowrite16_native iowrite16be
+#define ioread32_native  ioread32be
+#define iowrite32_native iowrite32be
+#else /* def __BIG_ENDIAN */
+#define ioread16_native ioread16
+#define iowrite16_native iowrite16
+#define ioread32_native  ioread32
+#define iowrite32_native iowrite32
+#endif /* def __BIG_ENDIAN else */
+#endif /* !ioread32_native */
+
+#endif
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <core/object.h>
+#include <core/device.h>
+#include <core/client.h>
+#include <core/device.h>
+#include <core/option.h>
+
+#include <core/class.h>
+
+#include <subdev/device.h>
+
+static DEFINE_MUTEX(nv_devices_mutex);
+static LIST_HEAD(nv_devices);
+
+struct nouveau_device *
+nouveau_device_find(u64 name)
+{
+       struct nouveau_device *device, *match = NULL;
+       mutex_lock(&nv_devices_mutex);
+       list_for_each_entry(device, &nv_devices, head) {
+               if (device->handle == name) {
+                       match = device;
+                       break;
+               }
+       }
+       mutex_unlock(&nv_devices_mutex);
+       return match;
+}
+
+/******************************************************************************
+ * nouveau_devobj (0x0080): class implementation
+ *****************************************************************************/
+struct nouveau_devobj {
+       struct nouveau_parent base;
+       struct nouveau_object *subdev[NVDEV_SUBDEV_NR];
+       bool created;
+};
+
+static const u64 disable_map[] = {
+       [NVDEV_SUBDEV_VBIOS]    = NV_DEVICE_DISABLE_VBIOS,
+       [NVDEV_SUBDEV_GPIO]     = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_I2C]      = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_DEVINIT]  = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_MC]       = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_TIMER]    = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_FB]       = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_VM]       = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_INSTMEM]  = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_BAR]      = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_VOLT]     = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_FAN0]     = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_CLOCK]    = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_SUBDEV_THERM]    = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_ENGINE_DMAOBJ]   = NV_DEVICE_DISABLE_CORE,
+       [NVDEV_ENGINE_GR]       = NV_DEVICE_DISABLE_GRAPH,
+       [NVDEV_ENGINE_MPEG]     = NV_DEVICE_DISABLE_MPEG,
+       [NVDEV_ENGINE_ME]       = NV_DEVICE_DISABLE_ME,
+       [NVDEV_ENGINE_VP]       = NV_DEVICE_DISABLE_VP,
+       [NVDEV_ENGINE_CRYPT]    = NV_DEVICE_DISABLE_CRYPT,
+       [NVDEV_ENGINE_BSP]      = NV_DEVICE_DISABLE_BSP,
+       [NVDEV_ENGINE_PPP]      = NV_DEVICE_DISABLE_PPP,
+       [NVDEV_ENGINE_COPY0]    = NV_DEVICE_DISABLE_COPY0,
+       [NVDEV_ENGINE_COPY1]    = NV_DEVICE_DISABLE_COPY1,
+       [NVDEV_ENGINE_UNK1C1]   = NV_DEVICE_DISABLE_UNK1C1,
+       [NVDEV_ENGINE_FIFO]     = NV_DEVICE_DISABLE_FIFO,
+       [NVDEV_ENGINE_DISP]     = NV_DEVICE_DISABLE_DISP,
+       [NVDEV_SUBDEV_NR]       = 0,
+};
+
+static int
+nouveau_devobj_ctor(struct nouveau_object *parent,
+                   struct nouveau_object *engine,
+                   struct nouveau_oclass *oclass, void *data, u32 size,
+                   struct nouveau_object **pobject)
+{
+       struct nouveau_client *client = nv_client(parent);
+       struct nouveau_object *subdev = NULL;
+       struct nouveau_device *device;
+       struct nouveau_devobj *devobj;
+       struct nv_device_class *args = data;
+       u64 disable, boot0, strap;
+       u64 mmio_base, mmio_size;
+       void __iomem *map;
+       int ret, i;
+
+       if (size < sizeof(struct nv_device_class))
+               return -EINVAL;
+
+       /* find the device subdev that matches what the client requested */
+       device = nv_device(client->device);
+       if (args->device != ~0) {
+               device = nouveau_device_find(args->device);
+               if (!device)
+                       return -ENODEV;
+       }
+
+       ret = nouveau_parent_create(parent, nv_object(device), oclass, 0, NULL,
+                                   (1ULL << NVDEV_ENGINE_DMAOBJ) |
+                                   (1ULL << NVDEV_ENGINE_FIFO) |
+                                   (1ULL << NVDEV_ENGINE_DISP), &devobj);
+       *pobject = nv_object(devobj);
+       if (ret)
+               return ret;
+
+       mmio_base = pci_resource_start(device->pdev, 0);
+       mmio_size = pci_resource_len(device->pdev, 0);
+
+       /* translate api disable mask into internal mapping */
+       disable = args->debug0;
+       for (i = 0; i < NVDEV_SUBDEV_NR; i++) {
+               if (args->disable & disable_map[i])
+                       disable |= (1ULL << i);
+       }
+
+       /* identify the chipset, and determine classes of subdev/engines */
+       if (!(args->disable & NV_DEVICE_DISABLE_IDENTIFY) &&
+           !device->card_type) {
+               map = ioremap(mmio_base, 0x102000);
+               if (map == NULL) {
+               }
+
+               /* switch mmio to cpu's native endianness */
+#ifndef __BIG_ENDIAN
+               if (ioread32_native(map + 0x000004) != 0x00000000)
+#else
+               if (ioread32_native(map + 0x000004) == 0x00000000)
+#endif
+                       iowrite32_native(0x01000001, map + 0x000004);
+
+               /* read boot0 and strapping information */
+               boot0 = ioread32_native(map + 0x000000);
+               strap = ioread32_native(map + 0x101000);
+               iounmap(map);
+
+               /* determine chipset and derive architecture from it */
+               if ((boot0 & 0x0f000000) > 0) {
+                       device->chipset = (boot0 & 0xff00000) >> 20;
+                       switch (device->chipset & 0xf0) {
+                       case 0x10: device->card_type = NV_10; break;
+                       case 0x20: device->card_type = NV_20; break;
+                       case 0x30: device->card_type = NV_30; break;
+                       case 0x40:
+                       case 0x60: device->card_type = NV_40; break;
+                       case 0x50:
+                       case 0x80:
+                       case 0x90:
+                       case 0xa0: device->card_type = NV_50; break;
+                       case 0xc0: device->card_type = NV_C0; break;
+                       case 0xd0: device->card_type = NV_D0; break;
+                       case 0xe0: device->card_type = NV_E0; break;
+                       default:
+                               break;
+                       }
+               } else
+               if ((boot0 & 0xff00fff0) == 0x20004000) {
+                       if (boot0 & 0x00f00000)
+                               device->chipset = 0x05;
+                       else
+                               device->chipset = 0x04;
+                       device->card_type = NV_04;
+               }
+
+               switch (device->card_type) {
+               case NV_04: ret = nv04_identify(device); break;
+               case NV_10: ret = nv10_identify(device); break;
+               case NV_20: ret = nv20_identify(device); break;
+               case NV_30: ret = nv30_identify(device); break;
+               case NV_40: ret = nv40_identify(device); break;
+               case NV_50: ret = nv50_identify(device); break;
+               case NV_C0:
+               case NV_D0: ret = nvc0_identify(device); break;
+               case NV_E0: ret = nve0_identify(device); break;
+               default:
+                       ret = -EINVAL;
+                       break;
+               }
+
+               if (ret) {
+                       nv_error(device, "unknown chipset, 0x%08x\n", boot0);
+                       return ret;
+               }
+
+               nv_info(device, "BOOT0  : 0x%08x\n", boot0);
+               nv_info(device, "Chipset: NV%02X\n", device->chipset);
+               nv_info(device, "Family : NV%02X\n", device->card_type);
+
+               /* determine frequency of timing crystal */
+               if ( device->chipset < 0x17 ||
+                   (device->chipset >= 0x20 && device->chipset <= 0x25))
+                       strap &= 0x00000040;
+               else
+                       strap &= 0x00400040;
+
+               switch (strap) {
+               case 0x00000000: device->crystal = 13500; break;
+               case 0x00000040: device->crystal = 14318; break;
+               case 0x00400000: device->crystal = 27000; break;
+               case 0x00400040: device->crystal = 25000; break;
+               }
+
+               nv_debug(device, "crystal freq: %dKHz\n", device->crystal);
+       }
+
+       if (!(args->disable & NV_DEVICE_DISABLE_MMIO) &&
+           !nv_subdev(device)->mmio) {
+               nv_subdev(device)->mmio  = ioremap(mmio_base, mmio_size);
+               if (!nv_subdev(device)->mmio) {
+                       nv_error(device, "unable to map device registers\n");
+                       return ret;
+               }
+       }
+
+       /* ensure requested subsystems are available for use */
+       for (i = 0; i < NVDEV_SUBDEV_NR; i++) {
+               if (!(oclass = device->oclass[i]) || (disable & (1ULL << i)))
+                       continue;
+
+               if (!device->subdev[i]) {
+                       ret = nouveau_object_ctor(nv_object(device), NULL,
+                                                 oclass, NULL, i, &subdev);
+                       if (ret)
+                               return ret;
+
+                       if (nv_iclass(subdev, NV_ENGINE_CLASS))
+                               nouveau_subdev_reset(subdev);
+               } else {
+                       nouveau_object_ref(device->subdev[i], &subdev);
+               }
+
+               if (!nv_iclass(subdev, NV_ENGINE_CLASS)) {
+                       ret = nouveau_object_inc(subdev);
+                       if (ret) {
+                               nouveau_object_ref(NULL, &subdev);
+                               return ret;
+                       }
+               }
+
+               nouveau_object_ref(subdev, &devobj->subdev[i]);
+               nouveau_object_ref(NULL, &subdev);
+       }
+
+       return 0;
+}
+
+static void
+nouveau_devobj_dtor(struct nouveau_object *object)
+{
+       struct nouveau_devobj *devobj = (void *)object;
+       int i;
+
+       for (i = NVDEV_SUBDEV_NR - 1; i >= 0; i--)
+               nouveau_object_ref(NULL, &devobj->subdev[i]);
+
+       nouveau_parent_destroy(&devobj->base);
+}
+
+static int
+nouveau_devobj_init(struct nouveau_object *object)
+{
+       struct nouveau_devobj *devobj = (void *)object;
+       struct nouveau_object *subdev;
+       int ret, i;
+
+       ret = nouveau_parent_init(&devobj->base);
+       if (ret)
+               return ret;
+
+       for (i = 0; devobj->created && i < NVDEV_SUBDEV_NR; i++) {
+               if ((subdev = devobj->subdev[i])) {
+                       if (!nv_iclass(subdev, NV_ENGINE_CLASS)) {
+                               ret = nouveau_object_inc(subdev);
+                               if (ret)
+                                       goto fail;
+                       }
+               }
+       }
+
+       devobj->created = true;
+       return 0;
+
+fail:
+       for (--i; i >= 0; i--) {
+               if ((subdev = devobj->subdev[i])) {
+                       if (!nv_iclass(subdev, NV_ENGINE_CLASS))
+                               nouveau_object_dec(subdev, false);
+               }
+       }
+
+       return ret;
+}
+
+static int
+nouveau_devobj_fini(struct nouveau_object *object, bool suspend)
+{
+       struct nouveau_devobj *devobj = (void *)object;
+       struct nouveau_object *subdev;
+       int ret, i;
+
+       for (i = NVDEV_SUBDEV_NR - 1; i >= 0; i--) {
+               if ((subdev = devobj->subdev[i])) {
+                       if (!nv_iclass(subdev, NV_ENGINE_CLASS)) {
+                               ret = nouveau_object_dec(subdev, suspend);
+                               if (ret && suspend)
+                                       goto fail;
+                       }
+               }
+       }
+
+       ret = nouveau_parent_fini(&devobj->base, suspend);
+fail:
+       for (; ret && suspend && i < NVDEV_SUBDEV_NR; i++) {
+               if ((subdev = devobj->subdev[i])) {
+                       if (!nv_iclass(subdev, NV_ENGINE_CLASS)) {
+                               ret = nouveau_object_inc(subdev);
+                               if (ret) {
+                                       /* XXX */
+                               }
+                       }
+               }
+       }
+
+       return ret;
+}
+
+static u8
+nouveau_devobj_rd08(struct nouveau_object *object, u32 addr)
+{
+       return nv_rd08(object->engine, addr);
+}
+
+static u16
+nouveau_devobj_rd16(struct nouveau_object *object, u32 addr)
+{
+       return nv_rd16(object->engine, addr);
+}
+
+static u32
+nouveau_devobj_rd32(struct nouveau_object *object, u32 addr)
+{
+       return nv_rd32(object->engine, addr);
+}
+
+static void
+nouveau_devobj_wr08(struct nouveau_object *object, u32 addr, u8 data)
+{
+       nv_wr08(object->engine, addr, data);
+}
+
+static void
+nouveau_devobj_wr16(struct nouveau_object *object, u32 addr, u16 data)
+{
+       nv_wr16(object->engine, addr, data);
+}
+
+static void
+nouveau_devobj_wr32(struct nouveau_object *object, u32 addr, u32 data)
+{
+       nv_wr32(object->engine, addr, data);
+}
+
+static struct nouveau_ofuncs
+nouveau_devobj_ofuncs = {
+       .ctor = nouveau_devobj_ctor,
+       .dtor = nouveau_devobj_dtor,
+       .init = nouveau_devobj_init,
+       .fini = nouveau_devobj_fini,
+       .rd08 = nouveau_devobj_rd08,
+       .rd16 = nouveau_devobj_rd16,
+       .rd32 = nouveau_devobj_rd32,
+       .wr08 = nouveau_devobj_wr08,
+       .wr16 = nouveau_devobj_wr16,
+       .wr32 = nouveau_devobj_wr32,
+};
+
+/******************************************************************************
+ * nouveau_device: engine functions
+ *****************************************************************************/
+struct nouveau_oclass
+nouveau_device_sclass[] = {
+       { 0x0080, &nouveau_devobj_ofuncs },
+       {}
+};
+
+static struct nouveau_oclass
+nouveau_device_oclass = {
+       .handle = NV_SUBDEV(DEVICE, 0x00),
+       .ofuncs = &(struct nouveau_ofuncs) {
+       },
+};
+
+int
+nouveau_device_create_(struct pci_dev *pdev, u64 name, const char *sname,
+                      const char *cfg, const char *dbg,
+                      int length, void **pobject)
+{
+       struct nouveau_device *device;
+       int ret = -EEXIST;
+
+       mutex_lock(&nv_devices_mutex);
+       list_for_each_entry(device, &nv_devices, head) {
+               if (device->handle == name)
+                       goto done;
+       }
+
+       ret = nouveau_subdev_create_(NULL, NULL, &nouveau_device_oclass, 0,
+                                    "DEVICE", "device", length, pobject);
+       device = *pobject;
+       if (ret)
+               goto done;
+
+       atomic_set(&nv_object(device)->usecount, 2);
+       device->pdev = pdev;
+       device->handle = name;
+       device->cfgopt = cfg;
+       device->dbgopt = dbg;
+       device->name = sname;
+
+       nv_subdev(device)->debug = nouveau_dbgopt(device->dbgopt, "DEVICE");
+       list_add(&device->head, &nv_devices);
+done:
+       mutex_unlock(&nv_devices_mutex);
+       return ret;
+}
+
+void
+nouveau_device_destroy(struct nouveau_device **pdevice)
+{
+       struct nouveau_device *device = *pdevice;
+       if (device) {
+               mutex_lock(&nv_devices_mutex);
+               list_del(&device->head);
+               mutex_unlock(&nv_devices_mutex);
+               if (device->base.mmio)
+                       iounmap(device->base.mmio);
+               nouveau_subdev_destroy(&device->base);
+       }
+       *pdevice = NULL;
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/device.h>
+
+int
+nv04_identify(struct nouveau_device *device)
+{
+       switch (device->chipset) {
+       case 0x04:
+               break;
+       case 0x05:
+               break;
+       default:
+               nv_fatal(device, "unknown RIVA chipset\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/device.h>
+
+int
+nv10_identify(struct nouveau_device *device)
+{
+       switch (device->chipset) {
+       case 0x10:
+               break;
+       case 0x15:
+               break;
+       case 0x16:
+               break;
+       case 0x1a:
+               break;
+       case 0x11:
+               break;
+       case 0x17:
+               break;
+       case 0x1f:
+               break;
+       case 0x18:
+               break;
+       default:
+               nv_fatal(device, "unknown Celsius chipset\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/device.h>
+
+int
+nv20_identify(struct nouveau_device *device)
+{
+       switch (device->chipset) {
+       case 0x20:
+               break;
+       case 0x25:
+               break;
+       case 0x28:
+               break;
+       case 0x2a:
+               break;
+       default:
+               nv_fatal(device, "unknown Kelvin chipset\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/device.h>
+
+int
+nv30_identify(struct nouveau_device *device)
+{
+       switch (device->chipset) {
+       case 0x30:
+               break;
+       case 0x35:
+               break;
+       case 0x31:
+               break;
+       case 0x36:
+               break;
+       case 0x34:
+               break;
+       default:
+               nv_fatal(device, "unknown Rankine chipset\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/device.h>
+
+int
+nv40_identify(struct nouveau_device *device)
+{
+       switch (device->chipset) {
+       case 0x40:
+               break;
+       case 0x41:
+               break;
+       case 0x42:
+               break;
+       case 0x43:
+               break;
+       case 0x45:
+               break;
+       case 0x47:
+               break;
+       case 0x49:
+               break;
+       case 0x4b:
+               break;
+       case 0x44:
+               break;
+       case 0x46:
+               break;
+       case 0x4a:
+               break;
+       case 0x4c:
+               break;
+       case 0x4e:
+               break;
+       case 0x63:
+               break;
+       case 0x67:
+               break;
+       case 0x68:
+               break;
+       default:
+               nv_fatal(device, "unknown Curie chipset\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/device.h>
+
+int
+nv50_identify(struct nouveau_device *device)
+{
+       switch (device->chipset) {
+       case 0x50:
+               break;
+       case 0x84:
+               break;
+       case 0x86:
+               break;
+       case 0x92:
+               break;
+       case 0x94:
+               break;
+       case 0x96:
+               break;
+       case 0x98:
+               break;
+       case 0xa0:
+               break;
+       case 0xaa:
+               break;
+       case 0xac:
+               break;
+       case 0xa3:
+               break;
+       case 0xa5:
+               break;
+       case 0xa8:
+               break;
+       case 0xaf:
+               break;
+       default:
+               nv_fatal(device, "unknown Tesla chipset\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/device.h>
+
+int
+nvc0_identify(struct nouveau_device *device)
+{
+       switch (device->chipset) {
+       case 0xc0:
+               break;
+       case 0xc4:
+               break;
+       case 0xc3:
+               break;
+       case 0xce:
+               break;
+       case 0xcf:
+               break;
+       case 0xc1:
+               break;
+       case 0xc8:
+               break;
+       case 0xd9:
+               break;
+       default:
+               nv_fatal(device, "unknown Fermi chipset\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
 
--- /dev/null
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include <subdev/device.h>
+
+int
+nve0_identify(struct nouveau_device *device)
+{
+       switch (device->chipset) {
+       case 0xe4:
+               break;
+       case 0xe7:
+               break;
+       default:
+               nv_fatal(device, "unknown Kepler chipset\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}