/* xf86drm.c -- User-level interface to DRM device * Created: Tue Jan 5 08:16:21 1999 by faith@precisioninsight.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: Rickard E. (Rik) Faith * Kevin E. Martin * * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/xf86drm.c,v 1.25 2001/08/27 17:40:59 dawes Exp $ * */ #ifdef XFree86Server # include "xf86.h" # include "xf86_OSproc.h" # include "xf86_ansic.h" # define _DRM_MALLOC xalloc # define _DRM_FREE xfree # ifndef XFree86LOADER # include # endif #else # include # include # include # include # include # include # include # include # include # include # define stat_t struct stat # include # include # include # include # ifdef DRM_USE_MALLOC # define _DRM_MALLOC malloc # define _DRM_FREE free extern int xf86InstallSIGIOHandler(int fd, void (*f)(int, void *), void *); extern int xf86RemoveSIGIOHandler(int fd); # else # include # define _DRM_MALLOC Xmalloc # define _DRM_FREE Xfree # endif #endif /* No longer needed with CVS kernel modules on alpha #if defined(__alpha__) && defined(__linux__) extern unsigned long _bus_base(void); #define BUS_BASE _bus_base() #endif */ /* Not all systems have MAP_FAILED defined */ #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif #include "xf86drm.h" #include "drm.h" #ifndef DRM_MAJOR #define DRM_MAJOR 226 /* Linux */ #endif #ifndef __linux__ #undef DRM_MAJOR #define DRM_MAJOR 145 /* Should set in drm.h for *BSD */ #endif #ifndef DRM_MAX_MINOR #define DRM_MAX_MINOR 16 #endif #ifdef __linux__ #include /* for makedev() */ #endif #ifndef makedev /* This definition needs to be changed on some systems if dev_t is a structure. If there is a header file we can get it from, there would be best. */ #define makedev(x,y) ((dev_t)(((x) << 8) | (y))) #endif #define DRM_MSG_VERBOSITY 3 static void drmMsg(const char *format, ...) { va_list ap; #ifndef XFree86Server const char *env; if ((env = getenv("LIBGL_DEBUG")) && strstr(env, "verbose")) #endif { va_start(ap, format); #ifdef XFree86Server xf86VDrvMsgVerb(-1, X_NONE, DRM_MSG_VERBOSITY, format, ap); #else vfprintf(stderr, format, ap); #endif va_end(ap); } } static void *drmHashTable = NULL; /* Context switch callbacks */ typedef struct drmHashEntry { int fd; void (*f)(int, void *, void *); void *tagTable; } drmHashEntry; void *drmMalloc(int size) { void *pt; if ((pt = _DRM_MALLOC(size))) memset(pt, 0, size); return pt; } void drmFree(void *pt) { if (pt) _DRM_FREE(pt); } /* drmStrdup can't use strdup(3), since it doesn't call _DRM_MALLOC... */ static char *drmStrdup(const char *s) { char *retval = NULL; if (s) { retval = _DRM_MALLOC(strlen(s)+1); strcpy(retval, s); } return retval; } static unsigned long drmGetKeyFromFd(int fd) { stat_t st; st.st_rdev = 0; fstat(fd, &st); return st.st_rdev; } static drmHashEntry *drmGetEntry(int fd) { unsigned long key = drmGetKeyFromFd(fd); void *value; drmHashEntry *entry; if (!drmHashTable) drmHashTable = drmHashCreate(); if (drmHashLookup(drmHashTable, key, &value)) { entry = drmMalloc(sizeof(*entry)); entry->fd = fd; entry->f = NULL; entry->tagTable = drmHashCreate(); drmHashInsert(drmHashTable, key, entry); } else { entry = value; } return entry; } static int drmOpenDevice(long dev, int minor) { stat_t st; char buf[64]; int fd; mode_t dirmode = DRM_DEV_DIRMODE; mode_t devmode = DRM_DEV_MODE; int isroot = !geteuid(); #if defined(XFree86Server) uid_t user = DRM_DEV_UID; gid_t group = DRM_DEV_GID; #endif drmMsg("drmOpenDevice: minor is %d\n", minor); #if defined(XFree86Server) devmode = xf86ConfigDRI.mode ? xf86ConfigDRI.mode : DRM_DEV_MODE; dirmode = (devmode & S_IRUSR) ? S_IXUSR : 0; dirmode |= (devmode & S_IRGRP) ? S_IXGRP : 0; dirmode |= (devmode & S_IROTH) ? S_IXOTH : 0; dirmode |= devmode; devmode &= ~(S_IXUSR|S_IXGRP|S_IXOTH); group = (xf86ConfigDRI.group >= 0) ? xf86ConfigDRI.group : DRM_DEV_GID; #endif if (stat(DRM_DIR_NAME, &st)) { if (!isroot) return DRM_ERR_NOT_ROOT; remove(DRM_DIR_NAME); mkdir(DRM_DIR_NAME, dirmode); } #if defined(XFree86Server) chown(DRM_DIR_NAME, user, group); chmod(DRM_DIR_NAME, dirmode); #endif sprintf(buf, DRM_DEV_NAME, DRM_DIR_NAME, minor); drmMsg("drmOpenDevice: node name is %s\n", buf); if (stat(buf, &st)) { if (!isroot) return DRM_ERR_NOT_ROOT; remove(buf); mknod(buf, S_IFCHR | devmode, dev); } #if defined(XFree86Server) chown(buf, user, group); chmod(buf, devmode); #endif fd = open(buf, O_RDWR, 0); drmMsg("drmOpenDevice: open result is %d, (%s)\n", fd, fd < 0 ? strerror(errno) : "OK"); if (fd >= 0) return fd; if (st.st_rdev != dev) { if (!isroot) return DRM_ERR_NOT_ROOT; remove(buf); mknod(buf, S_IFCHR | devmode, dev); } fd = open(buf, O_RDWR, 0); drmMsg("drmOpenDevice: open result is %d, (%s)\n", fd, fd < 0 ? strerror(errno) : "OK"); drmMsg("drmOpenDevice: Open failed\n"); remove(buf); return -errno; } static int drmOpenMinor(int minor, int create) { int fd; char buf[64]; if (create) return drmOpenDevice(makedev(DRM_MAJOR, minor), minor); sprintf(buf, DRM_DEV_NAME, DRM_DIR_NAME, minor); if ((fd = open(buf, O_RDWR, 0)) >= 0) return fd; return -errno; } /* drmAvailable looks for (DRM_MAJOR, 0) and returns 1 if it returns information for DRM_IOCTL_VERSION. For backward compatibility with older Linux implementations, /proc/dri is also checked. */ int drmAvailable(void) { drmVersionPtr version; int retval = 0; int fd; if ((fd = drmOpenMinor(0, 1)) < 0) { /* Try proc for backward Linux compatibility */ if (!access("/proc/dri/0", R_OK)) return 1; return 0; } if ((version = drmGetVersion(fd))) { retval = 1; drmFreeVersion(version); } close(fd); return retval; } static int drmOpenByBusid(const char *busid) { int i; int fd; const char *buf; drmMsg("drmOpenByBusid: busid is %s\n", busid); for (i = 0; i < DRM_MAX_MINOR; i++) { fd = drmOpenMinor(i, 1); drmMsg("drmOpenByBusid: drmOpenMinor returns %d\n", fd); if (fd >= 0) { buf = drmGetBusid(fd); drmMsg("drmOpenByBusid: drmGetBusid reports %s\n", buf); if (buf && !strcmp(buf, busid)) { drmFreeBusid(buf); return fd; } if (buf) drmFreeBusid(buf); close(fd); } } return -1; } static int drmOpenByName(const char *name) { int i; int fd; drmVersionPtr version; char * id; if (!drmAvailable()) { #if !defined(XFree86Server) return -1; #else /* try to load the kernel module now */ if (!xf86LoadKernelModule(name)) { ErrorF("[drm] failed to load kernel module \"%s\"\n", name); return -1; } #endif } /* * Open the first minor number that matches the driver name and isn't * already in use. If it's in use it will have a busid assigned already. */ for (i = 0; i < DRM_MAX_MINOR; i++) { if ((fd = drmOpenMinor(i, 1)) >= 0) { if ((version = drmGetVersion(fd))) { if (!strcmp(version->name, name)) { drmFreeVersion(version); id = drmGetBusid(fd); drmMsg("drmGetBusid returned '%s'\n", id ? id : "NULL"); if (!id || !*id) { if (id) { drmFreeBusid(id); } return fd; } else { drmFreeBusid(id); } } else { drmFreeVersion(version); } } close(fd); } } #ifdef __linux__ /* Backward-compatibility /proc support */ for (i = 0; i < 8; i++) { char proc_name[64], buf[512]; char *driver, *pt, *devstring; int retcode; sprintf(proc_name, "/proc/dri/%d/name", i); if ((fd = open(proc_name, 0, 0)) >= 0) { retcode = read(fd, buf, sizeof(buf)-1); close(fd); if (retcode) { buf[retcode-1] = '\0'; for (driver = pt = buf; *pt && *pt != ' '; ++pt) ; if (*pt) { /* Device is next */ *pt = '\0'; if (!strcmp(driver, name)) { /* Match */ for (devstring = ++pt; *pt && *pt != ' '; ++pt) ; if (*pt) { /* Found busid */ return drmOpenByBusid(++pt); } else { /* No busid */ return drmOpenDevice(strtol(devstring, NULL, 0),i); } } } } } } #endif return -1; } /* drmOpen looks up the specified name and busid, and opens the device found. The entry in /dev/dri is created if necessary (and if root). A file descriptor is returned. On error, the return value is negative. */ int drmOpen(const char *name, const char *busid) { if (busid) return drmOpenByBusid(busid); return drmOpenByName(name); } void drmFreeVersion(drmVersionPtr v) { if (!v) return; if (v->name) drmFree(v->name); if (v->date) drmFree(v->date); if (v->desc) drmFree(v->desc); drmFree(v); } static void drmFreeKernelVersion(drm_version_t *v) { if (!v) return; if (v->name) drmFree(v->name); if (v->date) drmFree(v->date); if (v->desc) drmFree(v->desc); drmFree(v); } static void drmCopyVersion(drmVersionPtr d, const drm_version_t *s) { d->version_major = s->version_major; d->version_minor = s->version_minor; d->version_patchlevel = s->version_patchlevel; d->name_len = s->name_len; d->name = drmStrdup(s->name); d->date_len = s->date_len; d->date = drmStrdup(s->date); d->desc_len = s->desc_len; d->desc = drmStrdup(s->desc); } /* drmGet Version obtains the driver version information via an ioctl. Similar * information is available via /proc/dri. */ drmVersionPtr drmGetVersion(int fd) { drmVersionPtr retval; drm_version_t *version = drmMalloc(sizeof(*version)); /* First, get the lengths */ version->name_len = 0; version->name = NULL; version->date_len = 0; version->date = NULL; version->desc_len = 0; version->desc = NULL; if (ioctl(fd, DRM_IOCTL_VERSION, version)) { drmFreeKernelVersion(version); return NULL; } /* Now, allocate space and get the data */ if (version->name_len) version->name = drmMalloc(version->name_len + 1); if (version->date_len) version->date = drmMalloc(version->date_len + 1); if (version->desc_len) version->desc = drmMalloc(version->desc_len + 1); if (ioctl(fd, DRM_IOCTL_VERSION, version)) { drmFreeKernelVersion(version); return NULL; } /* The results might not be null-terminated strings, so terminate them. */ if (version->name_len) version->name[version->name_len] = '\0'; if (version->date_len) version->date[version->date_len] = '\0'; if (version->desc_len) version->desc[version->desc_len] = '\0'; /* Now, copy it all back into the client-visible data structure... */ retval = drmMalloc(sizeof(*retval)); drmCopyVersion(retval, version); drmFreeKernelVersion(version); return retval; } /* drmGetLibVersion set version information for the drm user space library. * this version number is driver indepedent */ drmVersionPtr drmGetLibVersion(int fd) { drm_version_t *version = drmMalloc(sizeof(*version)); /* Version history: * revision 1.0.x = original DRM interface with no drmGetLibVersion * entry point and many drm extensions * revision 1.1.x = added drmCommand entry points for device extensions * added drmGetLibVersion to identify libdrm.a version */ version->version_major = 1; version->version_minor = 1; version->version_patchlevel = 0; return (drmVersionPtr)version; } void drmFreeBusid(const char *busid) { drmFree((void *)busid); } char *drmGetBusid(int fd) { drm_unique_t u; u.unique_len = 0; u.unique = NULL; if (ioctl(fd, DRM_IOCTL_GET_UNIQUE, &u)) return NULL; u.unique = drmMalloc(u.unique_len + 1); if (ioctl(fd, DRM_IOCTL_GET_UNIQUE, &u)) return NULL; u.unique[u.unique_len] = '\0'; return u.unique; } int drmSetBusid(int fd, const char *busid) { drm_unique_t u; u.unique = (char *)busid; u.unique_len = strlen(busid); if (ioctl(fd, DRM_IOCTL_SET_UNIQUE, &u)) { return -errno; } return 0; } int drmGetMagic(int fd, drmMagicPtr magic) { drm_auth_t auth; *magic = 0; if (ioctl(fd, DRM_IOCTL_GET_MAGIC, &auth)) return -errno; *magic = auth.magic; return 0; } int drmAuthMagic(int fd, drmMagic magic) { drm_auth_t auth; auth.magic = magic; if (ioctl(fd, DRM_IOCTL_AUTH_MAGIC, &auth)) return -errno; return 0; } int drmAddMap(int fd, drmHandle offset, drmSize size, drmMapType type, drmMapFlags flags, drmHandlePtr handle) { drm_map_t map; map.offset = offset; /* No longer needed with CVS kernel modules on alpha #ifdef __alpha__ if (type != DRM_SHM) map.offset += BUS_BASE; #endif */ map.size = size; map.handle = 0; map.type = type; map.flags = flags; if (ioctl(fd, DRM_IOCTL_ADD_MAP, &map)) return -errno; if (handle) *handle = (drmHandle)map.handle; return 0; } int drmRmMap(int fd, drmHandle handle) { drm_map_t map; map.handle = (void *)handle; if(ioctl(fd, DRM_IOCTL_RM_MAP, &map)) return -errno; return 0; } int drmAddBufs(int fd, int count, int size, drmBufDescFlags flags, int agp_offset) { drm_buf_desc_t request; request.count = count; request.size = size; request.low_mark = 0; request.high_mark = 0; request.flags = flags; request.agp_start = agp_offset; if (ioctl(fd, DRM_IOCTL_ADD_BUFS, &request)) return -errno; return request.count; } int drmMarkBufs(int fd, double low, double high) { drm_buf_info_t info; int i; info.count = 0; info.list = NULL; if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) return -EINVAL; if (!info.count) return -EINVAL; if (!(info.list = drmMalloc(info.count * sizeof(*info.list)))) return -ENOMEM; if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) { int retval = -errno; drmFree(info.list); return retval; } for (i = 0; i < info.count; i++) { info.list[i].low_mark = low * info.list[i].count; info.list[i].high_mark = high * info.list[i].count; if (ioctl(fd, DRM_IOCTL_MARK_BUFS, &info.list[i])) { int retval = -errno; drmFree(info.list); return retval; } } drmFree(info.list); return 0; } int drmFreeBufs(int fd, int count, int *list) { drm_buf_free_t request; request.count = count; request.list = list; if (ioctl(fd, DRM_IOCTL_FREE_BUFS, &request)) return -errno; return 0; } int drmClose(int fd) { unsigned long key = drmGetKeyFromFd(fd); drmHashEntry *entry = drmGetEntry(fd); drmHashDestroy(entry->tagTable); entry->fd = 0; entry->f = NULL; entry->tagTable = NULL; drmHashDelete(drmHashTable, key); drmFree(entry); return close(fd); } int drmMap(int fd, drmHandle handle, drmSize size, drmAddressPtr address) { static unsigned long pagesize_mask = 0; if (fd < 0) return -EINVAL; if (!pagesize_mask) pagesize_mask = getpagesize() - 1; size = (size + pagesize_mask) & ~pagesize_mask; *address = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, handle); if (*address == MAP_FAILED) return -errno; return 0; } int drmUnmap(drmAddress address, drmSize size) { return munmap(address, size); } drmBufInfoPtr drmGetBufInfo(int fd) { drm_buf_info_t info; drmBufInfoPtr retval; int i; info.count = 0; info.list = NULL; if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) return NULL; if (info.count) { if (!(info.list = drmMalloc(info.count * sizeof(*info.list)))) return NULL; if (ioctl(fd, DRM_IOCTL_INFO_BUFS, &info)) { drmFree(info.list); return NULL; } /* Now, copy it all back into the client-visible data structure... */ retval = drmMalloc(sizeof(*retval)); retval->count = info.count; retval->list = drmMalloc(info.count * sizeof(*retval->list)); for (i = 0; i < info.count; i++) { retval->list[i].count = info.list[i].count; retval->list[i].size = info.list[i].size; retval->list[i].low_mark = info.list[i].low_mark; retval->list[i].high_mark = info.list[i].high_mark; } drmFree(info.list); return retval; } return NULL; } drmBufMapPtr drmMapBufs(int fd) { drm_buf_map_t bufs; drmBufMapPtr retval; int i; bufs.count = 0; bufs.list = NULL; if (ioctl(fd, DRM_IOCTL_MAP_BUFS, &bufs)) return NULL; if (bufs.count) { if (!(bufs.list = drmMalloc(bufs.count * sizeof(*bufs.list)))) return NULL; if (ioctl(fd, DRM_IOCTL_MAP_BUFS, &bufs)) { drmFree(bufs.list); return NULL; } /* Now, copy it all back into the client-visible data structure... */ retval = drmMalloc(sizeof(*retval)); retval->count = bufs.count; retval->list = drmMalloc(bufs.count * sizeof(*retval->list)); for (i = 0; i < bufs.count; i++) { retval->list[i].idx = bufs.list[i].idx; retval->list[i].total = bufs.list[i].total; retval->list[i].used = 0; retval->list[i].address = bufs.list[i].address; } return retval; } return NULL; } int drmUnmapBufs(drmBufMapPtr bufs) { int i; for (i = 0; i < bufs->count; i++) { munmap(bufs->list[i].address, bufs->list[i].total); } return 0; } #define DRM_DMA_RETRY 16 int drmDMA(int fd, drmDMAReqPtr request) { drm_dma_t dma; int ret, i = 0; /* Copy to hidden structure */ dma.context = request->context; dma.send_count = request->send_count; dma.send_indices = request->send_list; dma.send_sizes = request->send_sizes; dma.flags = request->flags; dma.request_count = request->request_count; dma.request_size = request->request_size; dma.request_indices = request->request_list; dma.request_sizes = request->request_sizes; do { ret = ioctl( fd, DRM_IOCTL_DMA, &dma ); } while ( ret && errno == EAGAIN && i++ < DRM_DMA_RETRY ); if ( ret == 0 ) { request->granted_count = dma.granted_count; return 0; } else { return -errno; } } int drmGetLock(int fd, drmContext context, drmLockFlags flags) { drm_lock_t lock; lock.context = context; lock.flags = 0; if (flags & DRM_LOCK_READY) lock.flags |= _DRM_LOCK_READY; if (flags & DRM_LOCK_QUIESCENT) lock.flags |= _DRM_LOCK_QUIESCENT; if (flags & DRM_LOCK_FLUSH) lock.flags |= _DRM_LOCK_FLUSH; if (flags & DRM_LOCK_FLUSH_ALL) lock.flags |= _DRM_LOCK_FLUSH_ALL; if (flags & DRM_HALT_ALL_QUEUES) lock.flags |= _DRM_HALT_ALL_QUEUES; if (flags & DRM_HALT_CUR_QUEUES) lock.flags |= _DRM_HALT_CUR_QUEUES; while (ioctl(fd, DRM_IOCTL_LOCK, &lock)) ; return 0; } int drmUnlock(int fd, drmContext context) { drm_lock_t lock; lock.context = context; lock.flags = 0; return ioctl(fd, DRM_IOCTL_UNLOCK, &lock); } drmContextPtr drmGetReservedContextList(int fd, int *count) { drm_ctx_res_t res; drm_ctx_t *list; drmContextPtr retval; int i; res.count = 0; res.contexts = NULL; if (ioctl(fd, DRM_IOCTL_RES_CTX, &res)) return NULL; if (!res.count) return NULL; if (!(list = drmMalloc(res.count * sizeof(*list)))) return NULL; if (!(retval = drmMalloc(res.count * sizeof(*retval)))) { drmFree(list); return NULL; } res.contexts = list; if (ioctl(fd, DRM_IOCTL_RES_CTX, &res)) return NULL; for (i = 0; i < res.count; i++) retval[i] = list[i].handle; drmFree(list); *count = res.count; return retval; } void drmFreeReservedContextList(drmContextPtr pt) { drmFree(pt); } int drmCreateContext(int fd, drmContextPtr handle) { drm_ctx_t ctx; ctx.flags = 0; /* Modified with functions below */ if (ioctl(fd, DRM_IOCTL_ADD_CTX, &ctx)) return -errno; *handle = ctx.handle; return 0; } int drmSwitchToContext(int fd, drmContext context) { drm_ctx_t ctx; ctx.handle = context; if (ioctl(fd, DRM_IOCTL_SWITCH_CTX, &ctx)) return -errno; return 0; } int drmSetContextFlags(int fd, drmContext context, drmContextFlags flags) { drm_ctx_t ctx; /* Context preserving means that no context switched are done between DMA buffers from one context and the next. This is suitable for use in the X server (which promises to maintain hardware context, or in the client-side library when buffers are swapped on behalf of two threads. */ ctx.handle = context; ctx.flags = 0; if (flags & DRM_CONTEXT_PRESERVED) ctx.flags |= _DRM_CONTEXT_PRESERVED; if (flags & DRM_CONTEXT_2DONLY) ctx.flags |= _DRM_CONTEXT_2DONLY; if (ioctl(fd, DRM_IOCTL_MOD_CTX, &ctx)) return -errno; return 0; } int drmGetContextFlags(int fd, drmContext context, drmContextFlagsPtr flags) { drm_ctx_t ctx; ctx.handle = context; if (ioctl(fd, DRM_IOCTL_GET_CTX, &ctx)) return -errno; *flags = 0; if (ctx.flags & _DRM_CONTEXT_PRESERVED) *flags |= DRM_CONTEXT_PRESERVED; if (ctx.flags & _DRM_CONTEXT_2DONLY) *flags |= DRM_CONTEXT_2DONLY; return 0; } int drmDestroyContext(int fd, drmContext handle) { drm_ctx_t ctx; ctx.handle = handle; if (ioctl(fd, DRM_IOCTL_RM_CTX, &ctx)) return -errno; return 0; } int drmCreateDrawable(int fd, drmDrawablePtr handle) { drm_draw_t draw; if (ioctl(fd, DRM_IOCTL_ADD_DRAW, &draw)) return -errno; *handle = draw.handle; return 0; } int drmDestroyDrawable(int fd, drmDrawable handle) { drm_draw_t draw; draw.handle = handle; if (ioctl(fd, DRM_IOCTL_RM_DRAW, &draw)) return -errno; return 0; } int drmAgpAcquire(int fd) { if (ioctl(fd, DRM_IOCTL_AGP_ACQUIRE, NULL)) return -errno; return 0; } int drmAgpRelease(int fd) { if (ioctl(fd, DRM_IOCTL_AGP_RELEASE, NULL)) return -errno; return 0; } int drmAgpEnable(int fd, unsigned long mode) { drm_agp_mode_t m; m.mode = mode; if (ioctl(fd, DRM_IOCTL_AGP_ENABLE, &m)) return -errno; return 0; } int drmAgpAlloc(int fd, unsigned long size, unsigned long type, unsigned long *address, unsigned long *handle) { drm_agp_buffer_t b; *handle = 0; b.size = size; b.handle = 0; b.type = type; if (ioctl(fd, DRM_IOCTL_AGP_ALLOC, &b)) return -errno; if (address != 0UL) *address = b.physical; *handle = b.handle; return 0; } int drmAgpFree(int fd, unsigned long handle) { drm_agp_buffer_t b; b.size = 0; b.handle = handle; if (ioctl(fd, DRM_IOCTL_AGP_FREE, &b)) return -errno; return 0; } int drmAgpBind(int fd, unsigned long handle, unsigned long offset) { drm_agp_binding_t b; b.handle = handle; b.offset = offset; if (ioctl(fd, DRM_IOCTL_AGP_BIND, &b)) return -errno; return 0; } int drmAgpUnbind(int fd, unsigned long handle) { drm_agp_binding_t b; b.handle = handle; b.offset = 0; if (ioctl(fd, DRM_IOCTL_AGP_UNBIND, &b)) return -errno; return 0; } int drmAgpVersionMajor(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return -errno; return i.agp_version_major; } int drmAgpVersionMinor(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return -errno; return i.agp_version_minor; } unsigned long drmAgpGetMode(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.mode; } unsigned long drmAgpBase(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.aperture_base; } unsigned long drmAgpSize(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.aperture_size; } unsigned long drmAgpMemoryUsed(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.memory_used; } unsigned long drmAgpMemoryAvail(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.memory_allowed; } unsigned int drmAgpVendorId(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.id_vendor; } unsigned int drmAgpDeviceId(int fd) { drm_agp_info_t i; if (ioctl(fd, DRM_IOCTL_AGP_INFO, &i)) return 0; return i.id_device; } int drmScatterGatherAlloc(int fd, unsigned long size, unsigned long *handle) { drm_scatter_gather_t sg; *handle = 0; sg.size = size; sg.handle = 0; if (ioctl(fd, DRM_IOCTL_SG_ALLOC, &sg)) return -errno; *handle = sg.handle; return 0; } int drmScatterGatherFree(int fd, unsigned long handle) { drm_scatter_gather_t sg; sg.size = 0; sg.handle = handle; if (ioctl(fd, DRM_IOCTL_SG_FREE, &sg)) return -errno; return 0; } int drmWaitVBlank(int fd, drmVBlankPtr vbl) { int ret; do { ret = ioctl(fd, DRM_IOCTL_WAIT_VBLANK, vbl); } while (ret && errno == EINTR); return ret; } int drmError(int err, const char *label) { switch (err) { case DRM_ERR_NO_DEVICE: fprintf(stderr, "%s: no device\n", label); break; case DRM_ERR_NO_ACCESS: fprintf(stderr, "%s: no access\n", label); break; case DRM_ERR_NOT_ROOT: fprintf(stderr, "%s: not root\n", label); break; case DRM_ERR_INVALID: fprintf(stderr, "%s: invalid args\n", label);break; default: if (err < 0) err = -err; fprintf( stderr, "%s: error %d (%s)\n", label, err, strerror(err) ); break; } return 1; } int drmCtlInstHandler(int fd, int irq) { drm_control_t ctl; ctl.func = DRM_INST_HANDLER; ctl.irq = irq; if (ioctl(fd, DRM_IOCTL_CONTROL, &ctl)) return -errno; return 0; } int drmCtlUninstHandler(int fd) { drm_control_t ctl; ctl.func = DRM_UNINST_HANDLER; ctl.irq = 0; if (ioctl(fd, DRM_IOCTL_CONTROL, &ctl)) return -errno; return 0; } int drmFinish(int fd, int context, drmLockFlags flags) { drm_lock_t lock; lock.context = context; lock.flags = 0; if (flags & DRM_LOCK_READY) lock.flags |= _DRM_LOCK_READY; if (flags & DRM_LOCK_QUIESCENT) lock.flags |= _DRM_LOCK_QUIESCENT; if (flags & DRM_LOCK_FLUSH) lock.flags |= _DRM_LOCK_FLUSH; if (flags & DRM_LOCK_FLUSH_ALL) lock.flags |= _DRM_LOCK_FLUSH_ALL; if (flags & DRM_HALT_ALL_QUEUES) lock.flags |= _DRM_HALT_ALL_QUEUES; if (flags & DRM_HALT_CUR_QUEUES) lock.flags |= _DRM_HALT_CUR_QUEUES; if (ioctl(fd, DRM_IOCTL_FINISH, &lock)) return -errno; return 0; } int drmGetInterruptFromBusID(int fd, int busnum, int devnum, int funcnum) { drm_irq_busid_t p; p.busnum = busnum; p.devnum = devnum; p.funcnum = funcnum; if (ioctl(fd, DRM_IOCTL_IRQ_BUSID, &p)) return -errno; return p.irq; } int drmAddContextTag(int fd, drmContext context, void *tag) { drmHashEntry *entry = drmGetEntry(fd); if (drmHashInsert(entry->tagTable, context, tag)) { drmHashDelete(entry->tagTable, context); drmHashInsert(entry->tagTable, context, tag); } return 0; } int drmDelContextTag(int fd, drmContext context) { drmHashEntry *entry = drmGetEntry(fd); return drmHashDelete(entry->tagTable, context); } void *drmGetContextTag(int fd, drmContext context) { drmHashEntry *entry = drmGetEntry(fd); void *value; if (drmHashLookup(entry->tagTable, context, &value)) return NULL; return value; } int drmAddContextPrivateMapping(int fd, drmContext ctx_id, drmHandle handle) { drm_ctx_priv_map_t map; map.ctx_id = ctx_id; map.handle = (void *)handle; if (ioctl(fd, DRM_IOCTL_SET_SAREA_CTX, &map)) return -errno; return 0; } int drmGetContextPrivateMapping(int fd, drmContext ctx_id, drmHandlePtr handle) { drm_ctx_priv_map_t map; map.ctx_id = ctx_id; if (ioctl(fd, DRM_IOCTL_GET_SAREA_CTX, &map)) return -errno; if (handle) *handle = (drmHandle)map.handle; return 0; } int drmGetMap(int fd, int idx, drmHandle *offset, drmSize *size, drmMapType *type, drmMapFlags *flags, drmHandle *handle, int *mtrr) { drm_map_t map; map.offset = idx; if (ioctl(fd, DRM_IOCTL_GET_MAP, &map)) return -errno; *offset = map.offset; *size = map.size; *type = map.type; *flags = map.flags; *handle = (unsigned long)map.handle; *mtrr = map.mtrr; return 0; } int drmGetClient(int fd, int idx, int *auth, int *pid, int *uid, unsigned long *magic, unsigned long *iocs) { drm_client_t client; client.idx = idx; if (ioctl(fd, DRM_IOCTL_GET_CLIENT, &client)) return -errno; *auth = client.auth; *pid = client.pid; *uid = client.uid; *magic = client.magic; *iocs = client.iocs; return 0; } int drmGetStats(int fd, drmStatsT *stats) { drm_stats_t s; int i; if (ioctl(fd, DRM_IOCTL_GET_STATS, &s)) return -errno; stats->count = 0; memset(stats, 0, sizeof(*stats)); if (s.count > sizeof(stats->data)/sizeof(stats->data[0])) return -1; #define SET_VALUE \ stats->data[i].long_format = "%-20.20s"; \ stats->data[i].rate_format = "%8.8s"; \ stats->data[i].isvalue = 1; \ stats->data[i].verbose = 0 #define SET_COUNT \ stats->data[i].long_format = "%-20.20s"; \ stats->data[i].rate_format = "%5.5s"; \ stats->data[i].isvalue = 0; \ stats->data[i].mult_names = "kgm"; \ stats->data[i].mult = 1000; \ stats->data[i].verbose = 0 #define SET_BYTE \ stats->data[i].long_format = "%-20.20s"; \ stats->data[i].rate_format = "%5.5s"; \ stats->data[i].isvalue = 0; \ stats->data[i].mult_names = "KGM"; \ stats->data[i].mult = 1024; \ stats->data[i].verbose = 0 stats->count = s.count; for (i = 0; i < s.count; i++) { stats->data[i].value = s.data[i].value; switch (s.data[i].type) { case _DRM_STAT_LOCK: stats->data[i].long_name = "Lock"; stats->data[i].rate_name = "Lock"; SET_VALUE; break; case _DRM_STAT_OPENS: stats->data[i].long_name = "Opens"; stats->data[i].rate_name = "O"; SET_COUNT; stats->data[i].verbose = 1; break; case _DRM_STAT_CLOSES: stats->data[i].long_name = "Closes"; stats->data[i].rate_name = "Lock"; SET_COUNT; stats->data[i].verbose = 1; break; case _DRM_STAT_IOCTLS: stats->data[i].long_name = "Ioctls"; stats->data[i].rate_name = "Ioc/s"; SET_COUNT; break; case _DRM_STAT_LOCKS: stats->data[i].long_name = "Locks"; stats->data[i].rate_name = "Lck/s"; SET_COUNT; break; case _DRM_STAT_UNLOCKS: stats->data[i].long_name = "Unlocks"; stats->data[i].rate_name = "Unl/s"; SET_COUNT; break; case _DRM_STAT_IRQ: stats->data[i].long_name = "IRQs"; stats->data[i].rate_name = "IRQ/s"; SET_COUNT; break; case _DRM_STAT_PRIMARY: stats->data[i].long_name = "Primary Bytes"; stats->data[i].rate_name = "PB/s"; SET_BYTE; break; case _DRM_STAT_SECONDARY: stats->data[i].long_name = "Secondary Bytes"; stats->data[i].rate_name = "SB/s"; SET_BYTE; break; case _DRM_STAT_DMA: stats->data[i].long_name = "DMA"; stats->data[i].rate_name = "DMA/s"; SET_COUNT; break; case _DRM_STAT_SPECIAL: stats->data[i].long_name = "Special DMA"; stats->data[i].rate_name = "dma/s"; SET_COUNT; break; case _DRM_STAT_MISSED: stats->data[i].long_name = "Miss"; stats->data[i].rate_name = "Ms/s"; SET_COUNT; break; case _DRM_STAT_VALUE: stats->data[i].long_name = "Value"; stats->data[i].rate_name = "Value"; SET_VALUE; break; case _DRM_STAT_BYTE: stats->data[i].long_name = "Bytes"; stats->data[i].rate_name = "B/s"; SET_BYTE; break; case _DRM_STAT_COUNT: default: stats->data[i].long_name = "Count"; stats->data[i].rate_name = "Cnt/s"; SET_COUNT; break; } } return 0; } int drmCommandNone(int fd, unsigned long drmCommandIndex) { void *data = NULL; /* dummy */ unsigned long request; request = DRM_IO( DRM_COMMAND_BASE + drmCommandIndex); if (ioctl(fd, request, data)) { return -errno; } return 0; } int drmCommandRead(int fd, unsigned long drmCommandIndex, void *data, unsigned long size ) { unsigned long request; request = DRM_IOC( DRM_IOC_READ, DRM_IOCTL_BASE, DRM_COMMAND_BASE + drmCommandIndex, size); if (ioctl(fd, request, data)) { return -errno; } return 0; } int drmCommandWrite(int fd, unsigned long drmCommandIndex, void *data, unsigned long size ) { unsigned long request; request = DRM_IOC( DRM_IOC_WRITE, DRM_IOCTL_BASE, DRM_COMMAND_BASE + drmCommandIndex, size); if (ioctl(fd, request, data)) { return -errno; } return 0; } int drmCommandWriteRead(int fd, unsigned long drmCommandIndex, void *data, unsigned long size ) { unsigned long request; request = DRM_IOC( DRM_IOC_READ|DRM_IOC_WRITE, DRM_IOCTL_BASE, DRM_COMMAND_BASE + drmCommandIndex, size); if (ioctl(fd, request, data)) { return -errno; } return 0; } #if defined(XFree86Server) || defined(DRM_USE_MALLOC) static void drmSIGIOHandler(int interrupt, void *closure) { unsigned long key; void *value; ssize_t count; drm_ctx_t ctx; typedef void (*_drmCallback)(int, void *, void *); char buf[256]; drmContext old; drmContext new; void *oldctx; void *newctx; char *pt; drmHashEntry *entry; if (!drmHashTable) return; if (drmHashFirst(drmHashTable, &key, &value)) { entry = value; do { #if 0 fprintf(stderr, "Trying %d\n", entry->fd); #endif if ((count = read(entry->fd, buf, sizeof(buf)))) { buf[count] = '\0'; #if 0 fprintf(stderr, "Got %s\n", buf); #endif for (pt = buf; *pt != ' '; ++pt); /* Find first space */ ++pt; old = strtol(pt, &pt, 0); new = strtol(pt, NULL, 0); oldctx = drmGetContextTag(entry->fd, old); newctx = drmGetContextTag(entry->fd, new); #if 0 fprintf(stderr, "%d %d %p %p\n", old, new, oldctx, newctx); #endif ((_drmCallback)entry->f)(entry->fd, oldctx, newctx); ctx.handle = new; ioctl(entry->fd, DRM_IOCTL_NEW_CTX, &ctx); } } while (drmHashNext(drmHashTable, &key, &value)); } } int drmInstallSIGIOHandler(int fd, void (*f)(int, void *, void *)) { drmHashEntry *entry; entry = drmGetEntry(fd); entry->f = f; return xf86InstallSIGIOHandler(fd, drmSIGIOHandler, 0); } int drmRemoveSIGIOHandler(int fd) { drmHashEntry *entry = drmGetEntry(fd); entry->f = NULL; return xf86RemoveSIGIOHandler(fd); } #endif >1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343
/* mach64_dma.c -- DMA support for mach64 (Rage Pro) driver -*- linux-c -*-
 * Created: Sun Dec 03 19:20:26 2000 by gareth@valinux.com
 *
 * Copyright 2000 Gareth Hughes
 * Copyright 2002 Frank C. Earl
 * Copyright 2002-2003 Leif Delgass
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT OWNER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *   Gareth Hughes <gareth@valinux.com>
 *   Frank C. Earl <fearl@airmail.net>
 *   Leif Delgass <ldelgass@retinalburn.net>
 *   José Fonseca <j_r_fonseca@yahoo.co.uk>
 */

