From: Ben Skeggs Date: Thu, 5 Jul 2012 21:36:43 +0000 (+1000) Subject: drm/nouveau/core: pull in most of the new core infrastructure X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=9274f4a9ba7e70d1770e237fca16d52f27f0c728;p=openwrt%2Fstaging%2Fblogic.git drm/nouveau/core: pull in most of the new core infrastructure This commit provides most of the infrastructure to support a major overhaul of Nouveau's internals coming in the following commits. This work aims to take all the things we've learned over the last several years, and turn that into a cleaner architecture that's more maintainable going forward. RAMHT and MM bits of the new core have been left out for the moment, and will be pulled in as I go through the process of porting the code to become either subdev or engine modules. There are several main goals I wanted to achieve through this work: -- Reduce complexity The goal here was to make each component of the driver as independent as possible, which will ease maintainability and readability, and provide a good base for resetting locked up GPU units in the future. -- Better tracking of GPU units that are required at any given time This is for future PM work, we'll be able to tell exactly what parts of the GPU we need powered at any given point (etc). -- Expose all available NVIDIA GPUs to the client In order to support things such as multi-GPU channels, we want to be able to expose all the NVIDIA GPUs to the client over a single file descriptor so it can send a single push buffer to multiple GPUs. -- Untangle the core hardware support code from the DRM implementation This happened initially as an unexpected side-effect of developing the initial core infrastructure in userspace, but it turned into a goal of the whole project. Initial benefits will be the availablility of a number of userspace tools and tests using the same code as the driver itself, but will also be important as I look into some virtualisation ideas. v2: Ben Skeggs - fix duplicate assignments noticed by clang - implement some forgotten yelling in error path - ensure 64-bit engine mask is used everywhere v3: Marcin Slusarz - sparse fixes - inline nv_printk into nv_assert to prevent recursive inlining issues v4: Ben Skeggs - fixed minor memory leak on gpuobj destruction Signed-off-by: Ben Skeggs --- diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index 8b632934f01d..ccd0b38b3c3f 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -2,13 +2,31 @@ # 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 diff --git a/drivers/gpu/drm/nouveau/core/core/client.c b/drivers/gpu/drm/nouveau/core/core/client.c new file mode 100644 index 000000000000..e190f1849118 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/client.c @@ -0,0 +1,103 @@ +/* + * 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 +#include +#include +#include + +#include + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/core/engctx.c b/drivers/gpu/drm/nouveau/core/core/engctx.c new file mode 100644 index 000000000000..50dc16d29f20 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/engctx.c @@ -0,0 +1,230 @@ +/* + * 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 +#include +#include +#include +#include + +#include + +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); +} diff --git a/drivers/gpu/drm/nouveau/core/core/engine.c b/drivers/gpu/drm/nouveau/core/core/engine.c new file mode 100644 index 000000000000..ee2905b806c6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/engine.c @@ -0,0 +1,54 @@ +/* + * 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 +#include +#include + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/core/enum.c b/drivers/gpu/drm/nouveau/core/core/enum.c new file mode 100644 index 000000000000..7cc7133d82de --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/enum.c @@ -0,0 +1,67 @@ +/* + * 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 +#include + +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); +} diff --git a/drivers/gpu/drm/nouveau/core/core/gpuobj.c b/drivers/gpu/drm/nouveau/core/core/gpuobj.c new file mode 100644 index 000000000000..715fd318f234 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/gpuobj.c @@ -0,0 +1,318 @@ +/* + * 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 +#include + +#include +#include +#include + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/core/handle.c b/drivers/gpu/drm/nouveau/core/core/handle.c new file mode 100644 index 000000000000..f2ab2e85009c --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/handle.c @@ -0,0 +1,189 @@ +/* + * 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 +#include +#include + +#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; +} diff --git a/drivers/gpu/drm/nouveau/core/core/mm.c b/drivers/gpu/drm/nouveau/core/core/mm.c index 024dd2c722bb..bf8d4a5461cd 100644 --- a/drivers/gpu/drm/nouveau/core/core/mm.c +++ b/drivers/gpu/drm/nouveau/core/core/mm.c @@ -22,8 +22,7 @@ * Authors: Ben Skeggs */ -#include "drmP.h" -#include "nouveau_drv.h" +#include #include static inline void diff --git a/drivers/gpu/drm/nouveau/core/core/namedb.c b/drivers/gpu/drm/nouveau/core/core/namedb.c new file mode 100644 index 000000000000..1ce95a8709df --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/namedb.c @@ -0,0 +1,203 @@ +/* + * 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 +#include +#include +#include + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/core/object.c b/drivers/gpu/drm/nouveau/core/core/object.c new file mode 100644 index 000000000000..f37598e3f5aa --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/object.c @@ -0,0 +1,468 @@ +/* + * 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 +#include +#include +#include +#include + +#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 +} diff --git a/drivers/gpu/drm/nouveau/core/core/option.c b/drivers/gpu/drm/nouveau/core/core/option.c new file mode 100644 index 000000000000..1b53ebb57af3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/option.c @@ -0,0 +1,131 @@ +/* + * 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 +#include + +/* 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; +} diff --git a/drivers/gpu/drm/nouveau/core/core/parent.c b/drivers/gpu/drm/nouveau/core/core/parent.c new file mode 100644 index 000000000000..a1ea034611d5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/parent.c @@ -0,0 +1,139 @@ +/* + * 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 +#include + +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)); +} diff --git a/drivers/gpu/drm/nouveau/core/core/printk.c b/drivers/gpu/drm/nouveau/core/core/printk.c new file mode 100644 index 000000000000..134dd6d12975 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/printk.c @@ -0,0 +1,74 @@ +/* + * 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 +#include +#include +#include + +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); +} diff --git a/drivers/gpu/drm/nouveau/core/core/subdev.c b/drivers/gpu/drm/nouveau/core/core/subdev.c new file mode 100644 index 000000000000..f74c30aa33a0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/subdev.c @@ -0,0 +1,115 @@ +/* + * 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 +#include +#include +#include + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/include/core/class.h b/drivers/gpu/drm/nouveau/core/include/core/class.h new file mode 100644 index 000000000000..17326dd1cfb5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/class.h @@ -0,0 +1,77 @@ +#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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/client.h b/drivers/gpu/drm/nouveau/core/include/core/client.h new file mode 100644 index 000000000000..f5f45f228282 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/client.h @@ -0,0 +1,42 @@ +#ifndef __NOUVEAU_CLIENT_H__ +#define __NOUVEAU_CLIENT_H__ + +#include + +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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/debug.h b/drivers/gpu/drm/nouveau/core/include/core/debug.h new file mode 100644 index 000000000000..9ea18dfcb4d0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/debug.h @@ -0,0 +1,13 @@ +#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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/device.h b/drivers/gpu/drm/nouveau/core/include/core/device.h new file mode 100644 index 000000000000..4db7b01bf31c --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/device.h @@ -0,0 +1,122 @@ +#ifndef __NOUVEAU_DEVICE_H__ +#define __NOUVEAU_DEVICE_H__ + +#include +#include +#include + +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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/engctx.h b/drivers/gpu/drm/nouveau/core/include/core/engctx.h new file mode 100644 index 000000000000..cbc9eb3a0d1a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/engctx.h @@ -0,0 +1,61 @@ +#ifndef __NOUVEAU_ENGCTX_H__ +#define __NOUVEAU_ENGCTX_H__ + +#include +#include + +#include + +#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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/engine.h b/drivers/gpu/drm/nouveau/core/include/core/engine.h new file mode 100644 index 000000000000..1a7b0a7455ca --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/engine.h @@ -0,0 +1,54 @@ +#ifndef __NOUVEAU_ENGINE_H__ +#define __NOUVEAU_ENGINE_H__ + +#include +#include + +#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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/enum.h b/drivers/gpu/drm/nouveau/core/include/core/enum.h new file mode 100644 index 000000000000..62ec0416ebcf --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/enum.h @@ -0,0 +1,23 @@ +#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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/gpuobj.h b/drivers/gpu/drm/nouveau/core/include/core/gpuobj.h new file mode 100644 index 000000000000..7394a5ebb971 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/gpuobj.h @@ -0,0 +1,82 @@ +#ifndef __NOUVEAU_GPUOBJ_H__ +#define __NOUVEAU_GPUOBJ_H__ + +#include +#ifndef XXX_THIS_IS_A_HACK +#include +#include +#include +#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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/handle.h b/drivers/gpu/drm/nouveau/core/include/core/handle.h new file mode 100644 index 000000000000..fbfbe55a7884 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/handle.h @@ -0,0 +1,26 @@ +#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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/math.h b/drivers/gpu/drm/nouveau/core/include/core/math.h new file mode 100644 index 000000000000..f808131c5cd8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/math.h @@ -0,0 +1,16 @@ +#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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/mm.h b/drivers/gpu/drm/nouveau/core/include/core/mm.h index d2b4290f5abe..b4fd73b14c91 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/mm.h +++ b/drivers/gpu/drm/nouveau/core/include/core/mm.h @@ -22,8 +22,8 @@ * 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; diff --git a/drivers/gpu/drm/nouveau/core/include/core/namedb.h b/drivers/gpu/drm/nouveau/core/include/core/namedb.h new file mode 100644 index 000000000000..8897e0886085 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/namedb.h @@ -0,0 +1,56 @@ +#ifndef __NOUVEAU_NAMEDB_H__ +#define __NOUVEAU_NAMEDB_H__ + +#include + +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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/object.h b/drivers/gpu/drm/nouveau/core/include/core/object.h new file mode 100644 index 000000000000..818feabbf4a0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/object.h @@ -0,0 +1,188 @@ +#ifndef __NOUVEAU_OBJECT_H__ +#define __NOUVEAU_OBJECT_H__ + +#include +#include + +#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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/option.h b/drivers/gpu/drm/nouveau/core/include/core/option.h new file mode 100644 index 000000000000..27074957fd21 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/option.h @@ -0,0 +1,11 @@ +#ifndef __NOUVEAU_OPTION_H__ +#define __NOUVEAU_OPTION_H__ + +#include + +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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/parent.h b/drivers/gpu/drm/nouveau/core/include/core/parent.h new file mode 100644 index 000000000000..d3aa251a5eb6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/parent.h @@ -0,0 +1,64 @@ +#ifndef __NOUVEAU_PARENT_H__ +#define __NOUVEAU_PARENT_H__ + +#include +#include + +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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/printk.h b/drivers/gpu/drm/nouveau/core/include/core/printk.h new file mode 100644 index 000000000000..1d629664f32d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/printk.h @@ -0,0 +1,39 @@ +#ifndef __NOUVEAU_PRINTK_H__ +#define __NOUVEAU_PRINTK_H__ + +#include +#include + +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 diff --git a/drivers/gpu/drm/nouveau/core/include/core/subdev.h b/drivers/gpu/drm/nouveau/core/include/core/subdev.h new file mode 100644 index 000000000000..e9632e931616 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/subdev.h @@ -0,0 +1,118 @@ +#ifndef __NOUVEAU_SUBDEV_H__ +#define __NOUVEAU_SUBDEV_H__ + +#include + +#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 diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/device.h b/drivers/gpu/drm/nouveau/core/include/subdev/device.h new file mode 100644 index 000000000000..5eec03a40f6d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/device.h @@ -0,0 +1,25 @@ +#ifndef __NOUVEAU_SUBDEV_DEVICE_H__ +#define __NOUVEAU_SUBDEV_DEVICE_H__ + +#include + +#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 diff --git a/drivers/gpu/drm/nouveau/core/os.h b/drivers/gpu/drm/nouveau/core/os.h new file mode 100644 index 000000000000..3bfb478edea2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/os.h @@ -0,0 +1,46 @@ +#ifndef __NOUVEAU_OS_H__ +#define __NOUVEAU_OS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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 diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/base.c b/drivers/gpu/drm/nouveau/core/subdev/device/base.c new file mode 100644 index 000000000000..40456b99cb5f --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/device/base.c @@ -0,0 +1,461 @@ +/* + * 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 +#include +#include +#include +#include + +#include + +#include + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c new file mode 100644 index 000000000000..8ded9d34c327 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv04.c @@ -0,0 +1,41 @@ +/* + * 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 + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c new file mode 100644 index 000000000000..04d14d12157d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c @@ -0,0 +1,53 @@ +/* + * 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 + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c new file mode 100644 index 000000000000..1805bee23604 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c @@ -0,0 +1,45 @@ +/* + * 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 + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c new file mode 100644 index 000000000000..1e7a8b19a896 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c @@ -0,0 +1,47 @@ +/* + * 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 + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c new file mode 100644 index 000000000000..5c54f6154a0a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c @@ -0,0 +1,69 @@ +/* + * 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 + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c new file mode 100644 index 000000000000..a699bd9b9ad0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c @@ -0,0 +1,65 @@ +/* + * 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 + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c new file mode 100644 index 000000000000..805268c1d6b8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c @@ -0,0 +1,53 @@ +/* + * 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 + +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; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c new file mode 100644 index 000000000000..3c3fe69844fa --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c @@ -0,0 +1,41 @@ +/* + * 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 + +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; +}