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 | ||
ebb945a9 BS |
25 | #include <core/client.h> |
26 | #include <core/handle.h> | |
27 | #include <core/namedb.h> | |
28 | #include <core/gpuobj.h> | |
29 | #include <core/engctx.h> | |
9bd2ddba | 30 | #include <core/event.h> |
bbf8906b BS |
31 | #include <nvif/unpack.h> |
32 | #include <nvif/class.h> | |
ebb945a9 | 33 | #include <core/enum.h> |
4b223eef | 34 | |
ebb945a9 BS |
35 | #include <subdev/timer.h> |
36 | #include <subdev/bar.h> | |
52225551 | 37 | #include <subdev/fb.h> |
5ce3bf3c | 38 | #include <subdev/mmu.h> |
b2b09938 | 39 | |
ebb945a9 BS |
40 | #include <engine/dmaobj.h> |
41 | #include <engine/fifo.h> | |
b2b09938 BS |
42 | |
43 | struct nvc0_fifo_priv { | |
ebb945a9 | 44 | struct nouveau_fifo base; |
24e8341e BS |
45 | |
46 | struct work_struct fault; | |
47 | u64 mask; | |
48 | ||
a07d0e76 BS |
49 | struct { |
50 | struct nouveau_gpuobj *mem[2]; | |
51 | int active; | |
52 | wait_queue_head_t wait; | |
53 | } runlist; | |
24e8341e | 54 | |
9da226f6 BS |
55 | struct { |
56 | struct nouveau_gpuobj *mem; | |
57 | struct nouveau_vma bar; | |
58 | } user; | |
ec9c0883 | 59 | int spoon_nr; |
b2b09938 BS |
60 | }; |
61 | ||
ebb945a9 BS |
62 | struct nvc0_fifo_base { |
63 | struct nouveau_fifo_base base; | |
64 | struct nouveau_gpuobj *pgd; | |
65 | struct nouveau_vm *vm; | |
66 | }; | |
67 | ||
b2b09938 | 68 | struct nvc0_fifo_chan { |
c420b2dc | 69 | struct nouveau_fifo_chan base; |
e2822b7a BS |
70 | enum { |
71 | STOPPED, | |
72 | RUNNING, | |
73 | KILLED | |
74 | } state; | |
b2b09938 BS |
75 | }; |
76 | ||
ebb945a9 BS |
77 | /******************************************************************************* |
78 | * FIFO channel objects | |
79 | ******************************************************************************/ | |
80 | ||
b2b09938 | 81 | static void |
0357466d | 82 | nvc0_fifo_runlist_update(struct nvc0_fifo_priv *priv) |
b2b09938 | 83 | { |
ebb945a9 | 84 | struct nouveau_bar *bar = nouveau_bar(priv); |
b2b09938 BS |
85 | struct nouveau_gpuobj *cur; |
86 | int i, p; | |
87 | ||
fadb1719 | 88 | mutex_lock(&nv_subdev(priv)->mutex); |
a07d0e76 BS |
89 | cur = priv->runlist.mem[priv->runlist.active]; |
90 | priv->runlist.active = !priv->runlist.active; | |
b2b09938 BS |
91 | |
92 | for (i = 0, p = 0; i < 128; i++) { | |
e2822b7a BS |
93 | struct nvc0_fifo_chan *chan = (void *)priv->base.channel[i]; |
94 | if (chan && chan->state == RUNNING) { | |
95 | nv_wo32(cur, p + 0, i); | |
96 | nv_wo32(cur, p + 4, 0x00000004); | |
97 | p += 8; | |
98 | } | |
b2b09938 | 99 | } |
ebb945a9 | 100 | bar->flush(bar); |
b2b09938 | 101 | |
ebb945a9 BS |
102 | nv_wr32(priv, 0x002270, cur->addr >> 12); |
103 | nv_wr32(priv, 0x002274, 0x01f00000 | (p >> 3)); | |
e2822b7a | 104 | |
3cf6290a BS |
105 | if (wait_event_timeout(priv->runlist.wait, |
106 | !(nv_rd32(priv, 0x00227c) & 0x00100000), | |
107 | msecs_to_jiffies(2000)) == 0) | |
108 | nv_error(priv, "runlist update timeout\n"); | |
fadb1719 | 109 | mutex_unlock(&nv_subdev(priv)->mutex); |
b2b09938 | 110 | } |
4b223eef | 111 | |
c420b2dc | 112 | static int |
ebb945a9 BS |
113 | nvc0_fifo_context_attach(struct nouveau_object *parent, |
114 | struct nouveau_object *object) | |
4b223eef | 115 | { |
ebb945a9 BS |
116 | struct nouveau_bar *bar = nouveau_bar(parent); |
117 | struct nvc0_fifo_base *base = (void *)parent->parent; | |
118 | struct nouveau_engctx *ectx = (void *)object; | |
119 | u32 addr; | |
120 | int ret; | |
b2b09938 | 121 | |
ebb945a9 | 122 | switch (nv_engidx(object->engine)) { |
37a5d028 BS |
123 | case NVDEV_ENGINE_SW : return 0; |
124 | case NVDEV_ENGINE_GR : addr = 0x0210; break; | |
125 | case NVDEV_ENGINE_CE0 : addr = 0x0230; break; | |
126 | case NVDEV_ENGINE_CE1 : addr = 0x0240; break; | |
127 | case NVDEV_ENGINE_MSVLD : addr = 0x0270; break; | |
128 | case NVDEV_ENGINE_MSPDEC: addr = 0x0250; break; | |
129 | case NVDEV_ENGINE_MSPPP : addr = 0x0260; break; | |
ebb945a9 BS |
130 | default: |
131 | return -EINVAL; | |
132 | } | |
b2b09938 | 133 | |
ebb945a9 BS |
134 | if (!ectx->vma.node) { |
135 | ret = nouveau_gpuobj_map_vm(nv_gpuobj(ectx), base->vm, | |
136 | NV_MEM_ACCESS_RW, &ectx->vma); | |
137 | if (ret) | |
138 | return ret; | |
4c2d4222 BS |
139 | |
140 | nv_engctx(ectx)->addr = nv_gpuobj(base)->addr >> 12; | |
b2b09938 BS |
141 | } |
142 | ||
ebb945a9 BS |
143 | nv_wo32(base, addr + 0x00, lower_32_bits(ectx->vma.offset) | 4); |
144 | nv_wo32(base, addr + 0x04, upper_32_bits(ectx->vma.offset)); | |
145 | bar->flush(bar); | |
146 | return 0; | |
4b223eef BS |
147 | } |
148 | ||
ebb945a9 BS |
149 | static int |
150 | nvc0_fifo_context_detach(struct nouveau_object *parent, bool suspend, | |
151 | struct nouveau_object *object) | |
4b223eef | 152 | { |
ebb945a9 BS |
153 | struct nouveau_bar *bar = nouveau_bar(parent); |
154 | struct nvc0_fifo_priv *priv = (void *)parent->engine; | |
155 | struct nvc0_fifo_base *base = (void *)parent->parent; | |
156 | struct nvc0_fifo_chan *chan = (void *)parent; | |
157 | u32 addr; | |
158 | ||
159 | switch (nv_engidx(object->engine)) { | |
37a5d028 BS |
160 | case NVDEV_ENGINE_SW : return 0; |
161 | case NVDEV_ENGINE_GR : addr = 0x0210; break; | |
162 | case NVDEV_ENGINE_CE0 : addr = 0x0230; break; | |
163 | case NVDEV_ENGINE_CE1 : addr = 0x0240; break; | |
164 | case NVDEV_ENGINE_MSVLD : addr = 0x0270; break; | |
165 | case NVDEV_ENGINE_MSPDEC: addr = 0x0250; break; | |
166 | case NVDEV_ENGINE_MSPPP : addr = 0x0260; break; | |
ebb945a9 BS |
167 | default: |
168 | return -EINVAL; | |
b2b09938 BS |
169 | } |
170 | ||
ebb945a9 BS |
171 | nv_wr32(priv, 0x002634, chan->base.chid); |
172 | if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) { | |
93260d3c MS |
173 | nv_error(priv, "channel %d [%s] kick timeout\n", |
174 | chan->base.chid, nouveau_client_name(chan)); | |
ebb945a9 BS |
175 | if (suspend) |
176 | return -EBUSY; | |
177 | } | |
178 | ||
edc260d0 BS |
179 | nv_wo32(base, addr + 0x00, 0x00000000); |
180 | nv_wo32(base, addr + 0x04, 0x00000000); | |
181 | bar->flush(bar); | |
ebb945a9 | 182 | return 0; |
b2b09938 BS |
183 | } |
184 | ||
185 | static int | |
ebb945a9 BS |
186 | nvc0_fifo_chan_ctor(struct nouveau_object *parent, |
187 | struct nouveau_object *engine, | |
188 | struct nouveau_oclass *oclass, void *data, u32 size, | |
189 | struct nouveau_object **pobject) | |
4b223eef | 190 | { |
bbf8906b BS |
191 | union { |
192 | struct nv50_channel_gpfifo_v0 v0; | |
193 | } *args = data; | |
ebb945a9 BS |
194 | struct nouveau_bar *bar = nouveau_bar(parent); |
195 | struct nvc0_fifo_priv *priv = (void *)engine; | |
196 | struct nvc0_fifo_base *base = (void *)parent; | |
197 | struct nvc0_fifo_chan *chan; | |
ebb945a9 BS |
198 | u64 usermem, ioffset, ilength; |
199 | int ret, i; | |
b2b09938 | 200 | |
bbf8906b BS |
201 | nv_ioctl(parent, "create channel gpfifo size %d\n", size); |
202 | if (nvif_unpack(args->v0, 0, 0, false)) { | |
203 | nv_ioctl(parent, "create channel gpfifo vers %d pushbuf %08x " | |
204 | "ioffset %016llx ilength %08x\n", | |
205 | args->v0.version, args->v0.pushbuf, args->v0.ioffset, | |
206 | args->v0.ilength); | |
207 | } else | |
208 | return ret; | |
ebb945a9 BS |
209 | |
210 | ret = nouveau_fifo_channel_create(parent, engine, oclass, 1, | |
211 | priv->user.bar.offset, 0x1000, | |
bbf8906b | 212 | args->v0.pushbuf, |
507ceb15 MP |
213 | (1ULL << NVDEV_ENGINE_SW) | |
214 | (1ULL << NVDEV_ENGINE_GR) | | |
aedf24ff BS |
215 | (1ULL << NVDEV_ENGINE_CE0) | |
216 | (1ULL << NVDEV_ENGINE_CE1) | | |
eccf7e8a | 217 | (1ULL << NVDEV_ENGINE_MSVLD) | |
37a5d028 | 218 | (1ULL << NVDEV_ENGINE_MSPDEC) | |
fd8666f7 | 219 | (1ULL << NVDEV_ENGINE_MSPPP), &chan); |
ebb945a9 BS |
220 | *pobject = nv_object(chan); |
221 | if (ret) | |
222 | return ret; | |
223 | ||
bbf8906b BS |
224 | args->v0.chid = chan->base.chid; |
225 | ||
ebb945a9 BS |
226 | nv_parent(chan)->context_attach = nvc0_fifo_context_attach; |
227 | nv_parent(chan)->context_detach = nvc0_fifo_context_detach; | |
228 | ||
229 | usermem = chan->base.chid * 0x1000; | |
bbf8906b BS |
230 | ioffset = args->v0.ioffset; |
231 | ilength = order_base_2(args->v0.ilength / 8); | |
ebb945a9 BS |
232 | |
233 | for (i = 0; i < 0x1000; i += 4) | |
234 | nv_wo32(priv->user.mem, usermem + i, 0x00000000); | |
235 | ||
236 | nv_wo32(base, 0x08, lower_32_bits(priv->user.mem->addr + usermem)); | |
237 | nv_wo32(base, 0x0c, upper_32_bits(priv->user.mem->addr + usermem)); | |
238 | nv_wo32(base, 0x10, 0x0000face); | |
239 | nv_wo32(base, 0x30, 0xfffff902); | |
240 | nv_wo32(base, 0x48, lower_32_bits(ioffset)); | |
241 | nv_wo32(base, 0x4c, upper_32_bits(ioffset) | (ilength << 16)); | |
242 | nv_wo32(base, 0x54, 0x00000002); | |
243 | nv_wo32(base, 0x84, 0x20400000); | |
244 | nv_wo32(base, 0x94, 0x30000001); | |
245 | nv_wo32(base, 0x9c, 0x00000100); | |
246 | nv_wo32(base, 0xa4, 0x1f1f1f1f); | |
247 | nv_wo32(base, 0xa8, 0x1f1f1f1f); | |
248 | nv_wo32(base, 0xac, 0x0000001f); | |
249 | nv_wo32(base, 0xb8, 0xf8000000); | |
250 | nv_wo32(base, 0xf8, 0x10003080); /* 0x002310 */ | |
251 | nv_wo32(base, 0xfc, 0x10000010); /* 0x002350 */ | |
252 | bar->flush(bar); | |
253 | return 0; | |
254 | } | |
b2b09938 | 255 | |
ebb945a9 BS |
256 | static int |
257 | nvc0_fifo_chan_init(struct nouveau_object *object) | |
258 | { | |
259 | struct nouveau_gpuobj *base = nv_gpuobj(object->parent); | |
260 | struct nvc0_fifo_priv *priv = (void *)object->engine; | |
261 | struct nvc0_fifo_chan *chan = (void *)object; | |
262 | u32 chid = chan->base.chid; | |
263 | int ret; | |
ec9c0883 | 264 | |
ebb945a9 BS |
265 | ret = nouveau_fifo_channel_init(&chan->base); |
266 | if (ret) | |
267 | return ret; | |
b2b09938 | 268 | |
ebb945a9 | 269 | nv_wr32(priv, 0x003000 + (chid * 8), 0xc0000000 | base->addr >> 12); |
e2822b7a BS |
270 | |
271 | if (chan->state == STOPPED && (chan->state = RUNNING) == RUNNING) { | |
272 | nv_wr32(priv, 0x003004 + (chid * 8), 0x001f0001); | |
273 | nvc0_fifo_runlist_update(priv); | |
274 | } | |
275 | ||
ebb945a9 BS |
276 | return 0; |
277 | } | |
b2b09938 | 278 | |
e99bf010 BS |
279 | static void nvc0_fifo_intr_engine(struct nvc0_fifo_priv *priv); |
280 | ||
ebb945a9 BS |
281 | static int |
282 | nvc0_fifo_chan_fini(struct nouveau_object *object, bool suspend) | |
283 | { | |
284 | struct nvc0_fifo_priv *priv = (void *)object->engine; | |
285 | struct nvc0_fifo_chan *chan = (void *)object; | |
286 | u32 chid = chan->base.chid; | |
b2b09938 | 287 | |
e2822b7a BS |
288 | if (chan->state == RUNNING && (chan->state = STOPPED) == STOPPED) { |
289 | nv_mask(priv, 0x003004 + (chid * 8), 0x00000001, 0x00000000); | |
290 | nvc0_fifo_runlist_update(priv); | |
291 | } | |
e99bf010 BS |
292 | |
293 | nvc0_fifo_intr_engine(priv); | |
294 | ||
ebb945a9 | 295 | nv_wr32(priv, 0x003000 + (chid * 8), 0x00000000); |
ebb945a9 BS |
296 | return nouveau_fifo_channel_fini(&chan->base, suspend); |
297 | } | |
0638df42 | 298 | |
ebb945a9 BS |
299 | static struct nouveau_ofuncs |
300 | nvc0_fifo_ofuncs = { | |
301 | .ctor = nvc0_fifo_chan_ctor, | |
302 | .dtor = _nouveau_fifo_channel_dtor, | |
303 | .init = nvc0_fifo_chan_init, | |
304 | .fini = nvc0_fifo_chan_fini, | |
6c6ae061 | 305 | .map = _nouveau_fifo_channel_map, |
ebb945a9 BS |
306 | .rd32 = _nouveau_fifo_channel_rd32, |
307 | .wr32 = _nouveau_fifo_channel_wr32, | |
867920f8 | 308 | .ntfy = _nouveau_fifo_channel_ntfy |
ebb945a9 | 309 | }; |
0638df42 | 310 | |
ebb945a9 BS |
311 | static struct nouveau_oclass |
312 | nvc0_fifo_sclass[] = { | |
bbf8906b | 313 | { FERMI_CHANNEL_GPFIFO, &nvc0_fifo_ofuncs }, |
ebb945a9 BS |
314 | {} |
315 | }; | |
316 | ||
317 | /******************************************************************************* | |
318 | * FIFO context - instmem heap and vm setup | |
319 | ******************************************************************************/ | |
4b223eef | 320 | |
c420b2dc | 321 | static int |
ebb945a9 BS |
322 | nvc0_fifo_context_ctor(struct nouveau_object *parent, |
323 | struct nouveau_object *engine, | |
324 | struct nouveau_oclass *oclass, void *data, u32 size, | |
325 | struct nouveau_object **pobject) | |
c420b2dc | 326 | { |
ebb945a9 BS |
327 | struct nvc0_fifo_base *base; |
328 | int ret; | |
c420b2dc | 329 | |
ebb945a9 BS |
330 | ret = nouveau_fifo_context_create(parent, engine, oclass, NULL, 0x1000, |
331 | 0x1000, NVOBJ_FLAG_ZERO_ALLOC | | |
332 | NVOBJ_FLAG_HEAP, &base); | |
333 | *pobject = nv_object(base); | |
334 | if (ret) | |
335 | return ret; | |
c420b2dc | 336 | |
f50c8054 BS |
337 | ret = nouveau_gpuobj_new(nv_object(base), NULL, 0x10000, 0x1000, 0, |
338 | &base->pgd); | |
ebb945a9 BS |
339 | if (ret) |
340 | return ret; | |
341 | ||
342 | nv_wo32(base, 0x0200, lower_32_bits(base->pgd->addr)); | |
343 | nv_wo32(base, 0x0204, upper_32_bits(base->pgd->addr)); | |
344 | nv_wo32(base, 0x0208, 0xffffffff); | |
345 | nv_wo32(base, 0x020c, 0x000000ff); | |
346 | ||
347 | ret = nouveau_vm_ref(nouveau_client(parent)->vm, &base->vm, base->pgd); | |
348 | if (ret) | |
349 | return ret; | |
c420b2dc | 350 | |
c420b2dc BS |
351 | return 0; |
352 | } | |
353 | ||
ebb945a9 BS |
354 | static void |
355 | nvc0_fifo_context_dtor(struct nouveau_object *object) | |
356 | { | |
357 | struct nvc0_fifo_base *base = (void *)object; | |
358 | nouveau_vm_ref(NULL, &base->vm, base->pgd); | |
359 | nouveau_gpuobj_ref(NULL, &base->pgd); | |
360 | nouveau_fifo_context_destroy(&base->base); | |
361 | } | |
362 | ||
363 | static struct nouveau_oclass | |
364 | nvc0_fifo_cclass = { | |
365 | .handle = NV_ENGCTX(FIFO, 0xc0), | |
366 | .ofuncs = &(struct nouveau_ofuncs) { | |
367 | .ctor = nvc0_fifo_context_ctor, | |
368 | .dtor = nvc0_fifo_context_dtor, | |
369 | .init = _nouveau_fifo_context_init, | |
370 | .fini = _nouveau_fifo_context_fini, | |
371 | .rd32 = _nouveau_fifo_context_rd32, | |
372 | .wr32 = _nouveau_fifo_context_wr32, | |
373 | }, | |
374 | }; | |
375 | ||
376 | /******************************************************************************* | |
377 | * PFIFO engine | |
378 | ******************************************************************************/ | |
c420b2dc | 379 | |
24e8341e BS |
380 | static inline int |
381 | nvc0_fifo_engidx(struct nvc0_fifo_priv *priv, u32 engn) | |
382 | { | |
383 | switch (engn) { | |
37a5d028 BS |
384 | case NVDEV_ENGINE_GR : engn = 0; break; |
385 | case NVDEV_ENGINE_MSVLD : engn = 1; break; | |
386 | case NVDEV_ENGINE_MSPPP : engn = 2; break; | |
387 | case NVDEV_ENGINE_MSPDEC: engn = 3; break; | |
388 | case NVDEV_ENGINE_CE0 : engn = 4; break; | |
389 | case NVDEV_ENGINE_CE1 : engn = 5; break; | |
24e8341e BS |
390 | default: |
391 | return -1; | |
392 | } | |
393 | ||
394 | return engn; | |
395 | } | |
396 | ||
397 | static inline struct nouveau_engine * | |
398 | nvc0_fifo_engine(struct nvc0_fifo_priv *priv, u32 engn) | |
399 | { | |
400 | switch (engn) { | |
401 | case 0: engn = NVDEV_ENGINE_GR; break; | |
eccf7e8a | 402 | case 1: engn = NVDEV_ENGINE_MSVLD; break; |
fd8666f7 | 403 | case 2: engn = NVDEV_ENGINE_MSPPP; break; |
37a5d028 | 404 | case 3: engn = NVDEV_ENGINE_MSPDEC; break; |
aedf24ff BS |
405 | case 4: engn = NVDEV_ENGINE_CE0; break; |
406 | case 5: engn = NVDEV_ENGINE_CE1; break; | |
24e8341e BS |
407 | default: |
408 | return NULL; | |
409 | } | |
410 | ||
411 | return nouveau_engine(priv, engn); | |
412 | } | |
413 | ||
414 | static void | |
415 | nvc0_fifo_recover_work(struct work_struct *work) | |
416 | { | |
417 | struct nvc0_fifo_priv *priv = container_of(work, typeof(*priv), fault); | |
418 | struct nouveau_object *engine; | |
419 | unsigned long flags; | |
420 | u32 engn, engm = 0; | |
421 | u64 mask, todo; | |
422 | ||
423 | spin_lock_irqsave(&priv->base.lock, flags); | |
424 | mask = priv->mask; | |
425 | priv->mask = 0ULL; | |
426 | spin_unlock_irqrestore(&priv->base.lock, flags); | |
427 | ||
428 | for (todo = mask; engn = __ffs64(todo), todo; todo &= ~(1 << engn)) | |
429 | engm |= 1 << nvc0_fifo_engidx(priv, engn); | |
430 | nv_mask(priv, 0x002630, engm, engm); | |
431 | ||
432 | for (todo = mask; engn = __ffs64(todo), todo; todo &= ~(1 << engn)) { | |
433 | if ((engine = (void *)nouveau_engine(priv, engn))) { | |
434 | nv_ofuncs(engine)->fini(engine, false); | |
435 | WARN_ON(nv_ofuncs(engine)->init(engine)); | |
436 | } | |
437 | } | |
438 | ||
439 | nvc0_fifo_runlist_update(priv); | |
440 | nv_wr32(priv, 0x00262c, engm); | |
441 | nv_mask(priv, 0x002630, engm, 0x00000000); | |
442 | } | |
443 | ||
444 | static void | |
445 | nvc0_fifo_recover(struct nvc0_fifo_priv *priv, struct nouveau_engine *engine, | |
446 | struct nvc0_fifo_chan *chan) | |
447 | { | |
24e8341e BS |
448 | u32 chid = chan->base.chid; |
449 | unsigned long flags; | |
450 | ||
451 | nv_error(priv, "%s engine fault on channel %d, recovering...\n", | |
452 | nv_subdev(engine)->name, chid); | |
453 | ||
454 | nv_mask(priv, 0x003004 + (chid * 0x08), 0x00000001, 0x00000000); | |
455 | chan->state = KILLED; | |
456 | ||
457 | spin_lock_irqsave(&priv->base.lock, flags); | |
ec0e5542 | 458 | priv->mask |= 1ULL << nv_engidx(engine); |
24e8341e BS |
459 | spin_unlock_irqrestore(&priv->base.lock, flags); |
460 | schedule_work(&priv->fault); | |
461 | } | |
462 | ||
083c2142 BS |
463 | static int |
464 | nvc0_fifo_swmthd(struct nvc0_fifo_priv *priv, u32 chid, u32 mthd, u32 data) | |
465 | { | |
466 | struct nvc0_fifo_chan *chan = NULL; | |
467 | struct nouveau_handle *bind; | |
468 | unsigned long flags; | |
469 | int ret = -EINVAL; | |
470 | ||
471 | spin_lock_irqsave(&priv->base.lock, flags); | |
472 | if (likely(chid >= priv->base.min && chid <= priv->base.max)) | |
473 | chan = (void *)priv->base.channel[chid]; | |
474 | if (unlikely(!chan)) | |
475 | goto out; | |
476 | ||
477 | bind = nouveau_namedb_get_class(nv_namedb(chan), 0x906e); | |
478 | if (likely(bind)) { | |
479 | if (!mthd || !nv_call(bind->object, mthd, data)) | |
480 | ret = 0; | |
481 | nouveau_namedb_put(bind); | |
482 | } | |
483 | ||
484 | out: | |
485 | spin_unlock_irqrestore(&priv->base.lock, flags); | |
486 | return ret; | |
487 | } | |
488 | ||
40476539 BS |
489 | static const struct nouveau_enum |
490 | nvc0_fifo_sched_reason[] = { | |
491 | { 0x0a, "CTXSW_TIMEOUT" }, | |
492 | {} | |
493 | }; | |
494 | ||
61fdf620 BS |
495 | static void |
496 | nvc0_fifo_intr_sched_ctxsw(struct nvc0_fifo_priv *priv) | |
497 | { | |
498 | struct nouveau_engine *engine; | |
499 | struct nvc0_fifo_chan *chan; | |
500 | u32 engn; | |
501 | ||
502 | for (engn = 0; engn < 6; engn++) { | |
503 | u32 stat = nv_rd32(priv, 0x002640 + (engn * 0x04)); | |
504 | u32 busy = (stat & 0x80000000); | |
505 | u32 save = (stat & 0x00100000); /* maybe? */ | |
506 | u32 unk0 = (stat & 0x00040000); | |
507 | u32 unk1 = (stat & 0x00001000); | |
508 | u32 chid = (stat & 0x0000007f); | |
509 | (void)save; | |
510 | ||
511 | if (busy && unk0 && unk1) { | |
512 | if (!(chan = (void *)priv->base.channel[chid])) | |
513 | continue; | |
514 | if (!(engine = nvc0_fifo_engine(priv, engn))) | |
515 | continue; | |
516 | nvc0_fifo_recover(priv, engine, chan); | |
517 | } | |
518 | } | |
519 | } | |
520 | ||
40476539 BS |
521 | static void |
522 | nvc0_fifo_intr_sched(struct nvc0_fifo_priv *priv) | |
523 | { | |
524 | u32 intr = nv_rd32(priv, 0x00254c); | |
525 | u32 code = intr & 0x000000ff; | |
526 | const struct nouveau_enum *en; | |
527 | char enunk[6] = ""; | |
528 | ||
529 | en = nouveau_enum_find(nvc0_fifo_sched_reason, code); | |
530 | if (!en) | |
531 | snprintf(enunk, sizeof(enunk), "UNK%02x", code); | |
532 | ||
533 | nv_error(priv, "SCHED_ERROR [ %s ]\n", en ? en->name : enunk); | |
61fdf620 BS |
534 | |
535 | switch (code) { | |
536 | case 0x0a: | |
537 | nvc0_fifo_intr_sched_ctxsw(priv); | |
538 | break; | |
539 | default: | |
540 | break; | |
541 | } | |
40476539 BS |
542 | } |
543 | ||
d439a5ac BS |
544 | static const struct nouveau_enum |
545 | nvc0_fifo_fault_engine[] = { | |
93260d3c | 546 | { 0x00, "PGRAPH", NULL, NVDEV_ENGINE_GR }, |
33f8c6d0 BS |
547 | { 0x03, "PEEPHOLE", NULL, NVDEV_ENGINE_IFB }, |
548 | { 0x04, "BAR1", NULL, NVDEV_SUBDEV_BAR }, | |
549 | { 0x05, "BAR3", NULL, NVDEV_SUBDEV_INSTMEM }, | |
93260d3c | 550 | { 0x07, "PFIFO", NULL, NVDEV_ENGINE_FIFO }, |
eccf7e8a | 551 | { 0x10, "PMSVLD", NULL, NVDEV_ENGINE_MSVLD }, |
fd8666f7 | 552 | { 0x11, "PMSPPP", NULL, NVDEV_ENGINE_MSPPP }, |
7a313473 | 553 | { 0x13, "PCOUNTER" }, |
37a5d028 | 554 | { 0x14, "PMSPDEC", NULL, NVDEV_ENGINE_MSPDEC }, |
aedf24ff BS |
555 | { 0x15, "PCE0", NULL, NVDEV_ENGINE_CE0 }, |
556 | { 0x16, "PCE1", NULL, NVDEV_ENGINE_CE1 }, | |
7a313473 | 557 | { 0x17, "PDAEMON" }, |
b2b09938 BS |
558 | {} |
559 | }; | |
560 | ||
d439a5ac BS |
561 | static const struct nouveau_enum |
562 | nvc0_fifo_fault_reason[] = { | |
e2966632 BS |
563 | { 0x00, "PT_NOT_PRESENT" }, |
564 | { 0x01, "PT_TOO_SHORT" }, | |
565 | { 0x02, "PAGE_NOT_PRESENT" }, | |
566 | { 0x03, "VM_LIMIT_EXCEEDED" }, | |
567 | { 0x04, "NO_CHANNEL" }, | |
568 | { 0x05, "PAGE_SYSTEM_ONLY" }, | |
569 | { 0x06, "PAGE_READ_ONLY" }, | |
570 | { 0x0a, "COMPRESSED_SYSRAM" }, | |
571 | { 0x0c, "INVALID_STORAGE_TYPE" }, | |
b2b09938 BS |
572 | {} |
573 | }; | |
574 | ||
d439a5ac BS |
575 | static const struct nouveau_enum |
576 | nvc0_fifo_fault_hubclient[] = { | |
7795bee0 BS |
577 | { 0x01, "PCOPY0" }, |
578 | { 0x02, "PCOPY1" }, | |
579 | { 0x04, "DISPATCH" }, | |
580 | { 0x05, "CTXCTL" }, | |
581 | { 0x06, "PFIFO" }, | |
582 | { 0x07, "BAR_READ" }, | |
583 | { 0x08, "BAR_WRITE" }, | |
584 | { 0x0b, "PVP" }, | |
fd8666f7 | 585 | { 0x0c, "PMSPPP" }, |
eccf7e8a | 586 | { 0x0d, "PMSVLD" }, |
7795bee0 BS |
587 | { 0x11, "PCOUNTER" }, |
588 | { 0x12, "PDAEMON" }, | |
589 | { 0x14, "CCACHE" }, | |
590 | { 0x15, "CCACHE_POST" }, | |
591 | {} | |
592 | }; | |
593 | ||
d439a5ac BS |
594 | static const struct nouveau_enum |
595 | nvc0_fifo_fault_gpcclient[] = { | |
7795bee0 BS |
596 | { 0x01, "TEX" }, |
597 | { 0x0c, "ESETUP" }, | |
598 | { 0x0e, "CTXCTL" }, | |
599 | { 0x0f, "PROP" }, | |
600 | {} | |
601 | }; | |
602 | ||
b2b09938 | 603 | static void |
d439a5ac | 604 | nvc0_fifo_intr_fault(struct nvc0_fifo_priv *priv, int unit) |
b2b09938 | 605 | { |
b3ccd34d BS |
606 | u32 inst = nv_rd32(priv, 0x002800 + (unit * 0x10)); |
607 | u32 valo = nv_rd32(priv, 0x002804 + (unit * 0x10)); | |
608 | u32 vahi = nv_rd32(priv, 0x002808 + (unit * 0x10)); | |
609 | u32 stat = nv_rd32(priv, 0x00280c + (unit * 0x10)); | |
d439a5ac | 610 | u32 gpc = (stat & 0x1f000000) >> 24; |
7795bee0 | 611 | u32 client = (stat & 0x00001f00) >> 8; |
d439a5ac BS |
612 | u32 write = (stat & 0x00000080); |
613 | u32 hub = (stat & 0x00000040); | |
614 | u32 reason = (stat & 0x0000000f); | |
24e8341e BS |
615 | struct nouveau_object *engctx = NULL, *object; |
616 | struct nouveau_engine *engine = NULL; | |
d439a5ac BS |
617 | const struct nouveau_enum *er, *eu, *ec; |
618 | char erunk[6] = ""; | |
619 | char euunk[6] = ""; | |
620 | char ecunk[6] = ""; | |
621 | char gpcid[3] = ""; | |
b2b09938 | 622 | |
d439a5ac BS |
623 | er = nouveau_enum_find(nvc0_fifo_fault_reason, reason); |
624 | if (!er) | |
625 | snprintf(erunk, sizeof(erunk), "UNK%02X", reason); | |
626 | ||
627 | eu = nouveau_enum_find(nvc0_fifo_fault_engine, unit); | |
628 | if (eu) { | |
33f8c6d0 BS |
629 | switch (eu->data2) { |
630 | case NVDEV_SUBDEV_BAR: | |
631 | nv_mask(priv, 0x001704, 0x00000000, 0x00000000); | |
632 | break; | |
633 | case NVDEV_SUBDEV_INSTMEM: | |
634 | nv_mask(priv, 0x001714, 0x00000000, 0x00000000); | |
635 | break; | |
636 | case NVDEV_ENGINE_IFB: | |
637 | nv_mask(priv, 0x001718, 0x00000000, 0x00000000); | |
638 | break; | |
639 | default: | |
d439a5ac BS |
640 | engine = nouveau_engine(priv, eu->data2); |
641 | if (engine) | |
642 | engctx = nouveau_engctx_get(engine, inst); | |
33f8c6d0 | 643 | break; |
d439a5ac | 644 | } |
7795bee0 | 645 | } else { |
d439a5ac | 646 | snprintf(euunk, sizeof(euunk), "UNK%02x", unit); |
7795bee0 | 647 | } |
93260d3c | 648 | |
d439a5ac BS |
649 | if (hub) { |
650 | ec = nouveau_enum_find(nvc0_fifo_fault_hubclient, client); | |
651 | } else { | |
652 | ec = nouveau_enum_find(nvc0_fifo_fault_gpcclient, client); | |
653 | snprintf(gpcid, sizeof(gpcid), "%d", gpc); | |
93260d3c | 654 | } |
d439a5ac BS |
655 | |
656 | if (!ec) | |
657 | snprintf(ecunk, sizeof(ecunk), "UNK%02x", client); | |
658 | ||
659 | nv_error(priv, "%s fault at 0x%010llx [%s] from %s/%s%s%s%s on " | |
660 | "channel 0x%010llx [%s]\n", write ? "write" : "read", | |
661 | (u64)vahi << 32 | valo, er ? er->name : erunk, | |
662 | eu ? eu->name : euunk, hub ? "" : "GPC", gpcid, hub ? "" : "/", | |
663 | ec ? ec->name : ecunk, (u64)inst << 12, | |
664 | nouveau_client_name(engctx)); | |
93260d3c | 665 | |
24e8341e BS |
666 | object = engctx; |
667 | while (object) { | |
668 | switch (nv_mclass(object)) { | |
bbf8906b | 669 | case FERMI_CHANNEL_GPFIFO: |
24e8341e BS |
670 | nvc0_fifo_recover(priv, engine, (void *)object); |
671 | break; | |
672 | } | |
673 | object = object->parent; | |
674 | } | |
675 | ||
93260d3c | 676 | nouveau_engctx_put(engctx); |
b2b09938 BS |
677 | } |
678 | ||
083c2142 BS |
679 | static const struct nouveau_bitfield |
680 | nvc0_fifo_pbdma_intr[] = { | |
681 | /* { 0x00008000, "" } seen with null ib push */ | |
682 | { 0x00200000, "ILLEGAL_MTHD" }, | |
683 | { 0x00800000, "EMPTY_SUBC" }, | |
684 | {} | |
685 | }; | |
d5316e25 | 686 | |
b2b09938 | 687 | static void |
083c2142 | 688 | nvc0_fifo_intr_pbdma(struct nvc0_fifo_priv *priv, int unit) |
b2b09938 | 689 | { |
ebb945a9 BS |
690 | u32 stat = nv_rd32(priv, 0x040108 + (unit * 0x2000)); |
691 | u32 addr = nv_rd32(priv, 0x0400c0 + (unit * 0x2000)); | |
692 | u32 data = nv_rd32(priv, 0x0400c4 + (unit * 0x2000)); | |
693 | u32 chid = nv_rd32(priv, 0x040120 + (unit * 0x2000)) & 0x7f; | |
694 | u32 subc = (addr & 0x00070000) >> 16; | |
b2b09938 | 695 | u32 mthd = (addr & 0x00003ffc); |
d5316e25 | 696 | u32 show = stat; |
b2b09938 | 697 | |
ebb945a9 BS |
698 | if (stat & 0x00800000) { |
699 | if (!nvc0_fifo_swmthd(priv, chid, mthd, data)) | |
700 | show &= ~0x00800000; | |
701 | } | |
702 | ||
d5316e25 | 703 | if (show) { |
0357466d BS |
704 | nv_error(priv, "PBDMA%d:", unit); |
705 | nouveau_bitfield_print(nvc0_fifo_pbdma_intr, show); | |
f533da10 | 706 | pr_cont("\n"); |
93260d3c | 707 | nv_error(priv, |
0357466d | 708 | "PBDMA%d: ch %d [%s] subc %d mthd 0x%04x data 0x%08x\n", |
93260d3c MS |
709 | unit, chid, |
710 | nouveau_client_name_for_fifo_chid(&priv->base, chid), | |
711 | subc, mthd, data); | |
d5316e25 | 712 | } |
b2b09938 | 713 | |
ebb945a9 BS |
714 | nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008); |
715 | nv_wr32(priv, 0x040108 + (unit * 0x2000), stat); | |
b2b09938 BS |
716 | } |
717 | ||
a07d0e76 BS |
718 | static void |
719 | nvc0_fifo_intr_runlist(struct nvc0_fifo_priv *priv) | |
720 | { | |
721 | u32 intr = nv_rd32(priv, 0x002a00); | |
722 | ||
723 | if (intr & 0x10000000) { | |
724 | wake_up(&priv->runlist.wait); | |
725 | nv_wr32(priv, 0x002a00, 0x10000000); | |
726 | intr &= ~0x10000000; | |
727 | } | |
728 | ||
729 | if (intr) { | |
730 | nv_error(priv, "RUNLIST 0x%08x\n", intr); | |
731 | nv_wr32(priv, 0x002a00, intr); | |
732 | } | |
733 | } | |
734 | ||
e99bf010 BS |
735 | static void |
736 | nvc0_fifo_intr_engine_unit(struct nvc0_fifo_priv *priv, int engn) | |
737 | { | |
738 | u32 intr = nv_rd32(priv, 0x0025a8 + (engn * 0x04)); | |
739 | u32 inte = nv_rd32(priv, 0x002628); | |
740 | u32 unkn; | |
741 | ||
19a10828 BS |
742 | nv_wr32(priv, 0x0025a8 + (engn * 0x04), intr); |
743 | ||
e99bf010 BS |
744 | for (unkn = 0; unkn < 8; unkn++) { |
745 | u32 ints = (intr >> (unkn * 0x04)) & inte; | |
746 | if (ints & 0x1) { | |
867920f8 | 747 | nouveau_fifo_uevent(&priv->base); |
e99bf010 BS |
748 | ints &= ~1; |
749 | } | |
750 | if (ints) { | |
751 | nv_error(priv, "ENGINE %d %d %01x", engn, unkn, ints); | |
752 | nv_mask(priv, 0x002628, ints, 0); | |
753 | } | |
754 | } | |
e99bf010 BS |
755 | } |
756 | ||
757 | static void | |
758 | nvc0_fifo_intr_engine(struct nvc0_fifo_priv *priv) | |
759 | { | |
760 | u32 mask = nv_rd32(priv, 0x0025a4); | |
761 | while (mask) { | |
762 | u32 unit = __ffs(mask); | |
763 | nvc0_fifo_intr_engine_unit(priv, unit); | |
764 | mask &= ~(1 << unit); | |
765 | } | |
766 | } | |
767 | ||
b2b09938 | 768 | static void |
ebb945a9 | 769 | nvc0_fifo_intr(struct nouveau_subdev *subdev) |
b2b09938 | 770 | { |
ebb945a9 BS |
771 | struct nvc0_fifo_priv *priv = (void *)subdev; |
772 | u32 mask = nv_rd32(priv, 0x002140); | |
773 | u32 stat = nv_rd32(priv, 0x002100) & mask; | |
b2b09938 | 774 | |
32256c87 BS |
775 | if (stat & 0x00000001) { |
776 | u32 intr = nv_rd32(priv, 0x00252c); | |
777 | nv_warn(priv, "INTR 0x00000001: 0x%08x\n", intr); | |
778 | nv_wr32(priv, 0x002100, 0x00000001); | |
779 | stat &= ~0x00000001; | |
780 | } | |
781 | ||
cc8cd647 | 782 | if (stat & 0x00000100) { |
40476539 | 783 | nvc0_fifo_intr_sched(priv); |
ebb945a9 | 784 | nv_wr32(priv, 0x002100, 0x00000100); |
cc8cd647 BS |
785 | stat &= ~0x00000100; |
786 | } | |
787 | ||
32256c87 BS |
788 | if (stat & 0x00010000) { |
789 | u32 intr = nv_rd32(priv, 0x00256c); | |
790 | nv_warn(priv, "INTR 0x00010000: 0x%08x\n", intr); | |
791 | nv_wr32(priv, 0x002100, 0x00010000); | |
792 | stat &= ~0x00010000; | |
793 | } | |
794 | ||
795 | if (stat & 0x01000000) { | |
796 | u32 intr = nv_rd32(priv, 0x00258c); | |
797 | nv_warn(priv, "INTR 0x01000000: 0x%08x\n", intr); | |
798 | nv_wr32(priv, 0x002100, 0x01000000); | |
799 | stat &= ~0x01000000; | |
800 | } | |
801 | ||
b2b09938 | 802 | if (stat & 0x10000000) { |
d439a5ac BS |
803 | u32 mask = nv_rd32(priv, 0x00259c); |
804 | while (mask) { | |
805 | u32 unit = __ffs(mask); | |
806 | nvc0_fifo_intr_fault(priv, unit); | |
807 | nv_wr32(priv, 0x00259c, (1 << unit)); | |
808 | mask &= ~(1 << unit); | |
b2b09938 | 809 | } |
b2b09938 BS |
810 | stat &= ~0x10000000; |
811 | } | |
812 | ||
813 | if (stat & 0x20000000) { | |
083c2142 BS |
814 | u32 mask = nv_rd32(priv, 0x0025a0); |
815 | while (mask) { | |
816 | u32 unit = __ffs(mask); | |
817 | nvc0_fifo_intr_pbdma(priv, unit); | |
818 | nv_wr32(priv, 0x0025a0, (1 << unit)); | |
819 | mask &= ~(1 << unit); | |
b2b09938 | 820 | } |
b2b09938 BS |
821 | stat &= ~0x20000000; |
822 | } | |
823 | ||
cc8cd647 | 824 | if (stat & 0x40000000) { |
a07d0e76 | 825 | nvc0_fifo_intr_runlist(priv); |
cc8cd647 BS |
826 | stat &= ~0x40000000; |
827 | } | |
828 | ||
32256c87 | 829 | if (stat & 0x80000000) { |
e99bf010 | 830 | nvc0_fifo_intr_engine(priv); |
32256c87 BS |
831 | stat &= ~0x80000000; |
832 | } | |
833 | ||
b2b09938 | 834 | if (stat) { |
22a7a27b BS |
835 | nv_error(priv, "INTR 0x%08x\n", stat); |
836 | nv_mask(priv, 0x002140, stat, 0x00000000); | |
ebb945a9 | 837 | nv_wr32(priv, 0x002100, stat); |
b2b09938 | 838 | } |
b2b09938 | 839 | } |
c420b2dc | 840 | |
9bd2ddba | 841 | static void |
79ca2770 | 842 | nvc0_fifo_uevent_init(struct nvkm_event *event, int type, int index) |
9bd2ddba | 843 | { |
79ca2770 BS |
844 | struct nouveau_fifo *fifo = container_of(event, typeof(*fifo), uevent); |
845 | nv_mask(fifo, 0x002140, 0x80000000, 0x80000000); | |
9bd2ddba BS |
846 | } |
847 | ||
848 | static void | |
79ca2770 | 849 | nvc0_fifo_uevent_fini(struct nvkm_event *event, int type, int index) |
9bd2ddba | 850 | { |
79ca2770 BS |
851 | struct nouveau_fifo *fifo = container_of(event, typeof(*fifo), uevent); |
852 | nv_mask(fifo, 0x002140, 0x80000000, 0x00000000); | |
9bd2ddba BS |
853 | } |
854 | ||
79ca2770 BS |
855 | static const struct nvkm_event_func |
856 | nvc0_fifo_uevent_func = { | |
867920f8 | 857 | .ctor = nouveau_fifo_uevent_ctor, |
79ca2770 BS |
858 | .init = nvc0_fifo_uevent_init, |
859 | .fini = nvc0_fifo_uevent_fini, | |
860 | }; | |
861 | ||
ebb945a9 BS |
862 | static int |
863 | nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine, | |
864 | struct nouveau_oclass *oclass, void *data, u32 size, | |
865 | struct nouveau_object **pobject) | |
866 | { | |
867 | struct nvc0_fifo_priv *priv; | |
868 | int ret; | |
869 | ||
870 | ret = nouveau_fifo_create(parent, engine, oclass, 0, 127, &priv); | |
871 | *pobject = nv_object(priv); | |
872 | if (ret) | |
873 | return ret; | |
874 | ||
24e8341e BS |
875 | INIT_WORK(&priv->fault, nvc0_fifo_recover_work); |
876 | ||
f50c8054 | 877 | ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0x1000, 0, |
a07d0e76 | 878 | &priv->runlist.mem[0]); |
ebb945a9 BS |
879 | if (ret) |
880 | return ret; | |
881 | ||
f50c8054 | 882 | ret = nouveau_gpuobj_new(nv_object(priv), NULL, 0x1000, 0x1000, 0, |
a07d0e76 | 883 | &priv->runlist.mem[1]); |
ebb945a9 BS |
884 | if (ret) |
885 | return ret; | |
886 | ||
a07d0e76 BS |
887 | init_waitqueue_head(&priv->runlist.wait); |
888 | ||
f50c8054 | 889 | ret = nouveau_gpuobj_new(nv_object(priv), NULL, 128 * 0x1000, 0x1000, 0, |
ebb945a9 BS |
890 | &priv->user.mem); |
891 | if (ret) | |
892 | return ret; | |
893 | ||
894 | ret = nouveau_gpuobj_map(priv->user.mem, NV_MEM_ACCESS_RW, | |
895 | &priv->user.bar); | |
896 | if (ret) | |
897 | return ret; | |
898 | ||
79ca2770 BS |
899 | ret = nvkm_event_init(&nvc0_fifo_uevent_func, 1, 1, &priv->base.uevent); |
900 | if (ret) | |
901 | return ret; | |
9bd2ddba | 902 | |
ebb945a9 BS |
903 | nv_subdev(priv)->unit = 0x00000100; |
904 | nv_subdev(priv)->intr = nvc0_fifo_intr; | |
905 | nv_engine(priv)->cclass = &nvc0_fifo_cclass; | |
906 | nv_engine(priv)->sclass = nvc0_fifo_sclass; | |
907 | return 0; | |
908 | } | |
909 | ||
c420b2dc | 910 | static void |
ebb945a9 | 911 | nvc0_fifo_dtor(struct nouveau_object *object) |
c420b2dc | 912 | { |
ebb945a9 | 913 | struct nvc0_fifo_priv *priv = (void *)object; |
c420b2dc | 914 | |
18c9b959 | 915 | nouveau_gpuobj_unmap(&priv->user.bar); |
9da226f6 | 916 | nouveau_gpuobj_ref(NULL, &priv->user.mem); |
a07d0e76 BS |
917 | nouveau_gpuobj_ref(NULL, &priv->runlist.mem[0]); |
918 | nouveau_gpuobj_ref(NULL, &priv->runlist.mem[1]); | |
c420b2dc | 919 | |
ebb945a9 | 920 | nouveau_fifo_destroy(&priv->base); |
c420b2dc BS |
921 | } |
922 | ||
ebb945a9 BS |
923 | static int |
924 | nvc0_fifo_init(struct nouveau_object *object) | |
c420b2dc | 925 | { |
ebb945a9 BS |
926 | struct nvc0_fifo_priv *priv = (void *)object; |
927 | int ret, i; | |
c420b2dc | 928 | |
ebb945a9 BS |
929 | ret = nouveau_fifo_init(&priv->base); |
930 | if (ret) | |
931 | return ret; | |
c420b2dc | 932 | |
ebb945a9 BS |
933 | nv_wr32(priv, 0x000204, 0xffffffff); |
934 | nv_wr32(priv, 0x002204, 0xffffffff); | |
c420b2dc | 935 | |
ebb945a9 | 936 | priv->spoon_nr = hweight32(nv_rd32(priv, 0x002204)); |
0357466d | 937 | nv_debug(priv, "%d PBDMA unit(s)\n", priv->spoon_nr); |
c420b2dc | 938 | |
0357466d | 939 | /* assign engines to PBDMAs */ |
ebb945a9 BS |
940 | if (priv->spoon_nr >= 3) { |
941 | nv_wr32(priv, 0x002208, ~(1 << 0)); /* PGRAPH */ | |
942 | nv_wr32(priv, 0x00220c, ~(1 << 1)); /* PVP */ | |
fd8666f7 | 943 | nv_wr32(priv, 0x002210, ~(1 << 1)); /* PMSPP */ |
eccf7e8a | 944 | nv_wr32(priv, 0x002214, ~(1 << 1)); /* PMSVLD */ |
ebb945a9 BS |
945 | nv_wr32(priv, 0x002218, ~(1 << 2)); /* PCE0 */ |
946 | nv_wr32(priv, 0x00221c, ~(1 << 1)); /* PCE1 */ | |
947 | } | |
c420b2dc | 948 | |
0357466d | 949 | /* PBDMA[n] */ |
ebb945a9 BS |
950 | for (i = 0; i < priv->spoon_nr; i++) { |
951 | nv_mask(priv, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000); | |
952 | nv_wr32(priv, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */ | |
953 | nv_wr32(priv, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTREN */ | |
954 | } | |
c420b2dc | 955 | |
ebb945a9 BS |
956 | nv_mask(priv, 0x002200, 0x00000001, 0x00000001); |
957 | nv_wr32(priv, 0x002254, 0x10000000 | priv->user.bar.offset >> 12); | |
9da226f6 | 958 | |
ebb945a9 | 959 | nv_wr32(priv, 0x002100, 0xffffffff); |
a07d0e76 | 960 | nv_wr32(priv, 0x002140, 0x7fffffff); |
e99bf010 | 961 | nv_wr32(priv, 0x002628, 0x00000001); /* ENGINE_INTR_EN */ |
ebb945a9 | 962 | return 0; |
c420b2dc | 963 | } |
ebb945a9 | 964 | |
16c4f227 BS |
965 | struct nouveau_oclass * |
966 | nvc0_fifo_oclass = &(struct nouveau_oclass) { | |
ebb945a9 BS |
967 | .handle = NV_ENGINE(FIFO, 0xc0), |
968 | .ofuncs = &(struct nouveau_ofuncs) { | |
969 | .ctor = nvc0_fifo_ctor, | |
970 | .dtor = nvc0_fifo_dtor, | |
971 | .init = nvc0_fifo_init, | |
972 | .fini = _nouveau_fifo_fini, | |
973 | }, | |
974 | }; |