Commit | Line | Data |
---|---|---|
5132f377 | 1 | /* |
ebb945a9 | 2 | * Copyright 2012 Red Hat Inc. |
5132f377 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> | |
30 | #include <core/class.h> | |
31 | #include <core/math.h> | |
32 | #include <core/enum.h> | |
5132f377 | 33 | |
ebb945a9 BS |
34 | #include <subdev/timer.h> |
35 | #include <subdev/bar.h> | |
36 | #include <subdev/vm.h> | |
5132f377 | 37 | |
ebb945a9 BS |
38 | #include <engine/dmaobj.h> |
39 | #include <engine/fifo.h> | |
5132f377 | 40 | |
dbff2dee BS |
41 | #define _(a,b) { (a), ((1 << (a)) | (b)) } |
42 | static const struct { | |
43 | int subdev; | |
44 | u32 mask; | |
45 | } fifo_engine[] = { | |
46 | _(NVDEV_ENGINE_GR , (1 << NVDEV_ENGINE_SW)), | |
47 | _(NVDEV_ENGINE_VP , 0), | |
48 | _(NVDEV_ENGINE_PPP , 0), | |
49 | _(NVDEV_ENGINE_BSP , 0), | |
50 | _(NVDEV_ENGINE_COPY0 , 0), | |
51 | _(NVDEV_ENGINE_COPY1 , 0), | |
52 | _(NVDEV_ENGINE_VENC , 0), | |
53 | }; | |
54 | #undef _ | |
55 | #define FIFO_ENGINE_NR ARRAY_SIZE(fifo_engine) | |
56 | ||
ebb945a9 | 57 | struct nve0_fifo_engn { |
5132f377 BS |
58 | struct nouveau_gpuobj *playlist[2]; |
59 | int cur_playlist; | |
60 | }; | |
61 | ||
62 | struct nve0_fifo_priv { | |
ebb945a9 | 63 | struct nouveau_fifo base; |
dbff2dee | 64 | struct nve0_fifo_engn engine[FIFO_ENGINE_NR]; |
5132f377 BS |
65 | struct { |
66 | struct nouveau_gpuobj *mem; | |
67 | struct nouveau_vma bar; | |
68 | } user; | |
69 | int spoon_nr; | |
70 | }; | |
71 | ||
ebb945a9 BS |
72 | struct nve0_fifo_base { |
73 | struct nouveau_fifo_base base; | |
74 | struct nouveau_gpuobj *pgd; | |
75 | struct nouveau_vm *vm; | |
76 | }; | |
77 | ||
5132f377 | 78 | struct nve0_fifo_chan { |
c420b2dc | 79 | struct nouveau_fifo_chan base; |
5132f377 BS |
80 | u32 engine; |
81 | }; | |
82 | ||
ebb945a9 BS |
83 | /******************************************************************************* |
84 | * FIFO channel objects | |
85 | ******************************************************************************/ | |
86 | ||
5132f377 | 87 | static void |
ebb945a9 | 88 | nve0_fifo_playlist_update(struct nve0_fifo_priv *priv, u32 engine) |
5132f377 | 89 | { |
ebb945a9 BS |
90 | struct nouveau_bar *bar = nouveau_bar(priv); |
91 | struct nve0_fifo_engn *engn = &priv->engine[engine]; | |
5132f377 BS |
92 | struct nouveau_gpuobj *cur; |
93 | u32 match = (engine << 16) | 0x00000001; | |
ebb945a9 | 94 | int i, p; |
5132f377 | 95 | |
ebb945a9 | 96 | cur = engn->playlist[engn->cur_playlist]; |
5132f377 | 97 | if (unlikely(cur == NULL)) { |
ebb945a9 BS |
98 | int ret = nouveau_gpuobj_new(nv_object(priv)->parent, NULL, |
99 | 0x8000, 0x1000, 0, &cur); | |
5132f377 | 100 | if (ret) { |
ebb945a9 | 101 | nv_error(priv, "playlist alloc failed\n"); |
5132f377 BS |
102 | return; |
103 | } | |
104 | ||
ebb945a9 | 105 | engn->playlist[engn->cur_playlist] = cur; |
5132f377 BS |
106 | } |
107 | ||
ebb945a9 | 108 | engn->cur_playlist = !engn->cur_playlist; |
5132f377 | 109 | |
ebb945a9 BS |
110 | for (i = 0, p = 0; i < priv->base.max; i++) { |
111 | u32 ctrl = nv_rd32(priv, 0x800004 + (i * 8)) & 0x001f0001; | |
5132f377 BS |
112 | if (ctrl != match) |
113 | continue; | |
114 | nv_wo32(cur, p + 0, i); | |
115 | nv_wo32(cur, p + 4, 0x00000000); | |
116 | p += 8; | |
117 | } | |
ebb945a9 | 118 | bar->flush(bar); |
5132f377 | 119 | |
ebb945a9 BS |
120 | nv_wr32(priv, 0x002270, cur->addr >> 12); |
121 | nv_wr32(priv, 0x002274, (engine << 20) | (p >> 3)); | |
122 | if (!nv_wait(priv, 0x002284 + (engine * 4), 0x00100000, 0x00000000)) | |
123 | nv_error(priv, "playlist %d update timeout\n", engine); | |
5132f377 BS |
124 | } |
125 | ||
c420b2dc | 126 | static int |
ebb945a9 BS |
127 | nve0_fifo_context_attach(struct nouveau_object *parent, |
128 | struct nouveau_object *object) | |
5132f377 | 129 | { |
ebb945a9 BS |
130 | struct nouveau_bar *bar = nouveau_bar(parent); |
131 | struct nve0_fifo_base *base = (void *)parent->parent; | |
132 | struct nouveau_engctx *ectx = (void *)object; | |
133 | u32 addr; | |
134 | int ret; | |
135 | ||
136 | switch (nv_engidx(object->engine)) { | |
137 | case NVDEV_ENGINE_SW : return 0; | |
dbff2dee BS |
138 | case NVDEV_ENGINE_GR : |
139 | case NVDEV_ENGINE_COPY0: | |
140 | case NVDEV_ENGINE_COPY1: addr = 0x0210; break; | |
ebb945a9 BS |
141 | default: |
142 | return -EINVAL; | |
5132f377 BS |
143 | } |
144 | ||
ebb945a9 BS |
145 | if (!ectx->vma.node) { |
146 | ret = nouveau_gpuobj_map_vm(nv_gpuobj(ectx), base->vm, | |
147 | NV_MEM_ACCESS_RW, &ectx->vma); | |
148 | if (ret) | |
149 | return ret; | |
4c2d4222 BS |
150 | |
151 | nv_engctx(ectx)->addr = nv_gpuobj(base)->addr >> 12; | |
ebb945a9 BS |
152 | } |
153 | ||
154 | nv_wo32(base, addr + 0x00, lower_32_bits(ectx->vma.offset) | 4); | |
155 | nv_wo32(base, addr + 0x04, upper_32_bits(ectx->vma.offset)); | |
156 | bar->flush(bar); | |
157 | return 0; | |
5132f377 BS |
158 | } |
159 | ||
ebb945a9 BS |
160 | static int |
161 | nve0_fifo_context_detach(struct nouveau_object *parent, bool suspend, | |
162 | struct nouveau_object *object) | |
5132f377 | 163 | { |
ebb945a9 BS |
164 | struct nouveau_bar *bar = nouveau_bar(parent); |
165 | struct nve0_fifo_priv *priv = (void *)parent->engine; | |
166 | struct nve0_fifo_base *base = (void *)parent->parent; | |
167 | struct nve0_fifo_chan *chan = (void *)parent; | |
168 | u32 addr; | |
169 | ||
170 | switch (nv_engidx(object->engine)) { | |
171 | case NVDEV_ENGINE_SW : return 0; | |
dbff2dee BS |
172 | case NVDEV_ENGINE_GR : |
173 | case NVDEV_ENGINE_COPY0: | |
174 | case NVDEV_ENGINE_COPY1: addr = 0x0210; break; | |
ebb945a9 BS |
175 | default: |
176 | return -EINVAL; | |
177 | } | |
178 | ||
179 | nv_wo32(base, addr + 0x00, 0x00000000); | |
180 | nv_wo32(base, addr + 0x04, 0x00000000); | |
181 | bar->flush(bar); | |
182 | ||
183 | nv_wr32(priv, 0x002634, chan->base.chid); | |
184 | if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) { | |
185 | nv_error(priv, "channel %d kick timeout\n", chan->base.chid); | |
186 | if (suspend) | |
187 | return -EBUSY; | |
5132f377 BS |
188 | } |
189 | ||
ebb945a9 | 190 | return 0; |
5132f377 BS |
191 | } |
192 | ||
193 | static int | |
ebb945a9 BS |
194 | nve0_fifo_chan_ctor(struct nouveau_object *parent, |
195 | struct nouveau_object *engine, | |
196 | struct nouveau_oclass *oclass, void *data, u32 size, | |
197 | struct nouveau_object **pobject) | |
5132f377 | 198 | { |
ebb945a9 BS |
199 | struct nouveau_bar *bar = nouveau_bar(parent); |
200 | struct nve0_fifo_priv *priv = (void *)engine; | |
201 | struct nve0_fifo_base *base = (void *)parent; | |
202 | struct nve0_fifo_chan *chan; | |
dbff2dee | 203 | struct nve0_channel_ind_class *args = data; |
ebb945a9 BS |
204 | u64 usermem, ioffset, ilength; |
205 | int ret, i; | |
206 | ||
207 | if (size < sizeof(*args)) | |
208 | return -EINVAL; | |
209 | ||
dbff2dee BS |
210 | for (i = 0; i < FIFO_ENGINE_NR; i++) { |
211 | if (args->engine & (1 << i)) { | |
212 | if (nouveau_engine(parent, fifo_engine[i].subdev)) { | |
213 | args->engine = (1 << i); | |
214 | break; | |
215 | } | |
216 | } | |
217 | } | |
218 | ||
219 | if (i == FIFO_ENGINE_NR) | |
220 | return -ENODEV; | |
221 | ||
ebb945a9 BS |
222 | ret = nouveau_fifo_channel_create(parent, engine, oclass, 1, |
223 | priv->user.bar.offset, 0x200, | |
224 | args->pushbuf, | |
dbff2dee | 225 | fifo_engine[i].mask, &chan); |
ebb945a9 BS |
226 | *pobject = nv_object(chan); |
227 | if (ret) | |
228 | return ret; | |
229 | ||
230 | nv_parent(chan)->context_attach = nve0_fifo_context_attach; | |
231 | nv_parent(chan)->context_detach = nve0_fifo_context_detach; | |
dbff2dee | 232 | chan->engine = i; |
ebb945a9 BS |
233 | |
234 | usermem = chan->base.chid * 0x200; | |
235 | ioffset = args->ioffset; | |
236 | ilength = log2i(args->ilength / 8); | |
237 | ||
238 | for (i = 0; i < 0x200; i += 4) | |
239 | nv_wo32(priv->user.mem, usermem + i, 0x00000000); | |
240 | ||
241 | nv_wo32(base, 0x08, lower_32_bits(priv->user.mem->addr + usermem)); | |
242 | nv_wo32(base, 0x0c, upper_32_bits(priv->user.mem->addr + usermem)); | |
243 | nv_wo32(base, 0x10, 0x0000face); | |
244 | nv_wo32(base, 0x30, 0xfffff902); | |
245 | nv_wo32(base, 0x48, lower_32_bits(ioffset)); | |
246 | nv_wo32(base, 0x4c, upper_32_bits(ioffset) | (ilength << 16)); | |
247 | nv_wo32(base, 0x84, 0x20400000); | |
248 | nv_wo32(base, 0x94, 0x30000001); | |
249 | nv_wo32(base, 0x9c, 0x00000100); | |
250 | nv_wo32(base, 0xac, 0x0000001f); | |
251 | nv_wo32(base, 0xe8, chan->base.chid); | |
252 | nv_wo32(base, 0xb8, 0xf8000000); | |
253 | nv_wo32(base, 0xf8, 0x10003080); /* 0x002310 */ | |
254 | nv_wo32(base, 0xfc, 0x10000010); /* 0x002350 */ | |
255 | bar->flush(bar); | |
256 | return 0; | |
257 | } | |
5132f377 | 258 | |
ebb945a9 BS |
259 | static int |
260 | nve0_fifo_chan_init(struct nouveau_object *object) | |
261 | { | |
262 | struct nouveau_gpuobj *base = nv_gpuobj(object->parent); | |
263 | struct nve0_fifo_priv *priv = (void *)object->engine; | |
264 | struct nve0_fifo_chan *chan = (void *)object; | |
265 | u32 chid = chan->base.chid; | |
266 | int ret; | |
5132f377 | 267 | |
ebb945a9 BS |
268 | ret = nouveau_fifo_channel_init(&chan->base); |
269 | if (ret) | |
270 | return ret; | |
5132f377 | 271 | |
dbff2dee | 272 | nv_mask(priv, 0x800004 + (chid * 8), 0x000f0000, chan->engine << 16); |
ebb945a9 BS |
273 | nv_wr32(priv, 0x800000 + (chid * 8), 0x80000000 | base->addr >> 12); |
274 | nv_mask(priv, 0x800004 + (chid * 8), 0x00000400, 0x00000400); | |
275 | nve0_fifo_playlist_update(priv, chan->engine); | |
276 | nv_mask(priv, 0x800004 + (chid * 8), 0x00000400, 0x00000400); | |
277 | return 0; | |
278 | } | |
5132f377 | 279 | |
ebb945a9 BS |
280 | static int |
281 | nve0_fifo_chan_fini(struct nouveau_object *object, bool suspend) | |
282 | { | |
283 | struct nve0_fifo_priv *priv = (void *)object->engine; | |
284 | struct nve0_fifo_chan *chan = (void *)object; | |
285 | u32 chid = chan->base.chid; | |
5132f377 | 286 | |
ebb945a9 BS |
287 | nv_mask(priv, 0x800004 + (chid * 8), 0x00000800, 0x00000800); |
288 | nve0_fifo_playlist_update(priv, chan->engine); | |
289 | nv_wr32(priv, 0x800000 + (chid * 8), 0x00000000); | |
5132f377 | 290 | |
ebb945a9 BS |
291 | return nouveau_fifo_channel_fini(&chan->base, suspend); |
292 | } | |
5132f377 | 293 | |
ebb945a9 BS |
294 | static struct nouveau_ofuncs |
295 | nve0_fifo_ofuncs = { | |
296 | .ctor = nve0_fifo_chan_ctor, | |
297 | .dtor = _nouveau_fifo_channel_dtor, | |
298 | .init = nve0_fifo_chan_init, | |
299 | .fini = nve0_fifo_chan_fini, | |
300 | .rd32 = _nouveau_fifo_channel_rd32, | |
301 | .wr32 = _nouveau_fifo_channel_wr32, | |
302 | }; | |
5132f377 | 303 | |
ebb945a9 BS |
304 | static struct nouveau_oclass |
305 | nve0_fifo_sclass[] = { | |
c97f8c92 | 306 | { NVE0_CHANNEL_IND_CLASS, &nve0_fifo_ofuncs }, |
ebb945a9 BS |
307 | {} |
308 | }; | |
309 | ||
310 | /******************************************************************************* | |
311 | * FIFO context - instmem heap and vm setup | |
312 | ******************************************************************************/ | |
5132f377 | 313 | |
c420b2dc | 314 | static int |
ebb945a9 BS |
315 | nve0_fifo_context_ctor(struct nouveau_object *parent, |
316 | struct nouveau_object *engine, | |
317 | struct nouveau_oclass *oclass, void *data, u32 size, | |
318 | struct nouveau_object **pobject) | |
c420b2dc | 319 | { |
ebb945a9 BS |
320 | struct nve0_fifo_base *base; |
321 | int ret; | |
c420b2dc | 322 | |
ebb945a9 BS |
323 | ret = nouveau_fifo_context_create(parent, engine, oclass, NULL, 0x1000, |
324 | 0x1000, NVOBJ_FLAG_ZERO_ALLOC, &base); | |
325 | *pobject = nv_object(base); | |
326 | if (ret) | |
327 | return ret; | |
c420b2dc | 328 | |
ebb945a9 BS |
329 | ret = nouveau_gpuobj_new(parent, NULL, 0x10000, 0x1000, 0, &base->pgd); |
330 | if (ret) | |
331 | return ret; | |
332 | ||
333 | nv_wo32(base, 0x0200, lower_32_bits(base->pgd->addr)); | |
334 | nv_wo32(base, 0x0204, upper_32_bits(base->pgd->addr)); | |
335 | nv_wo32(base, 0x0208, 0xffffffff); | |
336 | nv_wo32(base, 0x020c, 0x000000ff); | |
337 | ||
338 | ret = nouveau_vm_ref(nouveau_client(parent)->vm, &base->vm, base->pgd); | |
339 | if (ret) | |
340 | return ret; | |
c420b2dc | 341 | |
c420b2dc BS |
342 | return 0; |
343 | } | |
344 | ||
ebb945a9 BS |
345 | static void |
346 | nve0_fifo_context_dtor(struct nouveau_object *object) | |
347 | { | |
348 | struct nve0_fifo_base *base = (void *)object; | |
349 | nouveau_vm_ref(NULL, &base->vm, base->pgd); | |
350 | nouveau_gpuobj_ref(NULL, &base->pgd); | |
351 | nouveau_fifo_context_destroy(&base->base); | |
352 | } | |
353 | ||
354 | static struct nouveau_oclass | |
355 | nve0_fifo_cclass = { | |
356 | .handle = NV_ENGCTX(FIFO, 0xe0), | |
357 | .ofuncs = &(struct nouveau_ofuncs) { | |
358 | .ctor = nve0_fifo_context_ctor, | |
359 | .dtor = nve0_fifo_context_dtor, | |
360 | .init = _nouveau_fifo_context_init, | |
361 | .fini = _nouveau_fifo_context_fini, | |
362 | .rd32 = _nouveau_fifo_context_rd32, | |
363 | .wr32 = _nouveau_fifo_context_wr32, | |
364 | }, | |
365 | }; | |
366 | ||
367 | /******************************************************************************* | |
368 | * PFIFO engine | |
369 | ******************************************************************************/ | |
370 | ||
e6626254 | 371 | static const struct nouveau_enum nve0_fifo_fault_unit[] = { |
5132f377 BS |
372 | {} |
373 | }; | |
374 | ||
e6626254 | 375 | static const struct nouveau_enum nve0_fifo_fault_reason[] = { |
5132f377 BS |
376 | { 0x00, "PT_NOT_PRESENT" }, |
377 | { 0x01, "PT_TOO_SHORT" }, | |
378 | { 0x02, "PAGE_NOT_PRESENT" }, | |
379 | { 0x03, "VM_LIMIT_EXCEEDED" }, | |
380 | { 0x04, "NO_CHANNEL" }, | |
381 | { 0x05, "PAGE_SYSTEM_ONLY" }, | |
382 | { 0x06, "PAGE_READ_ONLY" }, | |
383 | { 0x0a, "COMPRESSED_SYSRAM" }, | |
384 | { 0x0c, "INVALID_STORAGE_TYPE" }, | |
385 | {} | |
386 | }; | |
387 | ||
e6626254 | 388 | static const struct nouveau_enum nve0_fifo_fault_hubclient[] = { |
5132f377 BS |
389 | {} |
390 | }; | |
391 | ||
e6626254 | 392 | static const struct nouveau_enum nve0_fifo_fault_gpcclient[] = { |
5132f377 BS |
393 | {} |
394 | }; | |
395 | ||
e6626254 | 396 | static const struct nouveau_bitfield nve0_fifo_subfifo_intr[] = { |
5132f377 BS |
397 | { 0x00200000, "ILLEGAL_MTHD" }, |
398 | { 0x00800000, "EMPTY_SUBC" }, | |
399 | {} | |
400 | }; | |
401 | ||
402 | static void | |
ebb945a9 | 403 | nve0_fifo_isr_vm_fault(struct nve0_fifo_priv *priv, int unit) |
5132f377 | 404 | { |
ebb945a9 BS |
405 | u32 inst = nv_rd32(priv, 0x2800 + (unit * 0x10)); |
406 | u32 valo = nv_rd32(priv, 0x2804 + (unit * 0x10)); | |
407 | u32 vahi = nv_rd32(priv, 0x2808 + (unit * 0x10)); | |
408 | u32 stat = nv_rd32(priv, 0x280c + (unit * 0x10)); | |
5132f377 BS |
409 | u32 client = (stat & 0x00001f00) >> 8; |
410 | ||
ebb945a9 BS |
411 | nv_error(priv, "PFIFO: %s fault at 0x%010llx [", (stat & 0x00000080) ? |
412 | "write" : "read", (u64)vahi << 32 | valo); | |
5132f377 BS |
413 | nouveau_enum_print(nve0_fifo_fault_reason, stat & 0x0000000f); |
414 | printk("] from "); | |
415 | nouveau_enum_print(nve0_fifo_fault_unit, unit); | |
416 | if (stat & 0x00000040) { | |
417 | printk("/"); | |
418 | nouveau_enum_print(nve0_fifo_fault_hubclient, client); | |
419 | } else { | |
420 | printk("/GPC%d/", (stat & 0x1f000000) >> 24); | |
421 | nouveau_enum_print(nve0_fifo_fault_gpcclient, client); | |
422 | } | |
423 | printk(" on channel 0x%010llx\n", (u64)inst << 12); | |
424 | } | |
425 | ||
e2b34fa0 | 426 | static int |
ebb945a9 | 427 | nve0_fifo_swmthd(struct nve0_fifo_priv *priv, u32 chid, u32 mthd, u32 data) |
e2b34fa0 | 428 | { |
ebb945a9 BS |
429 | struct nve0_fifo_chan *chan = NULL; |
430 | struct nouveau_handle *bind; | |
e2b34fa0 BS |
431 | unsigned long flags; |
432 | int ret = -EINVAL; | |
433 | ||
ebb945a9 BS |
434 | spin_lock_irqsave(&priv->base.lock, flags); |
435 | if (likely(chid >= priv->base.min && chid <= priv->base.max)) | |
436 | chan = (void *)priv->base.channel[chid]; | |
437 | if (unlikely(!chan)) | |
438 | goto out; | |
439 | ||
440 | bind = nouveau_namedb_get_class(nv_namedb(chan), 0x906e); | |
441 | if (likely(bind)) { | |
442 | if (!mthd || !nv_call(bind->object, mthd, data)) | |
443 | ret = 0; | |
444 | nouveau_namedb_put(bind); | |
e2b34fa0 | 445 | } |
ebb945a9 BS |
446 | |
447 | out: | |
448 | spin_unlock_irqrestore(&priv->base.lock, flags); | |
e2b34fa0 BS |
449 | return ret; |
450 | } | |
451 | ||
5132f377 | 452 | static void |
ebb945a9 | 453 | nve0_fifo_isr_subfifo_intr(struct nve0_fifo_priv *priv, int unit) |
5132f377 | 454 | { |
ebb945a9 BS |
455 | u32 stat = nv_rd32(priv, 0x040108 + (unit * 0x2000)); |
456 | u32 addr = nv_rd32(priv, 0x0400c0 + (unit * 0x2000)); | |
457 | u32 data = nv_rd32(priv, 0x0400c4 + (unit * 0x2000)); | |
458 | u32 chid = nv_rd32(priv, 0x040120 + (unit * 0x2000)) & 0xfff; | |
459 | u32 subc = (addr & 0x00070000) >> 16; | |
5132f377 | 460 | u32 mthd = (addr & 0x00003ffc); |
e2b34fa0 BS |
461 | u32 show = stat; |
462 | ||
463 | if (stat & 0x00200000) { | |
464 | if (mthd == 0x0054) { | |
ebb945a9 | 465 | if (!nve0_fifo_swmthd(priv, chid, 0x0500, 0x00000000)) |
e2b34fa0 BS |
466 | show &= ~0x00200000; |
467 | } | |
468 | } | |
5132f377 | 469 | |
ebb945a9 BS |
470 | if (stat & 0x00800000) { |
471 | if (!nve0_fifo_swmthd(priv, chid, mthd, data)) | |
472 | show &= ~0x00800000; | |
473 | } | |
474 | ||
e2b34fa0 | 475 | if (show) { |
ebb945a9 | 476 | nv_error(priv, "SUBFIFO%d:", unit); |
e2b34fa0 | 477 | nouveau_bitfield_print(nve0_fifo_subfifo_intr, show); |
ebb945a9 BS |
478 | printk("\n"); |
479 | nv_error(priv, "SUBFIFO%d: ch %d subc %d mthd 0x%04x " | |
480 | "data 0x%08x\n", | |
481 | unit, chid, subc, mthd, data); | |
e2b34fa0 | 482 | } |
5132f377 | 483 | |
ebb945a9 BS |
484 | nv_wr32(priv, 0x0400c0 + (unit * 0x2000), 0x80600008); |
485 | nv_wr32(priv, 0x040108 + (unit * 0x2000), stat); | |
5132f377 BS |
486 | } |
487 | ||
488 | static void | |
ebb945a9 | 489 | nve0_fifo_intr(struct nouveau_subdev *subdev) |
5132f377 | 490 | { |
ebb945a9 BS |
491 | struct nve0_fifo_priv *priv = (void *)subdev; |
492 | u32 mask = nv_rd32(priv, 0x002140); | |
493 | u32 stat = nv_rd32(priv, 0x002100) & mask; | |
5132f377 BS |
494 | |
495 | if (stat & 0x00000100) { | |
ebb945a9 BS |
496 | nv_warn(priv, "unknown status 0x00000100\n"); |
497 | nv_wr32(priv, 0x002100, 0x00000100); | |
5132f377 BS |
498 | stat &= ~0x00000100; |
499 | } | |
500 | ||
501 | if (stat & 0x10000000) { | |
ebb945a9 | 502 | u32 units = nv_rd32(priv, 0x00259c); |
5132f377 BS |
503 | u32 u = units; |
504 | ||
505 | while (u) { | |
506 | int i = ffs(u) - 1; | |
ebb945a9 | 507 | nve0_fifo_isr_vm_fault(priv, i); |
5132f377 BS |
508 | u &= ~(1 << i); |
509 | } | |
510 | ||
ebb945a9 | 511 | nv_wr32(priv, 0x00259c, units); |
5132f377 BS |
512 | stat &= ~0x10000000; |
513 | } | |
514 | ||
515 | if (stat & 0x20000000) { | |
ebb945a9 | 516 | u32 units = nv_rd32(priv, 0x0025a0); |
5132f377 BS |
517 | u32 u = units; |
518 | ||
519 | while (u) { | |
520 | int i = ffs(u) - 1; | |
ebb945a9 | 521 | nve0_fifo_isr_subfifo_intr(priv, i); |
5132f377 BS |
522 | u &= ~(1 << i); |
523 | } | |
524 | ||
ebb945a9 | 525 | nv_wr32(priv, 0x0025a0, units); |
5132f377 BS |
526 | stat &= ~0x20000000; |
527 | } | |
528 | ||
529 | if (stat & 0x40000000) { | |
ebb945a9 BS |
530 | nv_warn(priv, "unknown status 0x40000000\n"); |
531 | nv_mask(priv, 0x002a00, 0x00000000, 0x00000000); | |
5132f377 BS |
532 | stat &= ~0x40000000; |
533 | } | |
534 | ||
535 | if (stat) { | |
ebb945a9 BS |
536 | nv_fatal(priv, "unhandled status 0x%08x\n", stat); |
537 | nv_wr32(priv, 0x002100, stat); | |
538 | nv_wr32(priv, 0x002140, 0); | |
5132f377 BS |
539 | } |
540 | } | |
c420b2dc | 541 | |
ebb945a9 BS |
542 | static int |
543 | nve0_fifo_ctor(struct nouveau_object *parent, struct nouveau_object *engine, | |
544 | struct nouveau_oclass *oclass, void *data, u32 size, | |
545 | struct nouveau_object **pobject) | |
546 | { | |
547 | struct nve0_fifo_priv *priv; | |
548 | int ret; | |
549 | ||
550 | ret = nouveau_fifo_create(parent, engine, oclass, 0, 4095, &priv); | |
551 | *pobject = nv_object(priv); | |
552 | if (ret) | |
553 | return ret; | |
554 | ||
555 | ret = nouveau_gpuobj_new(parent, NULL, 4096 * 0x200, 0x1000, | |
556 | NVOBJ_FLAG_ZERO_ALLOC, &priv->user.mem); | |
557 | if (ret) | |
558 | return ret; | |
559 | ||
560 | ret = nouveau_gpuobj_map(priv->user.mem, NV_MEM_ACCESS_RW, | |
561 | &priv->user.bar); | |
562 | if (ret) | |
563 | return ret; | |
564 | ||
565 | nv_subdev(priv)->unit = 0x00000100; | |
566 | nv_subdev(priv)->intr = nve0_fifo_intr; | |
567 | nv_engine(priv)->cclass = &nve0_fifo_cclass; | |
568 | nv_engine(priv)->sclass = nve0_fifo_sclass; | |
569 | return 0; | |
570 | } | |
571 | ||
c420b2dc | 572 | static void |
ebb945a9 | 573 | nve0_fifo_dtor(struct nouveau_object *object) |
c420b2dc | 574 | { |
ebb945a9 | 575 | struct nve0_fifo_priv *priv = (void *)object; |
c420b2dc BS |
576 | int i; |
577 | ||
18c9b959 | 578 | nouveau_gpuobj_unmap(&priv->user.bar); |
c420b2dc BS |
579 | nouveau_gpuobj_ref(NULL, &priv->user.mem); |
580 | ||
ebb945a9 | 581 | for (i = 0; i < ARRAY_SIZE(priv->engine); i++) { |
c420b2dc | 582 | nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[1]); |
ebb945a9 | 583 | nouveau_gpuobj_ref(NULL, &priv->engine[i].playlist[0]); |
c420b2dc BS |
584 | } |
585 | ||
ebb945a9 | 586 | nouveau_fifo_destroy(&priv->base); |
c420b2dc BS |
587 | } |
588 | ||
ebb945a9 BS |
589 | static int |
590 | nve0_fifo_init(struct nouveau_object *object) | |
c420b2dc | 591 | { |
ebb945a9 BS |
592 | struct nve0_fifo_priv *priv = (void *)object; |
593 | int ret, i; | |
c420b2dc | 594 | |
ebb945a9 BS |
595 | ret = nouveau_fifo_init(&priv->base); |
596 | if (ret) | |
597 | return ret; | |
c420b2dc | 598 | |
ebb945a9 BS |
599 | /* enable all available PSUBFIFOs */ |
600 | nv_wr32(priv, 0x000204, 0xffffffff); | |
601 | priv->spoon_nr = hweight32(nv_rd32(priv, 0x000204)); | |
602 | nv_debug(priv, "%d subfifo(s)\n", priv->spoon_nr); | |
c420b2dc | 603 | |
ebb945a9 BS |
604 | /* PSUBFIFO[n] */ |
605 | for (i = 0; i < priv->spoon_nr; i++) { | |
606 | nv_mask(priv, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000); | |
607 | nv_wr32(priv, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */ | |
608 | nv_wr32(priv, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTREN */ | |
609 | } | |
c420b2dc | 610 | |
ebb945a9 | 611 | nv_wr32(priv, 0x002254, 0x10000000 | priv->user.bar.offset >> 12); |
c420b2dc | 612 | |
ebb945a9 BS |
613 | nv_wr32(priv, 0x002a00, 0xffffffff); |
614 | nv_wr32(priv, 0x002100, 0xffffffff); | |
615 | nv_wr32(priv, 0x002140, 0xbfffffff); | |
616 | return 0; | |
c420b2dc | 617 | } |
ebb945a9 BS |
618 | |
619 | struct nouveau_oclass | |
620 | nve0_fifo_oclass = { | |
621 | .handle = NV_ENGINE(FIFO, 0xe0), | |
622 | .ofuncs = &(struct nouveau_ofuncs) { | |
623 | .ctor = nve0_fifo_ctor, | |
624 | .dtor = nve0_fifo_dtor, | |
625 | .init = nve0_fifo_init, | |
626 | .fini = _nouveau_fifo_fini, | |
627 | }, | |
628 | }; |