#include "mach64.h"
#include "drmP.h"
#include "drm.h"
#include "mach64_drm.h"
#include "mach64_drv.h"


/* ================================================================
 * Engine, FIFO control
 */

int mach64_do_wait_for_fifo( drm_mach64_private_t *dev_priv, int entries )
{
	int slots = 0, i;

	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
		slots = (MACH64_READ( MACH64_FIFO_STAT ) &
			 MACH64_FIFO_SLOT_MASK);
		if ( slots <= (0x8000 >> entries) ) return 0;
		DRM_UDELAY( 1 );
	}

	DRM_INFO( "%s failed! slots=%d entries=%d\n", __FUNCTION__, slots, entries );
	return DRM_ERR(EBUSY);
}

int mach64_do_wait_for_idle( drm_mach64_private_t *dev_priv )
{
	int i, ret;

	ret = mach64_do_wait_for_fifo( dev_priv, 16 );
	if ( ret < 0 ) return ret;

	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
		if ( !(MACH64_READ( MACH64_GUI_STAT ) & MACH64_GUI_ACTIVE) ) {
			return 0;
		}
		DRM_UDELAY( 1 );
	}

	DRM_INFO( "%s failed! GUI_STAT=0x%08x\n", __FUNCTION__, 
		   MACH64_READ( MACH64_GUI_STAT ) );
	mach64_dump_ring_info( dev_priv );
	return DRM_ERR(EBUSY);
}

