1 /* $NetBSD: nouveau_nvkm_core_client.c,v 1.6 2021/12/19 11:34:44 riastradh Exp $ */ 2 3 /* 4 * Copyright 2012 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Ben Skeggs 25 */ 26 #include <sys/cdefs.h> 27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_core_client.c,v 1.6 2021/12/19 11:34:44 riastradh Exp $"); 28 29 #include <core/client.h> 30 #include <core/device.h> 31 #include <core/notify.h> 32 #include <core/option.h> 33 34 #include <nvif/class.h> 35 #include <nvif/event.h> 36 #include <nvif/if0000.h> 37 #include <nvif/unpack.h> 38 39 static int 40 nvkm_uclient_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, 41 struct nvkm_object **pobject) 42 { 43 union { 44 struct nvif_client_v0 v0; 45 } *args = argv; 46 struct nvkm_client *client; 47 int ret = -ENOSYS; 48 49 if (!(ret = nvif_unpack(ret, &argv, &argc, args->v0, 0, 0, false))){ 50 args->v0.name[sizeof(args->v0.name) - 1] = 0; 51 ret = nvkm_client_new(args->v0.name, args->v0.device, NULL, 52 NULL, oclass->client->ntfy, &client); 53 if (ret) 54 return ret; 55 } else 56 return ret; 57 58 client->object.client = oclass->client; 59 client->object.handle = oclass->handle; 60 client->object.route = oclass->route; 61 client->object.token = oclass->token; 62 client->object.object = oclass->object; 63 client->debug = oclass->client->debug; 64 *pobject = &client->object; 65 return 0; 66 } 67 68 const struct nvkm_sclass 69 nvkm_uclient_sclass = { 70 .oclass = NVIF_CLASS_CLIENT, 71 .minver = 0, 72 .maxver = 0, 73 .ctor = nvkm_uclient_new, 74 }; 75 76 struct nvkm_client_notify { 77 struct nvkm_client *client; 78 struct nvkm_notify n; 79 u8 version; 80 u8 size; 81 union { 82 struct nvif_notify_rep_v0 v0; 83 } rep; 84 }; 85 86 static int 87 nvkm_client_notify(struct nvkm_notify *n) 88 { 89 struct nvkm_client_notify *notify = container_of(n, typeof(*notify), n); 90 struct nvkm_client *client = notify->client; 91 return client->ntfy(¬ify->rep, notify->size, n->data, n->size); 92 } 93 94 int 95 nvkm_client_notify_put(struct nvkm_client *client, int index) 96 { 97 if (index < ARRAY_SIZE(client->notify)) { 98 if (client->notify[index]) { 99 nvkm_notify_put(&client->notify[index]->n); 100 return 0; 101 } 102 } 103 return -ENOENT; 104 } 105 106 int 107 nvkm_client_notify_get(struct nvkm_client *client, int index) 108 { 109 if (index < ARRAY_SIZE(client->notify)) { 110 if (client->notify[index]) { 111 nvkm_notify_get(&client->notify[index]->n); 112 return 0; 113 } 114 } 115 return -ENOENT; 116 } 117 118 int 119 nvkm_client_notify_del(struct nvkm_client *client, int index) 120 { 121 if (index < ARRAY_SIZE(client->notify)) { 122 if (client->notify[index]) { 123 nvkm_notify_fini(&client->notify[index]->n); 124 kfree(client->notify[index]); 125 client->notify[index] = NULL; 126 return 0; 127 } 128 } 129 return -ENOENT; 130 } 131 132 int 133 nvkm_client_notify_new(struct nvkm_object *object, 134 struct nvkm_event *event, void *data, u32 size) 135 { 136 struct nvkm_client *client = object->client; 137 struct nvkm_client_notify *notify; 138 union { 139 struct nvif_notify_req_v0 v0; 140 } *req = data; 141 u8 index, reply; 142 int ret = -ENOSYS; 143 144 for (index = 0; index < ARRAY_SIZE(client->notify); index++) { 145 if (!client->notify[index]) 146 break; 147 } 148 149 if (index == ARRAY_SIZE(client->notify)) 150 return -ENOSPC; 151 152 notify = kzalloc(sizeof(*notify), GFP_KERNEL); 153 if (!notify) 154 return -ENOMEM; 155 156 nvif_ioctl(object, "notify new size %d\n", size); 157 if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, true))) { 158 nvif_ioctl(object, "notify new vers %d reply %d route %02x " 159 "token %"PRIx64"", req->v0.version, 160 req->v0.reply, req->v0.route, req->v0.token); 161 notify->version = req->v0.version; 162 notify->size = sizeof(notify->rep.v0); 163 notify->rep.v0.version = req->v0.version; 164 notify->rep.v0.route = req->v0.route; 165 notify->rep.v0.token = req->v0.token; 166 reply = req->v0.reply; 167 } 168 169 if (ret == 0) { 170 ret = nvkm_notify_init(object, event, nvkm_client_notify, 171 false, data, size, reply, ¬ify->n); 172 if (ret == 0) { 173 client->notify[index] = notify; 174 notify->client = client; 175 return index; 176 } 177 } 178 179 kfree(notify); 180 return ret; 181 } 182 183 static const struct nvkm_object_func nvkm_client; 184 struct nvkm_client * 185 nvkm_client_search(struct nvkm_client *client, u64 handle) 186 { 187 struct nvkm_object *object; 188 189 object = nvkm_object_search(client, handle, &nvkm_client); 190 if (IS_ERR(object)) 191 return (void *)object; 192 193 return nvkm_client(object); 194 } 195 196 static int 197 nvkm_client_mthd_devlist(struct nvkm_client *client, void *data, u32 size) 198 { 199 union { 200 struct nvif_client_devlist_v0 v0; 201 } *args = data; 202 int ret = -ENOSYS; 203 204 nvif_ioctl(&client->object, "client devlist size %d\n", size); 205 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 206 nvif_ioctl(&client->object, "client devlist vers %d count %d\n", 207 args->v0.version, args->v0.count); 208 if (size == sizeof(args->v0.device[0]) * args->v0.count) { 209 ret = nvkm_device_list(args->v0.device, args->v0.count); 210 if (ret >= 0) { 211 args->v0.count = ret; 212 ret = 0; 213 } 214 } else { 215 ret = -EINVAL; 216 } 217 } 218 219 return ret; 220 } 221 222 static int 223 nvkm_client_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) 224 { 225 struct nvkm_client *client = nvkm_client(object); 226 switch (mthd) { 227 case NVIF_CLIENT_V0_DEVLIST: 228 return nvkm_client_mthd_devlist(client, data, size); 229 default: 230 break; 231 } 232 return -EINVAL; 233 } 234 235 static int 236 nvkm_client_child_new(const struct nvkm_oclass *oclass, 237 void *data, u32 size, struct nvkm_object **pobject) 238 { 239 return oclass->base.ctor(oclass, data, size, pobject); 240 } 241 242 static int 243 nvkm_client_child_get(struct nvkm_object *object, int index, 244 struct nvkm_oclass *oclass) 245 { 246 const struct nvkm_sclass *sclass; 247 248 switch (index) { 249 case 0: sclass = &nvkm_uclient_sclass; break; 250 case 1: sclass = &nvkm_udevice_sclass; break; 251 default: 252 return -EINVAL; 253 } 254 255 oclass->ctor = nvkm_client_child_new; 256 oclass->base = *sclass; 257 return 0; 258 } 259 260 static int 261 nvkm_client_fini(struct nvkm_object *object, bool suspend) 262 { 263 struct nvkm_client *client = nvkm_client(object); 264 const char *name[2] = { "fini", "suspend" }; 265 int i; 266 nvif_debug(object, "%s notify\n", name[suspend]); 267 for (i = 0; i < ARRAY_SIZE(client->notify); i++) 268 nvkm_client_notify_put(client, i); 269 return 0; 270 } 271 272 static void * 273 nvkm_client_dtor(struct nvkm_object *object) 274 { 275 struct nvkm_client *client = nvkm_client(object); 276 int i; 277 for (i = 0; i < ARRAY_SIZE(client->notify); i++) 278 nvkm_client_notify_del(client, i); 279 spin_lock_destroy(&client->lock); 280 return client; 281 } 282 283 static const struct nvkm_object_func 284 nvkm_client = { 285 .dtor = nvkm_client_dtor, 286 .fini = nvkm_client_fini, 287 .mthd = nvkm_client_mthd, 288 .sclass = nvkm_client_child_get, 289 }; 290 291 #ifdef __NetBSD__ 292 /* sync with nouveau_nvkm_core_object.c */ 293 static int 294 compare_object_nodes(void *cookie, const void *va, const void *vb) 295 { 296 const struct nvkm_object *oa = va; 297 const struct nvkm_object *ob = vb; 298 299 if (oa->object < ob->object) 300 return -1; 301 if (oa->object > ob->object) 302 return +1; 303 return 0; 304 } 305 306 static int 307 compare_object_key(void *cookie, const void *vo, const void *vk) 308 { 309 const struct nvkm_object *o = vo; 310 const u64 *k = vk; 311 312 if (o->object < *k) 313 return -1; 314 if (o->object > *k) 315 return +1; 316 return 0; 317 } 318 319 static const rb_tree_ops_t nvkm_client_objtree_ops = { 320 .rbto_compare_nodes = compare_object_nodes, 321 .rbto_compare_key = compare_object_key, 322 .rbto_node_offset = offsetof(struct nvkm_object, node), 323 }; 324 #endif 325 326 int 327 nvkm_client_new(const char *name, u64 device, const char *cfg, 328 const char *dbg, 329 int (*ntfy)(const void *, u32, const void *, u32), 330 struct nvkm_client **pclient) 331 { 332 struct nvkm_oclass oclass = { .base = nvkm_uclient_sclass }; 333 struct nvkm_client *client; 334 335 if (!(client = *pclient = kzalloc(sizeof(*client), GFP_KERNEL))) 336 return -ENOMEM; 337 oclass.client = client; 338 339 nvkm_object_ctor(&nvkm_client, &oclass, &client->object); 340 snprintf(client->name, sizeof(client->name), "%s", name); 341 client->device = device; 342 client->debug = nvkm_dbgopt(dbg, "CLIENT"); 343 #ifdef __NetBSD__ 344 rb_tree_init(&client->objtree, &nvkm_client_objtree_ops); 345 #else 346 client->objroot = RB_ROOT; 347 #endif 348 client->ntfy = ntfy; 349 INIT_LIST_HEAD(&client->umem); 350 spin_lock_init(&client->lock); 351 return 0; 352 } 353