diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_fb.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_fb.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nv50_fb.c b/drivers/gpu/drm/nouveau/nv50_fb.c new file mode 100644 index 00000000000..bdd2afe2920 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_fb.c | |||
@@ -0,0 +1,294 @@ | |||
1 | #include "drmP.h" | ||
2 | #include "drm.h" | ||
3 | #include "nouveau_drv.h" | ||
4 | #include "nouveau_drm.h" | ||
5 | |||
6 | struct nv50_fb_priv { | ||
7 | struct page *r100c08_page; | ||
8 | dma_addr_t r100c08; | ||
9 | }; | ||
10 | |||
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 | |||
31 | static int | ||
32 | nv50_fb_create(struct drm_device *dev) | ||
33 | { | ||
34 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
35 | struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; | ||
36 | struct nv50_fb_priv *priv; | ||
37 | u32 tagmem; | ||
38 | int ret; | ||
39 | |||
40 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
41 | if (!priv) | ||
42 | return -ENOMEM; | ||
43 | pfb->priv = priv; | ||
44 | |||
45 | priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO); | ||
46 | if (!priv->r100c08_page) { | ||
47 | nv50_fb_destroy(dev); | ||
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)) { | ||
54 | nv50_fb_destroy(dev); | ||
55 | return -EFAULT; | ||
56 | } | ||
57 | |||
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 | |||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | int | ||
70 | nv50_fb_init(struct drm_device *dev) | ||
71 | { | ||
72 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
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; | ||
82 | |||
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 | */ | ||
87 | nv_wr32(dev, 0x100c08, priv->r100c08 >> 8); | ||
88 | |||
89 | /* This is needed to get meaningful information from 100c90 | ||
90 | * on traps. No idea what these values mean exactly. */ | ||
91 | switch (dev_priv->chipset) { | ||
92 | case 0x50: | ||
93 | nv_wr32(dev, 0x100c90, 0x000707ff); | ||
94 | break; | ||
95 | case 0xa3: | ||
96 | case 0xa5: | ||
97 | case 0xa8: | ||
98 | nv_wr32(dev, 0x100c90, 0x000d0fff); | ||
99 | break; | ||
100 | case 0xaf: | ||
101 | nv_wr32(dev, 0x100c90, 0x089d1fff); | ||
102 | break; | ||
103 | default: | ||
104 | nv_wr32(dev, 0x100c90, 0x001d07ff); | ||
105 | break; | ||
106 | } | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | void | ||
112 | nv50_fb_takedown(struct drm_device *dev) | ||
113 | { | ||
114 | nv50_fb_destroy(dev); | ||
115 | } | ||
116 | |||
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 | |||
212 | void | ||
213 | nv50_fb_vm_trap(struct drm_device *dev, int display) | ||
214 | { | ||
215 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
216 | const struct nouveau_enum *en, *cl; | ||
217 | unsigned long flags; | ||
218 | u32 trap[6], idx, chinst; | ||
219 | u8 st0, st1, st2, st3; | ||
220 | int i, ch; | ||
221 | |||
222 | idx = nv_rd32(dev, 0x100c90); | ||
223 | if (!(idx & 0x80000000)) | ||
224 | return; | ||
225 | idx &= 0x00ffffff; | ||
226 | |||
227 | for (i = 0; i < 6; i++) { | ||
228 | nv_wr32(dev, 0x100c90, idx | i << 24); | ||
229 | trap[i] = nv_rd32(dev, 0x100c94); | ||
230 | } | ||
231 | nv_wr32(dev, 0x100c90, idx | 0x80000000); | ||
232 | |||
233 | if (!display) | ||
234 | return; | ||
235 | |||
236 | /* lookup channel id */ | ||
237 | chinst = (trap[2] << 16) | trap[1]; | ||
238 | spin_lock_irqsave(&dev_priv->channels.lock, flags); | ||
239 | for (ch = 0; ch < dev_priv->engine.fifo.channels; ch++) { | ||
240 | struct nouveau_channel *chan = dev_priv->channels.ptr[ch]; | ||
241 | |||
242 | if (!chan || !chan->ramin) | ||
243 | continue; | ||
244 | |||
245 | if (chinst == chan->ramin->vinst >> 12) | ||
246 | break; | ||
247 | } | ||
248 | spin_unlock_irqrestore(&dev_priv->channels.lock, flags); | ||
249 | |||
250 | /* decode status bits into something more useful */ | ||
251 | if (dev_priv->chipset < 0xa3 || | ||
252 | dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) { | ||
253 | st0 = (trap[0] & 0x0000000f) >> 0; | ||
254 | st1 = (trap[0] & 0x000000f0) >> 4; | ||
255 | st2 = (trap[0] & 0x00000f00) >> 8; | ||
256 | st3 = (trap[0] & 0x0000f000) >> 12; | ||
257 | } else { | ||
258 | st0 = (trap[0] & 0x000000ff) >> 0; | ||
259 | st1 = (trap[0] & 0x0000ff00) >> 8; | ||
260 | st2 = (trap[0] & 0x00ff0000) >> 16; | ||
261 | st3 = (trap[0] & 0xff000000) >> 24; | ||
262 | } | ||
263 | |||
264 | NV_INFO(dev, "VM: trapped %s at 0x%02x%04x%04x on ch %d [0x%08x] ", | ||
265 | (trap[5] & 0x00000100) ? "read" : "write", | ||
266 | trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, ch, chinst); | ||
267 | |||
268 | en = nouveau_enum_find(vm_engine, st0); | ||
269 | if (en) | ||
270 | printk("%s/", en->name); | ||
271 | else | ||
272 | printk("%02x/", st0); | ||
273 | |||
274 | cl = nouveau_enum_find(vm_client, st2); | ||
275 | if (cl) | ||
276 | printk("%s/", cl->name); | ||
277 | else | ||
278 | printk("%02x/", st2); | ||
279 | |||
280 | if (cl && cl->data) cl = nouveau_enum_find(cl->data, st3); | ||
281 | else if (en && en->data) cl = nouveau_enum_find(en->data, st3); | ||
282 | else cl = NULL; | ||
283 | if (cl) | ||
284 | printk("%s", cl->name); | ||
285 | else | ||
286 | printk("%02x", st3); | ||
287 | |||
288 | printk(" reason: "); | ||
289 | en = nouveau_enum_find(vm_fault, st1); | ||
290 | if (en) | ||
291 | printk("%s\n", en->name); | ||
292 | else | ||
293 | printk("0x%08x\n", st1); | ||
294 | } | ||