int mach64_wait_ring( drm_mach64_private_t *dev_priv, int n )
{
	drm_mach64_descriptor_ring_t *ring = &dev_priv->ring;
	int i;

	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
		mach64_update_ring_snapshot( dev_priv );
		if ( ring->space >= n ) {
			if (i > 0) {
				DRM_DEBUG( "%s: %d usecs\n", __FUNCTION__, i );
			}
			return 0;
		}
		DRM_UDELAY( 1 );
	}

	/* FIXME: This is being ignored... */
	DRM_ERROR( "failed!\n" );
	mach64_dump_ring_info( dev_priv );
	return DRM_ERR(EBUSY);
}

/* Wait until all DMA requests have been processed... */
static int mach64_ring_idle( drm_mach64_private_t *dev_priv )
{
	drm_mach64_descriptor_ring_t *ring = &dev_priv->ring;
	u32 head;
	int i;

	head = ring->head;
	i = 0;
	while ( i < dev_priv->usec_timeout ) {
		mach64_update_ring_snapshot( dev_priv );
		if ( ring->head == ring->tail && 
		     !(MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE) ) {
			if (i > 0) {
				DRM_DEBUG( "%s: %d usecs\n", __FUNCTION__, i );
			}
			return 0;
		} 
		if ( ring->head == head ) {
			++i;
		} else {
			head = ring->head;
			i = 0;
		}
		DRM_UDELAY( 1 );
	}

	DRM_INFO( "%s failed! GUI_STAT=0x%08x\n", __FUNCTION__, 
		   MACH64_READ( MACH64_GUI_STAT ) );
	mach64_dump_ring_info( dev_priv );
	return DRM_ERR(EBUSY);
}

