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 */ 24 #include "priv.h" 25 26 #include <core/client.h> 27 #include <subdev/fb.h> 28 #include <subdev/instmem.h> 29 #include <subdev/timer.h> 30 31 #include <nvif/class.h> 32 #include <nvif/unpack.h> 33 34 struct nvkm_udevice { 35 struct nvkm_parent base; 36 struct nvkm_device *device; 37 }; 38 39 static int 40 nvkm_udevice_info(struct nvkm_object *object, void *data, u32 size) 41 { 42 struct nvkm_udevice *udev = (void *)object; 43 struct nvkm_device *device = udev->device; 44 struct nvkm_fb *fb = device->fb; 45 struct nvkm_instmem *imem = device->imem; 46 union { 47 struct nv_device_info_v0 v0; 48 } *args = data; 49 int ret; 50 51 nvif_ioctl(object, "device info size %d\n", size); 52 if (nvif_unpack(args->v0, 0, 0, false)) { 53 nvif_ioctl(object, "device info vers %d\n", args->v0.version); 54 } else 55 return ret; 56 57 switch (device->chipset) { 58 case 0x01a: 59 case 0x01f: 60 case 0x04c: 61 case 0x04e: 62 case 0x063: 63 case 0x067: 64 case 0x068: 65 case 0x0aa: 66 case 0x0ac: 67 case 0x0af: 68 args->v0.platform = NV_DEVICE_INFO_V0_IGP; 69 break; 70 default: 71 if (device->pdev) { 72 if (pci_find_capability(device->pdev, PCI_CAP_ID_AGP)) 73 args->v0.platform = NV_DEVICE_INFO_V0_AGP; 74 else 75 if (pci_is_pcie(device->pdev)) 76 args->v0.platform = NV_DEVICE_INFO_V0_PCIE; 77 else 78 args->v0.platform = NV_DEVICE_INFO_V0_PCI; 79 } else { 80 args->v0.platform = NV_DEVICE_INFO_V0_SOC; 81 } 82 break; 83 } 84 85 switch (device->card_type) { 86 case NV_04: args->v0.family = NV_DEVICE_INFO_V0_TNT; break; 87 case NV_10: 88 case NV_11: args->v0.family = NV_DEVICE_INFO_V0_CELSIUS; break; 89 case NV_20: args->v0.family = NV_DEVICE_INFO_V0_KELVIN; break; 90 case NV_30: args->v0.family = NV_DEVICE_INFO_V0_RANKINE; break; 91 case NV_40: args->v0.family = NV_DEVICE_INFO_V0_CURIE; break; 92 case NV_50: args->v0.family = NV_DEVICE_INFO_V0_TESLA; break; 93 case NV_C0: args->v0.family = NV_DEVICE_INFO_V0_FERMI; break; 94 case NV_E0: args->v0.family = NV_DEVICE_INFO_V0_KEPLER; break; 95 case GM100: args->v0.family = NV_DEVICE_INFO_V0_MAXWELL; break; 96 default: 97 args->v0.family = 0; 98 break; 99 } 100 101 args->v0.chipset = device->chipset; 102 args->v0.revision = device->chiprev; 103 if (fb && fb->ram) 104 args->v0.ram_size = args->v0.ram_user = fb->ram->size; 105 else 106 args->v0.ram_size = args->v0.ram_user = 0; 107 if (imem && args->v0.ram_size > 0) 108 args->v0.ram_user = args->v0.ram_user - imem->reserved; 109 110 strncpy(args->v0.chip, device->cname, sizeof(args->v0.chip)); 111 strncpy(args->v0.name, device->cname, sizeof(args->v0.name)); 112 return 0; 113 } 114 115 static int 116 nvkm_udevice_time(struct nvkm_object *object, void *data, u32 size) 117 { 118 struct nvkm_udevice *udev = (void *)object; 119 struct nvkm_device *device = udev->device; 120 struct nvkm_timer *tmr = device->timer; 121 union { 122 struct nv_device_time_v0 v0; 123 } *args = data; 124 int ret; 125 126 if (nvif_unpack(args->v0, 0, 0, false)) { 127 args->v0.time = tmr->read(tmr); 128 } 129 130 return ret; 131 } 132 133 static int 134 nvkm_udevice_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) 135 { 136 switch (mthd) { 137 case NV_DEVICE_V0_INFO: 138 return nvkm_udevice_info(object, data, size); 139 case NV_DEVICE_V0_TIME: 140 return nvkm_udevice_time(object, data, size); 141 default: 142 break; 143 } 144 return -EINVAL; 145 } 146 147 static u8 148 nvkm_udevice_rd08(struct nvkm_object *object, u64 addr) 149 { 150 struct nvkm_udevice *udev = (void *)object; 151 return nvkm_rd08(udev->device, addr); 152 } 153 154 static u16 155 nvkm_udevice_rd16(struct nvkm_object *object, u64 addr) 156 { 157 struct nvkm_udevice *udev = (void *)object; 158 return nvkm_rd16(udev->device, addr); 159 } 160 161 static u32 162 nvkm_udevice_rd32(struct nvkm_object *object, u64 addr) 163 { 164 struct nvkm_udevice *udev = (void *)object; 165 return nvkm_rd32(udev->device, addr); 166 } 167 168 static void 169 nvkm_udevice_wr08(struct nvkm_object *object, u64 addr, u8 data) 170 { 171 struct nvkm_udevice *udev = (void *)object; 172 nvkm_wr08(udev->device, addr, data); 173 } 174 175 static void 176 nvkm_udevice_wr16(struct nvkm_object *object, u64 addr, u16 data) 177 { 178 struct nvkm_udevice *udev = (void *)object; 179 nvkm_wr16(udev->device, addr, data); 180 } 181 182 static void 183 nvkm_udevice_wr32(struct nvkm_object *object, u64 addr, u32 data) 184 { 185 struct nvkm_udevice *udev = (void *)object; 186 nvkm_wr32(udev->device, addr, data); 187 } 188 189 static int 190 nvkm_udevice_map(struct nvkm_object *object, u64 *addr, u32 *size) 191 { 192 struct nvkm_udevice *udev = (void *)object; 193 struct nvkm_device *device = udev->device; 194 *addr = nv_device_resource_start(device, 0); 195 *size = nv_device_resource_len(device, 0); 196 return 0; 197 } 198 199 static int 200 nvkm_udevice_fini(struct nvkm_object *object, bool suspend) 201 { 202 struct nvkm_udevice *udev = (void *)object; 203 struct nvkm_device *device = udev->device; 204 int ret = 0; 205 206 mutex_lock(&device->mutex); 207 if (!--device->refcount) { 208 ret = nvkm_device_fini(device, suspend); 209 if (ret && suspend) { 210 device->refcount++; 211 goto done; 212 } 213 } 214 215 done: 216 mutex_unlock(&device->mutex); 217 return ret; 218 } 219 220 static int 221 nvkm_udevice_init(struct nvkm_object *object) 222 { 223 struct nvkm_udevice *udev = (void *)object; 224 struct nvkm_device *device = udev->device; 225 int ret = 0; 226 227 mutex_lock(&device->mutex); 228 if (!device->refcount++) { 229 ret = nvkm_device_init(device); 230 if (ret) { 231 device->refcount--; 232 goto done; 233 } 234 } 235 236 done: 237 mutex_unlock(&device->mutex); 238 return ret; 239 } 240 241 static struct nvkm_oclass 242 nvkm_udevice_oclass_super = { 243 .handle = NV_DEVICE, 244 .ofuncs = &(struct nvkm_ofuncs) { 245 .dtor = _nvkm_parent_dtor, 246 .init = nvkm_udevice_init, 247 .fini = nvkm_udevice_fini, 248 .mthd = nvkm_udevice_mthd, 249 .map = nvkm_udevice_map, 250 .rd08 = nvkm_udevice_rd08, 251 .rd16 = nvkm_udevice_rd16, 252 .rd32 = nvkm_udevice_rd32, 253 .wr08 = nvkm_udevice_wr08, 254 .wr16 = nvkm_udevice_wr16, 255 .wr32 = nvkm_udevice_wr32, 256 } 257 }; 258 259 static int 260 nvkm_udevice_ctor(struct nvkm_object *parent, struct nvkm_object *engine, 261 struct nvkm_oclass *oclass, void *data, u32 size, 262 struct nvkm_object **pobject) 263 { 264 union { 265 struct nv_device_v0 v0; 266 } *args = data; 267 struct nvkm_client *client = nvkm_client(parent); 268 struct nvkm_device *device; 269 struct nvkm_udevice *udev; 270 int ret; 271 272 nvif_ioctl(parent, "create device size %d\n", size); 273 if (nvif_unpack(args->v0, 0, 0, false)) { 274 nvif_ioctl(parent, "create device v%d device %016llx\n", 275 args->v0.version, args->v0.device); 276 } else 277 return ret; 278 279 /* give priviledged clients register access */ 280 if (client->super) 281 oclass = &nvkm_udevice_oclass_super; 282 283 /* find the device subdev that matches what the client requested */ 284 if (args->v0.device != ~0) 285 device = nvkm_device_find(args->v0.device); 286 else 287 device = nvkm_device_find(client->device); 288 if (!device) 289 return -ENODEV; 290 291 ret = nvkm_parent_create(parent, NULL, oclass, 0, nvkm_control_oclass, 292 (1ULL << NVDEV_ENGINE_DMAOBJ) | 293 (1ULL << NVDEV_ENGINE_FIFO) | 294 (1ULL << NVDEV_ENGINE_DISP) | 295 (1ULL << NVDEV_ENGINE_PM), &udev); 296 *pobject = nv_object(udev); 297 if (ret) 298 return ret; 299 300 udev->device = device; 301 return 0; 302 } 303 304 struct nvkm_ofuncs 305 nvkm_udevice_ofuncs = { 306 .ctor = nvkm_udevice_ctor, 307 .dtor = _nvkm_parent_dtor, 308 .init = nvkm_udevice_init, 309 .fini = nvkm_udevice_fini, 310 .mthd = nvkm_udevice_mthd, 311 }; 312