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