static void mach64_ring_reset( drm_mach64_private_t *dev_priv )
{
	drm_mach64_descriptor_ring_t *ring = &dev_priv->ring;

	mach64_do_release_used_buffers( dev_priv );
	ring->head_addr = ring->start_addr;
	ring->head = ring->tail = 0;
	ring->space = ring->size;
	
	MACH64_WRITE( MACH64_BM_GUI_TABLE_CMD, 
		      ring->head_addr | MACH64_CIRCULAR_BUF_SIZE_16KB );

	dev_priv->ring_running = 0;
}

int mach64_do_dma_flush( drm_mach64_private_t *dev_priv )
{
	/* FIXME: It's not necessary to wait for idle when flushing 
	 * we just need to ensure the ring will be completely processed
	 * in finite time without another ioctl
	 */
	return mach64_ring_idle( dev_priv );
}

int mach64_do_dma_idle( drm_mach64_private_t *dev_priv )
{
	int ret;

	/* wait for completion */
	if ( (ret = mach64_ring_idle( dev_priv )) < 0 ) {
		DRM_ERROR( "%s failed BM_GUI_TABLE=0x%08x tail: %u\n", __FUNCTION__, 
			   MACH64_READ(MACH64_BM_GUI_TABLE), dev_priv->ring.tail );
		return ret;
	}

	mach64_ring_stop( dev_priv );

	/* clean up after pass */
	mach64_do_release_used_buffers( dev_priv );
	return 0;
}

/* Reset the engine.  This will stop the DMA if it is running.
 */
int mach64_do_engine_reset( drm_mach64_private_t *dev_priv )
{
	u32 tmp;

	DRM_DEBUG( "%s\n", __FUNCTION__ );

	/* Kill off any outstanding DMA transfers.
	 */
	tmp = MACH64_READ( MACH64_BUS_CNTL );
	MACH64_WRITE( MACH64_BUS_CNTL,
				  tmp | MACH64_BUS_MASTER_DIS );
	
	/* Reset the GUI engine (high to low transition).
	 */
	tmp = MACH64_READ( MACH64_GEN_TEST_CNTL );
	MACH64_WRITE( MACH64_GEN_TEST_CNTL,
				  tmp & ~MACH64_GUI_ENGINE_ENABLE );
	/* Enable the GUI engine
	 */
	tmp = MACH64_READ( MACH64_GEN_TEST_CNTL );
	MACH64_WRITE( MACH64_GEN_TEST_CNTL,
				  tmp | MACH64_GUI_ENGINE_ENABLE );

	/* ensure engine is not locked up by clearing any FIFO or HOST errors
	*/
	tmp = MACH64_READ( MACH64_BUS_CNTL );
	MACH64_WRITE( MACH64_BUS_CNTL, tmp | 0x00a00000 );

	/* Once GUI engine is restored, disable bus mastering */
	MACH64_WRITE( MACH64_SRC_CNTL, 0 );

	/* Reset descriptor ring */
	mach64_ring_reset( dev_priv );
	
	return 0;
}

/* ================================================================
 * Debugging output
 */

