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(¬ify->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, ¬ify->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