Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux...
[pandora-kernel.git] / drivers / gpu / drm / nouveau / nv50_fb.c
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 int
12 nv50_fb_create(struct drm_device *dev)
13 {
14         struct drm_nouveau_private *dev_priv = dev->dev_private;
15         struct nv50_fb_priv *priv;
16
17         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
18         if (!priv)
19                 return -ENOMEM;
20
21         priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
22         if (!priv->r100c08_page) {
23                 kfree(priv);
24                 return -ENOMEM;
25         }
26
27         priv->r100c08 = pci_map_page(dev->pdev, priv->r100c08_page, 0,
28                                      PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
29         if (pci_dma_mapping_error(dev->pdev, priv->r100c08)) {
30                 __free_page(priv->r100c08_page);
31                 kfree(priv);
32                 return -EFAULT;
33         }
34
35         dev_priv->engine.fb.priv = priv;
36         return 0;
37 }
38
39 int
40 nv50_fb_init(struct drm_device *dev)
41 {
42         struct drm_nouveau_private *dev_priv = dev->dev_private;
43         struct nv50_fb_priv *priv;
44         int ret;
45
46         if (!dev_priv->engine.fb.priv) {
47                 ret = nv50_fb_create(dev);
48                 if (ret)
49                         return ret;
50         }
51         priv = dev_priv->engine.fb.priv;
52
53         /* Not a clue what this is exactly.  Without pointing it at a
54          * scratch page, VRAM->GART blits with M2MF (as in DDX DFS)
55          * cause IOMMU "read from address 0" errors (rh#561267)
56          */
57         nv_wr32(dev, 0x100c08, priv->r100c08 >> 8);
58
59         /* This is needed to get meaningful information from 100c90
60          * on traps. No idea what these values mean exactly. */
61         switch (dev_priv->chipset) {
62         case 0x50:
63                 nv_wr32(dev, 0x100c90, 0x000707ff);
64                 break;
65         case 0xa3:
66         case 0xa5:
67         case 0xa8:
68                 nv_wr32(dev, 0x100c90, 0x000d0fff);
69                 break;
70         case 0xaf:
71                 nv_wr32(dev, 0x100c90, 0x089d1fff);
72                 break;
73         default:
74                 nv_wr32(dev, 0x100c90, 0x001d07ff);
75                 break;
76         }
77
78         return 0;
79 }
80
81 void
82 nv50_fb_takedown(struct drm_device *dev)
83 {
84         struct drm_nouveau_private *dev_priv = dev->dev_private;
85         struct nv50_fb_priv *priv;
86
87         priv = dev_priv->engine.fb.priv;
88         if (!priv)
89                 return;
90         dev_priv->engine.fb.priv = NULL;
91
92         pci_unmap_page(dev->pdev, priv->r100c08, PAGE_SIZE,
93                        PCI_DMA_BIDIRECTIONAL);
94         __free_page(priv->r100c08_page);
95         kfree(priv);
96 }
97
98 void
99 nv50_fb_vm_trap(struct drm_device *dev, int display, const char *name)
100 {
101         struct drm_nouveau_private *dev_priv = dev->dev_private;
102         unsigned long flags;
103         u32 trap[6], idx, chinst;
104         int i, ch;
105
106         idx = nv_rd32(dev, 0x100c90);
107         if (!(idx & 0x80000000))
108                 return;
109         idx &= 0x00ffffff;
110
111         for (i = 0; i < 6; i++) {
112                 nv_wr32(dev, 0x100c90, idx | i << 24);
113                 trap[i] = nv_rd32(dev, 0x100c94);
114         }
115         nv_wr32(dev, 0x100c90, idx | 0x80000000);
116
117         if (!display)
118                 return;
119
120         chinst = (trap[2] << 16) | trap[1];
121
122         spin_lock_irqsave(&dev_priv->channels.lock, flags);
123         for (ch = 0; ch < dev_priv->engine.fifo.channels; ch++) {
124                 struct nouveau_channel *chan = dev_priv->channels.ptr[ch];
125
126                 if (!chan || !chan->ramin)
127                         continue;
128
129                 if (chinst == chan->ramin->vinst >> 12)
130                         break;
131         }
132         spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
133
134         NV_INFO(dev, "%s - VM: Trapped %s at %02x%04x%04x status %08x "
135                      "channel %d (0x%08x)\n",
136                 name, (trap[5] & 0x100 ? "read" : "write"),
137                 trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff,
138                 trap[0], ch, chinst);
139 }