Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 91384758 authored by Linus Torvalds's avatar Linus Torvalds
Browse files


Pull nouveau drm updates from Ben Skeggs:
 "Apologies for not getting this done in time for Dave's drm-next merge
  window.  As he mentioned, a pre-existing bug reared its head a lot
  more obviously after this lot of changes.  It took quite a bit of time
  to track it down.  In any case, Dave suggested I try my luck by
  sending directly to you this time.

  Overview:

   - more code for Tegra GK20A from NVIDIA - probing, reclockig
   - better fix for Kepler GPUs that have the graphics engine powered
     off on startup, method courtesy of info provided by NVIDIA
   - unhardcoding of a bunch of graphics engine setup on
     Fermi/Kepler/Maxwell, will hopefully solve some issues people have
     noticed on higher-end models
   - support for "Zero Bandwidth Clear" on Fermi/Kepler/Maxwell, needs
     userspace support in general, but some lucky apps will benefit
     automagically
   - reviewed/exposed the full object APIs to userspace (finally), gives
     it access to perfctrs, ZBC controls, various events.  More to come
     in the future.
   - various other fixes"

Acked-by: default avatarDave Airlie <airlied@redhat.com>

* 'linux-3.17' of git://anongit.freedesktop.org/git/nouveau/linux-2.6: (87 commits)
  drm/nouveau: expose the full object/event interfaces to userspace
  drm/nouveau: fix headless mode
  drm/nouveau: hide sysfs pstate file behind an option again
  drm/nv50/disp: shhh compiler
  drm/gf100-/gr: implement the proper SetShaderExceptions method
  drm/gf100-/gr: remove some broken ltc bashing, for now
  drm/gf100-/gr: unhardcode attribute cb config
  drm/gf100-/gr: fetch tpcs-per-ppc info on startup
  drm/gf100-/gr: unhardcode pagepool config
  drm/gf100-/gr: unhardcode bundle cb config
  drm/gf100-/gr: improve initial context patch list helpers
  drm/gf100-/gr: add support for zero bandwidth clear
  drm/nouveau/ltc: add zbc drivers
  drm/nouveau/ltc: s/ltcg/ltc/ + cleanup
  drm/nouveau: use ram info from nvif_device
  drm/nouveau/disp: implement nvif event sources for vblank/connector notifiers
  drm/nouveau/disp: allow user direct access to channel control registers
  drm/nouveau/disp: audit and version display classes
  drm/nouveau/disp: audit and version SCANOUTPOS method
  drm/nv50-/disp: audit and version PIOR_PWR method
  ...
parents c23190c0 27111a23
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
config DRM_NOUVEAU
	tristate "Nouveau (nVidia) cards"
	tristate "Nouveau (NVIDIA) cards"
	depends on DRM && PCI
        select FW_LOADER
	select DRM_KMS_HELPER
@@ -23,7 +23,15 @@ config DRM_NOUVEAU
	select THERMAL if ACPI && X86
	select ACPI_VIDEO if ACPI && X86
	help
	  Choose this option for open-source nVidia support.
	  Choose this option for open-source NVIDIA support.

config NOUVEAU_PLATFORM_DRIVER
	tristate "Nouveau (NVIDIA) SoC GPUs"
	depends on DRM_NOUVEAU && ARCH_TEGRA
	default y
	help
	  Support for Nouveau platform driver, used for SoC GPUs as found
	  on NVIDIA Tegra K1.

config NOUVEAU_DEBUG
	int "Maximum debug level"