void mach64_dump_engine_info( drm_mach64_private_t *dev_priv )
{
	DRM_INFO( "\n" );
	if ( !dev_priv->is_pci )
	{
		DRM_INFO( "           AGP_BASE = 0x%08x\n", MACH64_READ( MACH64_AGP_BASE ) );
		DRM_INFO( "           AGP_CNTL = 0x%08x\n", MACH64_READ( MACH64_AGP_CNTL ) );
	}
	DRM_INFO( "     ALPHA_TST_CNTL = 0x%08x\n", MACH64_READ( MACH64_ALPHA_TST_CNTL ) );
	DRM_INFO( "\n" );
	DRM_INFO( "         BM_COMMAND = 0x%08x\n", MACH64_READ( MACH64_BM_COMMAND ) );
	DRM_INFO( "BM_FRAME_BUF_OFFSET = 0x%08x\n", MACH64_READ( MACH64_BM_FRAME_BUF_OFFSET ) );
	DRM_INFO( "       BM_GUI_TABLE = 0x%08x\n", MACH64_READ( MACH64_BM_GUI_TABLE ) );
	DRM_INFO( "          BM_STATUS = 0x%08x\n", MACH64_READ( MACH64_BM_STATUS ) );
	DRM_INFO( " BM_SYSTEM_MEM_ADDR = 0x%08x\n", MACH64_READ( MACH64_BM_SYSTEM_MEM_ADDR ) );
	DRM_INFO( "    BM_SYSTEM_TABLE = 0x%08x\n", MACH64_READ( MACH64_BM_SYSTEM_TABLE ) );
	DRM_INFO( "           BUS_CNTL = 0x%08x\n", MACH64_READ( MACH64_BUS_CNTL ) );
	DRM_INFO( "\n" );
	/* DRM_INFO( "         CLOCK_CNTL = 0x%08x\n", MACH64_READ( MACH64_CLOCK_CNTL ) ); */
	DRM_INFO( "        CLR_CMP_CLR = 0x%08x\n", MACH64_READ( MACH64_CLR_CMP_CLR ) );
	DRM_INFO( "       CLR_CMP_CNTL = 0x%08x\n", MACH64_READ( MACH64_CLR_CMP_CNTL ) );
	/* DRM_INFO( "        CLR_CMP_MSK = 0x%08x\n", MACH64_READ( MACH64_CLR_CMP_MSK ) ); */
	DRM_INFO( "     CONFIG_CHIP_ID = 0x%08x\n", MACH64_READ( MACH64_CONFIG_CHIP_ID ) );
	DRM_INFO( "        CONFIG_CNTL = 0x%08x\n", MACH64_READ( MACH64_CONFIG_CNTL ) );
	DRM_INFO( "       CONFIG_STAT0 = 0x%08x\n", MACH64_READ( MACH64_CONFIG_STAT0 ) );
	DRM_INFO( "       CONFIG_STAT1 = 0x%08x\n", MACH64_READ( MACH64_CONFIG_STAT1 ) );
	DRM_INFO( "       CONFIG_STAT2 = 0x%08x\n", MACH64_READ( MACH64_CONFIG_STAT2 ) );
	DRM_INFO( "            CRC_SIG = 0x%08x\n", MACH64_READ( MACH64_CRC_SIG ) );
	DRM_INFO( "  CUSTOM_MACRO_CNTL = 0x%08x\n", MACH64_READ( MACH64_CUSTOM_MACRO_CNTL ) );
	DRM_INFO( "\n" );
	/* DRM_INFO( "           DAC_CNTL = 0x%08x\n", MACH64_READ( MACH64_DAC_CNTL ) ); */
	/* DRM_INFO( "           DAC_REGS = 0x%08x\n", MACH64_READ( MACH64_DAC_REGS ) ); */
	DRM_INFO( "        DP_BKGD_CLR = 0x%08x\n", MACH64_READ( MACH64_DP_BKGD_CLR ) );
	DRM_INFO( "        DP_FRGD_CLR = 0x%08x\n", MACH64_READ( MACH64_DP_FRGD_CLR ) );
	DRM_INFO( "             DP_MIX = 0x%08x\n", MACH64_READ( MACH64_DP_MIX ) );
	DRM_INFO( "       DP_PIX_WIDTH = 0x%08x\n", MACH64_READ( MACH64_DP_PIX_WIDTH ) );
	DRM_INFO( "             DP_SRC = 0x%08x\n", MACH64_READ( MACH64_DP_SRC ) );
	DRM_INFO( "      DP_WRITE_MASK = 0x%08x\n", MACH64_READ( MACH64_DP_WRITE_MASK ) );
	DRM_INFO( "         DSP_CONFIG = 0x%08x\n", MACH64_READ( MACH64_DSP_CONFIG ) );
	DRM_INFO( "         DSP_ON_OFF = 0x%08x\n", MACH64_READ( MACH64_DSP_ON_OFF ) );
	DRM_INFO( "           DST_CNTL = 0x%08x\n", MACH64_READ( MACH64_DST_CNTL ) );
	DRM_INFO( "      DST_OFF_PITCH = 0x%08x\n", MACH64_READ( MACH64_DST_OFF_PITCH ) );
	DRM_INFO( "\n" );
	/* DRM_INFO( "       EXT_DAC_REGS = 0x%08x\n", MACH64_READ( MACH64_EXT_DAC_REGS ) ); */
	DRM_INFO( "       EXT_MEM_CNTL = 0x%08x\n", MACH64_READ( MACH64_EXT_MEM_CNTL ) );
	DRM_INFO( "\n" );
	DRM_INFO( "          FIFO_STAT = 0x%08x\n", MACH64_READ( MACH64_FIFO_STAT ) );
	DRM_INFO( "\n" );
	DRM_INFO( "      GEN_TEST_CNTL = 0x%08x\n", MACH64_READ( MACH64_GEN_TEST_CNTL ) );
	/* DRM_INFO( "              GP_IO = 0x%08x\n", MACH64_READ( MACH64_GP_IO ) ); */
	DRM_INFO( "   GUI_CMDFIFO_DATA = 0x%08x\n", MACH64_READ( MACH64_GUI_CMDFIFO_DATA ) );
	DRM_INFO( "  GUI_CMDFIFO_DEBUG = 0x%08x\n", MACH64_READ( MACH64_GUI_CMDFIFO_DEBUG ) );
	DRM_INFO( "           GUI_CNTL = 0x%08x\n", MACH64_READ( MACH64_GUI_CNTL ) );
	DRM_INFO( "           GUI_STAT = 0x%08x\n", MACH64_READ( MACH64_GUI_STAT ) );
	DRM_INFO( "      GUI_TRAJ_CNTL = 0x%08x\n", MACH64_READ( MACH64_GUI_TRAJ_CNTL ) );
	DRM_INFO( "\n" );
	DRM_INFO( "          HOST_CNTL = 0x%08x\n", MACH64_READ( MACH64_HOST_CNTL ) );
	DRM_INFO( "           HW_DEBUG = 0x%08x\n", MACH64_READ( MACH64_HW_DEBUG ) );
	DRM_INFO( "\n" );
	DRM_INFO( "    MEM_ADDR_CONFIG = 0x%08x\n", MACH64_READ( MACH64_MEM_ADDR_CONFIG ) );
	DRM_INFO( "       MEM_BUF_CNTL = 0x%08x\n", MACH64_READ( MACH64_MEM_BUF_CNTL ) );
	DRM_INFO( "\n" );
	DRM_INFO( "           PAT_REG0 = 0x%08x\n", MACH64_READ( MACH64_PAT_REG0 ) );
	DRM_INFO( "           PAT_REG1 = 0x%08x\n", MACH64_READ( MACH64_PAT_REG1 ) );
	DRM_INFO( "\n" );
	DRM_INFO( "            SC_LEFT = 0x%08x\n", MACH64_READ( MACH64_SC_LEFT ) );
	DRM_INFO( "           SC_RIGHT = 0x%08x\n", MACH64_READ( MACH64_SC_RIGHT ) );
	DRM_INFO( "             SC_TOP = 0x%08x\n", MACH64_READ( MACH64_SC_TOP ) );
	DRM_INFO( "          SC_BOTTOM = 0x%08x\n", MACH64_READ( MACH64_SC_BOTTOM ) );
	DRM_INFO( "\n" );
	DRM_INFO( "      SCALE_3D_CNTL = 0x%08x\n", MACH64_READ( MACH64_SCALE_3D_CNTL ) );
	DRM_INFO( "       SCRATCH_REG0 = 0x%08x\n", MACH64_READ( MACH64_SCRATCH_REG0 ) );
	DRM_INFO( "       SCRATCH_REG1 = 0x%08x\n", MACH64_READ( MACH64_SCRATCH_REG1 ) );
	DRM_INFO( "         SETUP_CNTL = 0x%08x\n", MACH64_READ( MACH64_SETUP_CNTL ) );
	DRM_INFO( "           SRC_CNTL = 0x%08x\n", MACH64_READ( MACH64_SRC_CNTL ) );
	DRM_INFO( "\n" );
	DRM_INFO( "           TEX_CNTL = 0x%08x\n", MACH64_READ( MACH64_TEX_CNTL ) );
	DRM_INFO( "     TEX_SIZE_PITCH = 0x%08x\n", MACH64_READ( MACH64_TEX_SIZE_PITCH ) );
	DRM_INFO( "       TIMER_CONFIG = 0x%08x\n", MACH64_READ( MACH64_TIMER_CONFIG ) );
	DRM_INFO( "\n" );
	DRM_INFO( "             Z_CNTL = 0x%08x\n", MACH64_READ( MACH64_Z_CNTL ) );
	DRM_INFO( "        Z_OFF_PITCH = 0x%08x\n", MACH64_READ( MACH64_Z_OFF_PITCH ) );
	DRM_INFO( "\n" );
}

#define MACH64_DUMP_CONTEXT	3

static void mach64_dump_buf_info( drm_mach64_private_t *dev_priv, drm_buf_t *buf)
{
	u32 addr = GETBUFADDR( buf );
	u32 used = buf->used >> 2;
	u32 sys_addr = MACH64_READ( MACH64_BM_SYSTEM_MEM_ADDR );
	u32 *p = GETBUFPTR( buf );
	int skipped = 0;

	DRM_INFO( "buffer contents:\n" );
	
	while ( used ) {
		u32 reg, count;

		reg = le32_to_cpu(*p++);
		if( addr <= GETBUFADDR( buf ) + MACH64_DUMP_CONTEXT * 4 ||
		    (addr >= sys_addr - MACH64_DUMP_CONTEXT * 4 &&
		    addr <= sys_addr + MACH64_DUMP_CONTEXT * 4) ||
		    addr >= GETBUFADDR( buf ) + buf->used - MACH64_DUMP_CONTEXT * 4) {
			DRM_INFO("%08x:  0x%08x\n", addr, reg);
		}
		addr += 4;
		used--;
		
		count = (reg >> 16) + 1;
		reg = reg & 0xffff;
		reg = MMSELECT( reg );
		while ( count && used ) {
			if( addr <= GETBUFADDR( buf ) + MACH64_DUMP_CONTEXT * 4 ||
			    (addr >= sys_addr - MACH64_DUMP_CONTEXT * 4 &&
			    addr <= sys_addr + MACH64_DUMP_CONTEXT * 4) ||
			    addr >= GETBUFADDR( buf ) + buf->used - MACH64_DUMP_CONTEXT * 4) {
				DRM_INFO("%08x:    0x%04x = 0x%08x\n", addr, reg, le32_to_cpu(*p));
				skipped = 0;
			} else {
				if( !skipped ) {
					DRM_INFO( "  ...\n" );
					skipped = 1;
				}
			}
			p++;
			addr += 4;
			used--;
			
			reg += 4;
			count--;
		}
	}
	
	DRM_INFO( "\n" );
}

void mach64_dump_ring_info( drm_mach64_private_t *dev_priv )
{
	drm_mach64_descriptor_ring_t *ring = &dev_priv->ring;
	int i, skipped;
	
	DRM_INFO( "\n" );
	
	DRM_INFO("ring contents:\n");
	DRM_INFO("  head_addr: 0x%08x head: %u tail: %u\n\n", 
		 ring->head_addr, ring->head, ring->tail );
	
	skipped = 0;
	for ( i = 0; i < ring->size / sizeof(u32); i += 4) {
		if( i <= MACH64_DUMP_CONTEXT * 4 || 
		    i >= ring->size / sizeof(u32) - MACH64_DUMP_CONTEXT * 4 ||
		    (i >= ring->tail - MACH64_DUMP_CONTEXT * 4 &&
		    i <= ring->tail + MACH64_DUMP_CONTEXT * 4) ||
		    (i >= ring->head - MACH64_DUMP_CONTEXT * 4 &&
		    i <= ring->head + MACH64_DUMP_CONTEXT * 4)) {
			DRM_INFO( "  0x%08x:  0x%08x 0x%08x 0x%08x 0x%08x%s%s\n",
				ring->start_addr + i * sizeof(u32),
				le32_to_cpu(((u32*) ring->start)[i + 0]),
				le32_to_cpu(((u32*) ring->start)[i + 1]),
				le32_to_cpu(((u32*) ring->start)[i + 2]), 
				le32_to_cpu(((u32*) ring->start)[i + 3]),
				i == ring->head ? " (head)" : "",
				i == ring->tail ? " (tail)" : ""
			);
			skipped = 0;
		} else {
			if( !skipped ) {
				DRM_INFO( "  ...\n" );
				skipped = 1;
			}
		}
	}

	DRM_INFO( "\n" );
	
	if( ring->head >= 0 && ring->head < ring->size / sizeof(u32) ) {
		struct list_head *ptr;
		u32 addr = le32_to_cpu(((u32 *)ring->start)[ring->head + 1]);

		list_for_each(ptr, &dev_priv->pending) {
			drm_mach64_freelist_t *entry = 
				list_entry(ptr, drm_mach64_freelist_t, list);
			drm_buf_t *buf = entry->buf;

			u32 buf_addr = GETBUFADDR( buf );
			
			if ( buf_addr <= addr && addr < buf_addr + buf->used ) {
				mach64_dump_buf_info ( dev_priv, buf );
			}
		}
	}

	DRM_INFO( "\n" );
	DRM_INFO( "       BM_GUI_TABLE = 0x%08x\n", MACH64_READ( MACH64_BM_GUI_TABLE ) );
	DRM_INFO( "\n" );
	DRM_INFO( "BM_FRAME_BUF_OFFSET = 0x%08x\n", MACH64_READ( MACH64_BM_FRAME_BUF_OFFSET ) );
	DRM_INFO( " BM_SYSTEM_MEM_ADDR = 0x%08x\n", MACH64_READ( MACH64_BM_SYSTEM_MEM_ADDR ) );
	DRM_INFO( "         BM_COMMAND = 0x%08x\n", MACH64_READ( MACH64_BM_COMMAND ) );
	DRM_INFO( "\n" );
	DRM_INFO( "          BM_STATUS = 0x%08x\n", MACH64_READ( MACH64_BM_STATUS ) );
	DRM_INFO( "           BUS_CNTL = 0x%08x\n", MACH64_READ( MACH64_BUS_CNTL ) );
	DRM_INFO( "          FIFO_STAT = 0x%08x\n", MACH64_READ( MACH64_FIFO_STAT ) );
	DRM_INFO( "           GUI_STAT = 0x%08x\n", MACH64_READ( MACH64_GUI_STAT ) );
	DRM_INFO( "           SRC_CNTL = 0x%08x\n", MACH64_READ( MACH64_SRC_CNTL ) );
}


/* ================================================================
 * DMA test and initialization
 */

