Commit | Line | Data |
---|---|---|
aa4cc5d2 BS |
1 | #include <linux/module.h> |
2 | ||
cb75d97e | 3 | #include <core/device.h> |
aa4cc5d2 | 4 | |
cb75d97e | 5 | #include "nouveau_drm.h" |
aa4cc5d2 | 6 | #include "nouveau_agp.h" |
cb75d97e | 7 | #include "nouveau_reg.h" |
aa4cc5d2 BS |
8 | |
9 | #if __OS_HAS_AGP | |
10 | MODULE_PARM_DESC(agpmode, "AGP mode (0 to disable AGP)"); | |
11 | static int nouveau_agpmode = -1; | |
12 | module_param_named(agpmode, nouveau_agpmode, int, 0400); | |
13 | ||
14 | static unsigned long | |
cb75d97e | 15 | get_agp_mode(struct nouveau_drm *drm, unsigned long mode) |
aa4cc5d2 | 16 | { |
cb75d97e | 17 | struct nouveau_device *device = nv_device(drm->device); |
aa4cc5d2 BS |
18 | |
19 | /* | |
20 | * FW seems to be broken on nv18, it makes the card lock up | |
21 | * randomly. | |
22 | */ | |
cb75d97e | 23 | if (device->chipset == 0x18) |
aa4cc5d2 BS |
24 | mode &= ~PCI_AGP_COMMAND_FW; |
25 | ||
26 | /* | |
27 | * AGP mode set in the command line. | |
28 | */ | |
29 | if (nouveau_agpmode > 0) { | |
30 | bool agpv3 = mode & 0x8; | |
31 | int rate = agpv3 ? nouveau_agpmode / 4 : nouveau_agpmode; | |
32 | ||
33 | mode = (mode & ~0x7) | (rate & 0x7); | |
34 | } | |
35 | ||
36 | return mode; | |
37 | } | |
38 | ||
39 | static bool | |
cb75d97e | 40 | nouveau_agp_enabled(struct nouveau_drm *drm) |
aa4cc5d2 | 41 | { |
cb75d97e | 42 | struct drm_device *dev = drm->dev; |
aa4cc5d2 BS |
43 | |
44 | if (!drm_pci_device_is_agp(dev) || !dev->agp) | |
45 | return false; | |
46 | ||
cb75d97e | 47 | if (drm->agp.stat == UNKNOWN) { |
aa4cc5d2 BS |
48 | if (!nouveau_agpmode) |
49 | return false; | |
650e1203 FJ |
50 | #ifdef __powerpc__ |
51 | /* Disable AGP by default on all PowerPC machines for | |
52 | * now -- At least some UniNorth-2 AGP bridges are | |
53 | * known to be broken: DMA from the host to the card | |
54 | * works just fine, but writeback from the card to the | |
55 | * host goes straight to memory untranslated bypassing | |
56 | * the GATT somehow, making them quite painful to deal | |
57 | * with... | |
58 | */ | |
59 | if (nouveau_agpmode == -1) | |
60 | return false; | |
61 | #endif | |
cb75d97e | 62 | return true; |
aa4cc5d2 BS |
63 | } |
64 | ||
cb75d97e | 65 | return (drm->agp.stat == ENABLED); |
aa4cc5d2 BS |
66 | } |
67 | #endif | |
68 | ||
69 | void | |
cb75d97e | 70 | nouveau_agp_reset(struct nouveau_drm *drm) |
aa4cc5d2 BS |
71 | { |
72 | #if __OS_HAS_AGP | |
cb75d97e BS |
73 | struct nouveau_device *device = nv_device(drm->device); |
74 | struct drm_device *dev = drm->dev; | |
aa4cc5d2 BS |
75 | u32 save[2]; |
76 | int ret; | |
77 | ||
cb75d97e | 78 | if (!nouveau_agp_enabled(drm)) |
aa4cc5d2 BS |
79 | return; |
80 | ||
81 | /* First of all, disable fast writes, otherwise if it's | |
82 | * already enabled in the AGP bridge and we disable the card's | |
83 | * AGP controller we might be locking ourselves out of it. */ | |
cb75d97e | 84 | if ((nv_rd32(device, NV04_PBUS_PCI_NV_19) | |
aa4cc5d2 BS |
85 | dev->agp->mode) & PCI_AGP_COMMAND_FW) { |
86 | struct drm_agp_info info; | |
87 | struct drm_agp_mode mode; | |
88 | ||
89 | ret = drm_agp_info(dev, &info); | |
90 | if (ret) | |
91 | return; | |
92 | ||
cb75d97e | 93 | mode.mode = get_agp_mode(drm, info.mode); |
aa4cc5d2 BS |
94 | mode.mode &= ~PCI_AGP_COMMAND_FW; |
95 | ||
96 | ret = drm_agp_enable(dev, mode); | |
97 | if (ret) | |
98 | return; | |
99 | } | |
100 | ||
101 | ||
102 | /* clear busmaster bit, and disable AGP */ | |
cb75d97e BS |
103 | save[0] = nv_mask(device, NV04_PBUS_PCI_NV_1, 0x00000004, 0x00000000); |
104 | nv_wr32(device, NV04_PBUS_PCI_NV_19, 0); | |
aa4cc5d2 BS |
105 | |
106 | /* reset PGRAPH, PFIFO and PTIMER */ | |
cb75d97e BS |
107 | save[1] = nv_mask(device, 0x000200, 0x00011100, 0x00000000); |
108 | nv_mask(device, 0x000200, 0x00011100, save[1]); | |
aa4cc5d2 BS |
109 | |
110 | /* and restore bustmaster bit (gives effect of resetting AGP) */ | |
cb75d97e | 111 | nv_wr32(device, NV04_PBUS_PCI_NV_1, save[0]); |
aa4cc5d2 BS |
112 | #endif |
113 | } | |
114 | ||
115 | void | |
cb75d97e | 116 | nouveau_agp_init(struct nouveau_drm *drm) |
aa4cc5d2 BS |
117 | { |
118 | #if __OS_HAS_AGP | |
cb75d97e BS |
119 | struct nouveau_device *device = nv_device(drm->device); |
120 | struct drm_device *dev = drm->dev; | |
aa4cc5d2 BS |
121 | struct drm_agp_info info; |
122 | struct drm_agp_mode mode; | |
123 | int ret; | |
124 | ||
cb75d97e | 125 | if (!nouveau_agp_enabled(drm)) |
aa4cc5d2 | 126 | return; |
cb75d97e | 127 | drm->agp.stat = DISABLE; |
aa4cc5d2 BS |
128 | |
129 | ret = drm_agp_acquire(dev); | |
130 | if (ret) { | |
cb75d97e | 131 | nv_error(device, "unable to acquire AGP: %d\n", ret); |
aa4cc5d2 BS |
132 | return; |
133 | } | |
134 | ||
135 | ret = drm_agp_info(dev, &info); | |
136 | if (ret) { | |
cb75d97e | 137 | nv_error(device, "unable to get AGP info: %d\n", ret); |
aa4cc5d2 BS |
138 | return; |
139 | } | |
140 | ||
141 | /* see agp.h for the AGPSTAT_* modes available */ | |
cb75d97e | 142 | mode.mode = get_agp_mode(drm, info.mode); |
aa4cc5d2 BS |
143 | |
144 | ret = drm_agp_enable(dev, mode); | |
145 | if (ret) { | |
cb75d97e | 146 | nv_error(device, "unable to enable AGP: %d\n", ret); |
aa4cc5d2 BS |
147 | return; |
148 | } | |
149 | ||
cb75d97e BS |
150 | drm->agp.stat = ENABLED; |
151 | drm->agp.base = info.aperture_base; | |
152 | drm->agp.size = info.aperture_size; | |
aa4cc5d2 BS |
153 | #endif |
154 | } | |
155 | ||
156 | void | |
cb75d97e | 157 | nouveau_agp_fini(struct nouveau_drm *drm) |
aa4cc5d2 BS |
158 | { |
159 | #if __OS_HAS_AGP | |
cb75d97e | 160 | struct drm_device *dev = drm->dev; |
aa4cc5d2 BS |
161 | if (dev->agp && dev->agp->acquired) |
162 | drm_agp_release(dev); | |
163 | #endif | |
164 | } |