diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/core')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/Kbuild | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/client.c | 266 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/engctx.c | 239 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/engine.c | 75 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/enum.c | 66 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/event.c | 99 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c | 316 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/handle.c | 221 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/ioctl.c | 526 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/mm.c | 304 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/namedb.c | 199 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/notify.c | 163 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/object.c | 330 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/option.c | 121 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/parent.c | 159 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/printk.c | 103 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/ramht.c | 106 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/core/subdev.c | 120 |
18 files changed, 3430 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/core/Kbuild b/drivers/gpu/drm/nouveau/nvkm/core/Kbuild new file mode 100644 index 000000000..a2bdb2069 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/Kbuild @@ -0,0 +1,17 @@ +nvkm-y := nvkm/core/client.o +nvkm-y += nvkm/core/engctx.o +nvkm-y += nvkm/core/engine.o +nvkm-y += nvkm/core/enum.o +nvkm-y += nvkm/core/event.o +nvkm-y += nvkm/core/gpuobj.o +nvkm-y += nvkm/core/handle.o +nvkm-y += nvkm/core/ioctl.o +nvkm-y += nvkm/core/mm.o +nvkm-y += nvkm/core/namedb.o +nvkm-y += nvkm/core/notify.o +nvkm-y += nvkm/core/object.o +nvkm-y += nvkm/core/option.o +nvkm-y += nvkm/core/parent.o +nvkm-y += nvkm/core/printk.o +nvkm-y += nvkm/core/ramht.o +nvkm-y += nvkm/core/subdev.o diff --git a/drivers/gpu/drm/nouveau/nvkm/core/client.c b/drivers/gpu/drm/nouveau/nvkm/core/client.c new file mode 100644 index 000000000..878a82f8f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/client.c @@ -0,0 +1,266 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include <core/client.h> +#include <core/device.h> +#include <core/handle.h> +#include <core/notify.h> +#include <core/option.h> + +#include <nvif/class.h> +#include <nvif/event.h> +#include <nvif/unpack.h> + +struct nvkm_client_notify { + struct nvkm_client *client; + struct nvkm_notify n; + u8 version; + u8 size; + union { + struct nvif_notify_rep_v0 v0; + } rep; +}; + +static int +nvkm_client_notify(struct nvkm_notify *n) +{ + struct nvkm_client_notify *notify = container_of(n, typeof(*notify), n); + struct nvkm_client *client = notify->client; + return client->ntfy(¬ify->rep, notify->size, n->data, n->size); +} + +int +nvkm_client_notify_put(struct nvkm_client *client, int index) +{ + if (index < ARRAY_SIZE(client->notify)) { + if (client->notify[index]) { + nvkm_notify_put(&client->notify[index]->n); + return 0; + } + } + return -ENOENT; +} + +int +nvkm_client_notify_get(struct nvkm_client *client, int index) +{ + if (index < ARRAY_SIZE(client->notify)) { + if (client->notify[index]) { + nvkm_notify_get(&client->notify[index]->n); + return 0; + } + } + return -ENOENT; +} + +int +nvkm_client_notify_del(struct nvkm_client *client, int index) +{ + if (index < ARRAY_SIZE(client->notify)) { + if (client->notify[index]) { + nvkm_notify_fini(&client->notify[index]->n); + kfree(client->notify[index]); + client->notify[index] = NULL; + return 0; + } + } + return -ENOENT; +} + +int +nvkm_client_notify_new(struct nvkm_object *object, + struct nvkm_event *event, void *data, u32 size) +{ + struct nvkm_client *client = nvkm_client(object); + struct nvkm_client_notify *notify; + union { + struct nvif_notify_req_v0 v0; + } *req = data; + u8 index, reply; + int ret; + + for (index = 0; index < ARRAY_SIZE(client->notify); index++) { + if (!client->notify[index]) + break; + } + + if (index == ARRAY_SIZE(client->notify)) + return -ENOSPC; + + notify = kzalloc(sizeof(*notify), GFP_KERNEL); + if (!notify) + return -ENOMEM; + + nv_ioctl(client, "notify new size %d\n", size); + if (nvif_unpack(req->v0, 0, 0, true)) { + nv_ioctl(client, "notify new vers %d reply %d route %02x " + "token %llx\n", req->v0.version, + req->v0.reply, req->v0.route, req->v0.token); + notify->version = req->v0.version; + notify->size = sizeof(notify->rep.v0); + notify->rep.v0.version = req->v0.version; + notify->rep.v0.route = req->v0.route; + notify->rep.v0.token = req->v0.token; + reply = req->v0.reply; + } + + if (ret == 0) { + ret = nvkm_notify_init(object, event, nvkm_client_notify, + false, data, size, reply, ¬ify->n); + if (ret == 0) { + client->notify[index] = notify; + notify->client = client; + return index; + } + } + + kfree(notify); + return ret; +} + +static int +nvkm_client_mthd_devlist(struct nvkm_object *object, void *data, u32 size) +{ + union { + struct nv_client_devlist_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "client devlist size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(object, "client devlist vers %d count %d\n", + args->v0.version, args->v0.count); + if (size == sizeof(args->v0.device[0]) * args->v0.count) { + ret = nvkm_device_list(args->v0.device, args->v0.count); + if (ret >= 0) { + args->v0.count = ret; + ret = 0; + } + } else { + ret = -EINVAL; + } + } + + return ret; +} + +static int +nvkm_client_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) +{ + switch (mthd) { + case NV_CLIENT_DEVLIST: + return nvkm_client_mthd_devlist(object, data, size); + default: + break; + } + return -EINVAL; +} + +static void +nvkm_client_dtor(struct nvkm_object *object) +{ + struct nvkm_client *client = (void *)object; + int i; + for (i = 0; i < ARRAY_SIZE(client->notify); i++) + nvkm_client_notify_del(client, i); + nvkm_object_ref(NULL, &client->device); + nvkm_handle_destroy(client->root); + nvkm_namedb_destroy(&client->namedb); +} + +static struct nvkm_oclass +nvkm_client_oclass = { + .ofuncs = &(struct nvkm_ofuncs) { + .dtor = nvkm_client_dtor, + .mthd = nvkm_client_mthd, + }, +}; + +int +nvkm_client_create_(const char *name, u64 devname, const char *cfg, + const char *dbg, int length, void **pobject) +{ + struct nvkm_object *device; + struct nvkm_client *client; + int ret; + + device = (void *)nvkm_device_find(devname); + if (!device) + return -ENODEV; + + ret = nvkm_namedb_create_(NULL, NULL, &nvkm_client_oclass, + NV_CLIENT_CLASS, NULL, + (1ULL << NVDEV_ENGINE_DEVICE), + length, pobject); + client = *pobject; + if (ret) + return ret; + + ret = nvkm_handle_create(nv_object(client), ~0, ~0, nv_object(client), + &client->root); + if (ret) + return ret; + + /* prevent init/fini being called, os in in charge of this */ + atomic_set(&nv_object(client)->usecount, 2); + + nvkm_object_ref(device, &client->device); + snprintf(client->name, sizeof(client->name), "%s", name); + client->debug = nvkm_dbgopt(dbg, "CLIENT"); + return 0; +} + +int +nvkm_client_init(struct nvkm_client *client) +{ + int ret; + nv_debug(client, "init running\n"); + ret = nvkm_handle_init(client->root); + nv_debug(client, "init completed with %d\n", ret); + return ret; +} + +int +nvkm_client_fini(struct nvkm_client *client, bool suspend) +{ + const char *name[2] = { "fini", "suspend" }; + int ret, i; + nv_debug(client, "%s running\n", name[suspend]); + nv_debug(client, "%s notify\n", name[suspend]); + for (i = 0; i < ARRAY_SIZE(client->notify); i++) + nvkm_client_notify_put(client, i); + nv_debug(client, "%s object\n", name[suspend]); + ret = nvkm_handle_fini(client->root, suspend); + nv_debug(client, "%s completed with %d\n", name[suspend], ret); + return ret; +} + +const char * +nvkm_client_name(void *obj) +{ + const char *client_name = "unknown"; + struct nvkm_client *client = nvkm_client(obj); + if (client) + client_name = client->name; + return client_name; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/engctx.c b/drivers/gpu/drm/nouveau/nvkm/core/engctx.c new file mode 100644 index 000000000..fb2acbca7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/engctx.c @@ -0,0 +1,239 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include <core/engctx.h> +#include <core/engine.h> +#include <core/client.h> + +static inline int +nvkm_engctx_exists(struct nvkm_object *parent, + struct nvkm_engine *engine, void **pobject) +{ + struct nvkm_engctx *engctx; + struct nvkm_object *parctx; + + list_for_each_entry(engctx, &engine->contexts, head) { + parctx = nv_pclass(nv_object(engctx), NV_PARENT_CLASS); + if (parctx == parent) { + atomic_inc(&nv_object(engctx)->refcount); + *pobject = engctx; + return 1; + } + } + + return 0; +} + +int +nvkm_engctx_create_(struct nvkm_object *parent, struct nvkm_object *engobj, + struct nvkm_oclass *oclass, struct nvkm_object *pargpu, + u32 size, u32 align, u32 flags, int length, void **pobject) +{ + struct nvkm_client *client = nvkm_client(parent); + struct nvkm_engine *engine = nv_engine(engobj); + struct nvkm_object *engctx; + unsigned long save; + int ret; + + /* check if this engine already has a context for the parent object, + * and reference it instead of creating a new one + */ + spin_lock_irqsave(&engine->lock, save); + ret = nvkm_engctx_exists(parent, engine, pobject); + spin_unlock_irqrestore(&engine->lock, save); + if (ret) + return ret; + + /* create the new context, supports creating both raw objects and + * objects backed by instance memory + */ + if (size) { + ret = nvkm_gpuobj_create_(parent, engobj, oclass, + NV_ENGCTX_CLASS, pargpu, size, + align, flags, length, pobject); + } else { + ret = nvkm_object_create_(parent, engobj, oclass, + NV_ENGCTX_CLASS, length, pobject); + } + + engctx = *pobject; + if (ret) + return ret; + + /* must take the lock again and re-check a context doesn't already + * exist (in case of a race) - the lock had to be dropped before as + * it's not possible to allocate the object with it held. + */ + spin_lock_irqsave(&engine->lock, save); + ret = nvkm_engctx_exists(parent, engine, pobject); + if (ret) { + spin_unlock_irqrestore(&engine->lock, save); + nvkm_object_ref(NULL, &engctx); + return ret; + } + + if (client->vm) + atomic_inc(&client->vm->engref[nv_engidx(engine)]); + list_add(&nv_engctx(engctx)->head, &engine->contexts); + nv_engctx(engctx)->addr = ~0ULL; + spin_unlock_irqrestore(&engine->lock, save); + return 0; +} + +void +nvkm_engctx_destroy(struct nvkm_engctx *engctx) +{ + struct nvkm_engine *engine = engctx->gpuobj.object.engine; + struct nvkm_client *client = nvkm_client(engctx); + unsigned long save; + + nvkm_gpuobj_unmap(&engctx->vma); + spin_lock_irqsave(&engine->lock, save); + list_del(&engctx->head); + spin_unlock_irqrestore(&engine->lock, save); + + if (client->vm) + atomic_dec(&client->vm->engref[nv_engidx(engine)]); + + if (engctx->gpuobj.size) + nvkm_gpuobj_destroy(&engctx->gpuobj); + else + nvkm_object_destroy(&engctx->gpuobj.object); +} + +int +nvkm_engctx_init(struct nvkm_engctx *engctx) +{ + struct nvkm_object *object = nv_object(engctx); + struct nvkm_subdev *subdev = nv_subdev(object->engine); + struct nvkm_object *parent; + struct nvkm_subdev *pardev; + int ret; + + ret = nvkm_gpuobj_init(&engctx->gpuobj); + 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 +nvkm_engctx_fini(struct nvkm_engctx *engctx, bool suspend) +{ + struct nvkm_object *object = nv_object(engctx); + struct nvkm_subdev *subdev = nv_subdev(object->engine); + struct nvkm_object *parent; + struct nvkm_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 nvkm_gpuobj_fini(&engctx->gpuobj, suspend); +} + +int +_nvkm_engctx_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_engctx *engctx; + int ret; + + ret = nvkm_engctx_create(parent, engine, oclass, NULL, 256, 256, + NVOBJ_FLAG_ZERO_ALLOC, &engctx); + *pobject = nv_object(engctx); + return ret; +} + +void +_nvkm_engctx_dtor(struct nvkm_object *object) +{ + nvkm_engctx_destroy(nv_engctx(object)); +} + +int +_nvkm_engctx_init(struct nvkm_object *object) +{ + return nvkm_engctx_init(nv_engctx(object)); +} + +int +_nvkm_engctx_fini(struct nvkm_object *object, bool suspend) +{ + return nvkm_engctx_fini(nv_engctx(object), suspend); +} + +struct nvkm_object * +nvkm_engctx_get(struct nvkm_engine *engine, u64 addr) +{ + struct nvkm_engctx *engctx; + unsigned long flags; + + spin_lock_irqsave(&engine->lock, flags); + list_for_each_entry(engctx, &engine->contexts, head) { + if (engctx->addr == addr) { + engctx->save = flags; + return nv_object(engctx); + } + } + spin_unlock_irqrestore(&engine->lock, flags); + return NULL; +} + +void +nvkm_engctx_put(struct nvkm_object *object) +{ + if (object) { + struct nvkm_engine *engine = nv_engine(object->engine); + struct nvkm_engctx *engctx = nv_engctx(object); + spin_unlock_irqrestore(&engine->lock, engctx->save); + } +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/engine.c b/drivers/gpu/drm/nouveau/nvkm/core/engine.c new file mode 100644 index 000000000..60820173c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/engine.c @@ -0,0 +1,75 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include <core/engine.h> +#include <core/device.h> +#include <core/option.h> + +struct nvkm_engine * +nvkm_engine(void *obj, int idx) +{ + obj = nvkm_subdev(obj, idx); + if (obj && nv_iclass(obj, NV_ENGINE_CLASS)) + return nv_engine(obj); + return NULL; +} + +int +nvkm_engine_create_(struct nvkm_object *parent, struct nvkm_object *engobj, + struct nvkm_oclass *oclass, bool enable, + const char *iname, const char *fname, + int length, void **pobject) +{ + struct nvkm_engine *engine; + int ret; + + ret = nvkm_subdev_create_(parent, engobj, oclass, NV_ENGINE_CLASS, + iname, fname, length, pobject); + engine = *pobject; + if (ret) + return ret; + + if (parent) { + struct nvkm_device *device = nv_device(parent); + int engidx = nv_engidx(engine); + + if (device->disable_mask & (1ULL << engidx)) { + if (!nvkm_boolopt(device->cfgopt, iname, false)) { + nv_debug(engine, "engine disabled by hw/fw\n"); + return -ENODEV; + } + + nv_warn(engine, "ignoring hw/fw engine disable\n"); + } + + if (!nvkm_boolopt(device->cfgopt, iname, enable)) { + if (!enable) + nv_warn(engine, "disabled, %s=1 to enable\n", iname); + return -ENODEV; + } + } + + INIT_LIST_HEAD(&engine->contexts); + spin_lock_init(&engine->lock); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/enum.c b/drivers/gpu/drm/nouveau/nvkm/core/enum.c new file mode 100644 index 000000000..4f92bfc13 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/enum.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 Nouveau Project + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include <core/enum.h> + +const struct nvkm_enum * +nvkm_enum_find(const struct nvkm_enum *en, u32 value) +{ + while (en->name) { + if (en->value == value) + return en; + en++; + } + + return NULL; +} + +const struct nvkm_enum * +nvkm_enum_print(const struct nvkm_enum *en, u32 value) +{ + en = nvkm_enum_find(en, value); + if (en) + pr_cont("%s", en->name); + else + pr_cont("(unknown enum 0x%08x)", value); + return en; +} + +void +nvkm_bitfield_print(const struct nvkm_bitfield *bf, u32 value) +{ + while (bf->name) { + if (value & bf->mask) { + pr_cont(" %s", bf->name); + value &= ~bf->mask; + } + + bf++; + } + + if (value) + pr_cont(" (unknown bits 0x%08x)", value); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/event.c b/drivers/gpu/drm/nouveau/nvkm/core/event.c new file mode 100644 index 000000000..4e8d3fa04 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/event.c @@ -0,0 +1,99 @@ +/* + * Copyright 2013-2014 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. + */ +#include <core/event.h> +#include <core/notify.h> + +void +nvkm_event_put(struct nvkm_event *event, u32 types, int index) +{ + assert_spin_locked(&event->refs_lock); + while (types) { + int type = __ffs(types); types &= ~(1 << type); + if (--event->refs[index * event->types_nr + type] == 0) { + if (event->func->fini) + event->func->fini(event, 1 << type, index); + } + } +} + +void +nvkm_event_get(struct nvkm_event *event, u32 types, int index) +{ + assert_spin_locked(&event->refs_lock); + while (types) { + int type = __ffs(types); types &= ~(1 << type); + if (++event->refs[index * event->types_nr + type] == 1) { + if (event->func->init) + event->func->init(event, 1 << type, index); + } + } +} + +void +nvkm_event_send(struct nvkm_event *event, u32 types, int index, + void *data, u32 size) +{ + struct nvkm_notify *notify; + unsigned long flags; + + if (!event->refs || WARN_ON(index >= event->index_nr)) + return; + + spin_lock_irqsave(&event->list_lock, flags); + list_for_each_entry(notify, &event->list, head) { + if (notify->index == index && (notify->types & types)) { + if (event->func->send) { + event->func->send(data, size, notify); + continue; + } + nvkm_notify_send(notify, data, size); + } + } + spin_unlock_irqrestore(&event->list_lock, flags); +} + +void +nvkm_event_fini(struct nvkm_event *event) +{ + if (event->refs) { + kfree(event->refs); + event->refs = NULL; + } +} + +int +nvkm_event_init(const struct nvkm_event_func *func, int types_nr, int index_nr, + struct nvkm_event *event) +{ + event->refs = kzalloc(sizeof(*event->refs) * index_nr * types_nr, + GFP_KERNEL); + if (!event->refs) + return -ENOMEM; + + event->func = func; + event->types_nr = types_nr; + event->index_nr = index_nr; + spin_lock_init(&event->refs_lock); + spin_lock_init(&event->list_lock); + INIT_LIST_HEAD(&event->list); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c b/drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c new file mode 100644 index 000000000..2eba801aa --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/gpuobj.c @@ -0,0 +1,316 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include <core/gpuobj.h> +#include <core/engine.h> + +#include <subdev/instmem.h> +#include <subdev/bar.h> +#include <subdev/mmu.h> + +void +nvkm_gpuobj_destroy(struct nvkm_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->node) + nvkm_mm_free(&nv_gpuobj(gpuobj->parent)->heap, &gpuobj->node); + + if (gpuobj->heap.block_size) + nvkm_mm_fini(&gpuobj->heap); + + nvkm_object_destroy(&gpuobj->object); +} + +int +nvkm_gpuobj_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 pclass, + struct nvkm_object *pargpu, u32 size, u32 align, u32 flags, + int length, void **pobject) +{ + struct nvkm_instmem *imem = nvkm_instmem(parent); + struct nvkm_bar *bar = nvkm_bar(parent); + struct nvkm_gpuobj *gpuobj; + struct nvkm_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 nvkm_instobj *iobj = (void *)parent; + struct nvkm_mem **mem = (void *)(iobj + 1); + struct nvkm_mem *node = *mem; + if (!bar->alloc(bar, parent, node, &pargpu)) { + nvkm_object_ref(NULL, &parent); + parent = pargpu; + } + } + } + + ret = nvkm_object_create_(parent, engine, oclass, pclass | + NV_GPUOBJ_CLASS, length, pobject); + nvkm_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 = nvkm_mm_head(heap, 0, 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 = nvkm_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 nvkm_gpuobj_class { + struct nvkm_object *pargpu; + u64 size; + u32 align; + u32 flags; +}; + +static int +_nvkm_gpuobj_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_gpuobj_class *args = data; + struct nvkm_gpuobj *object; + int ret; + + ret = nvkm_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 +_nvkm_gpuobj_dtor(struct nvkm_object *object) +{ + nvkm_gpuobj_destroy(nv_gpuobj(object)); +} + +int +_nvkm_gpuobj_init(struct nvkm_object *object) +{ + return nvkm_gpuobj_init(nv_gpuobj(object)); +} + +int +_nvkm_gpuobj_fini(struct nvkm_object *object, bool suspend) +{ + return nvkm_gpuobj_fini(nv_gpuobj(object), suspend); +} + +u32 +_nvkm_gpuobj_rd32(struct nvkm_object *object, u64 addr) +{ + struct nvkm_gpuobj *gpuobj = nv_gpuobj(object); + struct nvkm_ofuncs *pfuncs = nv_ofuncs(gpuobj->parent); + if (gpuobj->node) + addr += gpuobj->node->offset; + return pfuncs->rd32(gpuobj->parent, addr); +} + +void +_nvkm_gpuobj_wr32(struct nvkm_object *object, u64 addr, u32 data) +{ + struct nvkm_gpuobj *gpuobj = nv_gpuobj(object); + struct nvkm_ofuncs *pfuncs = nv_ofuncs(gpuobj->parent); + if (gpuobj->node) + addr += gpuobj->node->offset; + pfuncs->wr32(gpuobj->parent, addr, data); +} + +static struct nvkm_oclass +_nvkm_gpuobj_oclass = { + .handle = 0x00000000, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = _nvkm_gpuobj_ctor, + .dtor = _nvkm_gpuobj_dtor, + .init = _nvkm_gpuobj_init, + .fini = _nvkm_gpuobj_fini, + .rd32 = _nvkm_gpuobj_rd32, + .wr32 = _nvkm_gpuobj_wr32, + }, +}; + +int +nvkm_gpuobj_new(struct nvkm_object *parent, struct nvkm_object *pargpu, + u32 size, u32 align, u32 flags, + struct nvkm_gpuobj **pgpuobj) +{ + struct nvkm_object *engine = parent; + struct nvkm_gpuobj_class args = { + .pargpu = pargpu, + .size = size, + .align = align, + .flags = flags, + }; + + if (!nv_iclass(engine, NV_SUBDEV_CLASS)) + engine = &engine->engine->subdev.object; + BUG_ON(engine == NULL); + + return nvkm_object_ctor(parent, engine, &_nvkm_gpuobj_oclass, + &args, sizeof(args), + (struct nvkm_object **)pgpuobj); +} + +int +nvkm_gpuobj_map(struct nvkm_gpuobj *gpuobj, u32 access, struct nvkm_vma *vma) +{ + struct nvkm_bar *bar = nvkm_bar(gpuobj); + int ret = -EINVAL; + + if (bar && bar->umap) { + struct nvkm_instobj *iobj = (void *) + nv_pclass(nv_object(gpuobj), NV_MEMOBJ_CLASS); + struct nvkm_mem **mem = (void *)(iobj + 1); + ret = bar->umap(bar, *mem, access, vma); + } + + return ret; +} + +int +nvkm_gpuobj_map_vm(struct nvkm_gpuobj *gpuobj, struct nvkm_vm *vm, + u32 access, struct nvkm_vma *vma) +{ + struct nvkm_instobj *iobj = (void *) + nv_pclass(nv_object(gpuobj), NV_MEMOBJ_CLASS); + struct nvkm_mem **mem = (void *)(iobj + 1); + int ret; + + ret = nvkm_vm_get(vm, gpuobj->size, 12, access, vma); + if (ret) + return ret; + + nvkm_vm_map(vma, *mem); + return 0; +} + +void +nvkm_gpuobj_unmap(struct nvkm_vma *vma) +{ + if (vma->node) { + nvkm_vm_unmap(vma); + nvkm_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 +nvkm_gpudup_dtor(struct nvkm_object *object) +{ + struct nvkm_gpuobj *gpuobj = (void *)object; + nvkm_object_ref(NULL, &gpuobj->parent); + nvkm_object_destroy(&gpuobj->object); +} + +static struct nvkm_oclass +nvkm_gpudup_oclass = { + .handle = NV_GPUOBJ_CLASS, + .ofuncs = &(struct nvkm_ofuncs) { + .dtor = nvkm_gpudup_dtor, + .init = nvkm_object_init, + .fini = nvkm_object_fini, + }, +}; + +int +nvkm_gpuobj_dup(struct nvkm_object *parent, struct nvkm_gpuobj *base, + struct nvkm_gpuobj **pgpuobj) +{ + struct nvkm_gpuobj *gpuobj; + int ret; + + ret = nvkm_object_create(parent, &parent->engine->subdev.object, + &nvkm_gpudup_oclass, 0, &gpuobj); + *pgpuobj = gpuobj; + if (ret) + return ret; + + nvkm_object_ref(nv_object(base), &gpuobj->parent); + gpuobj->addr = base->addr; + gpuobj->size = base->size; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/handle.c b/drivers/gpu/drm/nouveau/nvkm/core/handle.c new file mode 100644 index 000000000..dc7ff10eb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/handle.c @@ -0,0 +1,221 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include <core/handle.h> +#include <core/client.h> + +#define hprintk(h,l,f,a...) do { \ + struct nvkm_client *c = nvkm_client((h)->object); \ + struct nvkm_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 +nvkm_handle_init(struct nvkm_handle *handle) +{ + struct nvkm_handle *item; + int ret; + + hprintk(handle, TRACE, "init running\n"); + ret = nvkm_object_inc(handle->object); + if (ret) + return ret; + + hprintk(handle, TRACE, "init children\n"); + list_for_each_entry(item, &handle->tree, head) { + ret = nvkm_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) { + nvkm_handle_fini(item, false); + } + + nvkm_object_dec(handle->object, false); + return ret; +} + +int +nvkm_handle_fini(struct nvkm_handle *handle, bool suspend) +{ + static char *name[2] = { "fini", "suspend" }; + struct nvkm_handle *item; + int ret; + + hprintk(handle, TRACE, "%s children\n", name[suspend]); + list_for_each_entry(item, &handle->tree, head) { + ret = nvkm_handle_fini(item, suspend); + if (ret && suspend) + goto fail; + } + + hprintk(handle, TRACE, "%s running\n", name[suspend]); + if (handle->object) { + ret = nvkm_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 = nvkm_handle_init(item); + if (rret) + hprintk(handle, FATAL, "failed to restart, %d\n", rret); + } + + return ret; +} + +int +nvkm_handle_create(struct nvkm_object *parent, u32 _parent, u32 _handle, + struct nvkm_object *object, struct nvkm_handle **phandle) +{ + struct nvkm_object *namedb; + struct nvkm_handle *handle; + int ret; + + namedb = parent; + while (!nv_iclass(namedb, NV_NAMEDB_CLASS)) + namedb = namedb->parent; + + handle = 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 = nvkm_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) { + nvkm_handle_destroy(handle); + return ret; + } + + handle->priv = ret; + } + + if (object != namedb) { + while (!nv_iclass(namedb, NV_CLIENT_CLASS)) + namedb = namedb->parent; + + handle->parent = nvkm_namedb_get(nv_namedb(namedb), _parent); + if (handle->parent) { + list_add(&handle->head, &handle->parent->tree); + nvkm_namedb_put(handle->parent); + } + } + + hprintk(handle, TRACE, "created\n"); + *phandle = handle; + return 0; +} + +void +nvkm_handle_destroy(struct nvkm_handle *handle) +{ + struct nvkm_handle *item, *temp; + + hprintk(handle, TRACE, "destroy running\n"); + list_for_each_entry_safe(item, temp, &handle->tree, head) { + nvkm_handle_destroy(item); + } + list_del(&handle->head); + + if (handle->priv != ~0) { + struct nvkm_object *parent = handle->parent->object; + nv_parent(parent)->object_detach(parent, handle->priv); + } + + hprintk(handle, TRACE, "destroy completed\n"); + nvkm_namedb_remove(handle); + kfree(handle); +} + +struct nvkm_object * +nvkm_handle_ref(struct nvkm_object *parent, u32 name) +{ + struct nvkm_object *object = NULL; + struct nvkm_handle *handle; + + while (!nv_iclass(parent, NV_NAMEDB_CLASS)) + parent = parent->parent; + + handle = nvkm_namedb_get(nv_namedb(parent), name); + if (handle) { + nvkm_object_ref(handle->object, &object); + nvkm_namedb_put(handle); + } + + return object; +} + +struct nvkm_handle * +nvkm_handle_get_class(struct nvkm_object *engctx, u16 oclass) +{ + struct nvkm_namedb *namedb; + if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS))) + return nvkm_namedb_get_class(namedb, oclass); + return NULL; +} + +struct nvkm_handle * +nvkm_handle_get_vinst(struct nvkm_object *engctx, u64 vinst) +{ + struct nvkm_namedb *namedb; + if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS))) + return nvkm_namedb_get_vinst(namedb, vinst); + return NULL; +} + +struct nvkm_handle * +nvkm_handle_get_cinst(struct nvkm_object *engctx, u32 cinst) +{ + struct nvkm_namedb *namedb; + if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS))) + return nvkm_namedb_get_cinst(namedb, cinst); + return NULL; +} + +void +nvkm_handle_put(struct nvkm_handle *handle) +{ + if (handle) + nvkm_namedb_put(handle); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c b/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c new file mode 100644 index 000000000..4459ff5f4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/ioctl.c @@ -0,0 +1,526 @@ +/* + * Copyright 2014 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 <bskeggs@redhat.com> + */ +#include <core/ioctl.h> +#include <core/client.h> +#include <core/engine.h> +#include <core/handle.h> +#include <core/namedb.h> + +#include <nvif/unpack.h> +#include <nvif/ioctl.h> + +static int +nvkm_ioctl_nop(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_nop none; + } *args = data; + int ret; + + nv_ioctl(object, "nop size %d\n", size); + if (nvif_unvers(args->none)) { + nv_ioctl(object, "nop\n"); + } + + return ret; +} + +static int +nvkm_ioctl_sclass(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_sclass_v0 v0; + } *args = data; + int ret; + + if (!nv_iclass(object, NV_PARENT_CLASS)) { + nv_debug(object, "cannot have children (sclass)\n"); + return -ENODEV; + } + + nv_ioctl(object, "sclass size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(object, "sclass vers %d count %d\n", + args->v0.version, args->v0.count); + if (size == args->v0.count * sizeof(args->v0.oclass[0])) { + ret = nvkm_parent_lclass(object, args->v0.oclass, + args->v0.count); + if (ret >= 0) { + args->v0.count = ret; + ret = 0; + } + } else { + ret = -EINVAL; + } + } + + return ret; +} + +static int +nvkm_ioctl_new(struct nvkm_handle *handle, void *data, u32 size) +{ + union { + struct nvif_ioctl_new_v0 v0; + } *args = data; + struct nvkm_client *client = nvkm_client(handle->object); + struct nvkm_object *engctx = NULL; + struct nvkm_object *object = NULL; + struct nvkm_parent *parent; + struct nvkm_object *engine; + struct nvkm_oclass *oclass; + u32 _handle, _oclass; + int ret; + + nv_ioctl(client, "new size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + _handle = args->v0.handle; + _oclass = args->v0.oclass; + } else + return ret; + + nv_ioctl(client, "new vers %d handle %08x class %08x " + "route %02x token %llx\n", + args->v0.version, _handle, _oclass, + args->v0.route, args->v0.token); + + if (!nv_iclass(handle->object, NV_PARENT_CLASS)) { + nv_debug(handle->object, "cannot have children (ctor)\n"); + ret = -ENODEV; + goto fail_class; + } + + parent = nv_parent(handle->object); + + /* check that parent supports the requested subclass */ + ret = nvkm_parent_sclass(&parent->object, _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 = nvkm_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 = nvkm_object_ctor(&parent->object, engine, + nv_engine(engine)->cclass, + data, size, &engctx); + if (ret) + goto fail_engctx; + } else { + nvkm_object_ref(&parent->object, &engctx); + } + + /* finally, create new object and bind it to its handle */ + ret = nvkm_object_ctor(engctx, engine, oclass, data, size, &object); + client->data = object; + if (ret) + goto fail_ctor; + + ret = nvkm_object_inc(object); + if (ret) + goto fail_init; + + ret = nvkm_handle_create(&parent->object, handle->name, + _handle, object, &handle); + if (ret) + goto fail_handle; + + ret = nvkm_handle_init(handle); + handle->route = args->v0.route; + handle->token = args->v0.token; + if (ret) + nvkm_handle_destroy(handle); + +fail_handle: + nvkm_object_dec(object, false); +fail_init: + nvkm_object_ref(NULL, &object); +fail_ctor: + nvkm_object_ref(NULL, &engctx); +fail_engctx: + if (engine) + nvkm_object_dec(engine, false); +fail_class: + return ret; +} + +static int +nvkm_ioctl_del(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_del none; + } *args = data; + int ret; + + nv_ioctl(object, "delete size %d\n", size); + if (nvif_unvers(args->none)) { + nv_ioctl(object, "delete\n"); + nvkm_handle_fini(handle, false); + nvkm_handle_destroy(handle); + } + + return ret; +} + +static int +nvkm_ioctl_mthd(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; + union { + struct nvif_ioctl_mthd_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "mthd size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(object, "mthd vers %d mthd %02x\n", + args->v0.version, args->v0.method); + if (ret = -ENODEV, ofuncs->mthd) + ret = ofuncs->mthd(object, args->v0.method, data, size); + } + + return ret; +} + + +static int +nvkm_ioctl_rd(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; + union { + struct nvif_ioctl_rd_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "rd size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "rd vers %d size %d addr %016llx\n", + args->v0.version, args->v0.size, args->v0.addr); + switch (args->v0.size) { + case 1: + if (ret = -ENODEV, ofuncs->rd08) { + args->v0.data = nv_ro08(object, args->v0.addr); + ret = 0; + } + break; + case 2: + if (ret = -ENODEV, ofuncs->rd16) { + args->v0.data = nv_ro16(object, args->v0.addr); + ret = 0; + } + break; + case 4: + if (ret = -ENODEV, ofuncs->rd32) { + args->v0.data = nv_ro32(object, args->v0.addr); + ret = 0; + } + break; + default: + ret = -EINVAL; + break; + } + } + + return ret; +} + +static int +nvkm_ioctl_wr(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; + union { + struct nvif_ioctl_wr_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "wr size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "wr vers %d size %d addr %016llx data %08x\n", + args->v0.version, args->v0.size, args->v0.addr, + args->v0.data); + switch (args->v0.size) { + case 1: + if (ret = -ENODEV, ofuncs->wr08) { + nv_wo08(object, args->v0.addr, args->v0.data); + ret = 0; + } + break; + case 2: + if (ret = -ENODEV, ofuncs->wr16) { + nv_wo16(object, args->v0.addr, args->v0.data); + ret = 0; + } + break; + case 4: + if (ret = -ENODEV, ofuncs->wr32) { + nv_wo32(object, args->v0.addr, args->v0.data); + ret = 0; + } + break; + default: + ret = -EINVAL; + break; + } + } + + return ret; +} + +static int +nvkm_ioctl_map(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; + union { + struct nvif_ioctl_map_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "map size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "map vers %d\n", args->v0.version); + if (ret = -ENODEV, ofuncs->map) { + ret = ofuncs->map(object, &args->v0.handle, + &args->v0.length); + } + } + + return ret; +} + +static int +nvkm_ioctl_unmap(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_unmap none; + } *args = data; + int ret; + + nv_ioctl(object, "unmap size %d\n", size); + if (nvif_unvers(args->none)) { + nv_ioctl(object, "unmap\n"); + } + + return ret; +} + +static int +nvkm_ioctl_ntfy_new(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_object *object = handle->object; + struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs; + union { + struct nvif_ioctl_ntfy_new_v0 v0; + } *args = data; + struct nvkm_event *event; + int ret; + + nv_ioctl(object, "ntfy new size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(object, "ntfy new vers %d event %02x\n", + args->v0.version, args->v0.event); + if (ret = -ENODEV, ofuncs->ntfy) + ret = ofuncs->ntfy(object, args->v0.event, &event); + if (ret == 0) { + ret = nvkm_client_notify_new(object, event, data, size); + if (ret >= 0) { + args->v0.index = ret; + ret = 0; + } + } + } + + return ret; +} + +static int +nvkm_ioctl_ntfy_del(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_client *client = nvkm_client(handle->object); + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_ntfy_del_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "ntfy del size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "ntfy del vers %d index %d\n", + args->v0.version, args->v0.index); + ret = nvkm_client_notify_del(client, args->v0.index); + } + + return ret; +} + +static int +nvkm_ioctl_ntfy_get(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_client *client = nvkm_client(handle->object); + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_ntfy_get_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "ntfy get size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "ntfy get vers %d index %d\n", + args->v0.version, args->v0.index); + ret = nvkm_client_notify_get(client, args->v0.index); + } + + return ret; +} + +static int +nvkm_ioctl_ntfy_put(struct nvkm_handle *handle, void *data, u32 size) +{ + struct nvkm_client *client = nvkm_client(handle->object); + struct nvkm_object *object = handle->object; + union { + struct nvif_ioctl_ntfy_put_v0 v0; + } *args = data; + int ret; + + nv_ioctl(object, "ntfy put size %d\n", size); + if (nvif_unpack(args->v0, 0, 0, false)) { + nv_ioctl(object, "ntfy put vers %d index %d\n", + args->v0.version, args->v0.index); + ret = nvkm_client_notify_put(client, args->v0.index); + } + + return ret; +} + +static struct { + int version; + int (*func)(struct nvkm_handle *, void *, u32); +} +nvkm_ioctl_v0[] = { + { 0x00, nvkm_ioctl_nop }, + { 0x00, nvkm_ioctl_sclass }, + { 0x00, nvkm_ioctl_new }, + { 0x00, nvkm_ioctl_del }, + { 0x00, nvkm_ioctl_mthd }, + { 0x00, nvkm_ioctl_rd }, + { 0x00, nvkm_ioctl_wr }, + { 0x00, nvkm_ioctl_map }, + { 0x00, nvkm_ioctl_unmap }, + { 0x00, nvkm_ioctl_ntfy_new }, + { 0x00, nvkm_ioctl_ntfy_del }, + { 0x00, nvkm_ioctl_ntfy_get }, + { 0x00, nvkm_ioctl_ntfy_put }, +}; + +static int +nvkm_ioctl_path(struct nvkm_handle *parent, u32 type, u32 nr, u32 *path, + void *data, u32 size, u8 owner, u8 *route, u64 *token) +{ + struct nvkm_handle *handle = parent; + struct nvkm_namedb *namedb; + struct nvkm_object *object; + int ret; + + while ((object = parent->object), nr--) { + nv_ioctl(object, "path 0x%08x\n", path[nr]); + if (!nv_iclass(object, NV_PARENT_CLASS)) { + nv_debug(object, "cannot have children (path)\n"); + return -EINVAL; + } + + if (!(namedb = (void *)nv_pclass(object, NV_NAMEDB_CLASS)) || + !(handle = nvkm_namedb_get(namedb, path[nr]))) { + nv_debug(object, "handle 0x%08x not found\n", path[nr]); + return -ENOENT; + } + nvkm_namedb_put(handle); + parent = handle; + } + + if (owner != NVIF_IOCTL_V0_OWNER_ANY && owner != handle->route) { + nv_ioctl(object, "object route != owner\n"); + return -EACCES; + } + *route = handle->route; + *token = handle->token; + + if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) { + if (nvkm_ioctl_v0[type].version == 0) + ret = nvkm_ioctl_v0[type].func(handle, data, size); + } + + return ret; +} + +int +nvkm_ioctl(struct nvkm_client *client, bool supervisor, + void *data, u32 size, void **hack) +{ + union { + struct nvif_ioctl_v0 v0; + } *args = data; + int ret; + + client->super = supervisor; + nv_ioctl(client, "size %d\n", size); + + if (nvif_unpack(args->v0, 0, 0, true)) { + nv_ioctl(client, "vers %d type %02x path %d owner %02x\n", + args->v0.version, args->v0.type, args->v0.path_nr, + args->v0.owner); + ret = nvkm_ioctl_path(client->root, args->v0.type, + args->v0.path_nr, args->v0.path, + data, size, args->v0.owner, + &args->v0.route, &args->v0.token); + } + + nv_ioctl(client, "return %d\n", ret); + if (hack) { + *hack = client->data; + client->data = NULL; + } + + client->super = false; + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/mm.c b/drivers/gpu/drm/nouveau/nvkm/core/mm.c new file mode 100644 index 000000000..7f458dfd5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/mm.c @@ -0,0 +1,304 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include <core/mm.h> + +#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \ + list_entry((root)->nl_entry.dir, struct nvkm_mm_node, nl_entry) + +static void +nvkm_mm_dump(struct nvkm_mm *mm, const char *header) +{ + struct nvkm_mm_node *node; + + printk(KERN_ERR "nvkm: %s\n", header); + printk(KERN_ERR "nvkm: node list:\n"); + list_for_each_entry(node, &mm->nodes, nl_entry) { + printk(KERN_ERR "nvkm: \t%08x %08x %d\n", + node->offset, node->length, node->type); + } + printk(KERN_ERR "nvkm: free list:\n"); + list_for_each_entry(node, &mm->free, fl_entry) { + printk(KERN_ERR "nvkm: \t%08x %08x %d\n", + node->offset, node->length, node->type); + } +} + +void +nvkm_mm_free(struct nvkm_mm *mm, struct nvkm_mm_node **pthis) +{ + struct nvkm_mm_node *this = *pthis; + + if (this) { + struct nvkm_mm_node *prev = node(this, prev); + struct nvkm_mm_node *next = node(this, next); + + if (prev && prev->type == NVKM_MM_TYPE_NONE) { + prev->length += this->length; + list_del(&this->nl_entry); + kfree(this); this = prev; + } + + if (next && next->type == NVKM_MM_TYPE_NONE) { + next->offset = this->offset; + next->length += this->length; + if (this->type == NVKM_MM_TYPE_NONE) + list_del(&this->fl_entry); + list_del(&this->nl_entry); + kfree(this); this = NULL; + } + + if (this && this->type != NVKM_MM_TYPE_NONE) { + list_for_each_entry(prev, &mm->free, fl_entry) { + if (this->offset < prev->offset) + break; + } + + list_add_tail(&this->fl_entry, &prev->fl_entry); + this->type = NVKM_MM_TYPE_NONE; + } + } + + *pthis = NULL; +} + +static struct nvkm_mm_node * +region_head(struct nvkm_mm *mm, struct nvkm_mm_node *a, u32 size) +{ + struct nvkm_mm_node *b; + + if (a->length == size) + return a; + + b = kmalloc(sizeof(*b), GFP_KERNEL); + if (unlikely(b == NULL)) + return NULL; + + b->offset = a->offset; + b->length = size; + b->heap = a->heap; + b->type = a->type; + a->offset += size; + a->length -= size; + list_add_tail(&b->nl_entry, &a->nl_entry); + if (b->type == NVKM_MM_TYPE_NONE) + list_add_tail(&b->fl_entry, &a->fl_entry); + + return b; +} + +int +nvkm_mm_head(struct nvkm_mm *mm, u8 heap, u8 type, u32 size_max, u32 size_min, + u32 align, struct nvkm_mm_node **pnode) +{ + struct nvkm_mm_node *prev, *this, *next; + u32 mask = align - 1; + u32 splitoff; + u32 s, e; + + BUG_ON(type == NVKM_MM_TYPE_NONE || type == NVKM_MM_TYPE_HOLE); + + list_for_each_entry(this, &mm->free, fl_entry) { + if (unlikely(heap != NVKM_MM_HEAP_ANY)) { + if (this->heap != heap) + continue; + } + e = this->offset + this->length; + s = this->offset; + + prev = node(this, prev); + if (prev && prev->type != type) + s = roundup(s, mm->block_size); + + next = node(this, next); + if (next && next->type != type) + e = rounddown(e, mm->block_size); + + s = (s + mask) & ~mask; + e &= ~mask; + if (s > e || e - s < size_min) + continue; + + splitoff = s - this->offset; + if (splitoff && !region_head(mm, this, splitoff)) + return -ENOMEM; + + this = region_head(mm, this, min(size_max, e - s)); + if (!this) + return -ENOMEM; + + this->type = type; + list_del(&this->fl_entry); + *pnode = this; + return 0; + } + + return -ENOSPC; +} + +static struct nvkm_mm_node * +region_tail(struct nvkm_mm *mm, struct nvkm_mm_node *a, u32 size) +{ + struct nvkm_mm_node *b; + + if (a->length == size) + return a; + + b = kmalloc(sizeof(*b), GFP_KERNEL); + if (unlikely(b == NULL)) + return NULL; + + a->length -= size; + b->offset = a->offset + a->length; + b->length = size; + b->heap = a->heap; + b->type = a->type; + + list_add(&b->nl_entry, &a->nl_entry); + if (b->type == NVKM_MM_TYPE_NONE) + list_add(&b->fl_entry, &a->fl_entry); + + return b; +} + +int +nvkm_mm_tail(struct nvkm_mm *mm, u8 heap, u8 type, u32 size_max, u32 size_min, + u32 align, struct nvkm_mm_node **pnode) +{ + struct nvkm_mm_node *prev, *this, *next; + u32 mask = align - 1; + + BUG_ON(type == NVKM_MM_TYPE_NONE || type == NVKM_MM_TYPE_HOLE); + + list_for_each_entry_reverse(this, &mm->free, fl_entry) { + u32 e = this->offset + this->length; + u32 s = this->offset; + u32 c = 0, a; + if (unlikely(heap != NVKM_MM_HEAP_ANY)) { + if (this->heap != heap) + continue; + } + + prev = node(this, prev); + if (prev && prev->type != type) + s = roundup(s, mm->block_size); + + next = node(this, next); + if (next && next->type != type) { + e = rounddown(e, mm->block_size); + c = next->offset - e; + } + + s = (s + mask) & ~mask; + a = e - s; + if (s > e || a < size_min) + continue; + + a = min(a, size_max); + s = (e - a) & ~mask; + c += (e - s) - a; + + if (c && !region_tail(mm, this, c)) + return -ENOMEM; + + this = region_tail(mm, this, a); + if (!this) + return -ENOMEM; + + this->type = type; + list_del(&this->fl_entry); + *pnode = this; + return 0; + } + + return -ENOSPC; +} + +int +nvkm_mm_init(struct nvkm_mm *mm, u32 offset, u32 length, u32 block) +{ + struct nvkm_mm_node *node, *prev; + u32 next; + + if (nvkm_mm_initialised(mm)) { + prev = list_last_entry(&mm->nodes, typeof(*node), nl_entry); + next = prev->offset + prev->length; + if (next != offset) { + BUG_ON(next > offset); + if (!(node = kzalloc(sizeof(*node), GFP_KERNEL))) + return -ENOMEM; + node->type = NVKM_MM_TYPE_HOLE; + node->offset = next; + node->length = offset - next; + list_add_tail(&node->nl_entry, &mm->nodes); + } + BUG_ON(block != mm->block_size); + } else { + INIT_LIST_HEAD(&mm->nodes); + INIT_LIST_HEAD(&mm->free); + mm->block_size = block; + mm->heap_nodes = 0; + } + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + if (length) { + node->offset = roundup(offset, mm->block_size); + node->length = rounddown(offset + length, mm->block_size); + node->length -= node->offset; + } + + list_add_tail(&node->nl_entry, &mm->nodes); + list_add_tail(&node->fl_entry, &mm->free); + node->heap = ++mm->heap_nodes; + return 0; +} + +int +nvkm_mm_fini(struct nvkm_mm *mm) +{ + struct nvkm_mm_node *node, *temp; + int nodes = 0; + + if (!nvkm_mm_initialised(mm)) + return 0; + + list_for_each_entry(node, &mm->nodes, nl_entry) { + if (node->type != NVKM_MM_TYPE_HOLE) { + if (++nodes > mm->heap_nodes) { + nvkm_mm_dump(mm, "mm not clean!"); + return -EBUSY; + } + } + } + + list_for_each_entry_safe(node, temp, &mm->nodes, nl_entry) { + list_del(&node->nl_entry); + kfree(node); + } + + mm->heap_nodes = 0; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/namedb.c b/drivers/gpu/drm/nouveau/nvkm/core/namedb.c new file mode 100644 index 000000000..6400767c5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/namedb.c @@ -0,0 +1,199 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include <core/namedb.h> +#include <core/gpuobj.h> +#include <core/handle.h> + +static struct nvkm_handle * +nvkm_namedb_lookup(struct nvkm_namedb *namedb, u32 name) +{ + struct nvkm_handle *handle; + + list_for_each_entry(handle, &namedb->list, node) { + if (handle->name == name) + return handle; + } + + return NULL; +} + +static struct nvkm_handle * +nvkm_namedb_lookup_class(struct nvkm_namedb *namedb, u16 oclass) +{ + struct nvkm_handle *handle; + + list_for_each_entry(handle, &namedb->list, node) { + if (nv_mclass(handle->object) == oclass) + return handle; + } + + return NULL; +} + +static struct nvkm_handle * +nvkm_namedb_lookup_vinst(struct nvkm_namedb *namedb, u64 vinst) +{ + struct nvkm_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 nvkm_handle * +nvkm_namedb_lookup_cinst(struct nvkm_namedb *namedb, u32 cinst) +{ + struct nvkm_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 +nvkm_namedb_insert(struct nvkm_namedb *namedb, u32 name, + struct nvkm_object *object, + struct nvkm_handle *handle) +{ + int ret = -EEXIST; + write_lock_irq(&namedb->lock); + if (!nvkm_namedb_lookup(namedb, name)) { + nvkm_object_ref(object, &handle->object); + handle->namedb = namedb; + list_add(&handle->node, &namedb->list); + ret = 0; + } + write_unlock_irq(&namedb->lock); + return ret; +} + +void +nvkm_namedb_remove(struct nvkm_handle *handle) +{ + struct nvkm_namedb *namedb = handle->namedb; + struct nvkm_object *object = handle->object; + write_lock_irq(&namedb->lock); + list_del(&handle->node); + write_unlock_irq(&namedb->lock); + nvkm_object_ref(NULL, &object); +} + +struct nvkm_handle * +nvkm_namedb_get(struct nvkm_namedb *namedb, u32 name) +{ + struct nvkm_handle *handle; + read_lock(&namedb->lock); + handle = nvkm_namedb_lookup(namedb, name); + if (handle == NULL) + read_unlock(&namedb->lock); + return handle; +} + +struct nvkm_handle * +nvkm_namedb_get_class(struct nvkm_namedb *namedb, u16 oclass) +{ + struct nvkm_handle *handle; + read_lock(&namedb->lock); + handle = nvkm_namedb_lookup_class(namedb, oclass); + if (handle == NULL) + read_unlock(&namedb->lock); + return handle; +} + +struct nvkm_handle * +nvkm_namedb_get_vinst(struct nvkm_namedb *namedb, u64 vinst) +{ + struct nvkm_handle *handle; + read_lock(&namedb->lock); + handle = nvkm_namedb_lookup_vinst(namedb, vinst); + if (handle == NULL) + read_unlock(&namedb->lock); + return handle; +} + +struct nvkm_handle * +nvkm_namedb_get_cinst(struct nvkm_namedb *namedb, u32 cinst) +{ + struct nvkm_handle *handle; + read_lock(&namedb->lock); + handle = nvkm_namedb_lookup_cinst(namedb, cinst); + if (handle == NULL) + read_unlock(&namedb->lock); + return handle; +} + +void +nvkm_namedb_put(struct nvkm_handle *handle) +{ + if (handle) + read_unlock(&handle->namedb->lock); +} + +int +nvkm_namedb_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 pclass, + struct nvkm_oclass *sclass, u64 engcls, + int length, void **pobject) +{ + struct nvkm_namedb *namedb; + int ret; + + ret = nvkm_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 +_nvkm_namedb_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_namedb *object; + int ret; + + ret = nvkm_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/nvkm/core/notify.c b/drivers/gpu/drm/nouveau/nvkm/core/notify.c new file mode 100644 index 000000000..023610d01 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/notify.c @@ -0,0 +1,163 @@ +/* + * Copyright 2014 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 <bskeggs@redhat.com> + */ +#include <core/notify.h> +#include <core/event.h> + +static inline void +nvkm_notify_put_locked(struct nvkm_notify *notify) +{ + if (notify->block++ == 0) + nvkm_event_put(notify->event, notify->types, notify->index); +} + +void +nvkm_notify_put(struct nvkm_notify *notify) +{ + struct nvkm_event *event = notify->event; + unsigned long flags; + if (likely(event) && + test_and_clear_bit(NVKM_NOTIFY_USER, ¬ify->flags)) { + spin_lock_irqsave(&event->refs_lock, flags); + nvkm_notify_put_locked(notify); + spin_unlock_irqrestore(&event->refs_lock, flags); + if (test_bit(NVKM_NOTIFY_WORK, ¬ify->flags)) + flush_work(¬ify->work); + } +} + +static inline void +nvkm_notify_get_locked(struct nvkm_notify *notify) +{ + if (--notify->block == 0) + nvkm_event_get(notify->event, notify->types, notify->index); +} + +void +nvkm_notify_get(struct nvkm_notify *notify) +{ + struct nvkm_event *event = notify->event; + unsigned long flags; + if (likely(event) && + !test_and_set_bit(NVKM_NOTIFY_USER, ¬ify->flags)) { + spin_lock_irqsave(&event->refs_lock, flags); + nvkm_notify_get_locked(notify); + spin_unlock_irqrestore(&event->refs_lock, flags); + } +} + +static inline void +nvkm_notify_func(struct nvkm_notify *notify) +{ + struct nvkm_event *event = notify->event; + int ret = notify->func(notify); + unsigned long flags; + if ((ret == NVKM_NOTIFY_KEEP) || + !test_and_clear_bit(NVKM_NOTIFY_USER, ¬ify->flags)) { + spin_lock_irqsave(&event->refs_lock, flags); + nvkm_notify_get_locked(notify); + spin_unlock_irqrestore(&event->refs_lock, flags); + } +} + +static void +nvkm_notify_work(struct work_struct *work) +{ + struct nvkm_notify *notify = container_of(work, typeof(*notify), work); + nvkm_notify_func(notify); +} + +void +nvkm_notify_send(struct nvkm_notify *notify, void *data, u32 size) +{ + struct nvkm_event *event = notify->event; + unsigned long flags; + + assert_spin_locked(&event->list_lock); + BUG_ON(size != notify->size); + + spin_lock_irqsave(&event->refs_lock, flags); + if (notify->block) { + spin_unlock_irqrestore(&event->refs_lock, flags); + return; + } + nvkm_notify_put_locked(notify); + spin_unlock_irqrestore(&event->refs_lock, flags); + + if (test_bit(NVKM_NOTIFY_WORK, ¬ify->flags)) { + memcpy((void *)notify->data, data, size); + schedule_work(¬ify->work); + } else { + notify->data = data; + nvkm_notify_func(notify); + notify->data = NULL; + } +} + +void +nvkm_notify_fini(struct nvkm_notify *notify) +{ + unsigned long flags; + if (notify->event) { + nvkm_notify_put(notify); + spin_lock_irqsave(¬ify->event->list_lock, flags); + list_del(¬ify->head); + spin_unlock_irqrestore(¬ify->event->list_lock, flags); + kfree((void *)notify->data); + notify->event = NULL; + } +} + +int +nvkm_notify_init(struct nvkm_object *object, struct nvkm_event *event, + int (*func)(struct nvkm_notify *), bool work, + void *data, u32 size, u32 reply, + struct nvkm_notify *notify) +{ + unsigned long flags; + int ret = -ENODEV; + if ((notify->event = event), event->refs) { + ret = event->func->ctor(object, data, size, notify); + if (ret == 0 && (ret = -EINVAL, notify->size == reply)) { + notify->flags = 0; + notify->block = 1; + notify->func = func; + notify->data = NULL; + if (ret = 0, work) { + INIT_WORK(¬ify->work, nvkm_notify_work); + set_bit(NVKM_NOTIFY_WORK, ¬ify->flags); + notify->data = kmalloc(reply, GFP_KERNEL); + if (!notify->data) + ret = -ENOMEM; + } + } + if (ret == 0) { + spin_lock_irqsave(&event->list_lock, flags); + list_add_tail(¬ify->head, &event->list); + spin_unlock_irqrestore(&event->list_lock, flags); + } + } + if (ret) + notify->event = NULL; + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/object.c b/drivers/gpu/drm/nouveau/nvkm/core/object.c new file mode 100644 index 000000000..979f3627d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/object.c @@ -0,0 +1,330 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include <core/object.h> +#include <core/engine.h> + +#ifdef NVKM_OBJECT_MAGIC +static struct list_head _objlist = LIST_HEAD_INIT(_objlist); +static DEFINE_SPINLOCK(_objlist_lock); +#endif + +int +nvkm_object_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 pclass, + int size, void **pobject) +{ + struct nvkm_object *object; + + object = *pobject = kzalloc(size, GFP_KERNEL); + if (!object) + return -ENOMEM; + + nvkm_object_ref(parent, &object->parent); + nvkm_object_ref(engine, (struct nvkm_object **)&object->engine); + object->oclass = oclass; + object->oclass->handle |= pclass; + atomic_set(&object->refcount, 1); + atomic_set(&object->usecount, 0); + +#ifdef NVKM_OBJECT_MAGIC + object->_magic = NVKM_OBJECT_MAGIC; + spin_lock(&_objlist_lock); + list_add(&object->list, &_objlist); + spin_unlock(&_objlist_lock); +#endif + return 0; +} + +int +_nvkm_object_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + if (size != 0) + return -ENOSYS; + return nvkm_object_create(parent, engine, oclass, 0, pobject); +} + +void +nvkm_object_destroy(struct nvkm_object *object) +{ +#ifdef NVKM_OBJECT_MAGIC + spin_lock(&_objlist_lock); + list_del(&object->list); + spin_unlock(&_objlist_lock); +#endif + nvkm_object_ref(NULL, (struct nvkm_object **)&object->engine); + nvkm_object_ref(NULL, &object->parent); + kfree(object); +} + +int +nvkm_object_init(struct nvkm_object *object) +{ + return 0; +} + +int +nvkm_object_fini(struct nvkm_object *object, bool suspend) +{ + return 0; +} + +struct nvkm_ofuncs +nvkm_object_ofuncs = { + .ctor = _nvkm_object_ctor, + .dtor = nvkm_object_destroy, + .init = nvkm_object_init, + .fini = nvkm_object_fini, +}; + +int +nvkm_object_ctor(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, void *data, u32 size, + struct nvkm_object **pobject) +{ + struct nvkm_ofuncs *ofuncs = oclass->ofuncs; + struct nvkm_object *object = NULL; + int ret; + + ret = ofuncs->ctor(parent, engine, oclass, data, size, &object); + *pobject = object; + if (ret < 0) { + if (ret != -ENODEV) { + nv_error(parent, "failed to create 0x%08x, %d\n", + oclass->handle, ret); + } + + if (object) { + ofuncs->dtor(object); + *pobject = NULL; + } + + return ret; + } + + if (ret == 0) { + nv_trace(object, "created\n"); + atomic_set(&object->refcount, 1); + } + + return 0; +} + +static void +nvkm_object_dtor(struct nvkm_object *object) +{ + nv_trace(object, "destroying\n"); + nv_ofuncs(object)->dtor(object); +} + +void +nvkm_object_ref(struct nvkm_object *obj, struct nvkm_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) + nvkm_object_dtor(*ref); + } + + *ref = obj; +} + +int +nvkm_object_inc(struct nvkm_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 = nvkm_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 = nvkm_object_inc(&object->engine->subdev.object); + 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); + atomic_set(&object->usecount, 1); + if (ret) { + nv_error(object, "init failed, %d\n", ret); + goto fail_self; + } + + nv_trace(object, "initialised\n"); + return 0; + +fail_self: + if (object->engine) { + mutex_lock(&nv_subdev(object->engine)->mutex); + nvkm_object_dec(&object->engine->subdev.object, false); + mutex_unlock(&nv_subdev(object->engine)->mutex); + } +fail_engine: + if (object->parent) + nvkm_object_dec(object->parent, false); +fail_parent: + atomic_dec(&object->usecount); + return ret; +} + +static int +nvkm_object_decf(struct nvkm_object *object) +{ + int ret; + + nv_trace(object, "stopping...\n"); + + ret = nv_ofuncs(object)->fini(object, false); + atomic_set(&object->usecount, 0); + if (ret) + nv_warn(object, "failed fini, %d\n", ret); + + if (object->engine) { + mutex_lock(&nv_subdev(object->engine)->mutex); + nvkm_object_dec(&object->engine->subdev.object, false); + mutex_unlock(&nv_subdev(object->engine)->mutex); + } + + if (object->parent) + nvkm_object_dec(object->parent, false); + + nv_trace(object, "stopped\n"); + return 0; +} + +static int +nvkm_object_decs(struct nvkm_object *object) +{ + int ret, rret; + + nv_trace(object, "suspending...\n"); + + ret = nv_ofuncs(object)->fini(object, true); + atomic_set(&object->usecount, 0); + if (ret) { + nv_error(object, "failed suspend, %d\n", ret); + return ret; + } + + if (object->engine) { + mutex_lock(&nv_subdev(object->engine)->mutex); + ret = nvkm_object_dec(&object->engine->subdev.object, 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 = nvkm_object_dec(object->parent, true); + if (ret) { + nv_warn(object, "parent failed suspend, %d\n", ret); + goto fail_parent; + } + } + + nv_trace(object, "suspended\n"); + return 0; + +fail_parent: + if (object->engine) { + mutex_lock(&nv_subdev(object->engine)->mutex); + rret = nvkm_object_inc(&object->engine->subdev.object); + 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 +nvkm_object_dec(struct nvkm_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 = nvkm_object_decs(object); + else + ret = nvkm_object_decf(object); + + if (ret) { + atomic_inc(&object->usecount); + return ret; + } + } + + return 0; +} + +void +nvkm_object_debug(void) +{ +#ifdef NVKM_OBJECT_MAGIC + struct nvkm_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/nvkm/core/option.c b/drivers/gpu/drm/nouveau/nvkm/core/option.c new file mode 100644 index 000000000..19d153f8c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/option.c @@ -0,0 +1,121 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include <core/option.h> +#include <core/debug.h> + +const char * +nvkm_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 +nvkm_boolopt(const char *optstr, const char *opt, bool value) +{ + int arglen; + + optstr = nvkm_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 +nvkm_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_NORMAL; + 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/nvkm/core/parent.c b/drivers/gpu/drm/nouveau/nvkm/core/parent.c new file mode 100644 index 000000000..dd56cd1ee --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/parent.c @@ -0,0 +1,159 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include <core/parent.h> +#include <core/client.h> +#include <core/engine.h> + +int +nvkm_parent_sclass(struct nvkm_object *parent, u16 handle, + struct nvkm_object **pengine, + struct nvkm_oclass **poclass) +{ + struct nvkm_sclass *sclass; + struct nvkm_engine *engine; + struct nvkm_oclass *oclass; + u64 mask; + + sclass = nv_parent(parent)->sclass; + while (sclass) { + if ((sclass->oclass->handle & 0xffff) == handle) { + *pengine = &parent->engine->subdev.object; + *poclass = sclass->oclass; + return 0; + } + + sclass = sclass->sclass; + } + + mask = nv_parent(parent)->engine; + while (mask) { + int i = __ffs64(mask); + + if (nv_iclass(parent, NV_CLIENT_CLASS)) + engine = nv_engine(nv_client(parent)->device); + else + engine = nvkm_engine(parent, i); + + if (engine) { + 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 +nvkm_parent_lclass(struct nvkm_object *parent, u32 *lclass, int size) +{ + struct nvkm_sclass *sclass; + struct nvkm_engine *engine; + struct nvkm_oclass *oclass; + int nr = -1, i; + u64 mask; + + sclass = nv_parent(parent)->sclass; + while (sclass) { + if (++nr < size) + lclass[nr] = sclass->oclass->handle & 0xffff; + sclass = sclass->sclass; + } + + mask = nv_parent(parent)->engine; + while (i = __ffs64(mask), mask) { + engine = nvkm_engine(parent, i); + if (engine && (oclass = engine->sclass)) { + while (oclass->ofuncs) { + if (++nr < size) + lclass[nr] = oclass->handle & 0xffff; + oclass++; + } + } + + mask &= ~(1ULL << i); + } + + return nr + 1; +} + +int +nvkm_parent_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 pclass, + struct nvkm_oclass *sclass, u64 engcls, + int size, void **pobject) +{ + struct nvkm_parent *object; + struct nvkm_sclass *nclass; + int ret; + + ret = nvkm_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; +} + +void +nvkm_parent_destroy(struct nvkm_parent *parent) +{ + struct nvkm_sclass *sclass; + + while ((sclass = parent->sclass)) { + parent->sclass = sclass->sclass; + kfree(sclass); + } + + nvkm_object_destroy(&parent->object); +} + + +void +_nvkm_parent_dtor(struct nvkm_object *object) +{ + nvkm_parent_destroy(nv_parent(object)); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/printk.c b/drivers/gpu/drm/nouveau/nvkm/core/printk.c new file mode 100644 index 000000000..4a220eb91 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/printk.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 <core/printk.h> +#include <core/client.h> +#include <core/device.h> + +int nv_info_debug_level = NV_DBG_INFO_NORMAL; + +void +nv_printk_(struct nvkm_object *object, int level, const char *fmt, ...) +{ + static const char name[] = { '!', 'E', 'W', ' ', 'D', 'T', 'P', 'S' }; + const char *pfx; + char mfmt[256]; + va_list args; + + switch (level) { + case NV_DBG_FATAL: + pfx = KERN_CRIT; + break; + case NV_DBG_ERROR: + pfx = KERN_ERR; + break; + case NV_DBG_WARN: + pfx = KERN_WARNING; + break; + case NV_DBG_INFO_NORMAL: + pfx = KERN_INFO; + break; + case NV_DBG_DEBUG: + case NV_DBG_PARANOIA: + case NV_DBG_TRACE: + case NV_DBG_SPAM: + default: + pfx = KERN_DEBUG; + break; + } + + if (object && !nv_iclass(object, NV_CLIENT_CLASS)) { + struct nvkm_object *device; + struct nvkm_object *subdev; + char obuf[64], *ofmt = ""; + + if (object->engine == NULL) { + subdev = object; + while (subdev && !nv_iclass(subdev, NV_SUBDEV_CLASS)) + subdev = subdev->parent; + } else { + subdev = &object->engine->subdev.object; + } + + device = subdev; + if (device->parent) + device = device->parent; + + if (object != subdev) { + snprintf(obuf, sizeof(obuf), "[0x%08x]", + nv_hclass(object)); + ofmt = obuf; + } + + 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[%8s] %s", pfx, + name[level], nv_client(object)->name, 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/nvkm/core/ramht.c b/drivers/gpu/drm/nouveau/nvkm/core/ramht.c new file mode 100644 index 000000000..ebd4d1547 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/ramht.c @@ -0,0 +1,106 @@ +/* + * 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. + */ +#include <core/ramht.h> +#include <core/engine.h> + +#include <subdev/bar.h> + +static u32 +nvkm_ramht_hash(struct nvkm_ramht *ramht, int chid, u32 handle) +{ + u32 hash = 0; + + while (handle) { + hash ^= (handle & ((1 << ramht->bits) - 1)); + handle >>= ramht->bits; + } + + hash ^= chid << (ramht->bits - 4); + hash = hash << 3; + return hash; +} + +int +nvkm_ramht_insert(struct nvkm_ramht *ramht, int chid, u32 handle, u32 context) +{ + struct nvkm_bar *bar = nvkm_bar(ramht); + u32 co, ho; + + co = ho = nvkm_ramht_hash(ramht, chid, handle); + do { + if (!nv_ro32(ramht, co + 4)) { + nv_wo32(ramht, co + 0, handle); + nv_wo32(ramht, co + 4, context); + if (bar) + bar->flush(bar); + return co; + } + + co += 8; + if (co >= nv_gpuobj(ramht)->size) + co = 0; + } while (co != ho); + + return -ENOMEM; +} + +void +nvkm_ramht_remove(struct nvkm_ramht *ramht, int cookie) +{ + struct nvkm_bar *bar = nvkm_bar(ramht); + nv_wo32(ramht, cookie + 0, 0x00000000); + nv_wo32(ramht, cookie + 4, 0x00000000); + if (bar) + bar->flush(bar); +} + +static struct nvkm_oclass +nvkm_ramht_oclass = { + .handle = 0x0000abcd, + .ofuncs = &(struct nvkm_ofuncs) { + .ctor = NULL, + .dtor = _nvkm_gpuobj_dtor, + .init = _nvkm_gpuobj_init, + .fini = _nvkm_gpuobj_fini, + .rd32 = _nvkm_gpuobj_rd32, + .wr32 = _nvkm_gpuobj_wr32, + }, +}; + +int +nvkm_ramht_new(struct nvkm_object *parent, struct nvkm_object *pargpu, + u32 size, u32 align, struct nvkm_ramht **pramht) +{ + struct nvkm_ramht *ramht; + int ret; + + ret = nvkm_gpuobj_create(parent, parent->engine ? + &parent->engine->subdev.object : parent, /* <nv50 ramht */ + &nvkm_ramht_oclass, 0, pargpu, size, + align, NVOBJ_FLAG_ZERO_ALLOC, &ramht); + *pramht = ramht; + if (ret) + return ret; + + ramht->bits = order_base_2(nv_gpuobj(ramht)->size >> 3); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/core/subdev.c b/drivers/gpu/drm/nouveau/nvkm/core/subdev.c new file mode 100644 index 000000000..c5fb3a793 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/core/subdev.c @@ -0,0 +1,120 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs + */ +#include <core/subdev.h> +#include <core/device.h> +#include <core/option.h> + +struct nvkm_subdev * +nvkm_subdev(void *obj, int idx) +{ + struct nvkm_object *object = nv_object(obj); + while (object && !nv_iclass(object, NV_SUBDEV_CLASS)) + object = object->parent; + if (object == NULL || nv_subidx(nv_subdev(object)) != idx) + object = nv_device(obj)->subdev[idx]; + return object ? nv_subdev(object) : NULL; +} + +void +nvkm_subdev_reset(struct nvkm_object *subdev) +{ + nv_trace(subdev, "resetting...\n"); + nv_ofuncs(subdev)->fini(subdev, false); + nv_debug(subdev, "reset\n"); +} + +int +nvkm_subdev_init(struct nvkm_subdev *subdev) +{ + int ret = nvkm_object_init(&subdev->object); + if (ret) + return ret; + + nvkm_subdev_reset(&subdev->object); + return 0; +} + +int +_nvkm_subdev_init(struct nvkm_object *object) +{ + return nvkm_subdev_init(nv_subdev(object)); +} + +int +nvkm_subdev_fini(struct nvkm_subdev *subdev, bool suspend) +{ + if (subdev->unit) { + nv_mask(subdev, 0x000200, subdev->unit, 0x00000000); + nv_mask(subdev, 0x000200, subdev->unit, subdev->unit); + } + + return nvkm_object_fini(&subdev->object, suspend); +} + +int +_nvkm_subdev_fini(struct nvkm_object *object, bool suspend) +{ + return nvkm_subdev_fini(nv_subdev(object), suspend); +} + +void +nvkm_subdev_destroy(struct nvkm_subdev *subdev) +{ + int subidx = nv_hclass(subdev) & 0xff; + nv_device(subdev)->subdev[subidx] = NULL; + nvkm_object_destroy(&subdev->object); +} + +void +_nvkm_subdev_dtor(struct nvkm_object *object) +{ + nvkm_subdev_destroy(nv_subdev(object)); +} + +int +nvkm_subdev_create_(struct nvkm_object *parent, struct nvkm_object *engine, + struct nvkm_oclass *oclass, u32 pclass, + const char *subname, const char *sysname, + int size, void **pobject) +{ + struct nvkm_subdev *subdev; + int ret; + + ret = nvkm_object_create_(parent, engine, oclass, pclass | + NV_SUBDEV_CLASS, size, pobject); + subdev = *pobject; + if (ret) + return ret; + + __mutex_init(&subdev->mutex, subname, &oclass->lock_class_key); + subdev->name = subname; + + if (parent) { + struct nvkm_device *device = nv_device(parent); + subdev->debug = nvkm_dbgopt(device->dbgopt, subname); + subdev->mmio = nv_subdev(device)->mmio; + } + + return 0; +} |