]>
Commit | Line | Data |
---|---|---|
a1bfb29a 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 | */ | |
2a9f847f | 24 | #define nvkm_udevice(p) container_of((p), struct nvkm_udevice, object) |
a1bfb29a | 25 | #include "priv.h" |
0e29998a | 26 | #include "ctrl.h" |
a1bfb29a BS |
27 | |
28 | #include <core/client.h> | |
29 | #include <subdev/fb.h> | |
30 | #include <subdev/instmem.h> | |
d61f4c17 | 31 | #include <subdev/timer.h> |
a1bfb29a BS |
32 | |
33 | #include <nvif/class.h> | |
34 | #include <nvif/unpack.h> | |
35 | ||
36 | struct nvkm_udevice { | |
2a9f847f | 37 | struct nvkm_object object; |
a1bfb29a BS |
38 | struct nvkm_device *device; |
39 | }; | |
40 | ||
41 | static int | |
2a9f847f | 42 | nvkm_udevice_info(struct nvkm_udevice *udev, void *data, u32 size) |
a1bfb29a | 43 | { |
2a9f847f | 44 | struct nvkm_object *object = &udev->object; |
a1bfb29a BS |
45 | struct nvkm_device *device = udev->device; |
46 | struct nvkm_fb *fb = device->fb; | |
47 | struct nvkm_instmem *imem = device->imem; | |
48 | union { | |
49 | struct nv_device_info_v0 v0; | |
50 | } *args = data; | |
51 | int ret; | |
52 | ||
53 | nvif_ioctl(object, "device info size %d\n", size); | |
54 | if (nvif_unpack(args->v0, 0, 0, false)) { | |
55 | nvif_ioctl(object, "device info vers %d\n", args->v0.version); | |
56 | } else | |
57 | return ret; | |
58 | ||
59 | switch (device->chipset) { | |
60 | case 0x01a: | |
61 | case 0x01f: | |
62 | case 0x04c: | |
63 | case 0x04e: | |
64 | case 0x063: | |
65 | case 0x067: | |
66 | case 0x068: | |
67 | case 0x0aa: | |
68 | case 0x0ac: | |
69 | case 0x0af: | |
70 | args->v0.platform = NV_DEVICE_INFO_V0_IGP; | |
71 | break; | |
72 | default: | |
73 | if (device->pdev) { | |
74 | if (pci_find_capability(device->pdev, PCI_CAP_ID_AGP)) | |
75 | args->v0.platform = NV_DEVICE_INFO_V0_AGP; | |
76 | else | |
77 | if (pci_is_pcie(device->pdev)) | |
78 | args->v0.platform = NV_DEVICE_INFO_V0_PCIE; | |
79 | else | |
80 | args->v0.platform = NV_DEVICE_INFO_V0_PCI; | |
81 | } else { | |
82 | args->v0.platform = NV_DEVICE_INFO_V0_SOC; | |
83 | } | |
84 | break; | |
85 | } | |
86 | ||
87 | switch (device->card_type) { | |
88 | case NV_04: args->v0.family = NV_DEVICE_INFO_V0_TNT; break; | |
89 | case NV_10: | |
90 | case NV_11: args->v0.family = NV_DEVICE_INFO_V0_CELSIUS; break; | |
91 | case NV_20: args->v0.family = NV_DEVICE_INFO_V0_KELVIN; break; | |
92 | case NV_30: args->v0.family = NV_DEVICE_INFO_V0_RANKINE; break; | |
93 | case NV_40: args->v0.family = NV_DEVICE_INFO_V0_CURIE; break; | |
94 | case NV_50: args->v0.family = NV_DEVICE_INFO_V0_TESLA; break; | |
95 | case NV_C0: args->v0.family = NV_DEVICE_INFO_V0_FERMI; break; | |
96 | case NV_E0: args->v0.family = NV_DEVICE_INFO_V0_KEPLER; break; | |
97 | case GM100: args->v0.family = NV_DEVICE_INFO_V0_MAXWELL; break; | |
98 | default: | |
99 | args->v0.family = 0; | |
100 | break; | |
101 | } | |
102 | ||
103 | args->v0.chipset = device->chipset; | |
104 | args->v0.revision = device->chiprev; | |
105 | if (fb && fb->ram) | |
106 | args->v0.ram_size = args->v0.ram_user = fb->ram->size; | |
107 | else | |
108 | args->v0.ram_size = args->v0.ram_user = 0; | |
109 | if (imem && args->v0.ram_size > 0) | |
110 | args->v0.ram_user = args->v0.ram_user - imem->reserved; | |
111 | ||
6cf813fb BS |
112 | strncpy(args->v0.chip, device->chip->name, sizeof(args->v0.chip)); |
113 | strncpy(args->v0.name, device->name, sizeof(args->v0.name)); | |
a1bfb29a BS |
114 | return 0; |
115 | } | |
116 | ||
d61f4c17 | 117 | static int |
2a9f847f | 118 | nvkm_udevice_time(struct nvkm_udevice *udev, void *data, u32 size) |
d61f4c17 | 119 | { |
d61f4c17 | 120 | struct nvkm_device *device = udev->device; |
d61f4c17 BS |
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)) { | |
31649ecf | 127 | args->v0.time = nvkm_timer_read(device->timer); |
d61f4c17 BS |
128 | } |
129 | ||
130 | return ret; | |
131 | } | |
132 | ||
a1bfb29a BS |
133 | static int |
134 | nvkm_udevice_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) | |
135 | { | |
2a9f847f | 136 | struct nvkm_udevice *udev = nvkm_udevice(object); |
a1bfb29a BS |
137 | switch (mthd) { |
138 | case NV_DEVICE_V0_INFO: | |
2a9f847f | 139 | return nvkm_udevice_info(udev, data, size); |
d61f4c17 | 140 | case NV_DEVICE_V0_TIME: |
2a9f847f | 141 | return nvkm_udevice_time(udev, data, size); |
a1bfb29a BS |
142 | default: |
143 | break; | |
144 | } | |
145 | return -EINVAL; | |
146 | } | |
147 | ||
2a9f847f BS |
148 | static int |
149 | nvkm_udevice_rd08(struct nvkm_object *object, u64 addr, u8 *data) | |
a1bfb29a | 150 | { |
2a9f847f BS |
151 | struct nvkm_udevice *udev = nvkm_udevice(object); |
152 | *data = nvkm_rd08(udev->device, addr); | |
153 | return 0; | |
a1bfb29a BS |
154 | } |
155 | ||
2a9f847f BS |
156 | static int |
157 | nvkm_udevice_rd16(struct nvkm_object *object, u64 addr, u16 *data) | |
a1bfb29a | 158 | { |
2a9f847f BS |
159 | struct nvkm_udevice *udev = nvkm_udevice(object); |
160 | *data = nvkm_rd16(udev->device, addr); | |
161 | return 0; | |
a1bfb29a BS |
162 | } |
163 | ||
2a9f847f BS |
164 | static int |
165 | nvkm_udevice_rd32(struct nvkm_object *object, u64 addr, u32 *data) | |
a1bfb29a | 166 | { |
2a9f847f BS |
167 | struct nvkm_udevice *udev = nvkm_udevice(object); |
168 | *data = nvkm_rd32(udev->device, addr); | |
169 | return 0; | |
a1bfb29a BS |
170 | } |
171 | ||
2a9f847f | 172 | static int |
a1bfb29a BS |
173 | nvkm_udevice_wr08(struct nvkm_object *object, u64 addr, u8 data) |
174 | { | |
2a9f847f | 175 | struct nvkm_udevice *udev = nvkm_udevice(object); |
a1bfb29a | 176 | nvkm_wr08(udev->device, addr, data); |
2a9f847f | 177 | return 0; |
a1bfb29a BS |
178 | } |
179 | ||
2a9f847f | 180 | static int |
a1bfb29a BS |
181 | nvkm_udevice_wr16(struct nvkm_object *object, u64 addr, u16 data) |
182 | { | |
2a9f847f | 183 | struct nvkm_udevice *udev = nvkm_udevice(object); |
a1bfb29a | 184 | nvkm_wr16(udev->device, addr, data); |
2a9f847f | 185 | return 0; |
a1bfb29a BS |
186 | } |
187 | ||
2a9f847f | 188 | static int |
a1bfb29a BS |
189 | nvkm_udevice_wr32(struct nvkm_object *object, u64 addr, u32 data) |
190 | { | |
2a9f847f | 191 | struct nvkm_udevice *udev = nvkm_udevice(object); |
a1bfb29a | 192 | nvkm_wr32(udev->device, addr, data); |
2a9f847f | 193 | return 0; |
a1bfb29a BS |
194 | } |
195 | ||
196 | static int | |
197 | nvkm_udevice_map(struct nvkm_object *object, u64 *addr, u32 *size) | |
198 | { | |
2a9f847f | 199 | struct nvkm_udevice *udev = nvkm_udevice(object); |
a1bfb29a BS |
200 | struct nvkm_device *device = udev->device; |
201 | *addr = nv_device_resource_start(device, 0); | |
202 | *size = nv_device_resource_len(device, 0); | |
203 | return 0; | |
204 | } | |
205 | ||
a1e88736 BS |
206 | static int |
207 | nvkm_udevice_fini(struct nvkm_object *object, bool suspend) | |
208 | { | |
2a9f847f | 209 | struct nvkm_udevice *udev = nvkm_udevice(object); |
a1e88736 BS |
210 | struct nvkm_device *device = udev->device; |
211 | int ret = 0; | |
212 | ||
213 | mutex_lock(&device->mutex); | |
214 | if (!--device->refcount) { | |
215 | ret = nvkm_device_fini(device, suspend); | |
216 | if (ret && suspend) { | |
217 | device->refcount++; | |
218 | goto done; | |
219 | } | |
220 | } | |
221 | ||
222 | done: | |
223 | mutex_unlock(&device->mutex); | |
224 | return ret; | |
225 | } | |
226 | ||
227 | static int | |
228 | nvkm_udevice_init(struct nvkm_object *object) | |
229 | { | |
2a9f847f | 230 | struct nvkm_udevice *udev = nvkm_udevice(object); |
a1e88736 BS |
231 | struct nvkm_device *device = udev->device; |
232 | int ret = 0; | |
233 | ||
234 | mutex_lock(&device->mutex); | |
235 | if (!device->refcount++) { | |
236 | ret = nvkm_device_init(device); | |
237 | if (ret) { | |
238 | device->refcount--; | |
239 | goto done; | |
240 | } | |
241 | } | |
242 | ||
243 | done: | |
244 | mutex_unlock(&device->mutex); | |
245 | return ret; | |
246 | } | |
247 | ||
a1bfb29a | 248 | static int |
2a9f847f BS |
249 | nvkm_udevice_child_new(const struct nvkm_oclass *oclass, |
250 | void *data, u32 size, struct nvkm_object **pobject) | |
251 | { | |
252 | struct nvkm_udevice *udev = nvkm_udevice(oclass->parent); | |
0e29998a BS |
253 | const struct nvkm_device_oclass *sclass = oclass->priv; |
254 | return sclass->ctor(udev->device, oclass, data, size, pobject); | |
2a9f847f BS |
255 | } |
256 | ||
257 | static int | |
258 | nvkm_udevice_child_get(struct nvkm_object *object, int index, | |
259 | struct nvkm_oclass *oclass) | |
260 | { | |
261 | struct nvkm_udevice *udev = nvkm_udevice(object); | |
262 | struct nvkm_device *device = udev->device; | |
263 | struct nvkm_engine *engine; | |
264 | u64 mask = (1ULL << NVDEV_ENGINE_DMAOBJ) | | |
265 | (1ULL << NVDEV_ENGINE_FIFO) | | |
266 | (1ULL << NVDEV_ENGINE_DISP) | | |
267 | (1ULL << NVDEV_ENGINE_PM); | |
0710cc31 | 268 | const struct nvkm_device_oclass *sclass = NULL; |
2a9f847f BS |
269 | int i; |
270 | ||
0710cc31 | 271 | for (; i = __ffs64(mask), mask && !sclass; mask &= ~(1ULL << i)) { |
0710cc31 BS |
272 | if (!(engine = nvkm_device_engine(device, i)) || |
273 | !(engine->func->base.sclass)) | |
274 | continue; | |
275 | oclass->engine = engine; | |
276 | ||
277 | index -= engine->func->base.sclass(oclass, index, &sclass); | |
2a9f847f BS |
278 | } |
279 | ||
0710cc31 BS |
280 | if (!sclass) { |
281 | switch (index) { | |
282 | case 0: sclass = &nvkm_control_oclass; break; | |
283 | default: | |
284 | return -EINVAL; | |
285 | } | |
286 | oclass->base = sclass->base; | |
2a9f847f BS |
287 | } |
288 | ||
0e29998a | 289 | oclass->ctor = nvkm_udevice_child_new; |
0e29998a BS |
290 | oclass->priv = sclass; |
291 | return 0; | |
2a9f847f BS |
292 | } |
293 | ||
294 | static const struct nvkm_object_func | |
295 | nvkm_udevice_super = { | |
296 | .init = nvkm_udevice_init, | |
297 | .fini = nvkm_udevice_fini, | |
298 | .mthd = nvkm_udevice_mthd, | |
299 | .map = nvkm_udevice_map, | |
300 | .rd08 = nvkm_udevice_rd08, | |
301 | .rd16 = nvkm_udevice_rd16, | |
302 | .rd32 = nvkm_udevice_rd32, | |
303 | .wr08 = nvkm_udevice_wr08, | |
304 | .wr16 = nvkm_udevice_wr16, | |
305 | .wr32 = nvkm_udevice_wr32, | |
306 | .sclass = nvkm_udevice_child_get, | |
307 | }; | |
308 | ||
309 | static const struct nvkm_object_func | |
310 | nvkm_udevice = { | |
311 | .init = nvkm_udevice_init, | |
312 | .fini = nvkm_udevice_fini, | |
313 | .mthd = nvkm_udevice_mthd, | |
314 | .sclass = nvkm_udevice_child_get, | |
315 | }; | |
316 | ||
317 | int | |
318 | nvkm_udevice_new(const struct nvkm_oclass *oclass, void *data, u32 size, | |
a1bfb29a BS |
319 | struct nvkm_object **pobject) |
320 | { | |
321 | union { | |
322 | struct nv_device_v0 v0; | |
323 | } *args = data; | |
2a9f847f BS |
324 | struct nvkm_client *client = oclass->client; |
325 | struct nvkm_object *parent = &client->object; | |
326 | const struct nvkm_object_func *func; | |
a1bfb29a BS |
327 | struct nvkm_udevice *udev; |
328 | int ret; | |
329 | ||
330 | nvif_ioctl(parent, "create device size %d\n", size); | |
331 | if (nvif_unpack(args->v0, 0, 0, false)) { | |
332 | nvif_ioctl(parent, "create device v%d device %016llx\n", | |
333 | args->v0.version, args->v0.device); | |
334 | } else | |
335 | return ret; | |
336 | ||
337 | /* give priviledged clients register access */ | |
338 | if (client->super) | |
2a9f847f BS |
339 | func = &nvkm_udevice_super; |
340 | else | |
341 | func = &nvkm_udevice; | |
a1bfb29a | 342 | |
2a9f847f BS |
343 | if (!(udev = kzalloc(sizeof(*udev), GFP_KERNEL))) |
344 | return -ENOMEM; | |
345 | nvkm_object_ctor(func, oclass, &udev->object); | |
346 | *pobject = &udev->object; | |
347 | ||
348 | /* find the device that matches what the client requested */ | |
4e7e62d6 | 349 | if (args->v0.device != ~0) |
2a9f847f | 350 | udev->device = nvkm_device_find(args->v0.device); |
4e7e62d6 | 351 | else |
2a9f847f BS |
352 | udev->device = nvkm_device_find(client->device); |
353 | if (!udev->device) | |
4e7e62d6 | 354 | return -ENODEV; |
a1bfb29a | 355 | |
a1bfb29a BS |
356 | return 0; |
357 | } | |
358 | ||
2a9f847f BS |
359 | const struct nvkm_sclass |
360 | nvkm_udevice_sclass = { | |
361 | .oclass = NV_DEVICE, | |
362 | .minver = 0, | |
363 | .maxver = 0, | |
364 | .ctor = nvkm_udevice_new, | |
a1bfb29a | 365 | }; |