]>
Commit | Line | Data |
---|---|---|
304424e1 MK |
1 | #include "drmP.h" |
2 | #include "drm.h" | |
3 | #include "nouveau_drv.h" | |
94580299 | 4 | #include <nouveau_drm.h> |
02a841d4 | 5 | #include <engine/fifo.h> |
304424e1 | 6 | |
20f63afe BS |
7 | struct nv50_fb_priv { |
8 | struct page *r100c08_page; | |
9 | dma_addr_t r100c08; | |
10 | }; | |
11 | ||
8f7286f8 BS |
12 | static void |
13 | nv50_fb_destroy(struct drm_device *dev) | |
14 | { | |
15 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
16 | struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; | |
17 | struct nv50_fb_priv *priv = pfb->priv; | |
18 | ||
19 | if (drm_mm_initialized(&pfb->tag_heap)) | |
20 | drm_mm_takedown(&pfb->tag_heap); | |
21 | ||
22 | if (priv->r100c08_page) { | |
23 | pci_unmap_page(dev->pdev, priv->r100c08, PAGE_SIZE, | |
24 | PCI_DMA_BIDIRECTIONAL); | |
25 | __free_page(priv->r100c08_page); | |
26 | } | |
27 | ||
28 | kfree(priv); | |
29 | pfb->priv = NULL; | |
30 | } | |
31 | ||
20f63afe BS |
32 | static int |
33 | nv50_fb_create(struct drm_device *dev) | |
34 | { | |
35 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
8f7286f8 | 36 | struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; |
20f63afe | 37 | struct nv50_fb_priv *priv; |
8f7286f8 BS |
38 | u32 tagmem; |
39 | int ret; | |
20f63afe BS |
40 | |
41 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | |
42 | if (!priv) | |
43 | return -ENOMEM; | |
8f7286f8 | 44 | pfb->priv = priv; |
20f63afe BS |
45 | |
46 | priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO); | |
47 | if (!priv->r100c08_page) { | |
8f7286f8 | 48 | nv50_fb_destroy(dev); |
20f63afe BS |
49 | return -ENOMEM; |
50 | } | |
51 | ||
52 | priv->r100c08 = pci_map_page(dev->pdev, priv->r100c08_page, 0, | |
53 | PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); | |
54 | if (pci_dma_mapping_error(dev->pdev, priv->r100c08)) { | |
8f7286f8 | 55 | nv50_fb_destroy(dev); |
20f63afe BS |
56 | return -EFAULT; |
57 | } | |
58 | ||
8f7286f8 BS |
59 | tagmem = nv_rd32(dev, 0x100320); |
60 | NV_DEBUG(dev, "%d tags available\n", tagmem); | |
61 | ret = drm_mm_init(&pfb->tag_heap, 0, tagmem); | |
62 | if (ret) { | |
63 | nv50_fb_destroy(dev); | |
64 | return ret; | |
65 | } | |
66 | ||
20f63afe BS |
67 | return 0; |
68 | } | |
69 | ||
304424e1 MK |
70 | int |
71 | nv50_fb_init(struct drm_device *dev) | |
72 | { | |
304424e1 | 73 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
20f63afe BS |
74 | struct nv50_fb_priv *priv; |
75 | int ret; | |
76 | ||
77 | if (!dev_priv->engine.fb.priv) { | |
78 | ret = nv50_fb_create(dev); | |
79 | if (ret) | |
80 | return ret; | |
81 | } | |
82 | priv = dev_priv->engine.fb.priv; | |
304424e1 | 83 | |
4eb3033c BS |
84 | /* Not a clue what this is exactly. Without pointing it at a |
85 | * scratch page, VRAM->GART blits with M2MF (as in DDX DFS) | |
86 | * cause IOMMU "read from address 0" errors (rh#561267) | |
87 | */ | |
20f63afe | 88 | nv_wr32(dev, 0x100c08, priv->r100c08 >> 8); |
4eb3033c BS |
89 | |
90 | /* This is needed to get meaningful information from 100c90 | |
91 | * on traps. No idea what these values mean exactly. */ | |
304424e1 MK |
92 | switch (dev_priv->chipset) { |
93 | case 0x50: | |
9fea1bcb | 94 | nv_wr32(dev, 0x100c90, 0x000707ff); |
304424e1 | 95 | break; |
f9aafdd3 | 96 | case 0xa3: |
304424e1 MK |
97 | case 0xa5: |
98 | case 0xa8: | |
9fea1bcb BS |
99 | nv_wr32(dev, 0x100c90, 0x000d0fff); |
100 | break; | |
101 | case 0xaf: | |
102 | nv_wr32(dev, 0x100c90, 0x089d1fff); | |
304424e1 MK |
103 | break; |
104 | default: | |
9fea1bcb | 105 | nv_wr32(dev, 0x100c90, 0x001d07ff); |
304424e1 MK |
106 | break; |
107 | } | |
108 | ||
109 | return 0; | |
110 | } | |
111 | ||
112 | void | |
113 | nv50_fb_takedown(struct drm_device *dev) | |
114 | { | |
8f7286f8 | 115 | nv50_fb_destroy(dev); |
304424e1 | 116 | } |
d96773e7 | 117 | |
312d1d5f BS |
118 | static struct nouveau_enum vm_dispatch_subclients[] = { |
119 | { 0x00000000, "GRCTX", NULL }, | |
120 | { 0x00000001, "NOTIFY", NULL }, | |
121 | { 0x00000002, "QUERY", NULL }, | |
122 | { 0x00000003, "COND", NULL }, | |
123 | { 0x00000004, "M2M_IN", NULL }, | |
124 | { 0x00000005, "M2M_OUT", NULL }, | |
125 | { 0x00000006, "M2M_NOTIFY", NULL }, | |
126 | {} | |
127 | }; | |
128 | ||
129 | static struct nouveau_enum vm_ccache_subclients[] = { | |
130 | { 0x00000000, "CB", NULL }, | |
131 | { 0x00000001, "TIC", NULL }, | |
132 | { 0x00000002, "TSC", NULL }, | |
133 | {} | |
134 | }; | |
135 | ||
136 | static struct nouveau_enum vm_prop_subclients[] = { | |
137 | { 0x00000000, "RT0", NULL }, | |
138 | { 0x00000001, "RT1", NULL }, | |
139 | { 0x00000002, "RT2", NULL }, | |
140 | { 0x00000003, "RT3", NULL }, | |
141 | { 0x00000004, "RT4", NULL }, | |
142 | { 0x00000005, "RT5", NULL }, | |
143 | { 0x00000006, "RT6", NULL }, | |
144 | { 0x00000007, "RT7", NULL }, | |
145 | { 0x00000008, "ZETA", NULL }, | |
146 | { 0x00000009, "LOCAL", NULL }, | |
147 | { 0x0000000a, "GLOBAL", NULL }, | |
148 | { 0x0000000b, "STACK", NULL }, | |
149 | { 0x0000000c, "DST2D", NULL }, | |
150 | {} | |
151 | }; | |
152 | ||
153 | static struct nouveau_enum vm_pfifo_subclients[] = { | |
154 | { 0x00000000, "PUSHBUF", NULL }, | |
155 | { 0x00000001, "SEMAPHORE", NULL }, | |
156 | {} | |
157 | }; | |
158 | ||
159 | static struct nouveau_enum vm_bar_subclients[] = { | |
160 | { 0x00000000, "FB", NULL }, | |
161 | { 0x00000001, "IN", NULL }, | |
162 | {} | |
163 | }; | |
164 | ||
165 | static struct nouveau_enum vm_client[] = { | |
166 | { 0x00000000, "STRMOUT", NULL }, | |
167 | { 0x00000003, "DISPATCH", vm_dispatch_subclients }, | |
168 | { 0x00000004, "PFIFO_WRITE", NULL }, | |
169 | { 0x00000005, "CCACHE", vm_ccache_subclients }, | |
170 | { 0x00000006, "PPPP", NULL }, | |
171 | { 0x00000007, "CLIPID", NULL }, | |
172 | { 0x00000008, "PFIFO_READ", NULL }, | |
173 | { 0x00000009, "VFETCH", NULL }, | |
174 | { 0x0000000a, "TEXTURE", NULL }, | |
175 | { 0x0000000b, "PROP", vm_prop_subclients }, | |
176 | { 0x0000000c, "PVP", NULL }, | |
177 | { 0x0000000d, "PBSP", NULL }, | |
178 | { 0x0000000e, "PCRYPT", NULL }, | |
179 | { 0x0000000f, "PCOUNTER", NULL }, | |
180 | { 0x00000011, "PDAEMON", NULL }, | |
181 | {} | |
182 | }; | |
183 | ||
184 | static struct nouveau_enum vm_engine[] = { | |
185 | { 0x00000000, "PGRAPH", NULL }, | |
186 | { 0x00000001, "PVP", NULL }, | |
187 | { 0x00000004, "PEEPHOLE", NULL }, | |
188 | { 0x00000005, "PFIFO", vm_pfifo_subclients }, | |
189 | { 0x00000006, "BAR", vm_bar_subclients }, | |
190 | { 0x00000008, "PPPP", NULL }, | |
191 | { 0x00000009, "PBSP", NULL }, | |
192 | { 0x0000000a, "PCRYPT", NULL }, | |
193 | { 0x0000000b, "PCOUNTER", NULL }, | |
194 | { 0x0000000c, "SEMAPHORE_BG", NULL }, | |
195 | { 0x0000000d, "PCOPY", NULL }, | |
196 | { 0x0000000e, "PDAEMON", NULL }, | |
197 | {} | |
198 | }; | |
199 | ||
200 | static struct nouveau_enum vm_fault[] = { | |
201 | { 0x00000000, "PT_NOT_PRESENT", NULL }, | |
202 | { 0x00000001, "PT_TOO_SHORT", NULL }, | |
203 | { 0x00000002, "PAGE_NOT_PRESENT", NULL }, | |
204 | { 0x00000003, "PAGE_SYSTEM_ONLY", NULL }, | |
205 | { 0x00000004, "PAGE_READ_ONLY", NULL }, | |
206 | { 0x00000006, "NULL_DMAOBJ", NULL }, | |
207 | { 0x00000007, "WRONG_MEMTYPE", NULL }, | |
208 | { 0x0000000b, "VRAM_LIMIT", NULL }, | |
209 | { 0x0000000f, "DMAOBJ_LIMIT", NULL }, | |
210 | {} | |
211 | }; | |
212 | ||
d96773e7 | 213 | void |
6fdb383e | 214 | nv50_fb_vm_trap(struct drm_device *dev, int display) |
d96773e7 | 215 | { |
c420b2dc | 216 | struct nouveau_fifo_priv *pfifo = nv_engine(dev, NVOBJ_ENGINE_FIFO); |
d96773e7 | 217 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
312d1d5f | 218 | const struct nouveau_enum *en, *cl; |
cff5c133 | 219 | unsigned long flags; |
d96773e7 | 220 | u32 trap[6], idx, chinst; |
312d1d5f | 221 | u8 st0, st1, st2, st3; |
d96773e7 BS |
222 | int i, ch; |
223 | ||
224 | idx = nv_rd32(dev, 0x100c90); | |
225 | if (!(idx & 0x80000000)) | |
226 | return; | |
227 | idx &= 0x00ffffff; | |
228 | ||
229 | for (i = 0; i < 6; i++) { | |
230 | nv_wr32(dev, 0x100c90, idx | i << 24); | |
231 | trap[i] = nv_rd32(dev, 0x100c94); | |
232 | } | |
233 | nv_wr32(dev, 0x100c90, idx | 0x80000000); | |
234 | ||
235 | if (!display) | |
236 | return; | |
237 | ||
312d1d5f | 238 | /* lookup channel id */ |
d96773e7 | 239 | chinst = (trap[2] << 16) | trap[1]; |
cff5c133 | 240 | spin_lock_irqsave(&dev_priv->channels.lock, flags); |
c420b2dc | 241 | for (ch = 0; ch < pfifo->channels; ch++) { |
cff5c133 | 242 | struct nouveau_channel *chan = dev_priv->channels.ptr[ch]; |
d96773e7 BS |
243 | |
244 | if (!chan || !chan->ramin) | |
245 | continue; | |
246 | ||
247 | if (chinst == chan->ramin->vinst >> 12) | |
248 | break; | |
249 | } | |
cff5c133 | 250 | spin_unlock_irqrestore(&dev_priv->channels.lock, flags); |
d96773e7 | 251 | |
312d1d5f BS |
252 | /* decode status bits into something more useful */ |
253 | if (dev_priv->chipset < 0xa3 || | |
254 | dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) { | |
255 | st0 = (trap[0] & 0x0000000f) >> 0; | |
256 | st1 = (trap[0] & 0x000000f0) >> 4; | |
257 | st2 = (trap[0] & 0x00000f00) >> 8; | |
258 | st3 = (trap[0] & 0x0000f000) >> 12; | |
259 | } else { | |
260 | st0 = (trap[0] & 0x000000ff) >> 0; | |
261 | st1 = (trap[0] & 0x0000ff00) >> 8; | |
262 | st2 = (trap[0] & 0x00ff0000) >> 16; | |
263 | st3 = (trap[0] & 0xff000000) >> 24; | |
264 | } | |
265 | ||
266 | NV_INFO(dev, "VM: trapped %s at 0x%02x%04x%04x on ch %d [0x%08x] ", | |
267 | (trap[5] & 0x00000100) ? "read" : "write", | |
268 | trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, ch, chinst); | |
269 | ||
270 | en = nouveau_enum_find(vm_engine, st0); | |
271 | if (en) | |
272 | printk("%s/", en->name); | |
273 | else | |
274 | printk("%02x/", st0); | |
275 | ||
276 | cl = nouveau_enum_find(vm_client, st2); | |
277 | if (cl) | |
278 | printk("%s/", cl->name); | |
279 | else | |
280 | printk("%02x/", st2); | |
281 | ||
282 | if (cl && cl->data) cl = nouveau_enum_find(cl->data, st3); | |
283 | else if (en && en->data) cl = nouveau_enum_find(en->data, st3); | |
284 | else cl = NULL; | |
285 | if (cl) | |
286 | printk("%s", cl->name); | |
287 | else | |
288 | printk("%02x", st3); | |
289 | ||
290 | printk(" reason: "); | |
291 | en = nouveau_enum_find(vm_fault, st1); | |
292 | if (en) | |
293 | printk("%s\n", en->name); | |
294 | else | |
295 | printk("0x%08x\n", st1); | |
d96773e7 | 296 | } |