Commit | Line | Data |
---|---|---|
9274f4a9 BS |
1 | /* |
2 | * Copyright 2012 Red Hat Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: Ben Skeggs | |
23 | */ | |
9274f4a9 | 24 | #include <core/client.h> |
5025407b | 25 | #include <core/device.h> |
5025407b | 26 | #include <core/notify.h> |
9274f4a9 BS |
27 | #include <core/option.h> |
28 | ||
5025407b | 29 | #include <nvif/class.h> |
8ec2a6ec | 30 | #include <nvif/event.h> |
13db6d6e | 31 | #include <nvif/if0000.h> |
5025407b | 32 | #include <nvif/unpack.h> |
9274f4a9 | 33 | |
8ec2a6ec | 34 | struct nvkm_client_notify { |
5025407b | 35 | struct nvkm_client *client; |
8ec2a6ec BS |
36 | struct nvkm_notify n; |
37 | u8 version; | |
38 | u8 size; | |
39 | union { | |
40 | struct nvif_notify_rep_v0 v0; | |
41 | } rep; | |
42 | }; | |
43 | ||
44 | static int | |
45 | nvkm_client_notify(struct nvkm_notify *n) | |
46 | { | |
47 | struct nvkm_client_notify *notify = container_of(n, typeof(*notify), n); | |
5025407b | 48 | struct nvkm_client *client = notify->client; |
8ec2a6ec BS |
49 | return client->ntfy(¬ify->rep, notify->size, n->data, n->size); |
50 | } | |
51 | ||
52 | int | |
5025407b | 53 | nvkm_client_notify_put(struct nvkm_client *client, int index) |
8ec2a6ec BS |
54 | { |
55 | if (index < ARRAY_SIZE(client->notify)) { | |
56 | if (client->notify[index]) { | |
57 | nvkm_notify_put(&client->notify[index]->n); | |
58 | return 0; | |
59 | } | |
60 | } | |
61 | return -ENOENT; | |
62 | } | |
63 | ||
64 | int | |
5025407b | 65 | nvkm_client_notify_get(struct nvkm_client *client, int index) |
8ec2a6ec BS |
66 | { |
67 | if (index < ARRAY_SIZE(client->notify)) { | |
68 | if (client->notify[index]) { | |
69 | nvkm_notify_get(&client->notify[index]->n); | |
70 | return 0; | |
71 | } | |
72 | } | |
73 | return -ENOENT; | |
74 | } | |
75 | ||
76 | int | |
5025407b | 77 | nvkm_client_notify_del(struct nvkm_client *client, int index) |
8ec2a6ec BS |
78 | { |
79 | if (index < ARRAY_SIZE(client->notify)) { | |
80 | if (client->notify[index]) { | |
81 | nvkm_notify_fini(&client->notify[index]->n); | |
82 | kfree(client->notify[index]); | |
83 | client->notify[index] = NULL; | |
84 | return 0; | |
85 | } | |
86 | } | |
87 | return -ENOENT; | |
88 | } | |
89 | ||
90 | int | |
5025407b | 91 | nvkm_client_notify_new(struct nvkm_object *object, |
8ec2a6ec BS |
92 | struct nvkm_event *event, void *data, u32 size) |
93 | { | |
fbd58ebd | 94 | struct nvkm_client *client = object->client; |
8ec2a6ec BS |
95 | struct nvkm_client_notify *notify; |
96 | union { | |
97 | struct nvif_notify_req_v0 v0; | |
98 | } *req = data; | |
99 | u8 index, reply; | |
f01c4e68 | 100 | int ret = -ENOSYS; |
8ec2a6ec BS |
101 | |
102 | for (index = 0; index < ARRAY_SIZE(client->notify); index++) { | |
103 | if (!client->notify[index]) | |
104 | break; | |
105 | } | |
106 | ||
107 | if (index == ARRAY_SIZE(client->notify)) | |
108 | return -ENOSPC; | |
109 | ||
110 | notify = kzalloc(sizeof(*notify), GFP_KERNEL); | |
111 | if (!notify) | |
112 | return -ENOMEM; | |
113 | ||
53003941 | 114 | nvif_ioctl(object, "notify new size %d\n", size); |
f01c4e68 | 115 | if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, true))) { |
53003941 BS |
116 | nvif_ioctl(object, "notify new vers %d reply %d route %02x " |
117 | "token %llx\n", req->v0.version, | |
118 | req->v0.reply, req->v0.route, req->v0.token); | |
8ec2a6ec BS |
119 | notify->version = req->v0.version; |
120 | notify->size = sizeof(notify->rep.v0); | |
121 | notify->rep.v0.version = req->v0.version; | |
122 | notify->rep.v0.route = req->v0.route; | |
123 | notify->rep.v0.token = req->v0.token; | |
124 | reply = req->v0.reply; | |
125 | } | |
126 | ||
127 | if (ret == 0) { | |
996f5a08 BS |
128 | ret = nvkm_notify_init(object, event, nvkm_client_notify, |
129 | false, data, size, reply, ¬ify->n); | |
8ec2a6ec BS |
130 | if (ret == 0) { |
131 | client->notify[index] = notify; | |
132 | notify->client = client; | |
7caa63c0 | 133 | return index; |
8ec2a6ec BS |
134 | } |
135 | } | |
136 | ||
137 | kfree(notify); | |
7caa63c0 | 138 | return ret; |
8ec2a6ec BS |
139 | } |
140 | ||
803c1787 | 141 | static int |
5025407b | 142 | nvkm_client_mthd_devlist(struct nvkm_object *object, void *data, u32 size) |
803c1787 BS |
143 | { |
144 | union { | |
145 | struct nv_client_devlist_v0 v0; | |
146 | } *args = data; | |
f01c4e68 | 147 | int ret = -ENOSYS; |
803c1787 | 148 | |
53003941 | 149 | nvif_ioctl(object, "client devlist size %d\n", size); |
f01c4e68 | 150 | if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { |
53003941 BS |
151 | nvif_ioctl(object, "client devlist vers %d count %d\n", |
152 | args->v0.version, args->v0.count); | |
803c1787 | 153 | if (size == sizeof(args->v0.device[0]) * args->v0.count) { |
5025407b | 154 | ret = nvkm_device_list(args->v0.device, args->v0.count); |
803c1787 BS |
155 | if (ret >= 0) { |
156 | args->v0.count = ret; | |
157 | ret = 0; | |
158 | } | |
159 | } else { | |
160 | ret = -EINVAL; | |
161 | } | |
162 | } | |
163 | ||
164 | return ret; | |
165 | } | |
166 | ||
167 | static int | |
5025407b | 168 | nvkm_client_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) |
803c1787 BS |
169 | { |
170 | switch (mthd) { | |
171 | case NV_CLIENT_DEVLIST: | |
5025407b | 172 | return nvkm_client_mthd_devlist(object, data, size); |
803c1787 BS |
173 | default: |
174 | break; | |
175 | } | |
176 | return -EINVAL; | |
177 | } | |
178 | ||
24bd0930 BS |
179 | static int |
180 | nvkm_client_child_new(const struct nvkm_oclass *oclass, | |
181 | void *data, u32 size, struct nvkm_object **pobject) | |
182 | { | |
2a9f847f | 183 | return oclass->base.ctor(oclass, data, size, pobject); |
24bd0930 BS |
184 | } |
185 | ||
186 | static int | |
187 | nvkm_client_child_get(struct nvkm_object *object, int index, | |
188 | struct nvkm_oclass *oclass) | |
189 | { | |
2a9f847f BS |
190 | const struct nvkm_sclass *sclass; |
191 | ||
192 | switch (index) { | |
193 | case 0: sclass = &nvkm_udevice_sclass; break; | |
194 | default: | |
195 | return -EINVAL; | |
24bd0930 | 196 | } |
2a9f847f BS |
197 | |
198 | oclass->ctor = nvkm_client_child_new; | |
199 | oclass->base = *sclass; | |
200 | return 0; | |
24bd0930 BS |
201 | } |
202 | ||
203 | static const struct nvkm_object_func | |
204 | nvkm_client_object_func = { | |
205 | .mthd = nvkm_client_mthd, | |
206 | .sclass = nvkm_client_child_get, | |
9274f4a9 BS |
207 | }; |
208 | ||
bf81df9b | 209 | void |
fbd58ebd | 210 | nvkm_client_remove(struct nvkm_client *client, struct nvkm_object *object) |
bf81df9b | 211 | { |
fbd58ebd BS |
212 | if (!RB_EMPTY_NODE(&object->node)) |
213 | rb_erase(&object->node, &client->objroot); | |
bf81df9b BS |
214 | } |
215 | ||
216 | bool | |
fbd58ebd | 217 | nvkm_client_insert(struct nvkm_client *client, struct nvkm_object *object) |
bf81df9b BS |
218 | { |
219 | struct rb_node **ptr = &client->objroot.rb_node; | |
220 | struct rb_node *parent = NULL; | |
221 | ||
222 | while (*ptr) { | |
fbd58ebd BS |
223 | struct nvkm_object *this = |
224 | container_of(*ptr, typeof(*this), node); | |
bf81df9b | 225 | parent = *ptr; |
fbd58ebd | 226 | if (object->object < this->object) |
bf81df9b BS |
227 | ptr = &parent->rb_left; |
228 | else | |
fbd58ebd | 229 | if (object->object > this->object) |
bf81df9b BS |
230 | ptr = &parent->rb_right; |
231 | else | |
232 | return false; | |
233 | } | |
234 | ||
fbd58ebd BS |
235 | rb_link_node(&object->node, parent, ptr); |
236 | rb_insert_color(&object->node, &client->objroot); | |
bf81df9b BS |
237 | return true; |
238 | } | |
239 | ||
fbd58ebd | 240 | struct nvkm_object * |
bf81df9b BS |
241 | nvkm_client_search(struct nvkm_client *client, u64 handle) |
242 | { | |
243 | struct rb_node *node = client->objroot.rb_node; | |
244 | while (node) { | |
fbd58ebd BS |
245 | struct nvkm_object *object = |
246 | container_of(node, typeof(*object), node); | |
247 | if (handle < object->object) | |
bf81df9b BS |
248 | node = node->rb_left; |
249 | else | |
fbd58ebd | 250 | if (handle > object->object) |
bf81df9b BS |
251 | node = node->rb_right; |
252 | else | |
253 | return object; | |
254 | } | |
255 | return NULL; | |
256 | } | |
257 | ||
9274f4a9 | 258 | int |
76ecea5b BS |
259 | nvkm_client_fini(struct nvkm_client *client, bool suspend) |
260 | { | |
24bd0930 | 261 | struct nvkm_object *object = &client->object; |
76ecea5b | 262 | const char *name[2] = { "fini", "suspend" }; |
fbd58ebd BS |
263 | int i; |
264 | nvif_debug(object, "%s notify\n", name[suspend]); | |
76ecea5b BS |
265 | for (i = 0; i < ARRAY_SIZE(client->notify); i++) |
266 | nvkm_client_notify_put(client, i); | |
fbd58ebd | 267 | return nvkm_object_fini(&client->object, suspend); |
76ecea5b BS |
268 | } |
269 | ||
270 | int | |
271 | nvkm_client_init(struct nvkm_client *client) | |
272 | { | |
fbd58ebd | 273 | return nvkm_object_init(&client->object); |
76ecea5b BS |
274 | } |
275 | ||
276 | void | |
277 | nvkm_client_del(struct nvkm_client **pclient) | |
278 | { | |
279 | struct nvkm_client *client = *pclient; | |
280 | int i; | |
281 | if (client) { | |
282 | nvkm_client_fini(client, false); | |
283 | for (i = 0; i < ARRAY_SIZE(client->notify); i++) | |
284 | nvkm_client_notify_del(client, i); | |
fbd58ebd | 285 | nvkm_object_dtor(&client->object); |
24bd0930 | 286 | kfree(*pclient); |
76ecea5b BS |
287 | *pclient = NULL; |
288 | } | |
289 | } | |
290 | ||
291 | int | |
4e7e62d6 | 292 | nvkm_client_new(const char *name, u64 device, const char *cfg, |
76ecea5b | 293 | const char *dbg, struct nvkm_client **pclient) |
9274f4a9 | 294 | { |
24bd0930 | 295 | struct nvkm_oclass oclass = {}; |
5025407b | 296 | struct nvkm_client *client; |
9274f4a9 | 297 | |
24bd0930 BS |
298 | if (!(client = *pclient = kzalloc(sizeof(*client), GFP_KERNEL))) |
299 | return -ENOMEM; | |
300 | oclass.client = client; | |
9274f4a9 | 301 | |
24bd0930 | 302 | nvkm_object_ctor(&nvkm_client_object_func, &oclass, &client->object); |
fa6df8c1 | 303 | snprintf(client->name, sizeof(client->name), "%s", name); |
24bd0930 | 304 | client->device = device; |
5025407b | 305 | client->debug = nvkm_dbgopt(dbg, "CLIENT"); |
bf81df9b | 306 | client->objroot = RB_ROOT; |
0710cc31 | 307 | client->dmaroot = RB_ROOT; |
fbd58ebd | 308 | return 0; |
9274f4a9 | 309 | } |