static int mach64_bm_dma_test( drm_device_t *dev )
{
	drm_mach64_private_t *dev_priv = dev->dev_private;
	dma_addr_t data_handle;
	void *cpu_addr_data;
	u32 data_addr;
	u32 *table, *data;
	u32 expected[2];
	u32 src_cntl, pat_reg0, pat_reg1;
	int i, count, failed;

	DRM_DEBUG( "%s\n", __FUNCTION__ );

	table = (u32 *) dev_priv->ring.start;

	/* FIXME: get a dma buffer from the freelist here */
	DRM_DEBUG( "Allocating data memory ...\n" );
	cpu_addr_data = DRM(pci_alloc)( dev, 0x1000, 0x1000, 0xfffffffful, &data_handle );
	if (!cpu_addr_data || !data_handle) {
		DRM_INFO( "data-memory allocation failed!\n" );
		return DRM_ERR(ENOMEM);
	} else {
		data = (u32 *) cpu_addr_data;
		data_addr = (u32) data_handle;
	}

	/* Save the X server's value for SRC_CNTL and restore it
	 * in case our test fails.  This prevents the X server
	 * from disabling it's cache for this register
	 */
	src_cntl = MACH64_READ( MACH64_SRC_CNTL );
	pat_reg0 = MACH64_READ( MACH64_PAT_REG0 );
	pat_reg1 = MACH64_READ( MACH64_PAT_REG1 );

	mach64_do_wait_for_fifo( dev_priv, 3 );

	MACH64_WRITE( MACH64_SRC_CNTL, 0 );
	MACH64_WRITE( MACH64_PAT_REG0, 0x11111111 );
	MACH64_WRITE( MACH64_PAT_REG1, 0x11111111 );

	mach64_do_wait_for_idle( dev_priv );

	for (i=0; i < 2; i++) {
		u32 reg;
		reg = MACH64_READ( (MACH64_PAT_REG0 + i*4) );
		DRM_DEBUG( "(Before DMA Transfer) reg %d = 0x%08x\n", i, reg );
		if ( reg != 0x11111111 ) {
			DRM_INFO( "Error initializing test registers\n" );
			DRM_INFO( "resetting engine ...\n");
			mach64_do_engine_reset( dev_priv );
			DRM_INFO( "freeing data buffer memory.\n" );
			DRM(pci_free)( dev, 0x1000, cpu_addr_data, data_handle );
			return DRM_ERR(EIO);
		}
	}

	/* fill up a buffer with sets of 2 consecutive writes starting with PAT_REG0 */
	count = 0;

	data[count++] = cpu_to_le32(DMAREG(MACH64_PAT_REG0) | (1 << 16));
	data[count++] = expected[0] = 0x22222222;
	data[count++] = expected[1] = 0xaaaaaaaa;

	while (count < 1020) {
		data[count++] = cpu_to_le32(DMAREG(MACH64_PAT_REG0) | (1 << 16));
		data[count++] = 0x22222222;
		data[count++] = 0xaaaaaaaa;
	}
	data[count++] = cpu_to_le32(DMAREG(MACH64_SRC_CNTL) | (0 << 16));
	data[count++] = 0;

	DRM_DEBUG( "Preparing table ...\n" );
	table[MACH64_DMA_FRAME_BUF_OFFSET] = cpu_to_le32(MACH64_BM_ADDR + 
							 MACH64_APERTURE_OFFSET);
	table[MACH64_DMA_SYS_MEM_ADDR]     = cpu_to_le32(data_addr);
	table[MACH64_DMA_COMMAND]          = cpu_to_le32(count * sizeof( u32 ) 
							 | MACH64_DMA_HOLD_OFFSET 
							 | MACH64_DMA_EOL);
	table[MACH64_DMA_RESERVED]         = 0;

	DRM_DEBUG( "table[0] = 0x%08x\n", table[0] );
	DRM_DEBUG( "table[1] = 0x%08x\n", table[1] );
	DRM_DEBUG( "table[2] = 0x%08x\n", table[2] );
	DRM_DEBUG( "table[3] = 0x%08x\n", table[3] );

	for ( i = 0 ; i < 6 ; i++ ) {
		DRM_DEBUG( " data[%d] = 0x%08x\n", i, data[i] );
	}
	DRM_DEBUG( " ...\n" );
	for ( i = count-5 ; i < count ; i++ ) {
		DRM_DEBUG( " data[%d] = 0x%08x\n", i, data[i] );
	}

	DRM_MEMORYBARRIER();

	DRM_DEBUG( "waiting for idle...\n" );
	if ( ( i = mach64_do_wait_for_idle( dev_priv ) ) ) {
		DRM_INFO( "mach64_do_wait_for_idle failed (result=%d)\n", i);
		DRM_INFO( "resetting engine ...\n");
		mach64_do_engine_reset( dev_priv );
		mach64_do_wait_for_fifo( dev_priv, 3 );
		MACH64_WRITE( MACH64_SRC_CNTL, src_cntl );
		MACH64_WRITE( MACH64_PAT_REG0, pat_reg0 );
		MACH64_WRITE( MACH64_PAT_REG1, pat_reg1 );
		DRM_INFO( "freeing data buffer memory.\n" );
		DRM(pci_free)( dev, 0x1000, cpu_addr_data, data_handle );
		return i;
	}
	DRM_DEBUG( "waiting for idle...done\n" );

	DRM_DEBUG( "BUS_CNTL = 0x%08x\n", MACH64_READ( MACH64_BUS_CNTL ) );
	DRM_DEBUG( "SRC_CNTL = 0x%08x\n", MACH64_READ( MACH64_SRC_CNTL ) );
	DRM_DEBUG( "\n" );
	DRM_DEBUG( "data bus addr = 0x%08x\n", data_addr );
	DRM_DEBUG( "table bus addr = 0x%08x\n", dev_priv->ring.start_addr );

	DRM_DEBUG( "starting DMA transfer...\n" );
	MACH64_WRITE( MACH64_BM_GUI_TABLE_CMD,
			  dev_priv->ring.start_addr |
			  MACH64_CIRCULAR_BUF_SIZE_16KB );

	MACH64_WRITE( MACH64_SRC_CNTL, 
		      MACH64_SRC_BM_ENABLE | MACH64_SRC_BM_SYNC |
		      MACH64_SRC_BM_OP_SYSTEM_TO_REG );

	/* Kick off the transfer */
	DRM_DEBUG( "starting DMA transfer... done.\n" );
	MACH64_WRITE( MACH64_DST_HEIGHT_WIDTH, 0 );

	DRM_DEBUG( "waiting for idle...\n" );

	if ( ( i = mach64_do_wait_for_idle( dev_priv ) ) ) {
		/* engine locked up, dump register state and reset */
		DRM_INFO( "mach64_do_wait_for_idle failed (result=%d)\n", i);
		mach64_dump_engine_info( dev_priv );
		DRM_INFO( "resetting engine ...\n");
		mach64_do_engine_reset( dev_priv );
		mach64_do_wait_for_fifo( dev_priv, 3 );
		MACH64_WRITE( MACH64_SRC_CNTL, src_cntl );
		MACH64_WRITE( MACH64_PAT_REG0, pat_reg0 );
		MACH64_WRITE( MACH64_PAT_REG1, pat_reg1 );
		DRM_INFO( "freeing data buffer memory.\n" );
		DRM(pci_free)( dev, 0x1000, cpu_addr_data, data_handle );
		return i;
	}

	DRM_DEBUG( "waiting for idle...done\n" );

	/* restore SRC_CNTL */
	mach64_do_wait_for_fifo( dev_priv, 1 );
	MACH64_WRITE( MACH64_SRC_CNTL, src_cntl );

	failed = 0;

	/* Check register values to see if the GUI master operation succeeded */
	for ( i = 0; i < 2; i++ ) {
		u32 reg;
		reg = MACH64_READ( (MACH64_PAT_REG0 + i*4) );
		DRM_DEBUG( "(After DMA Transfer) reg %d = 0x%08x\n", i, reg );
		if (reg != expected[i]) {
			failed = -1;
		}
	}

	/* restore pattern registers */
	mach64_do_wait_for_fifo( dev_priv, 2 );
	MACH64_WRITE( MACH64_PAT_REG0, pat_reg0 );
	MACH64_WRITE( MACH64_PAT_REG1, pat_reg1 );

	DRM_DEBUG( "freeing data buffer memory.\n" );
	DRM(pci_free)( dev, 0x1000, cpu_addr_data, data_handle );
	DRM_DEBUG( "returning ...\n" );

	return failed;
}


