Commit | Line | Data |
---|---|---|
4b223eef | 1 | /* |
ebb945a9 | 2 | * Copyright 2012 Red Hat Inc. |
4b223eef BS |
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 | ||
02a841d4 BS |
25 | #include "nvc0.h" |
26 | #include "fuc/hubnvc0.fuc.h" | |
27 | #include "fuc/gpcnvc0.fuc.h" | |
0411de85 | 28 | |
ebb945a9 BS |
29 | /******************************************************************************* |
30 | * Graphics object classes | |
31 | ******************************************************************************/ | |
32 | ||
33 | static struct nouveau_oclass | |
34 | nvc0_graph_sclass[] = { | |
35 | { 0x902d, &nouveau_object_ofuncs }, | |
36 | { 0x9039, &nouveau_object_ofuncs }, | |
37 | { 0x9097, &nouveau_object_ofuncs }, | |
38 | { 0x90c0, &nouveau_object_ofuncs }, | |
39 | {} | |
40 | }; | |
41 | ||
42 | static struct nouveau_oclass | |
43 | nvc1_graph_sclass[] = { | |
44 | { 0x902d, &nouveau_object_ofuncs }, | |
45 | { 0x9039, &nouveau_object_ofuncs }, | |
46 | { 0x9097, &nouveau_object_ofuncs }, | |
47 | { 0x90c0, &nouveau_object_ofuncs }, | |
48 | { 0x9197, &nouveau_object_ofuncs }, | |
49 | {} | |
50 | }; | |
51 | ||
52 | static struct nouveau_oclass | |
53 | nvc8_graph_sclass[] = { | |
54 | { 0x902d, &nouveau_object_ofuncs }, | |
55 | { 0x9039, &nouveau_object_ofuncs }, | |
56 | { 0x9097, &nouveau_object_ofuncs }, | |
57 | { 0x90c0, &nouveau_object_ofuncs }, | |
58 | { 0x9197, &nouveau_object_ofuncs }, | |
59 | { 0x9297, &nouveau_object_ofuncs }, | |
60 | {} | |
61 | }; | |
62 | ||
63 | /******************************************************************************* | |
64 | * PGRAPH context | |
65 | ******************************************************************************/ | |
966a5b7d | 66 | |
ac1499d9 | 67 | int |
ebb945a9 BS |
68 | nvc0_graph_context_ctor(struct nouveau_object *parent, |
69 | struct nouveau_object *engine, | |
70 | struct nouveau_oclass *oclass, void *args, u32 size, | |
71 | struct nouveau_object **pobject) | |
966a5b7d | 72 | { |
ebb945a9 BS |
73 | struct nouveau_vm *vm = nouveau_client(parent)->vm; |
74 | struct nvc0_graph_priv *priv = (void *)engine; | |
ac1499d9 BS |
75 | struct nvc0_graph_data *data = priv->mmio_data; |
76 | struct nvc0_graph_mmio *mmio = priv->mmio_list; | |
ebb945a9 | 77 | struct nvc0_graph_chan *chan; |
966a5b7d | 78 | int ret, i; |
966a5b7d | 79 | |
ebb945a9 BS |
80 | /* allocate memory for context, and fill with default values */ |
81 | ret = nouveau_graph_context_create(parent, engine, oclass, NULL, | |
82 | priv->size, 0x100, | |
83 | NVOBJ_FLAG_ZERO_ALLOC, &chan); | |
84 | *pobject = nv_object(chan); | |
966a5b7d BS |
85 | if (ret) |
86 | return ret; | |
87 | ||
ac1499d9 BS |
88 | /* allocate memory for a "mmio list" buffer that's used by the HUB |
89 | * fuc to modify some per-context register settings on first load | |
90 | * of the context. | |
91 | */ | |
ebb945a9 | 92 | ret = nouveau_gpuobj_new(parent, NULL, 0x1000, 0x100, 0, &chan->mmio); |
73a60c0d BS |
93 | if (ret) |
94 | return ret; | |
95 | ||
ebb945a9 | 96 | ret = nouveau_gpuobj_map_vm(nv_gpuobj(chan->mmio), vm, |
3863c9bc | 97 | NV_MEM_ACCESS_RW | NV_MEM_ACCESS_SYS, |
ebb945a9 | 98 | &chan->mmio_vma); |
73a60c0d BS |
99 | if (ret) |
100 | return ret; | |
101 | ||
ac1499d9 BS |
102 | /* allocate buffers referenced by mmio list */ |
103 | for (i = 0; data->size && i < ARRAY_SIZE(priv->mmio_data); i++) { | |
ebb945a9 BS |
104 | ret = nouveau_gpuobj_new(parent, NULL, data->size, data->align, |
105 | 0, &chan->data[i].mem); | |
ac1499d9 BS |
106 | if (ret) |
107 | return ret; | |
73a60c0d | 108 | |
ebb945a9 BS |
109 | ret = nouveau_gpuobj_map_vm(chan->data[i].mem, vm, data->access, |
110 | &chan->data[i].vma); | |
ac1499d9 BS |
111 | if (ret) |
112 | return ret; | |
966a5b7d | 113 | |
ac1499d9 | 114 | data++; |
966a5b7d BS |
115 | } |
116 | ||
ac1499d9 BS |
117 | /* finally, fill in the mmio list and point the context at it */ |
118 | for (i = 0; mmio->addr && i < ARRAY_SIZE(priv->mmio_list); i++) { | |
119 | u32 addr = mmio->addr; | |
120 | u32 data = mmio->data; | |
966a5b7d | 121 | |
ac1499d9 | 122 | if (mmio->shift) { |
ebb945a9 | 123 | u64 info = chan->data[mmio->buffer].vma.offset; |
ac1499d9 BS |
124 | data |= info >> mmio->shift; |
125 | } | |
73a60c0d | 126 | |
ebb945a9 BS |
127 | nv_wo32(chan->mmio, chan->mmio_nr++ * 4, addr); |
128 | nv_wo32(chan->mmio, chan->mmio_nr++ * 4, data); | |
ac1499d9 BS |
129 | mmio++; |
130 | } | |
73a60c0d | 131 | |
ac1499d9 | 132 | for (i = 0; i < priv->size; i += 4) |
ebb945a9 | 133 | nv_wo32(chan, i, priv->data[i / 4]); |
966a5b7d | 134 | |
ac1499d9 | 135 | if (!priv->firmware) { |
ebb945a9 BS |
136 | nv_wo32(chan, 0x00, chan->mmio_nr / 2); |
137 | nv_wo32(chan, 0x04, chan->mmio_vma.offset >> 8); | |
0411de85 | 138 | } else { |
ebb945a9 BS |
139 | nv_wo32(chan, 0xf4, 0); |
140 | nv_wo32(chan, 0xf8, 0); | |
141 | nv_wo32(chan, 0x10, chan->mmio_nr / 2); | |
142 | nv_wo32(chan, 0x14, lower_32_bits(chan->mmio_vma.offset)); | |
143 | nv_wo32(chan, 0x18, upper_32_bits(chan->mmio_vma.offset)); | |
144 | nv_wo32(chan, 0x1c, 1); | |
145 | nv_wo32(chan, 0x20, 0); | |
146 | nv_wo32(chan, 0x28, 0); | |
147 | nv_wo32(chan, 0x2c, 0); | |
0411de85 | 148 | } |
966a5b7d | 149 | |
ebb945a9 | 150 | return 0; |
4b223eef BS |
151 | } |
152 | ||
ac1499d9 | 153 | void |
ebb945a9 | 154 | nvc0_graph_context_dtor(struct nouveau_object *object) |
4b223eef | 155 | { |
ebb945a9 | 156 | struct nvc0_graph_chan *chan = (void *)object; |
ac1499d9 BS |
157 | int i; |
158 | ||
ebb945a9 BS |
159 | for (i = 0; i < ARRAY_SIZE(chan->data); i++) { |
160 | nouveau_gpuobj_unmap(&chan->data[i].vma); | |
161 | nouveau_gpuobj_ref(NULL, &chan->data[i].mem); | |
ac1499d9 | 162 | } |
966a5b7d | 163 | |
ebb945a9 BS |
164 | nouveau_gpuobj_unmap(&chan->mmio_vma); |
165 | nouveau_gpuobj_ref(NULL, &chan->mmio); | |
ac1499d9 | 166 | |
ebb945a9 | 167 | nouveau_graph_context_destroy(&chan->base); |
4b223eef BS |
168 | } |
169 | ||
ebb945a9 BS |
170 | static struct nouveau_oclass |
171 | nvc0_graph_cclass = { | |
172 | .ofuncs = &(struct nouveau_ofuncs) { | |
173 | .ctor = nvc0_graph_context_ctor, | |
174 | .dtor = nvc0_graph_context_dtor, | |
175 | .init = _nouveau_graph_context_init, | |
176 | .fini = _nouveau_graph_context_fini, | |
177 | .rd32 = _nouveau_graph_context_rd32, | |
178 | .wr32 = _nouveau_graph_context_wr32, | |
179 | }, | |
180 | }; | |
181 | ||
182 | /******************************************************************************* | |
183 | * PGRAPH engine/subdev functions | |
184 | ******************************************************************************/ | |
185 | ||
186 | static void | |
187 | nvc0_graph_ctxctl_debug_unit(struct nvc0_graph_priv *priv, u32 base) | |
4b223eef | 188 | { |
ebb945a9 BS |
189 | nv_error(priv, "%06x - done 0x%08x\n", base, |
190 | nv_rd32(priv, base + 0x400)); | |
191 | nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base, | |
192 | nv_rd32(priv, base + 0x800), nv_rd32(priv, base + 0x804), | |
193 | nv_rd32(priv, base + 0x808), nv_rd32(priv, base + 0x80c)); | |
194 | nv_error(priv, "%06x - stat 0x%08x 0x%08x 0x%08x 0x%08x\n", base, | |
195 | nv_rd32(priv, base + 0x810), nv_rd32(priv, base + 0x814), | |
196 | nv_rd32(priv, base + 0x818), nv_rd32(priv, base + 0x81c)); | |
197 | } | |
198 | ||
199 | void | |
200 | nvc0_graph_ctxctl_debug(struct nvc0_graph_priv *priv) | |
201 | { | |
202 | u32 gpcnr = nv_rd32(priv, 0x409604) & 0xffff; | |
203 | u32 gpc; | |
204 | ||
205 | nvc0_graph_ctxctl_debug_unit(priv, 0x409000); | |
206 | for (gpc = 0; gpc < gpcnr; gpc++) | |
207 | nvc0_graph_ctxctl_debug_unit(priv, 0x502000 + (gpc * 0x8000)); | |
208 | } | |
209 | ||
210 | static void | |
211 | nvc0_graph_ctxctl_isr(struct nvc0_graph_priv *priv) | |
212 | { | |
213 | u32 ustat = nv_rd32(priv, 0x409c18); | |
214 | ||
215 | if (ustat & 0x00000001) | |
216 | nv_error(priv, "CTXCTRL ucode error\n"); | |
217 | if (ustat & 0x00080000) | |
218 | nv_error(priv, "CTXCTRL watchdog timeout\n"); | |
219 | if (ustat & ~0x00080001) | |
220 | nv_error(priv, "CTXCTRL 0x%08x\n", ustat); | |
221 | ||
222 | nvc0_graph_ctxctl_debug(priv); | |
223 | nv_wr32(priv, 0x409c20, ustat); | |
224 | } | |
225 | ||
226 | static void | |
227 | nvc0_graph_intr(struct nouveau_subdev *subdev) | |
228 | { | |
229 | struct nvc0_graph_priv *priv = (void *)subdev; | |
230 | struct nouveau_engine *engine = nv_engine(subdev); | |
231 | struct nouveau_handle *handle = NULL; | |
232 | u64 inst = (u64)(nv_rd32(priv, 0x409b00) & 0x0fffffff) << 12; | |
233 | u32 stat = nv_rd32(priv, 0x400100); | |
234 | u32 addr = nv_rd32(priv, 0x400704); | |
235 | u32 mthd = (addr & 0x00003ffc); | |
236 | u32 subc = (addr & 0x00070000) >> 16; | |
237 | u32 data = nv_rd32(priv, 0x400708); | |
238 | u32 code = nv_rd32(priv, 0x400110); | |
239 | u32 class = nv_rd32(priv, 0x404200 + (subc * 4)); | |
240 | ||
241 | if (stat & 0x00000010) { | |
242 | handle = nouveau_engctx_lookup_class(engine, inst, class); | |
243 | if (!handle || nv_call(handle->object, mthd, data)) { | |
244 | nv_error(priv, "ILLEGAL_MTHD ch 0x%010llx " | |
245 | "subc %d class 0x%04x mthd 0x%04x " | |
246 | "data 0x%08x\n", | |
247 | inst, subc, class, mthd, data); | |
248 | } | |
249 | nouveau_engctx_handle_put(handle); | |
250 | nv_wr32(priv, 0x400100, 0x00000010); | |
251 | stat &= ~0x00000010; | |
252 | } | |
253 | ||
254 | if (stat & 0x00000020) { | |
255 | nv_error(priv, "ILLEGAL_CLASS ch 0x%010llx subc %d " | |
256 | "class 0x%04x mthd 0x%04x data 0x%08x\n", | |
257 | inst, subc, class, mthd, data); | |
258 | nv_wr32(priv, 0x400100, 0x00000020); | |
259 | stat &= ~0x00000020; | |
260 | } | |
261 | ||
262 | if (stat & 0x00100000) { | |
263 | nv_error(priv, "DATA_ERROR ["); | |
264 | nouveau_enum_print(nv50_data_error_names, code); | |
265 | printk("] ch 0x%010llx subc %d class 0x%04x " | |
266 | "mthd 0x%04x data 0x%08x\n", | |
267 | inst, subc, class, mthd, data); | |
268 | nv_wr32(priv, 0x400100, 0x00100000); | |
269 | stat &= ~0x00100000; | |
270 | } | |
271 | ||
272 | if (stat & 0x00200000) { | |
273 | u32 trap = nv_rd32(priv, 0x400108); | |
274 | nv_error(priv, "TRAP ch 0x%010llx status 0x%08x\n", inst, trap); | |
275 | nv_wr32(priv, 0x400108, trap); | |
276 | nv_wr32(priv, 0x400100, 0x00200000); | |
277 | stat &= ~0x00200000; | |
278 | } | |
279 | ||
280 | if (stat & 0x00080000) { | |
281 | nvc0_graph_ctxctl_isr(priv); | |
282 | nv_wr32(priv, 0x400100, 0x00080000); | |
283 | stat &= ~0x00080000; | |
284 | } | |
285 | ||
286 | if (stat) { | |
287 | nv_error(priv, "unknown stat 0x%08x\n", stat); | |
288 | nv_wr32(priv, 0x400100, stat); | |
289 | } | |
290 | ||
291 | nv_wr32(priv, 0x400500, 0x00010001); | |
292 | } | |
293 | ||
294 | int | |
295 | nvc0_graph_ctor_fw(struct nvc0_graph_priv *priv, const char *fwname, | |
296 | struct nvc0_graph_fuc *fuc) | |
297 | { | |
298 | struct nouveau_device *device = nv_device(priv); | |
299 | const struct firmware *fw; | |
300 | char f[32]; | |
301 | int ret; | |
302 | ||
303 | snprintf(f, sizeof(f), "nouveau/nv%02x_%s", device->chipset, fwname); | |
304 | ret = request_firmware(&fw, f, &device->pdev->dev); | |
305 | if (ret) { | |
306 | snprintf(f, sizeof(f), "nouveau/%s", fwname); | |
307 | ret = request_firmware(&fw, f, &device->pdev->dev); | |
308 | if (ret) { | |
309 | nv_error(priv, "failed to load %s\n", fwname); | |
310 | return ret; | |
311 | } | |
312 | } | |
313 | ||
314 | fuc->size = fw->size; | |
315 | fuc->data = kmemdup(fw->data, fuc->size, GFP_KERNEL); | |
316 | release_firmware(fw); | |
317 | return (fuc->data != NULL) ? 0 : -ENOMEM; | |
966a5b7d BS |
318 | } |
319 | ||
320 | static int | |
ebb945a9 BS |
321 | nvc0_graph_ctor(struct nouveau_object *parent, struct nouveau_object *engine, |
322 | struct nouveau_oclass *oclass, void *data, u32 size, | |
323 | struct nouveau_object **pobject) | |
966a5b7d | 324 | { |
ebb945a9 BS |
325 | struct nouveau_device *device = nv_device(parent); |
326 | struct nvc0_graph_priv *priv; | |
327 | bool enable = true; | |
328 | int ret, i; | |
329 | ||
330 | switch (device->chipset) { | |
331 | case 0xd9: /* known broken without binary driver firmware */ | |
332 | enable = false; | |
333 | break; | |
334 | default: | |
335 | break; | |
336 | } | |
337 | ||
338 | ret = nouveau_graph_create(parent, engine, oclass, enable, &priv); | |
339 | *pobject = nv_object(priv); | |
340 | if (ret) | |
341 | return ret; | |
342 | ||
343 | nv_subdev(priv)->unit = 0x18001000; | |
344 | nv_subdev(priv)->intr = nvc0_graph_intr; | |
345 | nv_engine(priv)->cclass = &nvc0_graph_cclass; | |
346 | ||
347 | if (nouveau_boolopt(device->cfgopt, "NvGrUseFW", false)) { | |
348 | nv_info(priv, "using external firmware\n"); | |
349 | if (nvc0_graph_ctor_fw(priv, "fuc409c", &priv->fuc409c) || | |
350 | nvc0_graph_ctor_fw(priv, "fuc409d", &priv->fuc409d) || | |
351 | nvc0_graph_ctor_fw(priv, "fuc41ac", &priv->fuc41ac) || | |
352 | nvc0_graph_ctor_fw(priv, "fuc41ad", &priv->fuc41ad)) | |
353 | return -EINVAL; | |
354 | priv->firmware = true; | |
355 | } | |
356 | ||
357 | switch (nvc0_graph_class(priv)) { | |
358 | case 0x9097: | |
359 | nv_engine(priv)->sclass = nvc0_graph_sclass; | |
360 | break; | |
361 | case 0x9197: | |
362 | nv_engine(priv)->sclass = nvc1_graph_sclass; | |
363 | break; | |
364 | case 0x9297: | |
365 | nv_engine(priv)->sclass = nvc8_graph_sclass; | |
366 | break; | |
367 | } | |
368 | ||
369 | ret = nouveau_gpuobj_new(parent, NULL, 0x1000, 256, 0, &priv->unk4188b4); | |
370 | if (ret) | |
371 | return ret; | |
372 | ||
373 | ret = nouveau_gpuobj_new(parent, NULL, 0x1000, 256, 0, &priv->unk4188b8); | |
374 | if (ret) | |
375 | return ret; | |
376 | ||
377 | for (i = 0; i < 0x1000; i += 4) { | |
378 | nv_wo32(priv->unk4188b4, i, 0x00000010); | |
379 | nv_wo32(priv->unk4188b8, i, 0x00000010); | |
380 | } | |
381 | ||
382 | priv->rop_nr = (nv_rd32(priv, 0x409604) & 0x001f0000) >> 16; | |
383 | priv->gpc_nr = nv_rd32(priv, 0x409604) & 0x0000001f; | |
384 | for (i = 0; i < priv->gpc_nr; i++) { | |
385 | priv->tpc_nr[i] = nv_rd32(priv, GPC_UNIT(i, 0x2608)); | |
386 | priv->tpc_total += priv->tpc_nr[i]; | |
387 | } | |
388 | ||
389 | /*XXX: these need figuring out... though it might not even matter */ | |
390 | switch (nv_device(priv)->chipset) { | |
391 | case 0xc0: | |
392 | if (priv->tpc_total == 11) { /* 465, 3/4/4/0, 4 */ | |
393 | priv->magic_not_rop_nr = 0x07; | |
394 | } else | |
395 | if (priv->tpc_total == 14) { /* 470, 3/3/4/4, 5 */ | |
396 | priv->magic_not_rop_nr = 0x05; | |
397 | } else | |
398 | if (priv->tpc_total == 15) { /* 480, 3/4/4/4, 6 */ | |
399 | priv->magic_not_rop_nr = 0x06; | |
400 | } | |
401 | break; | |
402 | case 0xc3: /* 450, 4/0/0/0, 2 */ | |
403 | priv->magic_not_rop_nr = 0x03; | |
404 | break; | |
405 | case 0xc4: /* 460, 3/4/0/0, 4 */ | |
406 | priv->magic_not_rop_nr = 0x01; | |
407 | break; | |
408 | case 0xc1: /* 2/0/0/0, 1 */ | |
409 | priv->magic_not_rop_nr = 0x01; | |
410 | break; | |
411 | case 0xc8: /* 4/4/3/4, 5 */ | |
412 | priv->magic_not_rop_nr = 0x06; | |
413 | break; | |
414 | case 0xce: /* 4/4/0/0, 4 */ | |
415 | priv->magic_not_rop_nr = 0x03; | |
416 | break; | |
417 | case 0xcf: /* 4/0/0/0, 3 */ | |
418 | priv->magic_not_rop_nr = 0x03; | |
419 | break; | |
420 | case 0xd9: /* 1/0/0/0, 1 */ | |
421 | priv->magic_not_rop_nr = 0x01; | |
422 | break; | |
423 | } | |
424 | ||
4b223eef BS |
425 | return 0; |
426 | } | |
427 | ||
966a5b7d | 428 | static void |
ebb945a9 BS |
429 | nvc0_graph_dtor_fw(struct nvc0_graph_fuc *fuc) |
430 | { | |
431 | if (fuc->data) { | |
432 | kfree(fuc->data); | |
433 | fuc->data = NULL; | |
434 | } | |
435 | } | |
436 | ||
437 | void | |
438 | nvc0_graph_dtor(struct nouveau_object *object) | |
439 | { | |
440 | struct nvc0_graph_priv *priv = (void *)object; | |
441 | ||
442 | if (priv->data) | |
443 | kfree(priv->data); | |
444 | ||
445 | nvc0_graph_dtor_fw(&priv->fuc409c); | |
446 | nvc0_graph_dtor_fw(&priv->fuc409d); | |
447 | nvc0_graph_dtor_fw(&priv->fuc41ac); | |
448 | nvc0_graph_dtor_fw(&priv->fuc41ad); | |
449 | ||
450 | nouveau_gpuobj_ref(NULL, &priv->unk4188b8); | |
451 | nouveau_gpuobj_ref(NULL, &priv->unk4188b4); | |
452 | ||
453 | nouveau_graph_destroy(&priv->base); | |
454 | } | |
455 | ||
456 | static void | |
457 | nvc0_graph_init_obj418880(struct nvc0_graph_priv *priv) | |
966a5b7d | 458 | { |
966a5b7d BS |
459 | int i; |
460 | ||
ebb945a9 BS |
461 | nv_wr32(priv, GPC_BCAST(0x0880), 0x00000000); |
462 | nv_wr32(priv, GPC_BCAST(0x08a4), 0x00000000); | |
966a5b7d | 463 | for (i = 0; i < 4; i++) |
ebb945a9 BS |
464 | nv_wr32(priv, GPC_BCAST(0x0888) + (i * 4), 0x00000000); |
465 | nv_wr32(priv, GPC_BCAST(0x08b4), priv->unk4188b4->addr >> 8); | |
466 | nv_wr32(priv, GPC_BCAST(0x08b8), priv->unk4188b8->addr >> 8); | |
966a5b7d BS |
467 | } |
468 | ||
469 | static void | |
ebb945a9 | 470 | nvc0_graph_init_regs(struct nvc0_graph_priv *priv) |
966a5b7d | 471 | { |
ebb945a9 BS |
472 | nv_wr32(priv, 0x400080, 0x003083c2); |
473 | nv_wr32(priv, 0x400088, 0x00006fe7); | |
474 | nv_wr32(priv, 0x40008c, 0x00000000); | |
475 | nv_wr32(priv, 0x400090, 0x00000030); | |
476 | nv_wr32(priv, 0x40013c, 0x013901f7); | |
477 | nv_wr32(priv, 0x400140, 0x00000100); | |
478 | nv_wr32(priv, 0x400144, 0x00000000); | |
479 | nv_wr32(priv, 0x400148, 0x00000110); | |
480 | nv_wr32(priv, 0x400138, 0x00000000); | |
481 | nv_wr32(priv, 0x400130, 0x00000000); | |
482 | nv_wr32(priv, 0x400134, 0x00000000); | |
483 | nv_wr32(priv, 0x400124, 0x00000002); | |
966a5b7d BS |
484 | } |
485 | ||
486 | static void | |
ebb945a9 | 487 | nvc0_graph_init_gpc_0(struct nvc0_graph_priv *priv) |
966a5b7d | 488 | { |
c4afbe74 BS |
489 | const u32 magicgpc918 = DIV_ROUND_UP(0x00800000, priv->tpc_total); |
490 | u32 data[TPC_MAX / 8]; | |
ebb945a9 | 491 | u8 tpcnr[GPC_MAX]; |
aa58c405 | 492 | int i, gpc, tpc; |
f212949c | 493 | |
ebb945a9 | 494 | nv_wr32(priv, TPC_UNIT(0, 0, 0x5c), 1); /* affects TFB offset queries */ |
ffe2dee4 | 495 | |
f212949c EV |
496 | /* |
497 | * TP ROP UNKVAL(magic_not_rop_nr) | |
498 | * 450: 4/0/0/0 2 3 | |
499 | * 460: 3/4/0/0 4 1 | |
500 | * 465: 3/4/4/0 4 7 | |
501 | * 470: 3/3/4/4 5 5 | |
502 | * 480: 3/4/4/4 6 6 | |
f212949c EV |
503 | */ |
504 | ||
aa58c405 | 505 | memset(data, 0x00, sizeof(data)); |
ebb945a9 | 506 | memcpy(tpcnr, priv->tpc_nr, sizeof(priv->tpc_nr)); |
c4afbe74 | 507 | for (i = 0, gpc = -1; i < priv->tpc_total; i++) { |
aa58c405 BS |
508 | do { |
509 | gpc = (gpc + 1) % priv->gpc_nr; | |
ebb945a9 BS |
510 | } while (!tpcnr[gpc]); |
511 | tpc = priv->tpc_nr[gpc] - tpcnr[gpc]--; | |
aa58c405 BS |
512 | |
513 | data[i / 8] |= tpc << ((i % 8) * 4); | |
514 | } | |
515 | ||
ebb945a9 BS |
516 | nv_wr32(priv, GPC_BCAST(0x0980), data[0]); |
517 | nv_wr32(priv, GPC_BCAST(0x0984), data[1]); | |
518 | nv_wr32(priv, GPC_BCAST(0x0988), data[2]); | |
519 | nv_wr32(priv, GPC_BCAST(0x098c), data[3]); | |
966a5b7d BS |
520 | |
521 | for (gpc = 0; gpc < priv->gpc_nr; gpc++) { | |
ebb945a9 | 522 | nv_wr32(priv, GPC_UNIT(gpc, 0x0914), priv->magic_not_rop_nr << 8 | |
c4afbe74 | 523 | priv->tpc_nr[gpc]); |
ebb945a9 BS |
524 | nv_wr32(priv, GPC_UNIT(gpc, 0x0910), 0x00040000 | priv->tpc_total); |
525 | nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918); | |
966a5b7d BS |
526 | } |
527 | ||
ebb945a9 BS |
528 | nv_wr32(priv, GPC_BCAST(0x1bd4), magicgpc918); |
529 | nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800)); | |
966a5b7d BS |
530 | } |
531 | ||
532 | static void | |
ebb945a9 | 533 | nvc0_graph_init_units(struct nvc0_graph_priv *priv) |
966a5b7d | 534 | { |
ebb945a9 BS |
535 | nv_wr32(priv, 0x409c24, 0x000f0000); |
536 | nv_wr32(priv, 0x404000, 0xc0000000); /* DISPATCH */ | |
537 | nv_wr32(priv, 0x404600, 0xc0000000); /* M2MF */ | |
538 | nv_wr32(priv, 0x408030, 0xc0000000); | |
539 | nv_wr32(priv, 0x40601c, 0xc0000000); | |
540 | nv_wr32(priv, 0x404490, 0xc0000000); /* MACRO */ | |
541 | nv_wr32(priv, 0x406018, 0xc0000000); | |
542 | nv_wr32(priv, 0x405840, 0xc0000000); | |
543 | nv_wr32(priv, 0x405844, 0x00ffffff); | |
544 | nv_mask(priv, 0x419cc0, 0x00000008, 0x00000008); | |
545 | nv_mask(priv, 0x419eb4, 0x00001000, 0x00001000); | |
966a5b7d BS |
546 | } |
547 | ||
548 | static void | |
ebb945a9 | 549 | nvc0_graph_init_gpc_1(struct nvc0_graph_priv *priv) |
966a5b7d | 550 | { |
ebb945a9 | 551 | int gpc, tpc; |
966a5b7d BS |
552 | |
553 | for (gpc = 0; gpc < priv->gpc_nr; gpc++) { | |
ebb945a9 BS |
554 | nv_wr32(priv, GPC_UNIT(gpc, 0x0420), 0xc0000000); |
555 | nv_wr32(priv, GPC_UNIT(gpc, 0x0900), 0xc0000000); | |
556 | nv_wr32(priv, GPC_UNIT(gpc, 0x1028), 0xc0000000); | |
557 | nv_wr32(priv, GPC_UNIT(gpc, 0x0824), 0xc0000000); | |
558 | for (tpc = 0; tpc < priv->tpc_nr[gpc]; tpc++) { | |
559 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x508), 0xffffffff); | |
560 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x50c), 0xffffffff); | |
561 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x224), 0xc0000000); | |
562 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x48c), 0xc0000000); | |
563 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x084), 0xc0000000); | |
564 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x644), 0x001ffffe); | |
565 | nv_wr32(priv, TPC_UNIT(gpc, tpc, 0x64c), 0x0000000f); | |
966a5b7d | 566 | } |
ebb945a9 BS |
567 | nv_wr32(priv, GPC_UNIT(gpc, 0x2c90), 0xffffffff); |
568 | nv_wr32(priv, GPC_UNIT(gpc, 0x2c94), 0xffffffff); | |
966a5b7d BS |
569 | } |
570 | } | |
571 | ||
572 | static void | |
ebb945a9 | 573 | nvc0_graph_init_rop(struct nvc0_graph_priv *priv) |
966a5b7d | 574 | { |
966a5b7d BS |
575 | int rop; |
576 | ||
577 | for (rop = 0; rop < priv->rop_nr; rop++) { | |
ebb945a9 BS |
578 | nv_wr32(priv, ROP_UNIT(rop, 0x144), 0xc0000000); |
579 | nv_wr32(priv, ROP_UNIT(rop, 0x070), 0xc0000000); | |
580 | nv_wr32(priv, ROP_UNIT(rop, 0x204), 0xffffffff); | |
581 | nv_wr32(priv, ROP_UNIT(rop, 0x208), 0xffffffff); | |
966a5b7d BS |
582 | } |
583 | } | |
584 | ||
ebb945a9 BS |
585 | void |
586 | nvc0_graph_init_fw(struct nvc0_graph_priv *priv, u32 fuc_base, | |
587 | struct nvc0_graph_fuc *code, struct nvc0_graph_fuc *data) | |
966a5b7d | 588 | { |
fe799114 | 589 | int i; |
966a5b7d | 590 | |
ebb945a9 | 591 | nv_wr32(priv, fuc_base + 0x01c0, 0x01000000); |
fe799114 | 592 | for (i = 0; i < data->size / 4; i++) |
ebb945a9 | 593 | nv_wr32(priv, fuc_base + 0x01c4, data->data[i]); |
966a5b7d | 594 | |
ebb945a9 | 595 | nv_wr32(priv, fuc_base + 0x0180, 0x01000000); |
fe799114 | 596 | for (i = 0; i < code->size / 4; i++) { |
966a5b7d | 597 | if ((i & 0x3f) == 0) |
ebb945a9 BS |
598 | nv_wr32(priv, fuc_base + 0x0188, i >> 6); |
599 | nv_wr32(priv, fuc_base + 0x0184, code->data[i]); | |
966a5b7d | 600 | } |
966a5b7d BS |
601 | } |
602 | ||
603 | static int | |
ebb945a9 | 604 | nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv) |
966a5b7d | 605 | { |
966a5b7d | 606 | u32 r000260; |
0411de85 BS |
607 | int i; |
608 | ||
ac1499d9 BS |
609 | if (priv->firmware) { |
610 | /* load fuc microcode */ | |
ebb945a9 BS |
611 | r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000); |
612 | nvc0_graph_init_fw(priv, 0x409000, &priv->fuc409c, | |
ac1499d9 | 613 | &priv->fuc409d); |
ebb945a9 | 614 | nvc0_graph_init_fw(priv, 0x41a000, &priv->fuc41ac, |
ac1499d9 | 615 | &priv->fuc41ad); |
ebb945a9 | 616 | nv_wr32(priv, 0x000260, r000260); |
0411de85 | 617 | |
ac1499d9 | 618 | /* start both of them running */ |
ebb945a9 BS |
619 | nv_wr32(priv, 0x409840, 0xffffffff); |
620 | nv_wr32(priv, 0x41a10c, 0x00000000); | |
621 | nv_wr32(priv, 0x40910c, 0x00000000); | |
622 | nv_wr32(priv, 0x41a100, 0x00000002); | |
623 | nv_wr32(priv, 0x409100, 0x00000002); | |
624 | if (!nv_wait(priv, 0x409800, 0x00000001, 0x00000001)) | |
625 | nv_info(priv, "0x409800 wait failed\n"); | |
626 | ||
627 | nv_wr32(priv, 0x409840, 0xffffffff); | |
628 | nv_wr32(priv, 0x409500, 0x7fffffff); | |
629 | nv_wr32(priv, 0x409504, 0x00000021); | |
630 | ||
631 | nv_wr32(priv, 0x409840, 0xffffffff); | |
632 | nv_wr32(priv, 0x409500, 0x00000000); | |
633 | nv_wr32(priv, 0x409504, 0x00000010); | |
634 | if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { | |
635 | nv_error(priv, "fuc09 req 0x10 timeout\n"); | |
0411de85 BS |
636 | return -EBUSY; |
637 | } | |
ebb945a9 | 638 | priv->size = nv_rd32(priv, 0x409800); |
0411de85 | 639 | |
ebb945a9 BS |
640 | nv_wr32(priv, 0x409840, 0xffffffff); |
641 | nv_wr32(priv, 0x409500, 0x00000000); | |
642 | nv_wr32(priv, 0x409504, 0x00000016); | |
643 | if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { | |
644 | nv_error(priv, "fuc09 req 0x16 timeout\n"); | |
ac1499d9 BS |
645 | return -EBUSY; |
646 | } | |
647 | ||
ebb945a9 BS |
648 | nv_wr32(priv, 0x409840, 0xffffffff); |
649 | nv_wr32(priv, 0x409500, 0x00000000); | |
650 | nv_wr32(priv, 0x409504, 0x00000025); | |
651 | if (!nv_wait_ne(priv, 0x409800, 0xffffffff, 0x00000000)) { | |
652 | nv_error(priv, "fuc09 req 0x25 timeout\n"); | |
ac1499d9 BS |
653 | return -EBUSY; |
654 | } | |
655 | ||
ebb945a9 BS |
656 | if (priv->data == NULL) { |
657 | int ret = nvc0_grctx_generate(priv); | |
658 | if (ret) { | |
659 | nv_error(priv, "failed to construct context\n"); | |
660 | return ret; | |
661 | } | |
662 | } | |
663 | ||
664 | return 0; | |
0411de85 | 665 | } |
966a5b7d | 666 | |
ac1499d9 | 667 | /* load HUB microcode */ |
ebb945a9 BS |
668 | r000260 = nv_mask(priv, 0x000260, 0x00000001, 0x00000000); |
669 | nv_wr32(priv, 0x4091c0, 0x01000000); | |
ac1499d9 | 670 | for (i = 0; i < sizeof(nvc0_grhub_data) / 4; i++) |
ebb945a9 | 671 | nv_wr32(priv, 0x4091c4, nvc0_grhub_data[i]); |
ac1499d9 | 672 | |
ebb945a9 | 673 | nv_wr32(priv, 0x409180, 0x01000000); |
ac1499d9 BS |
674 | for (i = 0; i < sizeof(nvc0_grhub_code) / 4; i++) { |
675 | if ((i & 0x3f) == 0) | |
ebb945a9 BS |
676 | nv_wr32(priv, 0x409188, i >> 6); |
677 | nv_wr32(priv, 0x409184, nvc0_grhub_code[i]); | |
ac1499d9 BS |
678 | } |
679 | ||
680 | /* load GPC microcode */ | |
ebb945a9 | 681 | nv_wr32(priv, 0x41a1c0, 0x01000000); |
ac1499d9 | 682 | for (i = 0; i < sizeof(nvc0_grgpc_data) / 4; i++) |
ebb945a9 | 683 | nv_wr32(priv, 0x41a1c4, nvc0_grgpc_data[i]); |
ac1499d9 | 684 | |
ebb945a9 | 685 | nv_wr32(priv, 0x41a180, 0x01000000); |
ac1499d9 BS |
686 | for (i = 0; i < sizeof(nvc0_grgpc_code) / 4; i++) { |
687 | if ((i & 0x3f) == 0) | |
ebb945a9 BS |
688 | nv_wr32(priv, 0x41a188, i >> 6); |
689 | nv_wr32(priv, 0x41a184, nvc0_grgpc_code[i]); | |
ac1499d9 | 690 | } |
ebb945a9 | 691 | nv_wr32(priv, 0x000260, r000260); |
966a5b7d | 692 | |
ac1499d9 | 693 | /* start HUB ucode running, it'll init the GPCs */ |
ebb945a9 BS |
694 | nv_wr32(priv, 0x409800, nv_device(priv)->chipset); |
695 | nv_wr32(priv, 0x40910c, 0x00000000); | |
696 | nv_wr32(priv, 0x409100, 0x00000002); | |
697 | if (!nv_wait(priv, 0x409800, 0x80000000, 0x80000000)) { | |
698 | nv_error(priv, "HUB_INIT timed out\n"); | |
699 | nvc0_graph_ctxctl_debug(priv); | |
966a5b7d BS |
700 | return -EBUSY; |
701 | } | |
966a5b7d | 702 | |
ebb945a9 | 703 | priv->size = nv_rd32(priv, 0x409804); |
ac1499d9 | 704 | if (priv->data == NULL) { |
ebb945a9 | 705 | int ret = nvc0_grctx_generate(priv); |
ac1499d9 | 706 | if (ret) { |
ebb945a9 | 707 | nv_error(priv, "failed to construct context\n"); |
ac1499d9 BS |
708 | return ret; |
709 | } | |
966a5b7d | 710 | |
ac1499d9 | 711 | return 1; |
966a5b7d BS |
712 | } |
713 | ||
714 | return 0; | |
4b223eef BS |
715 | } |
716 | ||
7a45cd19 | 717 | static int |
ebb945a9 | 718 | nvc0_graph_init(struct nouveau_object *object) |
4b223eef | 719 | { |
ebb945a9 | 720 | struct nvc0_graph_priv *priv = (void *)object; |
966a5b7d BS |
721 | int ret; |
722 | ||
ac1499d9 | 723 | reset: |
ebb945a9 BS |
724 | ret = nouveau_graph_init(&priv->base); |
725 | if (ret) | |
726 | return ret; | |
727 | ||
728 | nvc0_graph_init_obj418880(priv); | |
729 | nvc0_graph_init_regs(priv); | |
730 | /*nvc0_graph_init_unitplemented_magics(priv);*/ | |
731 | nvc0_graph_init_gpc_0(priv); | |
732 | /*nvc0_graph_init_unitplemented_c242(priv);*/ | |
733 | ||
734 | nv_wr32(priv, 0x400500, 0x00010001); | |
735 | nv_wr32(priv, 0x400100, 0xffffffff); | |
736 | nv_wr32(priv, 0x40013c, 0xffffffff); | |
737 | ||
738 | nvc0_graph_init_units(priv); | |
739 | nvc0_graph_init_gpc_1(priv); | |
740 | nvc0_graph_init_rop(priv); | |
741 | ||
742 | nv_wr32(priv, 0x400108, 0xffffffff); | |
743 | nv_wr32(priv, 0x400138, 0xffffffff); | |
744 | nv_wr32(priv, 0x400118, 0xffffffff); | |
745 | nv_wr32(priv, 0x400130, 0xffffffff); | |
746 | nv_wr32(priv, 0x40011c, 0xffffffff); | |
747 | nv_wr32(priv, 0x400134, 0xffffffff); | |
748 | nv_wr32(priv, 0x400054, 0x34ce3464); | |
749 | ||
750 | ret = nvc0_graph_init_ctxctl(priv); | |
ac1499d9 BS |
751 | if (ret) { |
752 | if (ret == 1) | |
753 | goto reset; | |
a82dd49f | 754 | return ret; |
ac1499d9 | 755 | } |
a82dd49f | 756 | |
4b223eef BS |
757 | return 0; |
758 | } | |
759 | ||
ebb945a9 BS |
760 | struct nouveau_oclass |
761 | nvc0_graph_oclass = { | |
762 | .handle = NV_ENGINE(GR, 0xc0), | |
763 | .ofuncs = &(struct nouveau_ofuncs) { | |
764 | .ctor = nvc0_graph_ctor, | |
765 | .dtor = nvc0_graph_dtor, | |
766 | .init = nvc0_graph_init, | |
767 | .fini = _nouveau_graph_fini, | |
768 | }, | |
769 | }; |