diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_fb.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_fb.c | 71 |
1 files changed, 66 insertions, 5 deletions
diff --git a/drivers/gpu/drm/nouveau/nv50_fb.c b/drivers/gpu/drm/nouveau/nv50_fb.c index cd1988b15d2c..50290dea0ac4 100644 --- a/drivers/gpu/drm/nouveau/nv50_fb.c +++ b/drivers/gpu/drm/nouveau/nv50_fb.c | |||
@@ -3,30 +3,75 @@ | |||
3 | #include "nouveau_drv.h" | 3 | #include "nouveau_drv.h" |
4 | #include "nouveau_drm.h" | 4 | #include "nouveau_drm.h" |
5 | 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 | |||
6 | int | 39 | int |
7 | nv50_fb_init(struct drm_device *dev) | 40 | nv50_fb_init(struct drm_device *dev) |
8 | { | 41 | { |
9 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 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; | ||
10 | 52 | ||
11 | /* Not a clue what this is exactly. Without pointing it at a | 53 | /* Not a clue what this is exactly. Without pointing it at a |
12 | * scratch page, VRAM->GART blits with M2MF (as in DDX DFS) | 54 | * scratch page, VRAM->GART blits with M2MF (as in DDX DFS) |
13 | * cause IOMMU "read from address 0" errors (rh#561267) | 55 | * cause IOMMU "read from address 0" errors (rh#561267) |
14 | */ | 56 | */ |
15 | nv_wr32(dev, 0x100c08, dev_priv->gart_info.sg_dummy_bus >> 8); | 57 | nv_wr32(dev, 0x100c08, priv->r100c08 >> 8); |
16 | 58 | ||
17 | /* This is needed to get meaningful information from 100c90 | 59 | /* This is needed to get meaningful information from 100c90 |
18 | * on traps. No idea what these values mean exactly. */ | 60 | * on traps. No idea what these values mean exactly. */ |
19 | switch (dev_priv->chipset) { | 61 | switch (dev_priv->chipset) { |
20 | case 0x50: | 62 | case 0x50: |
21 | nv_wr32(dev, 0x100c90, 0x0707ff); | 63 | nv_wr32(dev, 0x100c90, 0x000707ff); |
22 | break; | 64 | break; |
23 | case 0xa3: | 65 | case 0xa3: |
24 | case 0xa5: | 66 | case 0xa5: |
25 | case 0xa8: | 67 | case 0xa8: |
26 | nv_wr32(dev, 0x100c90, 0x0d0fff); | 68 | nv_wr32(dev, 0x100c90, 0x000d0fff); |
69 | break; | ||
70 | case 0xaf: | ||
71 | nv_wr32(dev, 0x100c90, 0x089d1fff); | ||
27 | break; | 72 | break; |
28 | default: | 73 | default: |
29 | nv_wr32(dev, 0x100c90, 0x1d07ff); | 74 | nv_wr32(dev, 0x100c90, 0x001d07ff); |
30 | break; | 75 | break; |
31 | } | 76 | } |
32 | 77 | ||
@@ -36,12 +81,25 @@ nv50_fb_init(struct drm_device *dev) | |||
36 | void | 81 | void |
37 | nv50_fb_takedown(struct drm_device *dev) | 82 | nv50_fb_takedown(struct drm_device *dev) |
38 | { | 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); | ||
39 | } | 96 | } |
40 | 97 | ||
41 | void | 98 | void |
42 | nv50_fb_vm_trap(struct drm_device *dev, int display, const char *name) | 99 | nv50_fb_vm_trap(struct drm_device *dev, int display, const char *name) |
43 | { | 100 | { |
44 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 101 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
102 | unsigned long flags; | ||
45 | u32 trap[6], idx, chinst; | 103 | u32 trap[6], idx, chinst; |
46 | int i, ch; | 104 | int i, ch; |
47 | 105 | ||
@@ -60,8 +118,10 @@ nv50_fb_vm_trap(struct drm_device *dev, int display, const char *name) | |||
60 | return; | 118 | return; |
61 | 119 | ||
62 | chinst = (trap[2] << 16) | trap[1]; | 120 | chinst = (trap[2] << 16) | trap[1]; |
121 | |||
122 | spin_lock_irqsave(&dev_priv->channels.lock, flags); | ||
63 | for (ch = 0; ch < dev_priv->engine.fifo.channels; ch++) { | 123 | for (ch = 0; ch < dev_priv->engine.fifo.channels; ch++) { |
64 | struct nouveau_channel *chan = dev_priv->fifos[ch]; | 124 | struct nouveau_channel *chan = dev_priv->channels.ptr[ch]; |
65 | 125 | ||
66 | if (!chan || !chan->ramin) | 126 | if (!chan || !chan->ramin) |
67 | continue; | 127 | continue; |
@@ -69,6 +129,7 @@ nv50_fb_vm_trap(struct drm_device *dev, int display, const char *name) | |||
69 | if (chinst == chan->ramin->vinst >> 12) | 129 | if (chinst == chan->ramin->vinst >> 12) |
70 | break; | 130 | break; |
71 | } | 131 | } |
132 | spin_unlock_irqrestore(&dev_priv->channels.lock, flags); | ||
72 | 133 | ||
73 | NV_INFO(dev, "%s - VM: Trapped %s at %02x%04x%04x status %08x " | 134 | NV_INFO(dev, "%s - VM: Trapped %s at %02x%04x%04x status %08x " |
74 | "channel %d (0x%08x)\n", | 135 | "channel %d (0x%08x)\n", |