static int mach64_do_dma_init( drm_device_t *dev, drm_mach64_init_t *init )
{
	drm_mach64_private_t *dev_priv;
	u32 tmp;
	int i, ret;

	DRM_DEBUG( "%s\n", __FUNCTION__ );

	dev_priv = DRM(alloc)( sizeof(drm_mach64_private_t), DRM_MEM_DRIVER );
	if ( dev_priv == NULL )
		return DRM_ERR(ENOMEM);
	
	memset( dev_priv, 0, sizeof(drm_mach64_private_t) );

	dev_priv->is_pci	= init->is_pci;

	dev_priv->fb_bpp	= init->fb_bpp;
	dev_priv->front_offset  = init->front_offset;
	dev_priv->front_pitch   = init->front_pitch;
	dev_priv->back_offset   = init->back_offset;
	dev_priv->back_pitch    = init->back_pitch;

	dev_priv->depth_bpp     = init->depth_bpp;
	dev_priv->depth_offset  = init->depth_offset;
	dev_priv->depth_pitch   = init->depth_pitch;

	dev_priv->front_offset_pitch	= (((dev_priv->front_pitch/8) << 22) |
					   (dev_priv->front_offset >> 3));
	dev_priv->back_offset_pitch	= (((dev_priv->back_pitch/8) << 22) |
					   (dev_priv->back_offset >> 3));
	dev_priv->depth_offset_pitch	= (((dev_priv->depth_pitch/8) << 22) |
					   (dev_priv->depth_offset >> 3));

	dev_priv->usec_timeout		= 1000000;

	/* Set up the freelist, placeholder list and pending list */
	INIT_LIST_HEAD(&dev_priv->free_list);
	INIT_LIST_HEAD(&dev_priv->placeholders);
	INIT_LIST_HEAD(&dev_priv->pending);

	DRM_GETSAREA();

	if (!dev_priv->sarea) {
		DRM_ERROR("can not find sarea!\n");
		dev->dev_private = (void *)dev_priv;
	   	mach64_do_cleanup_dma(dev);
	   	return DRM_ERR(EINVAL);
	}
	dev_priv->fb = drm_core_findmap(dev, init->fb_offset);
	if (!dev_priv->fb) {
		DRM_ERROR("can not find frame buffer map!\n");
		dev->dev_private = (void *)dev_priv;
	   	mach64_do_cleanup_dma(dev);
	   	return DRM_ERR(EINVAL);
	}
	dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset);
	if (!dev_priv->mmio) {
		DRM_ERROR("can not find mmio map!\n");
		dev->dev_private = (void *)dev_priv;
	   	mach64_do_cleanup_dma(dev);
	   	return DRM_ERR(EINVAL);
	}

	dev_priv->sarea_priv = (drm_mach64_sarea_t *)
		((u8 *)dev_priv->sarea->handle +
		 init->sarea_priv_offset);

	if( !dev_priv->is_pci ) {
		dev_priv->ring_map = drm_core_findmap(dev, init->ring_offset);
		if ( !dev_priv->ring_map ) {
			DRM_ERROR( "can not find ring map!\n" );
			dev->dev_private = (void *)dev_priv;
			mach64_do_cleanup_dma(dev);
			return DRM_ERR(EINVAL);
		}
		drm_core_ioremap( dev_priv->ring_map, dev );
		if ( !dev_priv->ring_map->handle ) {
		        DRM_ERROR( "can not ioremap virtual address for"
                                   " descriptor ring\n" );
			dev->dev_private = (void *) dev_priv;
			mach64_do_cleanup_dma( dev );
			return DRM_ERR(ENOMEM);
                }
		dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset);
		if ( !dev->agp_buffer_map ) {
			DRM_ERROR( "can not find dma buffer map!\n" );
			dev->dev_private = (void *)dev_priv;
			mach64_do_cleanup_dma( dev );
			return DRM_ERR(EINVAL);
		}
		/* there might be a nicer way to do this - 
		   dev isn't passed all the way though the mach64 - DA */
		dev_priv->dev_buffers = dev->agp_buffer_map;

		drm_core_ioremap( dev->agp_buffer_map, dev );
		if ( !dev->agp_buffer_map->handle ) {
			DRM_ERROR( "can not ioremap virtual address for"
				   " dma buffer\n" );
			dev->dev_private = (void *) dev_priv;
			mach64_do_cleanup_dma( dev );
			return DRM_ERR(ENOMEM);
		}
		dev_priv->agp_textures = drm_core_findmap(dev, init->agp_textures_offset);
		if (!dev_priv->agp_textures) {
			DRM_ERROR( "can not find agp texture region!\n" );
			dev->dev_private = (void *)dev_priv;
			mach64_do_cleanup_dma( dev );
			return DRM_ERR(EINVAL);
		}
	}

	dev->dev_private = (void *) dev_priv;

	dev_priv->driver_mode = init->dma_mode;

	/* changing the FIFO size from the default causes problems with DMA */
	tmp = MACH64_READ( MACH64_GUI_CNTL );
	if ( (tmp & MACH64_CMDFIFO_SIZE_MASK) != MACH64_CMDFIFO_SIZE_128 ) {
		DRM_INFO( "Setting FIFO size to 128 entries\n");
		/* FIFO must be empty to change the FIFO depth */
		if ((ret=mach64_do_wait_for_idle( dev_priv ))) {
			DRM_ERROR("wait for idle failed before changing FIFO depth!\n");
			mach64_do_cleanup_dma( dev );
			return ret;
		}
		MACH64_WRITE( MACH64_GUI_CNTL, ( ( tmp & ~MACH64_CMDFIFO_SIZE_MASK ) \
						 | MACH64_CMDFIFO_SIZE_128 ) );
		/* need to read GUI_STAT for proper sync according to docs */
		if ((ret=mach64_do_wait_for_idle( dev_priv ))) {
			DRM_ERROR("wait for idle failed when changing FIFO depth!\n");
			mach64_do_cleanup_dma( dev );
			return ret;
		}
	}

	/* allocate descriptor memory from pci pool */
	DRM_DEBUG( "Allocating dma descriptor ring\n" );
	dev_priv->ring.size = 0x4000; /* 16KB */

	if ( dev_priv->is_pci ) {
		dev_priv->ring.start = DRM(pci_alloc)( dev, dev_priv->ring.size, 
						       dev_priv->ring.size, 0xfffffffful,
						       &dev_priv->ring.handle );

		if (!dev_priv->ring.start || !dev_priv->ring.handle) {
			DRM_ERROR( "Allocating dma descriptor ring failed\n");
			return DRM_ERR(ENOMEM);
		} else {
			dev_priv->ring.start_addr = (u32) dev_priv->ring.handle;
		}
        } else {
		dev_priv->ring.start = dev_priv->ring_map->handle;
		dev_priv->ring.start_addr = (u32) dev_priv->ring_map->offset;
	}

	memset( dev_priv->ring.start, 0, dev_priv->ring.size );
	DRM_INFO( "descriptor ring: cpu addr 0x%08x, bus addr: 0x%08x\n", 
		  (u32) dev_priv->ring.start, dev_priv->ring.start_addr );

	ret = 0;
	if ( dev_priv->driver_mode != MACH64_MODE_MMIO ) {

		/* enable block 1 registers and bus mastering */
		MACH64_WRITE( MACH64_BUS_CNTL, 
			      ( ( MACH64_READ(MACH64_BUS_CNTL) 
				  | MACH64_BUS_EXT_REG_EN ) 
				& ~MACH64_BUS_MASTER_DIS ) );

		/* try a DMA GUI-mastering pass and fall back to MMIO if it fails */
		DRM_DEBUG( "Starting DMA test...\n");
		if ( (ret=mach64_bm_dma_test( dev )) ) {
			dev_priv->driver_mode = MACH64_MODE_MMIO;
		}
	}

	switch (dev_priv->driver_mode) {
	case MACH64_MODE_MMIO:
		MACH64_WRITE( MACH64_BUS_CNTL, ( MACH64_READ(MACH64_BUS_CNTL) 
						 | MACH64_BUS_EXT_REG_EN
						 | MACH64_BUS_MASTER_DIS ) );
		if ( init->dma_mode == MACH64_MODE_MMIO )
			DRM_INFO( "Forcing pseudo-DMA mode\n" );
		else
			DRM_INFO( "DMA test failed (ret=%d), using pseudo-DMA mode\n", ret );
		break;
	case MACH64_MODE_DMA_SYNC:
		DRM_INFO( "DMA test succeeded, using synchronous DMA mode\n");
		break;
	case MACH64_MODE_DMA_ASYNC:
	default:
		DRM_INFO( "DMA test succeeded, using asynchronous DMA mode\n");
	}

	dev_priv->ring_running = 0;

	/* setup offsets for physical address of table start and end */
	dev_priv->ring.head_addr = dev_priv->ring.start_addr;
	dev_priv->ring.head = dev_priv->ring.tail = 0;
	dev_priv->ring.tail_mask = (dev_priv->ring.size / sizeof(u32)) - 1;
	dev_priv->ring.space = dev_priv->ring.size;

	/* setup physical address and size of descriptor table */
	mach64_do_wait_for_fifo( dev_priv, 1 );
	MACH64_WRITE( MACH64_BM_GUI_TABLE_CMD, 
		      ( dev_priv->ring.head_addr | MACH64_CIRCULAR_BUF_SIZE_16KB ) );

	/* init frame counter */
	dev_priv->sarea_priv->frames_queued = 0;
	for (i = 0; i < MACH64_MAX_QUEUED_FRAMES; i++) {
		dev_priv->frame_ofs[i] = ~0; /* All ones indicates placeholder */
	}

	/* Allocate the DMA buffer freelist */
	if ( (ret=mach64_init_freelist( dev )) ) {
		DRM_ERROR("Freelist allocation failed\n");
		mach64_do_cleanup_dma( dev );
		return ret;
	}

	return 0;
}

/* ===================================================================
 * MMIO Pseudo-DMA (intended primarily for debugging, not performance)
 */

int mach64_do_dispatch_pseudo_dma( drm_mach64_private_t *dev_priv )
{
	drm_mach64_descriptor_ring_t *ring = &dev_priv->ring;
	volatile u32 *ring_read;
	struct list_head *ptr;
	drm_mach64_freelist_t *entry;
	drm_buf_t *buf = NULL;
	u32 *buf_ptr;
	u32 used, reg, target;
	int fifo, count, found, ret, no_idle_wait;

	fifo = count = reg = no_idle_wait = 0;
	target = MACH64_BM_ADDR;

	if ( (ret=mach64_do_wait_for_idle( dev_priv )) < 0) {
		DRM_INFO( "%s: idle failed before pseudo-dma dispatch, resetting engine\n", 
			  __FUNCTION__);
		mach64_dump_engine_info( dev_priv );
		mach64_do_engine_reset( dev_priv );
		return ret;
	}

	ring_read = (u32 *) ring->start;

	while ( ring->tail != ring->head ) {
		u32 buf_addr, new_target, offset; 
		u32 bytes, remaining, head, eol;

		head = ring->head;

		new_target = le32_to_cpu( ring_read[head++] ) - MACH64_APERTURE_OFFSET;
		buf_addr   = le32_to_cpu( ring_read[head++] );
		eol        = le32_to_cpu( ring_read[head]   ) & MACH64_DMA_EOL;
		bytes      = le32_to_cpu( ring_read[head++] )
			& ~(MACH64_DMA_HOLD_OFFSET | MACH64_DMA_EOL);
		head++;
		head &= ring->tail_mask;

		/* can't wait for idle between a blit setup descriptor 
		 * and a HOSTDATA descriptor or the engine will lock
		 */
		if (new_target == MACH64_BM_HOSTDATA && target == MACH64_BM_ADDR)
			no_idle_wait = 1;

		target = new_target;

		found = 0;
		offset = 0;
		list_for_each(ptr, &dev_priv->pending)
		{
			entry = list_entry(ptr, drm_mach64_freelist_t, list);
			buf = entry->buf;
			offset = buf_addr - GETBUFADDR( buf );
			if (offset >= 0 && offset < MACH64_BUFFER_SIZE) {
				found = 1;
				break;
			}
		}

		if (!found || buf == NULL) {
			DRM_ERROR("Couldn't find pending buffer: head: %u tail: %u buf_addr: 0x%08x %s\n",
				  head, ring->tail, buf_addr, (eol ? "eol" : ""));
			mach64_dump_ring_info( dev_priv );
			mach64_do_engine_reset( dev_priv );
			return DRM_ERR(EINVAL);
		}

		/* Hand feed the buffer to the card via MMIO, waiting for the fifo 
		 * every 16 writes 
		 */
		DRM_DEBUG("target: (0x%08x) %s\n", target, 
				 (target == MACH64_BM_HOSTDATA ? "BM_HOSTDATA" : "BM_ADDR"));
		DRM_DEBUG("offset: %u bytes: %u used: %u\n", offset, bytes, buf->used);

		remaining = (buf->used - offset) >> 2; /* dwords remaining in buffer */
		used = bytes >> 2; /* dwords in buffer for this descriptor */
		buf_ptr = (u32 *)((char *)GETBUFPTR( buf ) + offset);

		while ( used ) {
			
			if (count == 0) {
				if (target == MACH64_BM_HOSTDATA) {
					reg = DMAREG(MACH64_HOST_DATA0);
					count = (remaining > 16) ? 16 : remaining;
					fifo = 0;
				} else {
					reg = le32_to_cpu(*buf_ptr++);
					used--;
					count = (reg >> 16) + 1;
				}

				reg = reg & 0xffff;
				reg = MMSELECT( reg );
			}
			while ( count && used ) {
				if ( !fifo ) {
					if (no_idle_wait) {
						if ( (ret=mach64_do_wait_for_fifo( dev_priv, 16 )) < 0 ) {
							no_idle_wait = 0;
							return ret;
						}
					} else {
						if ( (ret=mach64_do_wait_for_idle( dev_priv )) < 0 ) {
							return ret;
						}
					}
					fifo = 16;
				}
				--fifo;
				MACH64_WRITE(reg, le32_to_cpu(*buf_ptr++));
				used--; remaining--;
					
				reg += 4;
				count--;
			}
		}
		ring->head = head;
		ring->head_addr = ring->start_addr + (ring->head * sizeof(u32));
		ring->space += (4 * sizeof(u32));
	}

	if ( (ret=mach64_do_wait_for_idle( dev_priv )) < 0 ) {
		return ret;
	}
	MACH64_WRITE( MACH64_BM_GUI_TABLE_CMD, 
		      ring->head_addr | MACH64_CIRCULAR_BUF_SIZE_16KB );

	DRM_DEBUG( "%s completed\n", __FUNCTION__ );
	return 0;
}

/* ================================================================
 * DMA cleanup
 */

int mach64_do_cleanup_dma( drm_device_t *dev )
{
	DRM_DEBUG( "%s\n", __FUNCTION__ );

	/* Make sure interrupts are disabled here because the uninstall ioctl
	 * may not have been called from userspace and after dev_private
	 * is freed, it's too late.
	 */
	if ( dev->irq ) DRM(irq_uninstall)(dev);

	if ( dev->dev_private ) {
		drm_mach64_private_t *dev_priv = dev->dev_private;

		if ( dev_priv->is_pci ) {
			if ( (dev_priv->ring.start != NULL) && dev_priv->ring.handle ) {
				DRM(pci_free)( dev, dev_priv->ring.size, 
					       dev_priv->ring.start, dev_priv->ring.handle );
			}
		} else {
			if ( dev_priv->ring_map )
				drm_core_ioremapfree( dev_priv->ring_map, dev );
		}

		if ( dev->agp_buffer_map ) {
			drm_core_ioremapfree( dev->agp_buffer_map, dev );
			dev->agp_buffer_map = NULL;
		}

		mach64_destroy_freelist( dev );

		DRM(free)( dev_priv, sizeof(drm_mach64_private_t),
			   DRM_MEM_DRIVER );
		dev->dev_private = NULL;
	}

	return 0;
}

