]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c
drm/nouveau/fifo: trigger engine context unload before zeroing context pointer
[karo-tx-linux.git] / drivers / gpu / drm / nouveau / core / engine / fifo / nvc0.c
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
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>
30 #include <core/class.h>
31 #include <core/math.h>
32 #include <core/enum.h>
33
34 #include <subdev/timer.h>
35 #include <subdev/bar.h>
36 #include <subdev/vm.h>
37
38 #include <engine/dmaobj.h>
39 #include <engine/fifo.h>
40
41 struct nvc0_fifo_priv {
42         struct nouveau_fifo base;
43         struct nouveau_gpuobj *playlist[2];
44         int cur_playlist;
45         struct {
46                 struct nouveau_gpuobj *mem;
47                 struct nouveau_vma bar;
48         } user;
49         int spoon_nr;
50 };
51
52 struct nvc0_fifo_base {
53         struct nouveau_fifo_base base;
54         struct nouveau_gpuobj *pgd;
55         struct nouveau_vm *vm;
56 };
57
58 struct nvc0_fifo_chan {
59         struct nouveau_fifo_chan base;
60 };
61
62 /*******************************************************************************
63  * FIFO channel objects
64  ******************************************************************************/
65
66 static void
67 nvc0_fifo_playlist_update(struct nvc0_fifo_priv *priv)
68 {
69         struct nouveau_bar *bar = nouveau_bar(priv);
70         struct nouveau_gpuobj *cur;
71         int i, p;
72
73         cur = priv->playlist[priv->cur_playlist];
74         priv->cur_playlist = !priv->cur_playlist;
75
76         for (i = 0, p = 0; i < 128; i++) {
77                 if (!(nv_rd32(priv, 0x003004 + (i * 8)) & 1))
78                         continue;
79                 nv_wo32(cur, p + 0, i);
80                 nv_wo32(cur, p + 4, 0x00000004);
81                 p += 8;
82         }
83         bar->flush(bar);
84
85         nv_wr32(priv, 0x002270, cur->addr >> 12);
86         nv_wr32(priv, 0x002274, 0x01f00000 | (p >> 3));
87         if (!nv_wait(priv, 0x00227c, 0x00100000, 0x00000000))
88                 nv_error(priv, "playlist update failed\n");
89 }
90
91 static int
92 nvc0_fifo_context_attach(struct nouveau_object *parent,
93                          struct nouveau_object *object)
94 {
95         struct nouveau_bar *bar = nouveau_bar(parent);
96         struct nvc0_fifo_base *base = (void *)parent->parent;
97         struct nouveau_engctx *ectx = (void *)object;
98         u32 addr;
99         int ret;
100
101         switch (nv_engidx(object->engine)) {
102         case NVDEV_ENGINE_SW   : return 0;
103         case NVDEV_ENGINE_GR   : addr = 0x0210; break;
104         case NVDEV_ENGINE_COPY0: addr = 0x0230; break;
105         case NVDEV_ENGINE_COPY1: addr = 0x0240; break;
106         case NVDEV_ENGINE_BSP  : addr = 0x0270; break;
107         case NVDEV_ENGINE_VP   : addr = 0x0250; break;
108         case NVDEV_ENGINE_PPP  : addr = 0x0260; break;
109         default:
110                 return -EINVAL;
111         }
112
113         if (!ectx->vma.node) {
114                 ret = nouveau_gpuobj_map_vm(nv_gpuobj(ectx), base->vm,
115                                             NV_MEM_ACCESS_RW, &ectx->vma);
116                 if (ret)
117                         return ret;
118
119                 nv_engctx(ectx)->addr = nv_gpuobj(base)->addr >> 12;
120         }
121
122         nv_wo32(base, addr + 0x00, lower_32_bits(ectx->vma.offset) | 4);
123         nv_wo32(base, addr + 0x04, upper_32_bits(ectx->vma.offset));
124         bar->flush(bar);
125         return 0;
126 }
127
128 static int
129 nvc0_fifo_context_detach(struct nouveau_object *parent, bool suspend,
130                          struct nouveau_object *object)
131 {
132         struct nouveau_bar *bar = nouveau_bar(parent);
133         struct nvc0_fifo_priv *priv = (void *)parent->engine;
134         struct nvc0_fifo_base *base = (void *)parent->parent;
135         struct nvc0_fifo_chan *chan = (void *)parent;
136         u32 addr;
137
138         switch (nv_engidx(object->engine)) {
139         case NVDEV_ENGINE_SW   : return 0;
140         case NVDEV_ENGINE_GR   : addr = 0x0210; break;
141         case NVDEV_ENGINE_COPY0: addr = 0x0230; break;
142         case NVDEV_ENGINE_COPY1: addr = 0x0240; break;
143         case NVDEV_ENGINE_BSP  : addr = 0x0270; break;
144         case NVDEV_ENGINE_VP   : addr = 0x0250; break;
145         case NVDEV_ENGINE_PPP  : addr = 0x0260; break;
146         default:
147                 return -EINVAL;
148         }
149
150         nv_wr32(priv, 0x002634, chan->base.chid);
151         if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) {
152                 nv_error(priv, "channel %d kick timeout\n", chan->base.chid);
153                 if (suspend)
154                         return -EBUSY;
155         }
156
157         nv_wo32(base, addr + 0x00, 0x00000000);
158         nv_wo32(base, addr + 0x04, 0x00000000);
159         bar->flush(bar);
160         return 0;
161 }
162
163 static int
164 nvc0_fifo_chan_ctor(struct nouveau_object *parent,
165                     struct nouveau_object *engine,
166                     struct nouveau_oclass *oclass, void *data, u32 size,
167                     struct nouveau_object **pobject)
168 {
169         struct nouveau_bar *bar = nouveau_bar(parent);
170         struct nvc0_fifo_priv *priv = (void *)engine;
171         struct nvc0_fifo_base *base = (void *)parent;
172         struct nvc0_fifo_chan *chan;
173         struct nv50_channel_ind_class *args = data;
174         u64 usermem, ioffset, ilength;
175         int ret, i;
176
177         if (size < sizeof(*args))
178                 return -EINVAL;
179
180         ret = nouveau_fifo_channel_create(parent, engine, oclass, 1,
181                                           priv->user.bar.offset, 0x1000,
182                                           args->pushbuf,
183                                           (1ULL << NVDEV_ENGINE_SW) |
184                                           (1ULL << NVDEV_ENGINE_GR) |
185                                           (1ULL << NVDEV_ENGINE_COPY0) |
186                                           (1ULL << NVDEV_ENGINE_COPY1) |
187                                           (1ULL << NVDEV_ENGINE_BSP) |
188                                           (1ULL << NVDEV_ENGINE_VP) |
189                                           (1ULL << NVDEV_ENGINE_PPP), &chan);
190         *pobject = nv_object(chan);
191         if (ret)
192                 return ret;
193
194         nv_parent(chan)->context_attach = nvc0_fifo_context_attach;
195         nv_parent(chan)->context_detach = nvc0_fifo_context_detach;
196
197         usermem = chan->base.chid * 0x1000;
198         ioffset = args->ioffset;
199         ilength = log2i(args->ilength / 8);
200
201         for (i = 0; i < 0x1000; i += 4)
202                 nv_wo32(priv->user.mem, usermem + i, 0x00000000);
203
204         nv_wo32(base, 0x08, lower_32_bits(priv->user.mem->addr + usermem));
205         nv_wo32(base, 0x0c, upper_32_bits(priv->user.mem->addr + usermem));
206         nv_wo32(base, 0x10, 0x0000face);
207         nv_wo32(base, 0x30, 0xfffff902);
208         nv_wo32(base, 0x48, lower_32_bits(ioffset));
209         nv_wo32(base, 0x4c, upper_32_bits(ioffset) | (ilength << 16));
210         nv_wo32(base, 0x54, 0x00000002);
211         nv_wo32(base, 0x84, 0x20400000);
212         nv_wo32(base, 0x94, 0x30000001);
213         nv_wo32(base, 0x9c, 0x00000100);
214         nv_wo32(base, 0xa4, 0x1f1f1f1f);
215         nv_wo32(base, 0xa8, 0x1f1f1f1f);
216         nv_wo32(base, 0xac, 0x0000001f);
217         nv_wo32(base, 0xb8, 0xf8000000);
218         nv_wo32(base, 0xf8, 0x10003080); /* 0x002310 */
219         nv_wo32(base, 0xfc, 0x10000010); /* 0x002350 */
220         bar->flush(bar);
221         return 0;
222 }
223
224 static int
225 nvc0_fifo_chan_init(struct nouveau_object *object)
226 {
227         struct nouveau_gpuobj *base = nv_gpuobj(object->parent);
228         struct nvc0_fifo_priv *priv = (void *)object->engine;
229         struct nvc0_fifo_chan *chan = (void *)object;
230         u32 chid = chan->base.chid;
231         int ret;
232
233         ret = nouveau_fifo_channel_init(&chan->base);
234         if (ret)
235                 return ret;
236
237         nv_wr32(priv, 0x003000 + (chid * 8), 0xc0000000 | base->addr >> 12);
238         nv_wr32(priv, 0x003004 + (chid * 8), 0x001f0001);
239         nvc0_fifo_playlist_update(priv);
240         return 0;
241 }
242
243 static int
244 nvc0_fifo_chan_fini(struct nouveau_object *object, bool suspend)
245 {
246         struct nvc0_fifo_priv *priv = (void *)object->engine;
247         struct nvc0_fifo_chan *chan = (void *)object;
248         u32 chid = chan->base.chid;
249
250         nv_mask(priv, 0x003004 + (chid * 8), 0x00000001, 0x00000000);
251         nvc0_fifo_playlist_update(priv);
252         nv_wr32(priv, 0x003000 + (chid * 8), 0x00000000);
253
254         return nouveau_fifo_channel_fini(&chan->base, suspend);
255 }
256
257 static struct nouveau_ofuncs
258 nvc0_fifo_ofuncs = {
259         .ctor = nvc0_fifo_chan_ctor,
260         .dtor = _nouveau_fifo_channel_dtor,
261         .init = nvc0_fifo_chan_init,
262         .fini = nvc0_fifo_chan_fini,
263         .rd32 = _nouveau_fifo_channel_rd32,
264         .wr32 = _nouveau_fifo_channel_wr32,
265 };
266
267 static struct nouveau_oclass
268 nvc0_fifo_sclass[] = {
269         { NVC0_CHANNEL_IND_CLASS, &nvc0_fifo_ofuncs },
270         {}
271 };
272
273 /*******************************************************************************
274  * FIFO context - instmem heap and vm setup
275  ******************************************************************************/
276
277 static int
278 nvc0_fifo_context_ctor(struct nouveau_object *parent,
279                        struct nouveau_object *engine,
280                        struct nouveau_oclass *oclass, void *data, u32 size,
281                        struct nouveau_object **pobject)
282 {
283         struct nvc0_fifo_base *base;
284         int ret;
285
286         ret = nouveau_fifo_context_create(parent, engine, oclass, NULL, 0x1000,
287                                           0x1000, NVOBJ_FLAG_ZERO_ALLOC |
288                                           NVOBJ_FLAG_HEAP, &base);
289         *pobject = nv_object(base);
290         if (ret)
291                 return ret;
292
293         ret = nouveau_gpuobj_new(parent, NULL, 0x10000, 0x1000, 0, &base->pgd);
294         if (ret)
295                 return ret;
296
297         nv_wo32(base, 0x0200, lower_32_bits(base->pgd->addr));
298         nv_wo32(base, 0x0204, upper_32_bits(base->pgd->addr));
299         nv_wo32(base, 0x0208, 0xffffffff);
300         nv_wo32(base, 0x020c, 0x000000ff);
301
302         ret = nouveau_vm_ref(nouveau_client(parent)->vm, &base->vm, base->pgd);
303         if (ret)
304                 return ret;
305
306         return 0;
307 }
308
309 static void
310 nvc0_fifo_context_dtor(struct nouveau_object *object)
311 {
312         struct nvc0_fifo_base *base = (void *)object;
313         nouveau_vm_ref(NULL, &base->vm, base->pgd);
314         nouveau_gpuobj_ref(NULL, &base->pgd);
315         nouveau_fifo_context_destroy(&base->base);
316 }
317
318 static struct nouveau_oclass
319 nvc0_fifo_cclass = {
320         .handle = NV_ENGCTX(FIFO, 0xc0),
321         .ofuncs = &(struct nouveau_ofuncs) {
322                 .ctor = nvc0_fifo_context_ctor,
323                 .dtor = nvc0_fifo_context_dtor,
324                 .init = _nouveau_fifo_context_init,
325                 .fini = _nouveau_fifo_context_fini,
326                 .rd32 = _nouveau_fifo_context_rd32,
327                 .wr32 = _nouveau_fifo_context_wr32,
328         },
329 };
330
331 /*******************************************************************************
332  * PFIFO engine
333  ******************************************************************************/
334
335 static const struct nouveau_enum nvc0_fifo_fault_unit[] = {
336         { 0x00, "PGRAPH" },
337         { 0x03, "PEEPHOLE" },
338         { 0x04, "BAR1" },
339         { 0x05, "BAR3" },
340         { 0x07, "PFIFO" },
341         { 0x10, "PBSP" },
342         { 0x11, "PPPP" },
343         { 0x13, "PCOUNTER" },
344         { 0x14, "PVP" },
345         { 0x15, "PCOPY0" },
346         { 0x16, "PCOPY1" },
347         { 0x17, "PDAEMON" },
348         {}
349 };
350
351 static const struct nouveau_enum nvc0_fifo_fault_reason[] = {
352         { 0x00, "PT_NOT_PRESENT" },
353         { 0x01, "PT_TOO_SHORT" },
354         { 0x02, "PAGE_NOT_PRESENT" },
355         { 0x03, "VM_LIMIT_EXCEEDED" },
356         { 0x04, "NO_CHANNEL" },
357         { 0x05, "PAGE_SYSTEM_ONLY" },
358         { 0x06, "PAGE_READ_ONLY" },
359         { 0x0a, "COMPRESSED_SYSRAM" },
360         { 0x0c, "INVALID_STORAGE_TYPE" },
361         {}
362 };
363
364 static const struct nouveau_enum nvc0_fifo_fault_hubclient[] = {
365         { 0x01, "PCOPY0" },
366         { 0x02, "PCOPY1" },
367         { 0x04, "DISPATCH" },
368         { 0x05, "CTXCTL" },
369         { 0x06, "PFIFO" },
370         { 0x07, "BAR_READ" },
371         { 0x08, "BAR_WRITE" },
372         { 0x0b, "PVP" },
373         { 0x0c, "PPPP" },
374         { 0x0d, "PBSP" },
375         { 0x11, "PCOUNTER" },
376         { 0x12, "PDAEMON" },
377         { 0x14, "CCACHE" },
378         { 0x15, "CCACHE_POST" },
379         {}
380 };
381
382 static const struct nouveau_enum nvc0_fifo_fault_gpcclient[] = {
383         { 0x01, "TEX" },
384         { 0x0c, "ESETUP" },
385         { 0x0e, "CTXCTL" },
386         { 0x0f, "PROP" },
387         {}
388 };
389
390 static const struct nouveau_bitfield nvc0_fifo_subfifo_intr[] = {
391 /*      { 0x00008000, "" }      seen with null ib push */
392         { 0x00200000, "ILLEGAL_MTHD" },
393         { 0x00800000, "EMPTY_SUBC" },
394         {}
395 };
396
397 static void
398 nvc0_fifo_isr_vm_fault(struct nvc0_fifo_priv *priv, int unit)
399 {
400         u32 inst = nv_rd32(priv, 0x002800 + (unit * 0x10));
401         u32 valo = nv_rd32(priv, 0x002804 + (unit * 0x10));
402         u32 vahi = nv_rd32(priv, 0x002808 + (unit * 0x10));
403         u32 stat = nv_rd32(priv, 0x00280c + (unit * 0x10));
404         u32 client = (stat & 0x00001f00) >> 8;
405
406         switch (unit) {
407         case 3: /* PEEPHOLE */
408                 nv_mask(priv, 0x001718, 0x00000000, 0x00000000);
409                 break;
410         case 4: /* BAR1 */
411                 nv_mask(priv, 0x001704, 0x00000000, 0x00000000);
412                 break;
413         case 5: /* BAR3 */
414                 nv_mask(priv, 0x001714, 0x00000000, 0x00000000);
415                 break;
416         default:
417                 break;
418         }
419
420         nv_error(priv, "%s fault at 0x%010llx [", (stat & 0x00000080) ?
421                  "write" : "read", (u64)vahi << 32 | valo);
422         nouveau_enum_print(nvc0_fifo_fault_reason, stat & 0x0000000f);
423         printk("] from ");
424         nouveau_enum_print(nvc0_fifo_fault_unit, unit);
425         if (stat & 0x00000040) {
426                 printk("/");
427                 nouveau_enum_print(nvc0_fifo_fault_hubclient, client);
428         } else {
429                 printk("/GPC%d/", (stat & 0x1f000000) >> 24);
430                 nouveau_enum_print(nvc0_fifo_fault_gpcclient, client);
431         }
432         printk(" on channel 0x%010llx\n", (u64)inst << 12);
433 }
434
435 static int
436 nvc0_fifo_swmthd(struct nvc0_fifo_priv *priv, u32 chid, u32 mthd, u32 data)
437 {
438         struct nvc0_fifo_chan *chan = NULL;
439         struct nouveau_handle *bind;
440         unsigned long flags;
441         int ret = -EINVAL;
442
443         spin_lock_irqsave(&priv->base.lock, flags);
444         if (likely(chid >= priv->base.min && chid <= priv->base.max))
445                 chan = (void *)priv->base.channel[chid];
446         if (unlikely(!chan))
447                 goto out;
448
449         bind = nouveau_namedb_get_class(nv_namedb(chan), 0x906e);
450         if (likely(bind)) {
451                 if (!mthd || !nv_call(bind->object, mthd, data))
452                         ret = 0;
453                 nouveau_namedb_put(bind);
454         }
455
456 out:
457         spin_unlock_irqrestore(&priv->base.lock, flags);
458         return ret;
459 }
460
461 static void
462 nvc0_fifo_isr_subfifo_intr(struct nvc0_fifo_priv *priv, int unit)
463 {
464         u32 stat = nv_rd32(priv, 0x040108 + (unit * 0x2000));
465         u32 addr = nv_rd32(priv, 0x0400c0 + (unit * 0x2000));
466         u32 data = nv_rd32(priv, 0x0400c4 + (unit * 0x2000));
467         u32 chid = nv_rd32(priv, 0x040120 + (unit * 0x2000)) & 0x7f;
468         u32 subc = (addr & 0x00070000) >> 16;
469         u32 mthd = (addr & 0x00003ffc);
470         u32 show = stat;
471
472         if (stat & 0x00200000) {
473                 if (mthd == 0x0054) {
474                         if (!nvc0_fifo_swmthd(priv, chid, 0x0500, 0x00000000))
475                                 show &= ~0x00200000;
476                 }
477         }
478
479         if (stat & 0x00800000) {
480                 if (!nvc0_fifo_swmthd(priv, chid, mthd, data))
481                         show &= ~0x00800000;
482         }
483
484         if (show) {
485                 nv_error(priv, "SUBFIFO%d:", unit);
486                 nouveau_bitfield_print(nvc0_fifo_subfifo_intr, show);
487                 printk("\n");
488                 nv_error(priv, "SUBFIFO%d: ch %d subc %d mthd 0x%04x "
489                                "data 0x%08x\n",
490                          unit, chid, subc, mthd, data);
491         }
492
493         nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008);
494         nv_wr32(priv, 0x040108 + (unit * 0x2000), stat);
495 }
496
497 static void
498 nvc0_fifo_intr(struct nouveau_subdev *subdev)
499 {
500         struct nvc0_fifo_priv *priv = (void *)subdev;
501         u32 mask = nv_rd32(priv, 0x002140);
502         u32 stat = nv_rd32(priv, 0x002100) & mask;
503
504         if (stat & 0x00000100) {
505                 nv_warn(priv, "unknown status 0x00000100\n");
506                 nv_wr32(priv, 0x002100, 0x00000100);
507                 stat &= ~0x00000100;
508         }
509
510         if (stat & 0x10000000) {
511                 u32 units = nv_rd32(priv, 0x00259c);
512                 u32 u = units;
513
514                 while (u) {
515                         int i = ffs(u) - 1;
516                         nvc0_fifo_isr_vm_fault(priv, i);
517                         u &= ~(1 << i);
518                 }
519
520                 nv_wr32(priv, 0x00259c, units);
521                 stat &= ~0x10000000;
522         }
523
524         if (stat & 0x20000000) {
525                 u32 units = nv_rd32(priv, 0x0025a0);
526                 u32 u = units;
527
528                 while (u) {
529                         int i = ffs(u) - 1;
530                         nvc0_fifo_isr_subfifo_intr(priv, i);
531                         u &= ~(1 << i);
532                 }
533
534                 nv_wr32(priv, 0x0025a0, units);
535                 stat &= ~0x20000000;
536         }
537
538         if (stat & 0x40000000) {
539                 nv_warn(priv, "unknown status 0x40000000\n");
540                 nv_mask(priv, 0x002a00, 0x00000000, 0x00000000);
541                 stat &= ~0x40000000;
542         }
543
544         if (stat) {
545                 nv_fatal(priv, "unhandled status 0x%08x\n", stat);
546                 nv_wr32(priv, 0x002100, stat);
547                 nv_wr32(priv, 0x002140, 0);
548         }
549 }
550
551 static int
552 nvc0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
553                struct nouveau_oclass *oclass, void *data, u32 size,
554                struct nouveau_object **pobject)
555 {
556         struct nvc0_fifo_priv *priv;
557         int ret;
558
559         ret = nouveau_fifo_create(parent, engine, oclass, 0, 127, &priv);
560         *pobject = nv_object(priv);
561         if (ret)
562                 return ret;
563
564         ret = nouveau_gpuobj_new(parent, NULL, 0x1000, 0x1000, 0,
565                                 &priv->playlist[0]);
566         if (ret)
567                 return ret;
568
569         ret = nouveau_gpuobj_new(parent, NULL, 0x1000, 0x1000, 0,
570                                 &priv->playlist[1]);
571         if (ret)
572                 return ret;
573
574         ret = nouveau_gpuobj_new(parent, NULL, 128 * 0x1000, 0x1000, 0,
575                                 &priv->user.mem);
576         if (ret)
577                 return ret;
578
579         ret = nouveau_gpuobj_map(priv->user.mem, NV_MEM_ACCESS_RW,
580                                 &priv->user.bar);
581         if (ret)
582                 return ret;
583
584         nv_subdev(priv)->unit = 0x00000100;
585         nv_subdev(priv)->intr = nvc0_fifo_intr;
586         nv_engine(priv)->cclass = &nvc0_fifo_cclass;
587         nv_engine(priv)->sclass = nvc0_fifo_sclass;
588         return 0;
589 }
590
591 static void
592 nvc0_fifo_dtor(struct nouveau_object *object)
593 {
594         struct nvc0_fifo_priv *priv = (void *)object;
595
596         nouveau_gpuobj_unmap(&priv->user.bar);
597         nouveau_gpuobj_ref(NULL, &priv->user.mem);
598         nouveau_gpuobj_ref(NULL, &priv->playlist[1]);
599         nouveau_gpuobj_ref(NULL, &priv->playlist[0]);
600
601         nouveau_fifo_destroy(&priv->base);
602 }
603
604 static int
605 nvc0_fifo_init(struct nouveau_object *object)
606 {
607         struct nvc0_fifo_priv *priv = (void *)object;
608         int ret, i;
609
610         ret = nouveau_fifo_init(&priv->base);
611         if (ret)
612                 return ret;
613
614         nv_wr32(priv, 0x000204, 0xffffffff);
615         nv_wr32(priv, 0x002204, 0xffffffff);
616
617         priv->spoon_nr = hweight32(nv_rd32(priv, 0x002204));
618         nv_debug(priv, "%d subfifo(s)\n", priv->spoon_nr);
619
620         /* assign engines to subfifos */
621         if (priv->spoon_nr >= 3) {
622                 nv_wr32(priv, 0x002208, ~(1 << 0)); /* PGRAPH */
623                 nv_wr32(priv, 0x00220c, ~(1 << 1)); /* PVP */
624                 nv_wr32(priv, 0x002210, ~(1 << 1)); /* PPP */
625                 nv_wr32(priv, 0x002214, ~(1 << 1)); /* PBSP */
626                 nv_wr32(priv, 0x002218, ~(1 << 2)); /* PCE0 */
627                 nv_wr32(priv, 0x00221c, ~(1 << 1)); /* PCE1 */
628         }
629
630         /* PSUBFIFO[n] */
631         for (i = 0; i < priv->spoon_nr; i++) {
632                 nv_mask(priv, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000);
633                 nv_wr32(priv, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */
634                 nv_wr32(priv, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTREN */
635         }
636
637         nv_mask(priv, 0x002200, 0x00000001, 0x00000001);
638         nv_wr32(priv, 0x002254, 0x10000000 | priv->user.bar.offset >> 12);
639
640         nv_wr32(priv, 0x002a00, 0xffffffff); /* clears PFIFO.INTR bit 30 */
641         nv_wr32(priv, 0x002100, 0xffffffff);
642         nv_wr32(priv, 0x002140, 0xbfffffff);
643         return 0;
644 }
645
646 struct nouveau_oclass
647 nvc0_fifo_oclass = {
648         .handle = NV_ENGINE(FIFO, 0xc0),
649         .ofuncs = &(struct nouveau_ofuncs) {
650                 .ctor = nvc0_fifo_ctor,
651                 .dtor = nvc0_fifo_dtor,
652                 .init = nvc0_fifo_init,
653                 .fini = _nouveau_fifo_fini,
654         },
655 };