+23 −2
Original line number Diff line number Diff line
@@ -14,8 +14,10 @@ nouveau-y += core/core/enum.o
nouveau-y += core/core/event.o
nouveau-y += core/core/gpuobj.o
nouveau-y += core/core/handle.o
nouveau-y += core/core/ioctl.o
nouveau-y += core/core/mm.o
nouveau-y += core/core/namedb.o
nouveau-y += core/core/notify.o
nouveau-y += core/core/object.o
nouveau-y += core/core/option.o
nouveau-y += core/core/parent.o
@@ -26,6 +28,7 @@ nouveau-y += core/core/subdev.o
nouveau-y += core/subdev/bar/base.o
nouveau-y += core/subdev/bar/nv50.o
nouveau-y += core/subdev/bar/nvc0.o
nouveau-y += core/subdev/bar/gk20a.o
nouveau-y += core/subdev/bios/base.o
nouveau-y += core/subdev/bios/bit.o
nouveau-y += core/subdev/bios/boost.o
@@ -64,6 +67,7 @@ nouveau-y += core/subdev/clock/nva3.o
nouveau-y += core/subdev/clock/nvaa.o
nouveau-y += core/subdev/clock/nvc0.o
nouveau-y += core/subdev/clock/nve0.o
nouveau-y += core/subdev/clock/gk20a.o
nouveau-y += core/subdev/clock/pllnv04.o
nouveau-y += core/subdev/clock/pllnva3.o
nouveau-y += core/subdev/devinit/base.o
@@ -149,8 +153,10 @@ nouveau-y += core/subdev/instmem/base.o
nouveau-y += core/subdev/instmem/nv04.o
nouveau-y += core/subdev/instmem/nv40.o
nouveau-y += core/subdev/instmem/nv50.o
nouveau-y += core/subdev/ltcg/gf100.o
nouveau-y += core/subdev/ltcg/gm107.o
nouveau-y += core/subdev/ltc/base.o
nouveau-y += core/subdev/ltc/gf100.o
nouveau-y += core/subdev/ltc/gk104.o
nouveau-y += core/subdev/ltc/gm107.o
nouveau-y += core/subdev/mc/base.o
nouveau-y += core/subdev/mc/nv04.o
nouveau-y += core/subdev/mc/nv40.o
@@ -161,6 +167,7 @@ nouveau-y += core/subdev/mc/nv94.o
nouveau-y += core/subdev/mc/nv98.o
nouveau-y += core/subdev/mc/nvc0.o
nouveau-y += core/subdev/mc/nvc3.o
nouveau-y += core/subdev/mc/gk20a.o
nouveau-y += core/subdev/mxm/base.o
nouveau-y += core/subdev/mxm/mxms.o
nouveau-y += core/subdev/mxm/nv50.o
@@ -169,6 +176,7 @@ nouveau-y += core/subdev/pwr/memx.o
nouveau-y += core/subdev/pwr/nva3.o
nouveau-y += core/subdev/pwr/nvc0.o
nouveau-y += core/subdev/pwr/nvd0.o
nouveau-y += core/subdev/pwr/gk104.o
nouveau-y += core/subdev/pwr/nv108.o
nouveau-y += core/subdev/therm/base.o
nouveau-y += core/subdev/therm/fan.o
@@ -211,6 +219,7 @@ nouveau-y += core/engine/copy/nvc0.o
nouveau-y += core/engine/copy/nve0.o
nouveau-y += core/engine/crypt/nv84.o
nouveau-y += core/engine/crypt/nv98.o
nouveau-y += core/engine/device/acpi.o
nouveau-y += core/engine/device/base.o
nouveau-y += core/engine/device/ctrl.o
nouveau-y += core/engine/device/nv04.o
@@ -270,6 +279,7 @@ nouveau-y += core/engine/graph/ctxnvd9.o
nouveau-y += core/engine/graph/ctxnve4.o
nouveau-y += core/engine/graph/ctxgk20a.o
nouveau-y += core/engine/graph/ctxnvf0.o
nouveau-y += core/engine/graph/ctxgk110b.o
nouveau-y += core/engine/graph/ctxnv108.o
nouveau-y += core/engine/graph/ctxgm107.o
nouveau-y += core/engine/graph/nv04.o
@@ -291,6 +301,7 @@ nouveau-y += core/engine/graph/nvd9.o
nouveau-y += core/engine/graph/nve4.o
nouveau-y += core/engine/graph/gk20a.o
nouveau-y += core/engine/graph/nvf0.o
nouveau-y += core/engine/graph/gk110b.o
nouveau-y += core/engine/graph/nv108.o
nouveau-y += core/engine/graph/gm107.o
nouveau-y += core/engine/mpeg/nv31.o
@@ -318,11 +329,18 @@ nouveau-y += core/engine/vp/nv98.o
nouveau-y += core/engine/vp/nvc0.o
nouveau-y += core/engine/vp/nve0.o

# nvif
nouveau-y += nvif/object.o
nouveau-y += nvif/client.o
nouveau-y += nvif/device.o
nouveau-y += nvif/notify.o