/* ================================================================
 * IOCTL handlers
 */

int mach64_dma_init( DRM_IOCTL_ARGS )
{
	DRM_DEVICE;
	drm_mach64_init_t init;
		
	DRM_DEBUG( "%s\n", __FUNCTION__ );

	LOCK_TEST_WITH_RETURN( dev, filp );

	DRM_COPY_FROM_USER_IOCTL( init, (drm_mach64_init_t *)data, 
	    sizeof(init) );

	switch ( init.func ) {
	case DRM_MACH64_INIT_DMA:
		return mach64_do_dma_init( dev, &init );
	case DRM_MACH64_CLEANUP_DMA:
		return mach64_do_cleanup_dma( dev );
	}
		
	return DRM_ERR(EINVAL);
}

int mach64_dma_idle( DRM_IOCTL_ARGS )
{
	DRM_DEVICE;
	drm_mach64_private_t *dev_priv = dev->dev_private;

	DRM_DEBUG( "%s\n", __FUNCTION__ );

	LOCK_TEST_WITH_RETURN( dev, filp );

	return mach64_do_dma_idle( dev_priv );
}

int mach64_dma_flush( DRM_IOCTL_ARGS )
{
	DRM_DEVICE;
	drm_mach64_private_t *dev_priv = dev->dev_private;

	DRM_DEBUG( "%s\n", __FUNCTION__ );

	LOCK_TEST_WITH_RETURN( dev, filp );

	return mach64_do_dma_flush( dev_priv );
}

int mach64_engine_reset( DRM_IOCTL_ARGS )
{
	DRM_DEVICE;
	drm_mach64_private_t *dev_priv = dev->dev_private;
	
	DRM_DEBUG( "%s\n", __FUNCTION__ );

	LOCK_TEST_WITH_RETURN( dev, filp );

	return mach64_do_engine_reset( dev_priv );
}


/* ================================================================
 * Freelist management
 */

int mach64_init_freelist( drm_device_t *dev )
{
	drm_device_dma_t *dma = dev->dma;
	drm_mach64_private_t *dev_priv = dev->dev_private;
	drm_mach64_freelist_t *entry;
	struct list_head *ptr;
	int i;

	DRM_DEBUG("%s: adding %d buffers to freelist\n", __FUNCTION__, dma->buf_count);

	for ( i = 0 ; i < dma->buf_count ; i++ ) {
		if ((entry = 
		     (drm_mach64_freelist_t *) DRM(alloc)(sizeof(drm_mach64_freelist_t), 
							  DRM_MEM_BUFLISTS)) == NULL)
			return DRM_ERR(ENOMEM);
		memset( entry, 0, sizeof(drm_mach64_freelist_t) );
		entry->buf = dma->buflist[i];
		ptr = &entry->list;
		list_add_tail(ptr, &dev_priv->free_list);
	}

	return 0;
}

void mach64_destroy_freelist( drm_device_t *dev )
{
	drm_mach64_private_t *dev_priv = dev->dev_private;
	drm_mach64_freelist_t *entry;
	struct list_head *ptr;
	struct list_head *tmp;

	DRM_DEBUG("%s\n", __FUNCTION__);

	list_for_each_safe(ptr, tmp, &dev_priv->pending)
	{
		list_del(ptr);
		entry = list_entry(ptr, drm_mach64_freelist_t, list);
		DRM(free)(entry, sizeof(*entry), DRM_MEM_BUFLISTS);
	}
	list_for_each_safe(ptr, tmp, &dev_priv->placeholders)
	{
		list_del(ptr);
		entry = list_entry(ptr, drm_mach64_freelist_t, list);
		DRM(free)(entry, sizeof(*entry), DRM_MEM_BUFLISTS);
	}

	list_for_each_safe(ptr, tmp, &dev_priv->free_list)
	{
		list_del(ptr);
		entry = list_entry(ptr, drm_mach64_freelist_t, list);
		DRM(free)(entry, sizeof(*entry), DRM_MEM_BUFLISTS);
	}
}

/* IMPORTANT: This function should only be called when the engine is idle or locked up, 
 * as it assumes all buffers in the pending list have been completed by the hardware.
 */
int mach64_do_release_used_buffers( drm_mach64_private_t *dev_priv )
{
	struct list_head *ptr;
	struct list_head *tmp;
	drm_mach64_freelist_t *entry;
	int i;

	if ( list_empty(&dev_priv->pending) )
		return 0;

	/* Iterate the pending list and move all buffers into the freelist... */
	i = 0;
	list_for_each_safe(ptr, tmp, &dev_priv->pending)
	{
		entry = list_entry(ptr, drm_mach64_freelist_t, list);
		if (entry->discard) {
			entry->buf->pending = 0;
			list_del(ptr);
			list_add_tail(ptr, &dev_priv->free_list);
			i++;
		}
	}

	DRM_DEBUG( "%s: released %d buffers from pending list\n", __FUNCTION__, i );

        return 0;
}

drm_buf_t *mach64_freelist_get( drm_mach64_private_t *dev_priv )
{
	drm_mach64_descriptor_ring_t *ring = &dev_priv->ring;
	drm_mach64_freelist_t *entry;
	struct list_head *ptr;
	struct list_head *tmp;
	int t;

	if ( list_empty(&dev_priv->free_list) ) {
		u32 head, tail, ofs;

		if ( list_empty( &dev_priv->pending ) ) {
			DRM_ERROR( "Couldn't get buffer - pending and free lists empty\n" );
			t = 0;
			list_for_each( ptr, &dev_priv->placeholders ) {
				t++;
			}
			DRM_INFO( "Placeholders: %d\n", t );
			return NULL;
		}

		tail = ring->tail;
		for ( t = 0 ; t < dev_priv->usec_timeout ; t++ ) {
			mach64_ring_tick( dev_priv, ring );
			head = ring->head;

			if ( head == tail ) {
#if MACH64_EXTRA_CHECKING
				if ( MACH64_READ(MACH64_GUI_STAT) & MACH64_GUI_ACTIVE ) {
					DRM_ERROR( "Empty ring with non-idle engine!\n" );
					mach64_dump_ring_info( dev_priv );
					return NULL;
				}
#endif
				/* last pass is complete, so release everything */
				mach64_do_release_used_buffers( dev_priv );
				DRM_DEBUG( "%s: idle engine, freed all buffers.\n", __FUNCTION__ );
				if ( list_empty(&dev_priv->free_list) ) {
					DRM_ERROR( "Freelist empty with idle engine\n" );
					return NULL;
				}
				goto _freelist_entry_found;
			}
			/* Look for a completed buffer and bail out of the loop 
			 * as soon as we find one -- don't waste time trying
			 * to free extra bufs here, leave that to do_release_used_buffers
			 */
			list_for_each_safe(ptr, tmp, &dev_priv->pending) {
				entry = list_entry(ptr, drm_mach64_freelist_t, list);
				ofs = entry->ring_ofs;
				if ( entry->discard &&
				     ((head < tail && (ofs < head || ofs >= tail)) ||
				      (head > tail && (ofs < head && ofs >= tail))) ) {
#if MACH64_EXTRA_CHECKING
					int i;
					
					for ( i = head ; i != tail ; i = (i + 4) & ring->tail_mask ) {
						u32 o1 = le32_to_cpu(((u32 *)ring->start)[i + 1]);
						u32 o2 = GETBUFADDR( entry->buf );

						if ( o1 == o2 ) {
							DRM_ERROR ( "Attempting to free used buffer: "
								    "i=%d  buf=0x%08x\n", i, o1 );
							mach64_dump_ring_info( dev_priv );
							return NULL;
						}
					}
#endif
					/* found a processed buffer */
					entry->buf->pending = 0;
					list_del(ptr);
					entry->buf->used = 0;
					list_add_tail(ptr, &dev_priv->placeholders);
					DRM_DEBUG( "%s: freed processed buffer (head=%d tail=%d "
						   "buf ring ofs=%d).\n", __FUNCTION__, head, tail, ofs );
					return entry->buf;
				}
			}
			DRM_UDELAY( 1 );
		}
		mach64_dump_ring_info( dev_priv );
		DRM_ERROR( "timeout waiting for buffers: ring head_addr: 0x%08x head: %d tail: %d\n", 
			   ring->head_addr, ring->head, ring->tail );
		return NULL;
	}

_freelist_entry_found:
	ptr = dev_priv->free_list.next;
	list_del(ptr);
	entry = list_entry(ptr, drm_mach64_freelist_t, list);
	entry->buf->used = 0;
	list_add_tail(ptr, &dev_priv->placeholders);
	return entry->buf;
}

/* ================================================================
 * DMA buffer request and submission IOCTL handler
 */

static int mach64_dma_get_buffers( DRMFILE filp, drm_device_t *dev, drm_dma_t *d )
{
	int i;
	drm_buf_t *buf;
	drm_mach64_private_t *dev_priv = dev->dev_private;

	for ( i = d->granted_count ; i < d->request_count ; i++ ) {
		buf = mach64_freelist_get( dev_priv );
#if MACH64_EXTRA_CHECKING
		if ( !buf ) return DRM_ERR(EFAULT);
#else
		if ( !buf ) return DRM_ERR(EAGAIN);
#endif

		buf->filp = filp;

		if ( DRM_COPY_TO_USER( &d->request_indices[i], &buf->idx,
				   sizeof(buf->idx) ) )
			return DRM_ERR(EFAULT);
		if ( DRM_COPY_TO_USER( &d->request_sizes[i], &buf->total,
				   sizeof(buf->total) ) )
			return DRM_ERR(EFAULT);

		d->granted_count++;
	}
	return 0;
}

int mach64_dma_buffers( DRM_IOCTL_ARGS )
{
	DRM_DEVICE;
	drm_device_dma_t *dma = dev->dma;
	drm_dma_t d;
        int ret = 0;

        LOCK_TEST_WITH_RETURN( dev, filp );

	DRM_COPY_FROM_USER_IOCTL( d, (drm_dma_t *)data, sizeof(d) );

        /* Please don't send us buffers.
	 */
        if ( d.send_count != 0 ) 
        {
		DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n",
			   DRM_CURRENTPID, d.send_count );
		return DRM_ERR(EINVAL);
	}

	/* We'll send you buffers.
	 */
	if ( d.request_count < 0 || d.request_count > dma->buf_count ) 
	{
		DRM_ERROR( "Process %d trying to get %d buffers (of %d max)\n",
			   DRM_CURRENTPID, d.request_count, dma->buf_count );
		ret = DRM_ERR(EINVAL);
	}
        
	d.granted_count = 0;

	if ( d.request_count ) 
	{
		ret = mach64_dma_get_buffers( filp, dev, &d );
	}

	DRM_COPY_TO_USER_IOCTL( (drm_dma_t *)data, d, sizeof(d) );

        return ret;
}

static void mach64_driver_pretakedown(drm_device_t *dev)
{
	mach64_do_cleanup_dma( dev );					
}

void mach64_driver_register_fns(drm_device_t *dev)
{
	dev->driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL;
	dev->fn_tbl.pretakedown = mach64_driver_pretakedown;
	dev->fn_tbl.vblank_wait = mach64_driver_vblank_wait;
	dev->fn_tbl.irq_preinstall = mach64_driver_irq_preinstall;
	dev->fn_tbl.irq_postinstall = mach64_driver_irq_postinstall;
	dev->fn_tbl.irq_uninstall = mach64_driver_irq_uninstall;
	dev->fn_tbl.irq_handler = mach64_driver_irq_handler;
}