1c39f472eSBen Skeggs /*
2c39f472eSBen Skeggs  * Copyright 2012 Red Hat Inc.
3c39f472eSBen Skeggs  *
4c39f472eSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
5c39f472eSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
6c39f472eSBen Skeggs  * to deal in the Software without restriction, including without limitation
7c39f472eSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c39f472eSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
9c39f472eSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
10c39f472eSBen Skeggs  *
11c39f472eSBen Skeggs  * The above copyright notice and this permission notice shall be included in
12c39f472eSBen Skeggs  * all copies or substantial portions of the Software.
13c39f472eSBen Skeggs  *
14c39f472eSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15c39f472eSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16c39f472eSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17c39f472eSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18c39f472eSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19c39f472eSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20c39f472eSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
21c39f472eSBen Skeggs  *
22c39f472eSBen Skeggs  * Authors: Ben Skeggs
23c39f472eSBen Skeggs  */
24c39f472eSBen Skeggs #include <core/client.h>
255025407bSBen Skeggs #include <core/device.h>
26c39f472eSBen Skeggs #include <core/handle.h>
275025407bSBen Skeggs #include <core/notify.h>
28c39f472eSBen Skeggs #include <core/option.h>
295025407bSBen Skeggs 
30c39f472eSBen Skeggs #include <nvif/class.h>
31c39f472eSBen Skeggs #include <nvif/event.h>
325025407bSBen Skeggs #include <nvif/unpack.h>
33c39f472eSBen Skeggs 
34c39f472eSBen Skeggs struct nvkm_client_notify {
355025407bSBen Skeggs 	struct nvkm_client *client;
36c39f472eSBen Skeggs 	struct nvkm_notify n;
37c39f472eSBen Skeggs 	u8 version;
38c39f472eSBen Skeggs 	u8 size;
39c39f472eSBen Skeggs 	union {
40c39f472eSBen Skeggs 		struct nvif_notify_rep_v0 v0;
41c39f472eSBen Skeggs 	} rep;
42c39f472eSBen Skeggs };
43c39f472eSBen Skeggs 
44c39f472eSBen Skeggs static int
45c39f472eSBen Skeggs nvkm_client_notify(struct nvkm_notify *n)
46c39f472eSBen Skeggs {
47c39f472eSBen Skeggs 	struct nvkm_client_notify *notify = container_of(n, typeof(*notify), n);
485025407bSBen Skeggs 	struct nvkm_client *client = notify->client;
49c39f472eSBen Skeggs 	return client->ntfy(&notify->rep, notify->size, n->data, n->size);
50c39f472eSBen Skeggs }
51c39f472eSBen Skeggs 
52c39f472eSBen Skeggs int
535025407bSBen Skeggs nvkm_client_notify_put(struct nvkm_client *client, int index)
54c39f472eSBen Skeggs {
55c39f472eSBen Skeggs 	if (index < ARRAY_SIZE(client->notify)) {
56c39f472eSBen Skeggs 		if (client->notify[index]) {
57c39f472eSBen Skeggs 			nvkm_notify_put(&client->notify[index]->n);
58c39f472eSBen Skeggs 			return 0;
59c39f472eSBen Skeggs 		}
60c39f472eSBen Skeggs 	}
61c39f472eSBen Skeggs 	return -ENOENT;
62c39f472eSBen Skeggs }
63c39f472eSBen Skeggs 
64c39f472eSBen Skeggs int
655025407bSBen Skeggs nvkm_client_notify_get(struct nvkm_client *client, int index)
66c39f472eSBen Skeggs {
67c39f472eSBen Skeggs 	if (index < ARRAY_SIZE(client->notify)) {
68c39f472eSBen Skeggs 		if (client->notify[index]) {
69c39f472eSBen Skeggs 			nvkm_notify_get(&client->notify[index]->n);
70c39f472eSBen Skeggs 			return 0;
71c39f472eSBen Skeggs 		}
72c39f472eSBen Skeggs 	}
73c39f472eSBen Skeggs 	return -ENOENT;
74c39f472eSBen Skeggs }
75c39f472eSBen Skeggs 
76c39f472eSBen Skeggs int
775025407bSBen Skeggs nvkm_client_notify_del(struct nvkm_client *client, int index)
78c39f472eSBen Skeggs {
79c39f472eSBen Skeggs 	if (index < ARRAY_SIZE(client->notify)) {
80c39f472eSBen Skeggs 		if (client->notify[index]) {
81c39f472eSBen Skeggs 			nvkm_notify_fini(&client->notify[index]->n);
82c39f472eSBen Skeggs 			kfree(client->notify[index]);
83c39f472eSBen Skeggs 			client->notify[index] = NULL;
84c39f472eSBen Skeggs 			return 0;
85c39f472eSBen Skeggs 		}
86c39f472eSBen Skeggs 	}
87c39f472eSBen Skeggs 	return -ENOENT;
88c39f472eSBen Skeggs }
89c39f472eSBen Skeggs 
90c39f472eSBen Skeggs int
915025407bSBen Skeggs nvkm_client_notify_new(struct nvkm_object *object,
92c39f472eSBen Skeggs 		       struct nvkm_event *event, void *data, u32 size)
93c39f472eSBen Skeggs {
945025407bSBen Skeggs 	struct nvkm_client *client = nvkm_client(object);
95c39f472eSBen Skeggs 	struct nvkm_client_notify *notify;
96c39f472eSBen Skeggs 	union {
97c39f472eSBen Skeggs 		struct nvif_notify_req_v0 v0;
98c39f472eSBen Skeggs 	} *req = data;
99c39f472eSBen Skeggs 	u8  index, reply;
100c39f472eSBen Skeggs 	int ret;
101c39f472eSBen Skeggs 
102c39f472eSBen Skeggs 	for (index = 0; index < ARRAY_SIZE(client->notify); index++) {
103c39f472eSBen Skeggs 		if (!client->notify[index])
104c39f472eSBen Skeggs 			break;
105c39f472eSBen Skeggs 	}
106c39f472eSBen Skeggs 
107c39f472eSBen Skeggs 	if (index == ARRAY_SIZE(client->notify))
108c39f472eSBen Skeggs 		return -ENOSPC;
109c39f472eSBen Skeggs 
110c39f472eSBen Skeggs 	notify = kzalloc(sizeof(*notify), GFP_KERNEL);
111c39f472eSBen Skeggs 	if (!notify)
112c39f472eSBen Skeggs 		return -ENOMEM;
113c39f472eSBen Skeggs 
11453003941SBen Skeggs 	nvif_ioctl(object, "notify new size %d\n", size);
115c39f472eSBen Skeggs 	if (nvif_unpack(req->v0, 0, 0, true)) {
11653003941SBen Skeggs 		nvif_ioctl(object, "notify new vers %d reply %d route %02x "
117c39f472eSBen Skeggs 				   "token %llx\n", req->v0.version,
118c39f472eSBen Skeggs 			   req->v0.reply, req->v0.route, req->v0.token);
119c39f472eSBen Skeggs 		notify->version = req->v0.version;
120c39f472eSBen Skeggs 		notify->size = sizeof(notify->rep.v0);
121c39f472eSBen Skeggs 		notify->rep.v0.version = req->v0.version;
122c39f472eSBen Skeggs 		notify->rep.v0.route = req->v0.route;
123c39f472eSBen Skeggs 		notify->rep.v0.token = req->v0.token;
124c39f472eSBen Skeggs 		reply = req->v0.reply;
125c39f472eSBen Skeggs 	}
126c39f472eSBen Skeggs 
127c39f472eSBen Skeggs 	if (ret == 0) {
128c39f472eSBen Skeggs 		ret = nvkm_notify_init(object, event, nvkm_client_notify,
129c39f472eSBen Skeggs 				       false, data, size, reply, &notify->n);
130c39f472eSBen Skeggs 		if (ret == 0) {
131c39f472eSBen Skeggs 			client->notify[index] = notify;
132c39f472eSBen Skeggs 			notify->client = client;
133c39f472eSBen Skeggs 			return index;
134c39f472eSBen Skeggs 		}
135c39f472eSBen Skeggs 	}
136c39f472eSBen Skeggs 
137c39f472eSBen Skeggs 	kfree(notify);
138c39f472eSBen Skeggs 	return ret;
139c39f472eSBen Skeggs }
140c39f472eSBen Skeggs 
141c39f472eSBen Skeggs static int
1425025407bSBen Skeggs nvkm_client_mthd_devlist(struct nvkm_object *object, void *data, u32 size)
143c39f472eSBen Skeggs {
144c39f472eSBen Skeggs 	union {
145c39f472eSBen Skeggs 		struct nv_client_devlist_v0 v0;
146c39f472eSBen Skeggs 	} *args = data;
147c39f472eSBen Skeggs 	int ret;
148c39f472eSBen Skeggs 
14953003941SBen Skeggs 	nvif_ioctl(object, "client devlist size %d\n", size);
150c39f472eSBen Skeggs 	if (nvif_unpack(args->v0, 0, 0, true)) {
15153003941SBen Skeggs 		nvif_ioctl(object, "client devlist vers %d count %d\n",
152c39f472eSBen Skeggs 			   args->v0.version, args->v0.count);
153c39f472eSBen Skeggs 		if (size == sizeof(args->v0.device[0]) * args->v0.count) {
1545025407bSBen Skeggs 			ret = nvkm_device_list(args->v0.device, args->v0.count);
155c39f472eSBen Skeggs 			if (ret >= 0) {
156c39f472eSBen Skeggs 				args->v0.count = ret;
157c39f472eSBen Skeggs 				ret = 0;
158c39f472eSBen Skeggs 			}
159c39f472eSBen Skeggs 		} else {
160c39f472eSBen Skeggs 			ret = -EINVAL;
161c39f472eSBen Skeggs 		}
162c39f472eSBen Skeggs 	}
163c39f472eSBen Skeggs 
164c39f472eSBen Skeggs 	return ret;
165c39f472eSBen Skeggs }
166c39f472eSBen Skeggs 
167c39f472eSBen Skeggs static int
1685025407bSBen Skeggs nvkm_client_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
169c39f472eSBen Skeggs {
170c39f472eSBen Skeggs 	switch (mthd) {
171c39f472eSBen Skeggs 	case NV_CLIENT_DEVLIST:
1725025407bSBen Skeggs 		return nvkm_client_mthd_devlist(object, data, size);
173c39f472eSBen Skeggs 	default:
174c39f472eSBen Skeggs 		break;
175c39f472eSBen Skeggs 	}
176c39f472eSBen Skeggs 	return -EINVAL;
177c39f472eSBen Skeggs }
178c39f472eSBen Skeggs 
17924bd0930SBen Skeggs static int
18024bd0930SBen Skeggs nvkm_client_child_new(const struct nvkm_oclass *oclass,
18124bd0930SBen Skeggs 		      void *data, u32 size, struct nvkm_object **pobject)
18224bd0930SBen Skeggs {
18324bd0930SBen Skeggs 	static struct nvkm_oclass devobj = {
18424bd0930SBen Skeggs 		.handle = NV_DEVICE,
18524bd0930SBen Skeggs 		.ofuncs = &nvkm_udevice_ofuncs,
18624bd0930SBen Skeggs 	};
18724bd0930SBen Skeggs 	return nvkm_object_old(oclass->parent, NULL, &devobj, data, size, pobject);
18824bd0930SBen Skeggs }
18924bd0930SBen Skeggs 
19024bd0930SBen Skeggs static int
19124bd0930SBen Skeggs nvkm_client_child_get(struct nvkm_object *object, int index,
19224bd0930SBen Skeggs 		      struct nvkm_oclass *oclass)
19324bd0930SBen Skeggs {
19424bd0930SBen Skeggs 	if (index == 0) {
19524bd0930SBen Skeggs 		oclass->base.oclass = NV_DEVICE;
19624bd0930SBen Skeggs 		oclass->base.minver = 0;
19724bd0930SBen Skeggs 		oclass->base.maxver = 0;
19824bd0930SBen Skeggs 		oclass->ctor = nvkm_client_child_new;
19924bd0930SBen Skeggs 		return 0;
20024bd0930SBen Skeggs 	}
20124bd0930SBen Skeggs 	return -EINVAL;
20224bd0930SBen Skeggs }
20324bd0930SBen Skeggs 
20424bd0930SBen Skeggs static const struct nvkm_object_func
20524bd0930SBen Skeggs nvkm_client_object_func = {
2065025407bSBen Skeggs 	.mthd = nvkm_client_mthd,
20724bd0930SBen Skeggs 	.sclass = nvkm_client_child_get,
208c39f472eSBen Skeggs };
209c39f472eSBen Skeggs 
210bf81df9bSBen Skeggs void
211bf81df9bSBen Skeggs nvkm_client_remove(struct nvkm_client *client, struct nvkm_handle *object)
212bf81df9bSBen Skeggs {
213bf81df9bSBen Skeggs 	if (!RB_EMPTY_NODE(&object->rb))
214bf81df9bSBen Skeggs 		rb_erase(&object->rb, &client->objroot);
215bf81df9bSBen Skeggs }
216bf81df9bSBen Skeggs 
217bf81df9bSBen Skeggs bool
218bf81df9bSBen Skeggs nvkm_client_insert(struct nvkm_client *client, struct nvkm_handle *object)
219bf81df9bSBen Skeggs {
220bf81df9bSBen Skeggs 	struct rb_node **ptr = &client->objroot.rb_node;
221bf81df9bSBen Skeggs 	struct rb_node *parent = NULL;
222bf81df9bSBen Skeggs 
223bf81df9bSBen Skeggs 	while (*ptr) {
224bf81df9bSBen Skeggs 		struct nvkm_handle *this =
225bf81df9bSBen Skeggs 			container_of(*ptr, typeof(*this), rb);
226bf81df9bSBen Skeggs 		parent = *ptr;
227bf81df9bSBen Skeggs 		if (object->handle < this->handle)
228bf81df9bSBen Skeggs 			ptr = &parent->rb_left;
229bf81df9bSBen Skeggs 		else
230bf81df9bSBen Skeggs 		if (object->handle > this->handle)
231bf81df9bSBen Skeggs 			ptr = &parent->rb_right;
232bf81df9bSBen Skeggs 		else
233bf81df9bSBen Skeggs 			return false;
234bf81df9bSBen Skeggs 	}
235bf81df9bSBen Skeggs 
236bf81df9bSBen Skeggs 	rb_link_node(&object->rb, parent, ptr);
237bf81df9bSBen Skeggs 	rb_insert_color(&object->rb, &client->objroot);
238bf81df9bSBen Skeggs 	return true;
239bf81df9bSBen Skeggs }
240bf81df9bSBen Skeggs 
241bf81df9bSBen Skeggs struct nvkm_handle *
242bf81df9bSBen Skeggs nvkm_client_search(struct nvkm_client *client, u64 handle)
243bf81df9bSBen Skeggs {
244bf81df9bSBen Skeggs 	struct rb_node *node = client->objroot.rb_node;
245bf81df9bSBen Skeggs 	while (node) {
246bf81df9bSBen Skeggs 		struct nvkm_handle *object =
247bf81df9bSBen Skeggs 			container_of(node, typeof(*object), rb);
248bf81df9bSBen Skeggs 		if (handle < object->handle)
249bf81df9bSBen Skeggs 			node = node->rb_left;
250bf81df9bSBen Skeggs 		else
251bf81df9bSBen Skeggs 		if (handle > object->handle)
252bf81df9bSBen Skeggs 			node = node->rb_right;
253bf81df9bSBen Skeggs 		else
254bf81df9bSBen Skeggs 			return object;
255bf81df9bSBen Skeggs 	}
256bf81df9bSBen Skeggs 	return NULL;
257bf81df9bSBen Skeggs }
258bf81df9bSBen Skeggs 
259c39f472eSBen Skeggs int
2605025407bSBen Skeggs nvkm_client_fini(struct nvkm_client *client, bool suspend)
261c39f472eSBen Skeggs {
26224bd0930SBen Skeggs 	struct nvkm_object *object = &client->object;
263c39f472eSBen Skeggs 	const char *name[2] = { "fini", "suspend" };
264c39f472eSBen Skeggs 	int ret, i;
26553003941SBen Skeggs 	nvif_trace(object, "%s running\n", name[suspend]);
26653003941SBen Skeggs 	nvif_trace(object, "%s notify\n", name[suspend]);
267c39f472eSBen Skeggs 	for (i = 0; i < ARRAY_SIZE(client->notify); i++)
268c39f472eSBen Skeggs 		nvkm_client_notify_put(client, i);
26953003941SBen Skeggs 	nvif_trace(object, "%s object\n", name[suspend]);
2705025407bSBen Skeggs 	ret = nvkm_handle_fini(client->root, suspend);
27153003941SBen Skeggs 	nvif_trace(object, "%s completed with %d\n", name[suspend], ret);
272c39f472eSBen Skeggs 	return ret;
273c39f472eSBen Skeggs }
274c39f472eSBen Skeggs 
27576ecea5bSBen Skeggs int
27676ecea5bSBen Skeggs nvkm_client_init(struct nvkm_client *client)
27776ecea5bSBen Skeggs {
27824bd0930SBen Skeggs 	struct nvkm_object *object = &client->object;
27976ecea5bSBen Skeggs 	int ret;
28076ecea5bSBen Skeggs 	nvif_trace(object, "init running\n");
28176ecea5bSBen Skeggs 	ret = nvkm_handle_init(client->root);
28276ecea5bSBen Skeggs 	nvif_trace(object, "init completed with %d\n", ret);
28376ecea5bSBen Skeggs 	return ret;
28476ecea5bSBen Skeggs }
28576ecea5bSBen Skeggs 
28676ecea5bSBen Skeggs void
28776ecea5bSBen Skeggs nvkm_client_del(struct nvkm_client **pclient)
28876ecea5bSBen Skeggs {
28976ecea5bSBen Skeggs 	struct nvkm_client *client = *pclient;
29076ecea5bSBen Skeggs 	int i;
29176ecea5bSBen Skeggs 	if (client) {
29276ecea5bSBen Skeggs 		nvkm_client_fini(client, false);
29376ecea5bSBen Skeggs 		for (i = 0; i < ARRAY_SIZE(client->notify); i++)
29476ecea5bSBen Skeggs 			nvkm_client_notify_del(client, i);
29576ecea5bSBen Skeggs 		nvkm_handle_destroy(client->root);
29624bd0930SBen Skeggs 		kfree(*pclient);
29776ecea5bSBen Skeggs 		*pclient = NULL;
29876ecea5bSBen Skeggs 	}
29976ecea5bSBen Skeggs }
30076ecea5bSBen Skeggs 
30176ecea5bSBen Skeggs int
3024e7e62d6SBen Skeggs nvkm_client_new(const char *name, u64 device, const char *cfg,
30376ecea5bSBen Skeggs 		const char *dbg, struct nvkm_client **pclient)
30476ecea5bSBen Skeggs {
30524bd0930SBen Skeggs 	struct nvkm_oclass oclass = {};
30676ecea5bSBen Skeggs 	struct nvkm_client *client;
30776ecea5bSBen Skeggs 	int ret;
30876ecea5bSBen Skeggs 
30924bd0930SBen Skeggs 	if (!(client = *pclient = kzalloc(sizeof(*client), GFP_KERNEL)))
31024bd0930SBen Skeggs 		return -ENOMEM;
31124bd0930SBen Skeggs 	oclass.client = client;
31276ecea5bSBen Skeggs 
31324bd0930SBen Skeggs 	nvkm_object_ctor(&nvkm_client_object_func, &oclass, &client->object);
31476ecea5bSBen Skeggs 	snprintf(client->name, sizeof(client->name), "%s", name);
31524bd0930SBen Skeggs 	client->device = device;
31676ecea5bSBen Skeggs 	client->debug = nvkm_dbgopt(dbg, "CLIENT");
317bf81df9bSBen Skeggs 	client->objroot = RB_ROOT;
31824bd0930SBen Skeggs 
31924bd0930SBen Skeggs 	ret = nvkm_handle_create(NULL, ~0, &client->object, &client->root);
32024bd0930SBen Skeggs 	if (ret)
32124bd0930SBen Skeggs 		nvkm_client_del(pclient);
32224bd0930SBen Skeggs 	return ret;
32376ecea5bSBen Skeggs }
32476ecea5bSBen Skeggs 
325c39f472eSBen Skeggs const char *
3265025407bSBen Skeggs nvkm_client_name(void *obj)
327c39f472eSBen Skeggs {
328c39f472eSBen Skeggs 	const char *client_name = "unknown";
3295025407bSBen Skeggs 	struct nvkm_client *client = nvkm_client(obj);
330c39f472eSBen Skeggs 	if (client)
331c39f472eSBen Skeggs 		client_name = client->name;
332c39f472eSBen Skeggs 	return client_name;
333c39f472eSBen Skeggs }
334