# drm/core
nouveau-y += nouveau_drm.o nouveau_chan.o nouveau_dma.o nouveau_fence.o
nouveau-y += nouveau_vga.o nouveau_agp.o
nouveau-y += nouveau_ttm.o nouveau_sgdma.o nouveau_bo.o nouveau_gem.o
nouveau-y += nouveau_prime.o nouveau_abi16.o
nouveau-y += nouveau_nvif.o nouveau_usif.o
nouveau-y += nv04_fence.o nv10_fence.o nv17_fence.o
nouveau-y += nv50_fence.o nv84_fence.o nvc0_fence.o

@@ -349,3 +367,6 @@ nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
nouveau-$(CONFIG_DEBUG_FS) += nouveau_debugfs.o

obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o

# platform driver
obj-$(CONFIG_NOUVEAU_PLATFORM_DRIVER) += nouveau_platform.o
+160 −2
Original line number Diff line number Diff line
@@ -26,13 +26,167 @@
#include <core/client.h>
#include <core/handle.h>
#include <core/option.h>
#include <nvif/unpack.h>
#include <nvif/class.h>

#include <nvif/unpack.h>
#include <nvif/event.h>

#include <engine/device.h>

struct nvkm_client_notify {
	struct nouveau_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 nouveau_client *client = notify->client;
	return client->ntfy(&notify->rep, notify->size, n->data, n->size);
}

int
nvkm_client_notify_put(struct nouveau_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 nouveau_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 nouveau_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 nouveau_client *client,
		       struct nvkm_event *event, void *data, u32 size)
{
	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(event, nvkm_client_notify, false,
				       data, size, reply, &notify->n);
		if (ret == 0) {
			client->notify[index] = notify;
			notify->client = client;
			return 0;
		}
	}

	kfree(notify);
	return 0;
}

static int
nouveau_client_devlist(struct nouveau_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 = nouveau_device_list(args->v0.device,
						  args->v0.count);
			if (ret >= 0) {
				args->v0.count = ret;
				ret = 0;
			}
		} else {
			ret = -EINVAL;
		}
	}

	return ret;
}

static int
nouveau_client_mthd(struct nouveau_object *object, u32 mthd,
		    void *data, u32 size)
{
	switch (mthd) {
	case NV_CLIENT_DEVLIST:
		return nouveau_client_devlist(object, data, size);
	default:
		break;
	}
	return -EINVAL;
}

