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 38static int gartFd = -1; 39static int acquiredScreen = -1; 40static Bool initDone = FALSE; 41 42/* 43 * Close /dev/agpgart. This frees all associated memory allocated during 44 * this server generation. 45 */ 46Bool 47xf86GARTCloseScreen(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 */ 61static Bool 62GARTInit(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 119Bool 120xf86AgpGARTSupported(void) 121{ 122 return GARTInit(-1); 123} 124 125AgpInfoPtr 126xf86GetAGPInfo(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 free(info); 147 return NULL; 148 } 149 150 info->bridgeId = agpinf.bridge_id; 151 info->agpMode = agpinf.agp_mode; 152 info->base = agpinf.aper_base; 153 info->size = agpinf.aper_size; 154 info->totalPages = agpinf.pg_total; 155 info->systemPages = agpinf.pg_system; 156 info->usedPages = agpinf.pg_used; 157 158 xf86DrvMsg(screenNum, X_INFO, "Kernel reported %zu total, %zu used\n", 159 agpinf.pg_total, agpinf.pg_used); 160 161 return info; 162} 163 164/* 165 * XXX If multiple screens can acquire the GART, should we have a reference 166 * count instead of using acquiredScreen? 167 */ 168 169Bool 170xf86AcquireGART(int screenNum) 171{ 172 if (screenNum != -1 && !GARTInit(screenNum)) 173 return FALSE; 174 175 if (screenNum == -1 || acquiredScreen != screenNum) { 176 if (ioctl(gartFd, AGPIOC_ACQUIRE, 0) != 0) { 177 xf86DrvMsg(screenNum, X_WARNING, 178 "xf86AcquireGART: AGPIOC_ACQUIRE failed (%s)\n", 179 strerror(errno)); 180 return FALSE; 181 } 182 acquiredScreen = screenNum; 183 } 184 return TRUE; 185} 186 187Bool 188xf86ReleaseGART(int screenNum) 189{ 190 if (screenNum != -1 && !GARTInit(screenNum)) 191 return FALSE; 192 193 if (acquiredScreen == screenNum) { 194 /* 195 * The FreeBSD agp driver removes allocations on release. 196 * The Linux driver doesn't. xf86ReleaseGART() is expected 197 * to give up access to the GART, but not to remove any 198 * allocations. 199 */ 200#if !defined(__linux__) 201 if (screenNum == -1) 202#endif 203 { 204 if (ioctl(gartFd, AGPIOC_RELEASE, 0) != 0) { 205 xf86DrvMsg(screenNum, X_WARNING, 206 "xf86ReleaseGART: AGPIOC_RELEASE failed (%s)\n", 207 strerror(errno)); 208 return FALSE; 209 } 210 acquiredScreen = -1; 211 } 212 return TRUE; 213 } 214 return FALSE; 215} 216 217int 218xf86AllocateGARTMemory(int screenNum, unsigned long size, int type, 219 unsigned long *physical) 220{ 221 struct _agp_allocate alloc; 222 int pages; 223 224 /* 225 * Allocates "size" bytes of GART memory (rounds up to the next 226 * page multiple) or type "type". A handle (key) for the allocated 227 * memory is returned. On error, the return value is -1. 228 */ 229 230 if (!GARTInit(screenNum) || acquiredScreen != screenNum) 231 return -1; 232 233 pages = (size / AGP_PAGE_SIZE); 234 if (size % AGP_PAGE_SIZE != 0) 235 pages++; 236 237 /* XXX check for pages == 0? */ 238 239 alloc.pg_count = pages; 240 alloc.type = type; 241 242 if (ioctl(gartFd, AGPIOC_ALLOCATE, &alloc) != 0) { 243 xf86DrvMsg(screenNum, X_WARNING, "xf86AllocateGARTMemory: " 244 "allocation of %d pages failed\n\t(%s)\n", pages, 245 strerror(errno)); 246 return -1; 247 } 248 249 if (physical) 250 *physical = alloc.physical; 251 252 return alloc.key; 253} 254 255Bool 256xf86DeallocateGARTMemory(int screenNum, int key) 257{ 258 if (!GARTInit(screenNum) || acquiredScreen != screenNum) 259 return FALSE; 260 261 if (acquiredScreen != screenNum) { 262 xf86DrvMsg(screenNum, X_ERROR, 263 "xf86UnbindGARTMemory: AGP not acquired by this screen\n"); 264 return FALSE; 265 } 266 267#ifdef __linux__ 268 if (ioctl(gartFd, AGPIOC_DEALLOCATE, (int *) (uintptr_t) key) != 0) { 269#else 270 if (ioctl(gartFd, AGPIOC_DEALLOCATE, &key) != 0) { 271#endif 272 xf86DrvMsg(screenNum, X_WARNING, "xf86DeAllocateGARTMemory: " 273 "deallocation gart memory with key %d failed\n\t(%s)\n", 274 key, strerror(errno)); 275 return FALSE; 276 } 277 278 return TRUE; 279} 280 281/* Bind GART memory with "key" at "offset" */ 282Bool 283xf86BindGARTMemory(int screenNum, int key, unsigned long offset) 284{ 285 struct _agp_bind bind; 286 int pageOffset; 287 288 if (!GARTInit(screenNum) || acquiredScreen != screenNum) 289 return FALSE; 290 291 if (acquiredScreen != screenNum) { 292 xf86DrvMsg(screenNum, X_ERROR, 293 "xf86BindGARTMemory: AGP not acquired by this screen\n"); 294 return FALSE; 295 } 296 297 if (offset % AGP_PAGE_SIZE != 0) { 298 xf86DrvMsg(screenNum, X_WARNING, "xf86BindGARTMemory: " 299 "offset (0x%lx) is not page-aligned (%d)\n", 300 offset, AGP_PAGE_SIZE); 301 return FALSE; 302 } 303 pageOffset = offset / AGP_PAGE_SIZE; 304 305 xf86DrvMsgVerb(screenNum, X_INFO, 3, 306 "xf86BindGARTMemory: bind key %d at 0x%08lx " 307 "(pgoffset %d)\n", key, offset, pageOffset); 308 309 bind.pg_start = pageOffset; 310 bind.key = key; 311 312 if (ioctl(gartFd, AGPIOC_BIND, &bind) != 0) { 313 xf86DrvMsg(screenNum, X_WARNING, "xf86BindGARTMemory: " 314 "binding of gart memory with key %d\n" 315 "\tat offset 0x%lx failed (%s)\n", 316 key, offset, strerror(errno)); 317 return FALSE; 318 } 319 320 return TRUE; 321} 322 323/* Unbind GART memory with "key" */ 324Bool 325xf86UnbindGARTMemory(int screenNum, int key) 326{ 327 struct _agp_unbind unbind; 328 329 if (!GARTInit(screenNum) || acquiredScreen != screenNum) 330 return FALSE; 331 332 if (acquiredScreen != screenNum) { 333 xf86DrvMsg(screenNum, X_ERROR, 334 "xf86UnbindGARTMemory: AGP not acquired by this screen\n"); 335 return FALSE; 336 } 337 338 unbind.priority = 0; 339 unbind.key = key; 340 341 if (ioctl(gartFd, AGPIOC_UNBIND, &unbind) != 0) { 342 xf86DrvMsg(screenNum, X_WARNING, "xf86UnbindGARTMemory: " 343 "unbinding of gart memory with key %d " 344 "failed (%s)\n", key, strerror(errno)); 345 return FALSE; 346 } 347 348 xf86DrvMsgVerb(screenNum, X_INFO, 3, 349 "xf86UnbindGARTMemory: unbind key %d\n", key); 350 351 return TRUE; 352} 353 354/* XXX Interface may change. */ 355Bool 356xf86EnableAGP(int screenNum, CARD32 mode) 357{ 358 agp_setup setup; 359 360 if (!GARTInit(screenNum) || acquiredScreen != screenNum) 361 return FALSE; 362 363 setup.agp_mode = mode; 364 if (ioctl(gartFd, AGPIOC_SETUP, &setup) != 0) { 365 xf86DrvMsg(screenNum, X_WARNING, "xf86EnableAGP: " 366 "AGPIOC_SETUP with mode %ld failed (%s)\n", 367 (unsigned long) mode, strerror(errno)); 368 return FALSE; 369 } 370 371 return TRUE; 372} 373