xref: /OK3568_Linux_fs/external/xserver/hw/xfree86/os-support/linux/lnx_agp.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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
xf86GARTCloseScreen(int screenNum)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
GARTInit(int screenNum)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
xf86AgpGARTSupported(void)120 xf86AgpGARTSupported(void)
121 {
122     return GARTInit(-1);
123 }
124 
125 AgpInfoPtr
xf86GetAGPInfo(int screenNum)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
xf86AcquireGART(int screenNum)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
xf86ReleaseGART(int screenNum)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
xf86AllocateGARTMemory(int screenNum,unsigned long size,int type,unsigned long * physical)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
xf86DeallocateGARTMemory(int screenNum,int key)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 }
372