static void
nouveau_client_dtor(struct nouveau_object *object)
{
	struct nouveau_client *client = (void *)object;
	int i;
	for (i = 0; i < ARRAY_SIZE(client->notify); i++)
		nvkm_client_notify_del(client, i);
	nouveau_object_ref(NULL, &client->device);
	nouveau_handle_destroy(client->root);
	nouveau_namedb_destroy(&client->base);
@@ -42,6 +196,7 @@ static struct nouveau_oclass
nouveau_client_oclass = {
	.ofuncs = &(struct nouveau_ofuncs) {
		.dtor = nouveau_client_dtor,
		.mthd = nouveau_client_mthd,
	},
};

@@ -93,9 +248,12 @@ int
nouveau_client_fini(struct nouveau_client *client, bool suspend)
{
	const char *name[2] = { "fini", "suspend" };
	int ret;

	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 = nouveau_handle_fini(client->root, suspend);
	nv_debug(client, "%s completed with %d\n", name[suspend], ret);
	return ret;
+40 −136
Original line number Diff line number Diff line
/*
 * Copyright 2013 Red Hat Inc.
 * 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"),
@@ -24,173 +24,77 @@
#include <core/event.h>

void
nouveau_event_put(struct nouveau_eventh *handler)
nvkm_event_put(struct nvkm_event *event, u32 types, int index)
{
	struct nouveau_event *event = handler->event;
	unsigned long flags;
	u32 m, t;

	if (!__test_and_clear_bit(NVKM_EVENT_ENABLE, &handler->flags))
		return;

	spin_lock_irqsave(&event->refs_lock, flags);
	for (m = handler->types; t = __ffs(m), m; m &= ~(1 << t)) {
		if (!--event->refs[handler->index * event->types_nr + t]) {
			if (event->disable)
				event->disable(event, 1 << t, handler->index);
	BUG_ON(!spin_is_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);
		}

	}
	spin_unlock_irqrestore(&event->refs_lock, flags);
}

void
nouveau_event_get(struct nouveau_eventh *handler)
{
	struct nouveau_event *event = handler->event;
	unsigned long flags;
	u32 m, t;

	if (__test_and_set_bit(NVKM_EVENT_ENABLE, &handler->flags))
		return;

	spin_lock_irqsave(&event->refs_lock, flags);
	for (m = handler->types; t = __ffs(m), m; m &= ~(1 << t)) {
		if (!event->refs[handler->index * event->types_nr + t]++) {
			if (event->enable)
				event->enable(event, 1 << t, handler->index);
		}

	}
	spin_unlock_irqrestore(&event->refs_lock, flags);
}

static void
nouveau_event_fini(struct nouveau_eventh *handler)
{
	struct nouveau_event *event = handler->event;
	unsigned long flags;
	nouveau_event_put(handler);
	spin_lock_irqsave(&event->list_lock, flags);
	list_del(&handler->head);
	spin_unlock_irqrestore(&event->list_lock, flags);
}

static int
nouveau_event_init(struct nouveau_event *event, u32 types, int index,
		   int (*func)(void *, u32, int), void *priv,
		   struct nouveau_eventh *handler)
{
	unsigned long flags;

	if (types & ~((1 << event->types_nr) - 1))
		return -EINVAL;
	if (index >= event->index_nr)
		return -EINVAL;

	handler->event = event;
	handler->flags = 0;
	handler->types = types;
	handler->index = index;
	handler->func = func;
	handler->priv = priv;

	spin_lock_irqsave(&event->list_lock, flags);
	list_add_tail(&handler->head, &event->list[index]);
	spin_unlock_irqrestore(&event->list_lock, flags);
	return 0;
}

int
nouveau_event_new(struct nouveau_event *event, u32 types, int index,
		  int (*func)(void *, u32, int), void *priv,
		  struct nouveau_eventh **phandler)
nvkm_event_get(struct nvkm_event *event, u32 types, int index)
{
	struct nouveau_eventh *handler;
	int ret = -ENOMEM;

	if (event->check) {
		ret = event->check(event, types, index);
		if (ret)
			return ret;
	BUG_ON(!spin_is_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);
		}

	handler = *phandler = kmalloc(sizeof(*handler), GFP_KERNEL);
	if (handler) {
		ret = nouveau_event_init(event, types, index, func, priv, handler);
		if (ret)
			kfree(handler);
	}

	return ret;
}

void
nouveau_event_ref(struct nouveau_eventh *handler, struct nouveau_eventh **ref)
{
	BUG_ON(handler != NULL);
	if (*ref) {
		nouveau_event_fini(*ref);
		kfree(*ref);
	}
	*ref = handler;
}

void
nouveau_event_trigger(struct nouveau_event *event, u32 types, int index)
nvkm_event_send(struct nvkm_event *event, u32 types, int index,
		void *data, u32 size)
{
	struct nouveau_eventh *handler;
	struct nvkm_notify *notify;
	unsigned long flags;

	if (WARN_ON(index >= event->index_nr))
	if (!event->refs || WARN_ON(index >= event->index_nr))
		return;

	spin_lock_irqsave(&event->list_lock, flags);
	list_for_each_entry(handler, &event->list[index], head) {
		if (!test_bit(NVKM_EVENT_ENABLE, &handler->flags))
			continue;
		if (!(handler->types & types))
	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;
		if (handler->func(handler->priv, handler->types & types, index)
				!= NVKM_EVENT_DROP)
			continue;
		nouveau_event_put(handler);
			}
			nvkm_notify_send(notify, data, size);
		}
	}
	spin_unlock_irqrestore(&event->list_lock, flags);
}

void
nouveau_event_destroy(struct nouveau_event **pevent)
nvkm_event_fini(struct nvkm_event *event)
{
	struct nouveau_event *event = *pevent;
	if (event) {
		kfree(event);
		*pevent = NULL;
	if (event->refs) {
		kfree(event->refs);
		event->refs = NULL;
	}
}

int
nouveau_event_create(int types_nr, int index_nr, struct nouveau_event **pevent)
nvkm_event_init(const struct nvkm_event_func *func, int types_nr, int index_nr,
		struct nvkm_event *event)
{
	struct nouveau_event *event;
	int i;

	event = *pevent = kzalloc(sizeof(*event) + (index_nr * types_nr) *
				  sizeof(event->refs[0]), GFP_KERNEL);
	if (!event)
	event->refs = kzalloc(sizeof(*event->refs) * index_nr * types_nr,
			      GFP_KERNEL);
	if (!event->refs)
		return -ENOMEM;

	event->list = kmalloc(sizeof(*event->list) * index_nr, GFP_KERNEL);
	if (!event->list) {
		kfree(event);
		return -ENOMEM;
	}

	spin_lock_init(&event->list_lock);
	spin_lock_init(&event->refs_lock);
	for (i = 0; i < index_nr; i++)
		INIT_LIST_HEAD(&event->list[i]);
	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;
}
+113 −2
Original line number Diff line number Diff line
@@ -146,9 +146,7 @@ nouveau_handle_create(struct nouveau_object *parent, u32 _parent, u32 _handle,
	}

	hprintk(handle, TRACE, "created\n");

	*phandle = handle;

	return 0;
}

@@ -224,3 +222,116 @@ nouveau_handle_put(struct nouveau_handle *handle)
	if (handle)
		nouveau_namedb_put(handle);
}

int
nouveau_handle_new(struct nouveau_object *client, u32 _parent, u32 _handle,
		   u16 _oclass, void *data, u32 size,
		   struct nouveau_object **pobject)
{
	struct nouveau_object *parent = NULL;
	struct nouveau_object *engctx = NULL;
	struct nouveau_object *object = NULL;
	struct nouveau_object *engine;
	struct nouveau_oclass *oclass;
	struct nouveau_handle *handle;
	int ret;

	/* lookup parent object and ensure it *is* a parent */
	parent = nouveau_handle_ref(client, _parent);
	if (!parent) {
		nv_error(client, "parent 0x%08x not found\n", _parent);
		return -ENOENT;
	}

	if (!nv_iclass(parent, NV_PARENT_CLASS)) {
		nv_error(parent, "cannot have children\n");
		ret = -EINVAL;
		goto fail_class;
	}

	/* check that parent supports the requested subclass */
	ret = nouveau_parent_sclass(parent, _oclass, &engine, &oclass);
	if (ret) {
		nv_debug(parent, "illegal class 0x%04x\n", _oclass);
		goto fail_class;
	}

	/* make sure engine init has been completed *before* any objects
	 * it controls are created - the constructors may depend on
	 * state calculated at init (ie. default context construction)
	 */
	if (engine) {
		ret = nouveau_object_inc(engine);
		if (ret)
			goto fail_class;
	}

	/* if engine requires it, create a context object to insert
	 * between the parent and its children (eg. PGRAPH context)
	 */
	if (engine && nv_engine(engine)->cclass) {
		ret = nouveau_object_ctor(parent, engine,
					  nv_engine(engine)->cclass,
					  data, size, &engctx);
		if (ret)
			goto fail_engctx;
	} else {
		nouveau_object_ref(parent, &engctx);
	}

	/* finally, create new object and bind it to its handle */
	ret = nouveau_object_ctor(engctx, engine, oclass, data, size, &object);
	*pobject = object;
	if (ret)
		goto fail_ctor;

	ret = nouveau_object_inc(object);
	if (ret)
		goto fail_init;

	ret = nouveau_handle_create(parent, _parent, _handle, object, &handle);
	if (ret)
		goto fail_handle;

	ret = nouveau_handle_init(handle);
	if (ret)
		nouveau_handle_destroy(handle);

fail_handle:
	nouveau_object_dec(object, false);
fail_init:
	nouveau_object_ref(NULL, &object);
fail_ctor:
	nouveau_object_ref(NULL, &engctx);
fail_engctx:
	if (engine)
		nouveau_object_dec(engine, false);
fail_class:
	nouveau_object_ref(NULL, &parent);
	return ret;
}

int
nouveau_handle_del(struct nouveau_object *client, u32 _parent, u32 _handle)
{
	struct nouveau_object *parent = NULL;
	struct nouveau_object *namedb = NULL;
	struct nouveau_handle *handle = NULL;

	parent = nouveau_handle_ref(client, _parent);
	if (!parent)
		return -ENOENT;

	namedb = nv_pclass(parent, NV_NAMEDB_CLASS);
	if (namedb) {
		handle = nouveau_namedb_get(nv_namedb(namedb), _handle);
		if (handle) {
			nouveau_namedb_put(handle);
			nouveau_handle_fini(handle, false);
			nouveau_handle_destroy(handle);
		}
	}

	nouveau_object_ref(NULL, &parent);
	return handle ? 0 : -EINVAL;
}
Loading