Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Abstraction of the AGP GART interface. | |
3 | * | |
4 | * This version is for Linux and Free/Open/NetBSD. | |
5 | * | |
6 | * Copyright © 2000 VA Linux Systems, Inc. | |
7 | * Copyright © 2001 The XFree86 Project, Inc. | |
8 | */ | |
9 | ||
10 | #ifdef HAVE_XORG_CONFIG_H | |
11 | #include <xorg-config.h> | |
12 | #endif | |
13 | ||
14 | #include <X11/X.h> | |
15 | #include "xf86.h" | |
16 | #include "xf86Priv.h" | |
17 | #include "xf86_OSlib.h" | |
18 | #include "xf86OSpriv.h" | |
19 | ||
20 | #if defined(linux) | |
21 | #include <asm/ioctl.h> | |
22 | #include <linux/agpgart.h> | |
23 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) | |
24 | #include <sys/ioctl.h> | |
25 | #include <sys/agpio.h> | |
26 | #endif | |
27 | ||
28 | #ifndef AGP_DEVICE | |
29 | #define AGP_DEVICE "/dev/agpgart" | |
30 | #endif | |
31 | /* AGP page size is independent of the host page size. */ | |
32 | #ifndef AGP_PAGE_SIZE | |
33 | #define AGP_PAGE_SIZE 4096 | |
34 | #endif | |
35 | #define AGPGART_MAJOR_VERSION 0 | |
36 | #define AGPGART_MINOR_VERSION 99 | |
37 | ||
38 | static int gartFd = -1; | |
39 | static int acquiredScreen = -1; | |
40 | static Bool initDone = FALSE; | |
41 | ||
42 | /* | |
43 | * Close /dev/agpgart. This frees all associated memory allocated during | |
44 | * this server generation. | |
45 | */ | |
46 | Bool | |
47 | xf86GARTCloseScreen(int screenNum) | |
48 | { | |
49 | if (gartFd != -1) { | |
50 | close(gartFd); | |
51 | acquiredScreen = -1; | |
52 | gartFd = -1; | |
53 | initDone = FALSE; | |
54 | } | |
55 | return TRUE; | |
56 | } | |
57 | ||
58 | /* | |
59 | * Open /dev/agpgart. Keep it open until xf86GARTCloseScreen is called. | |
60 | */ | |
61 | static Bool | |
62 | GARTInit(int screenNum) | |
63 | { | |
64 | struct _agp_info agpinf; | |
65 | ||
66 | if (initDone) | |
67 | return gartFd != -1; | |
68 | ||
69 | initDone = TRUE; | |
70 | ||
71 | if (gartFd == -1) | |
72 | gartFd = open(AGP_DEVICE, O_RDWR, 0); | |
73 | else | |
74 | return FALSE; | |
75 | ||
76 | if (gartFd == -1) { | |
77 | xf86DrvMsg(screenNum, X_ERROR, | |
78 | "GARTInit: Unable to open " AGP_DEVICE " (%s)\n", | |
79 | strerror(errno)); | |
80 | return FALSE; | |
81 | } | |
82 | ||
83 | xf86AcquireGART(-1); | |
84 | /* Check the kernel driver version. */ | |
85 | if (ioctl(gartFd, AGPIOC_INFO, &agpinf) != 0) { | |
86 | xf86DrvMsg(screenNum, X_ERROR, | |
87 | "GARTInit: AGPIOC_INFO failed (%s)\n", strerror(errno)); | |
88 | close(gartFd); | |
89 | gartFd = -1; | |
90 | return FALSE; | |
91 | } | |
92 | xf86ReleaseGART(-1); | |
93 | ||
94 | #if defined(linux) | |
95 | /* Per Dave Jones, every effort will be made to keep the | |
96 | * agpgart interface backwards compatible, so allow all | |
97 | * future versions. | |
98 | */ | |
99 | if ( | |
100 | #if (AGPGART_MAJOR_VERSION > 0) /* quiet compiler */ | |
101 | agpinf.version.major < AGPGART_MAJOR_VERSION || | |
102 | #endif | |
103 | (agpinf.version.major == AGPGART_MAJOR_VERSION && | |
104 | agpinf.version.minor < AGPGART_MINOR_VERSION)) { | |
105 | xf86DrvMsg(screenNum, X_ERROR, | |
106 | "GARTInit: Kernel agpgart driver version is not current" | |
107 | " (%d.%d vs %d.%d)\n", | |
108 | agpinf.version.major, agpinf.version.minor, | |
109 | AGPGART_MAJOR_VERSION, AGPGART_MINOR_VERSION); | |
110 | close(gartFd); | |
111 | gartFd = -1; | |
112 | return FALSE; | |
113 | } | |
114 | #endif | |
115 | ||
116 | return TRUE; | |
117 | } | |
118 | ||
119 | Bool | |
120 | xf86AgpGARTSupported(void) | |
121 | { | |
122 | return GARTInit(-1); | |
123 | } | |
124 | ||
125 | AgpInfoPtr | |
126 | xf86GetAGPInfo(int screenNum) | |
127 | { | |
128 | struct _agp_info agpinf; | |
129 | AgpInfoPtr info; | |
130 | ||
131 | if (!GARTInit(screenNum)) | |
132 | return NULL; | |
133 | ||
134 | if ((info = calloc(sizeof(AgpInfo), 1)) == NULL) { | |
135 | xf86DrvMsg(screenNum, X_ERROR, | |
136 | "xf86GetAGPInfo: Failed to allocate AgpInfo\n"); | |
137 | return NULL; | |
138 | } | |
139 | ||
140 | memset((char *) &agpinf, 0, sizeof(agpinf)); | |
141 | ||
142 | if (ioctl(gartFd, AGPIOC_INFO, &agpinf) != 0) { | |
143 | xf86DrvMsg(screenNum, X_ERROR, | |
144 | "xf86GetAGPInfo: AGPIOC_INFO failed (%s)\n", | |
145 | strerror(errno)); | |
146 | return NULL; | |
147 | } | |
148 | ||
149 | info->bridgeId = agpinf.bridge_id; | |
150 | info->agpMode = agpinf.agp_mode; | |
151 | info->base = agpinf.aper_base; | |
152 | info->size = agpinf.aper_size; | |
153 | info->totalPages = agpinf.pg_total; | |
154 | info->systemPages = agpinf.pg_system; | |
155 | info->usedPages = agpinf.pg_used; | |
156 | ||
157 | xf86DrvMsg(screenNum, X_INFO, "Kernel reported %zu total, %zu used\n", | |
158 | agpinf.pg_total, agpinf.pg_used); | |
159 | ||
160 | return info; | |
161 | } | |
162 | ||
163 | /* | |
164 | * XXX If multiple screens can acquire the GART, should we have a reference | |
165 | * count instead of using acquiredScreen? | |
166 | */ | |
167 | ||
168 | Bool | |
169 | xf86AcquireGART(int screenNum) | |
170 | { | |
171 | if (screenNum != -1 && !GARTInit(screenNum)) | |
172 | return FALSE; | |
173 | ||
174 | if (screenNum == -1 || acquiredScreen != screenNum) { | |
175 | if (ioctl(gartFd, AGPIOC_ACQUIRE, 0) != 0) { | |
176 | xf86DrvMsg(screenNum, X_WARNING, | |
177 | "xf86AcquireGART: AGPIOC_ACQUIRE failed (%s)\n", | |
178 | strerror(errno)); | |
179 | return FALSE; | |
180 | } | |
181 | acquiredScreen = screenNum; | |
182 | } | |
183 | return TRUE; | |
184 | } | |
185 | ||
186 | Bool | |
187 | xf86ReleaseGART(int screenNum) | |
188 | { | |
189 | if (screenNum != -1 && !GARTInit(screenNum)) | |
190 | return FALSE; | |
191 | ||
192 | if (acquiredScreen == screenNum) { | |
193 | /* | |
194 | * The FreeBSD agp driver removes allocations on release. | |
195 | * The Linux driver doesn't. xf86ReleaseGART() is expected | |
196 | * to give up access to the GART, but not to remove any | |
197 | * allocations. | |
198 | */ | |
199 | #if !defined(linux) | |
200 | if (screenNum == -1) | |
201 | #endif | |
202 | { | |
203 | if (ioctl(gartFd, AGPIOC_RELEASE, 0) != 0) { | |
204 | xf86DrvMsg(screenNum, X_WARNING, | |
205 | "xf86ReleaseGART: AGPIOC_RELEASE failed (%s)\n", | |
206 | strerror(errno)); | |
207 | return FALSE; | |
208 | } | |
209 | acquiredScreen = -1; | |
210 | } | |
211 | return TRUE; | |
212 | } | |
213 | return FALSE; | |
214 | } | |
215 | ||
216 | int | |
217 | xf86AllocateGARTMemory(int screenNum, unsigned long size, int type, | |
218 | unsigned long *physical) | |
219 | { | |
220 | struct _agp_allocate alloc; | |
221 | int pages; | |
222 | ||
223 | /* | |
224 | * Allocates "size" bytes of GART memory (rounds up to the next | |
225 | * page multiple) or type "type". A handle (key) for the allocated | |
226 | * memory is returned. On error, the return value is -1. | |
227 | */ | |
228 | ||
229 | if (!GARTInit(screenNum) || acquiredScreen != screenNum) | |
230 | return -1; | |
231 | ||
232 | pages = (size / AGP_PAGE_SIZE); | |
233 | if (size % AGP_PAGE_SIZE != 0) | |
234 | pages++; | |
235 | ||
236 | /* XXX check for pages == 0? */ | |
237 | ||
238 | alloc.pg_count = pages; | |
239 | alloc.type = type; | |
240 | ||
241 | if (ioctl(gartFd, AGPIOC_ALLOCATE, &alloc) != 0) { | |
242 | xf86DrvMsg(screenNum, X_WARNING, "xf86AllocateGARTMemory: " | |
243 | "allocation of %d pages failed\n\t(%s)\n", pages, | |
244 | strerror(errno)); | |
245 | return -1; | |
246 | } | |
247 | ||
248 | if (physical) | |
249 | *physical = alloc.physical; | |
250 | ||
251 | return alloc.key; | |
252 | } | |
253 | ||
254 | Bool | |
255 | xf86DeallocateGARTMemory(int screenNum, int key) | |
256 | { | |
257 | if (!GARTInit(screenNum) || acquiredScreen != screenNum) | |
258 | return FALSE; | |
259 | ||
260 | if (acquiredScreen != screenNum) { | |
261 | xf86DrvMsg(screenNum, X_ERROR, | |
262 | "xf86UnbindGARTMemory: AGP not acquired by this screen\n"); | |
263 | return FALSE; | |
264 | } | |
265 | ||
266 | #ifdef __linux__ | |
267 | if (ioctl(gartFd, AGPIOC_DEALLOCATE, (int *) (uintptr_t) key) != 0) { | |
268 | #else | |
269 | if (ioctl(gartFd, AGPIOC_DEALLOCATE, &key) != 0) { | |
270 | #endif | |
271 | xf86DrvMsg(screenNum, X_WARNING, "xf86DeAllocateGARTMemory: " | |
272 | "deallocation gart memory with key %d failed\n\t(%s)\n", | |
273 | key, strerror(errno)); | |
274 | return FALSE; | |
275 | } | |
276 | ||
277 | return TRUE; | |
278 | } | |
279 | ||
280 | /* Bind GART memory with "key" at "offset" */ | |
281 | Bool | |
282 | xf86BindGARTMemory(int screenNum, int key, unsigned long offset) | |
283 | { | |
284 | struct _agp_bind bind; | |
285 | int pageOffset; | |
286 | ||
287 | if (!GARTInit(screenNum) || acquiredScreen != screenNum) | |
288 | return FALSE; | |
289 | ||
290 | if (acquiredScreen != screenNum) { | |
291 | xf86DrvMsg(screenNum, X_ERROR, | |
292 | "xf86BindGARTMemory: AGP not acquired by this screen\n"); | |
293 | return FALSE; | |
294 | } | |
295 | ||
296 | if (offset % AGP_PAGE_SIZE != 0) { | |
297 | xf86DrvMsg(screenNum, X_WARNING, "xf86BindGARTMemory: " | |
298 | "offset (0x%lx) is not page-aligned (%d)\n", | |
299 | offset, AGP_PAGE_SIZE); | |
300 | return FALSE; | |
301 | } | |
302 | pageOffset = offset / AGP_PAGE_SIZE; | |
303 | ||
304 | xf86DrvMsgVerb(screenNum, X_INFO, 3, | |
305 | "xf86BindGARTMemory: bind key %d at 0x%08lx " | |
306 | "(pgoffset %d)\n", key, offset, pageOffset); | |
307 | ||
308 | bind.pg_start = pageOffset; | |
309 | bind.key = key; | |
310 | ||
311 | if (ioctl(gartFd, AGPIOC_BIND, &bind) != 0) { | |
312 | xf86DrvMsg(screenNum, X_WARNING, "xf86BindGARTMemory: " | |
313 | "binding of gart memory with key %d\n" | |
314 | "\tat offset 0x%lx failed (%s)\n", | |
315 | key, offset, strerror(errno)); | |
316 | return FALSE; | |
317 | } | |
318 | ||
319 | return TRUE; | |
320 | } | |
321 | ||
322 | /* Unbind GART memory with "key" */ | |
323 | Bool | |
324 | xf86UnbindGARTMemory(int screenNum, int key) | |
325 | { | |
326 | struct _agp_unbind unbind; | |
327 | ||
328 | if (!GARTInit(screenNum) || acquiredScreen != screenNum) | |
329 | return FALSE; | |
330 | ||
331 | if (acquiredScreen != screenNum) { | |
332 | xf86DrvMsg(screenNum, X_ERROR, | |
333 | "xf86UnbindGARTMemory: AGP not acquired by this screen\n"); | |
334 | return FALSE; | |
335 | } | |
336 | ||
337 | unbind.priority = 0; | |
338 | unbind.key = key; | |
339 | ||
340 | if (ioctl(gartFd, AGPIOC_UNBIND, &unbind) != 0) { | |
341 | xf86DrvMsg(screenNum, X_WARNING, "xf86UnbindGARTMemory: " | |
342 | "unbinding of gart memory with key %d " | |
343 | "failed (%s)\n", key, strerror(errno)); | |
344 | return FALSE; | |
345 | } | |
346 | ||
347 | xf86DrvMsgVerb(screenNum, X_INFO, 3, | |
348 | "xf86UnbindGARTMemory: unbind key %d\n", key); | |
349 | ||
350 | return TRUE; | |
351 | } | |
352 | ||
353 | /* XXX Interface may change. */ | |
354 | Bool | |
355 | xf86EnableAGP(int screenNum, CARD32 mode) | |
356 | { | |
357 | agp_setup setup; | |
358 | ||
359 | if (!GARTInit(screenNum) || acquiredScreen != screenNum) | |
360 | return FALSE; | |
361 | ||
362 | setup.agp_mode = mode; | |
363 | if (ioctl(gartFd, AGPIOC_SETUP, &setup) != 0) { | |
364 | xf86DrvMsg(screenNum, X_WARNING, "xf86EnableAGP: " | |
365 | "AGPIOC_SETUP with mode %ld failed (%s)\n", | |
366 | (unsigned long) mode, strerror(errno)); | |
367 | return FALSE; | |
368 | } | |
369 | ||
370 | return TRUE; | |
371 | } |