diff options
author | David Dawes <dawes@xfree86.org> | 2001-05-01 17:07:59 +0000 |
---|---|---|
committer | David Dawes <dawes@xfree86.org> | 2001-05-01 17:07:59 +0000 |
commit | d5d79215ff83b171b9089f33c91fb2d93263bec9 (patch) | |
tree | f3263785e261ce00f8b7ab91b2cc2e43d2a64932 | |
parent | c176a0186e7007aa91cbfbd4f5e1ede383a02b65 (diff) | |
download | drm-d5d79215ff83b171b9089f33c91fb2d93263bec9.tar.gz |
Import of XFree86 4.0.99.3
46 files changed, 7253 insertions, 211 deletions
diff --git a/bsd/Imakefile b/bsd/Imakefile index f5819317..0e11ec50 100644 --- a/bsd/Imakefile +++ b/bsd/Imakefile @@ -1,7 +1,8 @@ -XCOMM $XFree86: xc/programs/Xserver/hw/xfree86/os-support/bsd/drm/kernel/Imakefile,v 1.4 2001/01/12 19:28:34 dawes Exp $ +XCOMM $XFree86: xc/programs/Xserver/hw/xfree86/os-support/bsd/drm/kernel/Imakefile,v 1.6 2001/04/18 14:52:43 dawes Exp $ #include <Server.tmpl> +#if 0 LinkSourceFile(xf86drm.c,..) LinkSourceFile(xf86drmHash.c,..) LinkSourceFile(xf86drmRandom.c,..) @@ -9,6 +10,7 @@ LinkSourceFile(xf86drmSL.c,..) LinkSourceFile(xf86drm.h,$(XF86OSSRC)) LinkSourceFile(xf86_OSproc.h,$(XF86OSSRC)) LinkSourceFile(sigio.c,$(XF86OSSRC)/shared) +#endif XCOMM Try to use the Linux version of the DRM headers. This avoids skew XCOMM and missing headers. If there's a need to break them out, they @@ -35,7 +37,7 @@ install:: $(MAKE) -f Makefile.bsd install #else all:: - echo 'Use "make -f Makefile.bsd" to manually build drm.o' + @echo 'Use "make -f Makefile.bsd" to manually build drm.o' #endif clean:: diff --git a/libdrm/xf86drm.c b/libdrm/xf86drm.c index a7be5031..ffc85d8c 100644 --- a/libdrm/xf86drm.c +++ b/libdrm/xf86drm.c @@ -27,7 +27,7 @@ * Authors: Rickard E. (Rik) Faith <faith@valinux.com> * Kevin E. Martin <martin@valinux.com> * - * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/xf86drm.c,v 1.18 2001/03/21 18:08:54 dawes Exp $ + * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/xf86drm.c,v 1.20 2001/04/18 18:44:39 dawes Exp $ * */ @@ -35,11 +35,9 @@ # include "xf86.h" # include "xf86_OSproc.h" # include "xf86_ansic.h" -# include "xf86Priv.h" # define _DRM_MALLOC xalloc # define _DRM_FREE xfree # ifndef XFree86LOADER -# include <sys/stat.h> # include <sys/mman.h> # endif #else @@ -53,6 +51,7 @@ # include <signal.h> # include <sys/types.h> # include <sys/stat.h> +# define stat_t struct stat # include <sys/ioctl.h> # include <sys/mman.h> # include <sys/time.h> @@ -68,7 +67,7 @@ extern int xf86RemoveSIGIOHandler(int fd); # endif #endif -#ifdef __alpha__ +#if defined(__alpha__) && defined(__linux__) extern unsigned long _bus_base(void); #define BUS_BASE _bus_base() #endif @@ -141,11 +140,7 @@ static char *drmStrdup(const char *s) static unsigned long drmGetKeyFromFd(int fd) { -#ifdef XFree86LOADER - struct xf86stat st; -#else - struct stat st; -#endif + stat_t st; st.st_rdev = 0; fstat(fd, &st); @@ -225,7 +220,7 @@ static int drmOpenDevice(long dev, int minor) return -errno; } -int drmOpenMinor(int minor, int create) +static int drmOpenMinor(int minor, int create) { int fd; char buf[64]; @@ -1007,6 +1002,28 @@ unsigned int drmAgpDeviceId(int fd) 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 drmError(int err, const char *label) { switch (err) { diff --git a/linux-core/ati_pcigart.c b/linux-core/ati_pcigart.c new file mode 100644 index 00000000..93c5148f --- /dev/null +++ b/linux-core/ati_pcigart.c @@ -0,0 +1,138 @@ +/* ati_pcigart.h -- ATI PCI GART support -*- linux-c -*- + * Created: Wed Dec 13 21:52:19 2000 by gareth@valinux.com + * + * 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: + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +#if PAGE_SIZE == 8192 +# define ATI_PCIGART_TABLE_ORDER 2 +# define ATI_PCIGART_TABLE_PAGES (1 << 2) +#elif PAGE_SIZE == 4096 +# define ATI_PCIGART_TABLE_ORDER 3 +# define ATI_PCIGART_TABLE_PAGES (1 << 3) +#elif +# error - PAGE_SIZE not 8K or 4K +#endif + +# define ATI_MAX_PCIGART_PAGES 8192 /* 32 MB aperture, 4K pages */ +# define ATI_PCIGART_PAGE_SIZE 4096 /* PCI GART page size */ + +static unsigned long DRM(ati_alloc_pcigart_table)( void ) +{ + unsigned long address; + struct page *page; + int i; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + address = __get_free_pages( GFP_KERNEL, ATI_PCIGART_TABLE_ORDER ); + if ( address == 0UL ) { + return 0; + } + + page = virt_to_page( address ); + + for ( i = 0 ; i <= ATI_PCIGART_TABLE_PAGES ; i++, page++ ) { + atomic_inc( &page->count ); + SetPageReserved( page ); + } + + DRM_DEBUG( "%s: returning 0x%08lx\n", __FUNCTION__, address ); + return address; +} + +static void DRM(ati_free_pcigart_table)( unsigned long address ) +{ + struct page *page; + int i; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !address ) return; + + page = virt_to_page( address ); + + for ( i = 0 ; i <= ATI_PCIGART_TABLE_PAGES ; i++, page++ ) { + atomic_dec( &page->count ); + ClearPageReserved( page ); + } + + free_pages( address, ATI_PCIGART_TABLE_ORDER ); +} + +unsigned long DRM(ati_pcigart_init)( drm_device_t *dev ) +{ + drm_sg_mem_t *entry = dev->sg; + unsigned long address; + unsigned long pages; + u32 *pci_gart, page_base; + int i, j; + + if ( !entry ) { + DRM_ERROR( "no scatter/gather memory!\n" ); + return 0; + } + + address = DRM(ati_alloc_pcigart_table)(); + if ( !address ) { + DRM_ERROR( "cannot allocate PCI GART page!\n" ); + return 0; + } + + pci_gart = (u32 *)address; + + pages = ( entry->pages <= ATI_MAX_PCIGART_PAGES ) + ? entry->pages : ATI_MAX_PCIGART_PAGES; + + memset( pci_gart, 0, ATI_MAX_PCIGART_PAGES * sizeof(u32) ); + + for ( i = 0 ; i < pages ; i++ ) { + page_base = virt_to_bus( entry->pagelist[i]->virtual ); + for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) { + *pci_gart++ = cpu_to_le32( page_base ); + page_base += ATI_PCIGART_PAGE_SIZE; + } + } + +#if __i386__ + asm volatile ( "wbinvd" ::: "memory" ); +#else + mb(); +#endif + + return address; +} + +int DRM(ati_pcigart_cleanup)( unsigned long address ) +{ + + if ( address ) { + DRM(ati_free_pcigart_table)( address ); + } + + return 0; +} diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 274e318a..227ec35a 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -53,7 +53,7 @@ #include <linux/sched.h> #include <linux/smp_lock.h> /* For (un)lock_kernel */ #include <linux/mm.h> -#ifdef __alpha__ +#if defined(__alpha__) || defined(__powerpc__) #include <asm/pgtable.h> /* For pte_wrprotect */ #endif #include <asm/io.h> @@ -145,6 +145,7 @@ #define DRM_MEM_BOUNDAGP 17 #define DRM_MEM_CTXBITMAP 18 #define DRM_MEM_STUB 19 +#define DRM_MEM_SGLISTS 20 #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) @@ -199,7 +200,7 @@ __cmpxchg_u32(volatile int *m, int old, int new) unsigned long prev, cmp; __asm__ __volatile__( - "1: ldl_l %0,%2\n" + "1: ldl_l %0,%5\n" " cmpeq %0,%3,%1\n" " beq %1,2f\n" " mov %4,%1\n" @@ -210,7 +211,8 @@ __cmpxchg_u32(volatile int *m, int old, int new) "3: br 1b\n" ".previous" : "=&r"(prev), "=&r"(cmp), "=m"(*m) - : "r"((long) old), "r"(new), "m"(*m)); + : "r"((long) old), "r"(new), "m"(*m) + : "memory" ); return prev; } @@ -221,7 +223,7 @@ __cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new) unsigned long prev, cmp; __asm__ __volatile__( - "1: ldq_l %0,%2\n" + "1: ldq_l %0,%5\n" " cmpeq %0,%3,%1\n" " beq %1,2f\n" " mov %4,%1\n" @@ -232,7 +234,8 @@ __cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new) "3: br 1b\n" ".previous" : "=&r"(prev), "=&r"(cmp), "=m"(*m) - : "r"((long) old), "r"(new), "m"(*m)); + : "r"((long) old), "r"(new), "m"(*m) + : "memory" ); return prev; } @@ -284,12 +287,43 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, return old; } +#elif defined(__powerpc__) +extern void __cmpxchg_called_with_bad_pointer(void); +static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, + unsigned long new, int size) +{ + unsigned long prev; + + switch (size) { + case 4: + __asm__ __volatile__( + "sync;" + "0: lwarx %0,0,%1 ;" + " cmpl 0,%0,%3;" + " bne 1f;" + " stwcx. %2,0,%1;" + " bne- 0b;" + "1: " + "sync;" + : "=&r"(prev) + : "r"(ptr), "r"(new), "r"(old) + : "cr0", "memory"); + return prev; + } + __cmpxchg_called_with_bad_pointer(); + return old; +} + +#endif /* i386, powerpc & alpha */ + +#ifndef __alpha__ #define cmpxchg(ptr,o,n) \ ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o), \ (unsigned long)(n),sizeof(*(ptr)))) -#endif /* i386 & alpha */ #endif +#endif /* !__HAVE_ARCH_CMPXCHG */ + /* Macros to make printk easier */ #define DRM_ERROR(fmt, arg...) \ printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ "] *ERROR* " fmt , ##arg) @@ -547,7 +581,8 @@ typedef struct drm_device_dma { unsigned long *pagelist; unsigned long byte_count; enum { - _DRM_DMA_USE_AGP = 0x01 + _DRM_DMA_USE_AGP = 0x01, + _DRM_DMA_USE_SG = 0x02 } flags; /* DMA support */ @@ -579,6 +614,13 @@ typedef struct drm_agp_head { } drm_agp_head_t; #endif +typedef struct drm_sg_mem { + unsigned long handle; + void *virtual; + int pages; + struct page **pagelist; +} drm_sg_mem_t; + typedef struct drm_sigdata { int context; drm_hw_lock_t *lock; @@ -667,6 +709,10 @@ typedef struct drm_device { #if __REALLY_HAVE_AGP drm_agp_head_t *agp; #endif +#ifdef __alpha__ + struct pci_controler *hose; +#endif + drm_sg_mem_t *sg; /* Scatter gather memory */ unsigned long *ctx_bitmap; void *dev_private; drm_sigdata_t sigdata; /* For block_all_signals */ @@ -718,6 +764,9 @@ extern unsigned long DRM(vm_shm_nopage)(struct vm_area_struct *vma, extern unsigned long DRM(vm_dma_nopage)(struct vm_area_struct *vma, unsigned long address, int write_access); +extern unsigned long DRM(vm_sg_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access); #else /* Return type changed in 2.3.23 */ extern struct page *DRM(vm_nopage)(struct vm_area_struct *vma, @@ -729,6 +778,9 @@ extern struct page *DRM(vm_shm_nopage)(struct vm_area_struct *vma, extern struct page *DRM(vm_dma_nopage)(struct vm_area_struct *vma, unsigned long address, int write_access); +extern struct page *DRM(vm_sg_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access); #endif extern void DRM(vm_open)(struct vm_area_struct *vma); extern void DRM(vm_close)(struct vm_area_struct *vma); @@ -947,5 +999,18 @@ extern int DRM(proc_cleanup)(int minor, struct proc_dir_entry *root, struct proc_dir_entry *dev_root); +#if __HAVE_SG + /* Scatter Gather Support (drm_scatter.h) */ +extern void DRM(sg_cleanup)(drm_sg_mem_t *entry); +extern int DRM(sg_alloc)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int DRM(sg_free)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +#endif + + /* ATI PCIGART support (ati_pcigart.h) */ +extern unsigned long DRM(ati_pcigart_init)(drm_device_t *dev); +extern int DRM(ati_pcigart_cleanup)(unsigned long address); + #endif /* __KERNEL__ */ #endif diff --git a/linux-core/drm_agpsupport.c b/linux-core/drm_agpsupport.c new file mode 100644 index 00000000..9b056c75 --- /dev/null +++ b/linux-core/drm_agpsupport.c @@ -0,0 +1,340 @@ +/* drm_agpsupport.h -- DRM support for AGP/GART backend -*- linux-c -*- + * Created: Mon Dec 13 09:56:45 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 + * VA LINUX SYSTEMS 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. + * + * Author: + * Rickard E. (Rik) Faith <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include <linux/module.h> + +#if __REALLY_HAVE_AGP + +#if LINUX_VERSION_CODE < 0x020400 +#include "agpsupport-pre24.h" +#else +#define DRM_AGP_GET (drm_agp_t *)inter_module_get("drm_agp") +#define DRM_AGP_PUT inter_module_put("drm_agp") +#endif + +static const drm_agp_t *drm_agp = NULL; + +int DRM(agp_info)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + agp_kern_info *kern; + drm_agp_info_t info; + + if (!dev->agp || !dev->agp->acquired || !drm_agp->copy_info) + return -EINVAL; + + kern = &dev->agp->agp_info; + info.agp_version_major = kern->version.major; + info.agp_version_minor = kern->version.minor; + info.mode = kern->mode; + info.aperture_base = kern->aper_base; + info.aperture_size = kern->aper_size * 1024 * 1024; + info.memory_allowed = kern->max_memory << PAGE_SHIFT; + info.memory_used = kern->current_memory << PAGE_SHIFT; + info.id_vendor = kern->device->vendor; + info.id_device = kern->device->device; + + if (copy_to_user((drm_agp_info_t *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +int DRM(agp_acquire)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode; + + if (!dev->agp || dev->agp->acquired || !drm_agp->acquire) + return -EINVAL; + if ((retcode = drm_agp->acquire())) return retcode; + dev->agp->acquired = 1; + return 0; +} + +int DRM(agp_release)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + if (!dev->agp || !dev->agp->acquired || !drm_agp->release) + return -EINVAL; + drm_agp->release(); + dev->agp->acquired = 0; + return 0; + +} + +void DRM(agp_do_release)(void) +{ + if (drm_agp->release) drm_agp->release(); +} + +int DRM(agp_enable)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_mode_t mode; + + if (!dev->agp || !dev->agp->acquired || !drm_agp->enable) + return -EINVAL; + + if (copy_from_user(&mode, (drm_agp_mode_t *)arg, sizeof(mode))) + return -EFAULT; + + dev->agp->mode = mode.mode; + drm_agp->enable(mode.mode); + dev->agp->base = dev->agp->agp_info.aper_base; + dev->agp->enabled = 1; + return 0; +} + +int DRM(agp_alloc)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_buffer_t request; + drm_agp_mem_t *entry; + agp_memory *memory; + unsigned long pages; + u32 type; + + if (!dev->agp || !dev->agp->acquired) return -EINVAL; + if (copy_from_user(&request, (drm_agp_buffer_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = DRM(alloc)(sizeof(*entry), DRM_MEM_AGPLISTS))) + return -ENOMEM; + + memset(entry, 0, sizeof(*entry)); + + pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE; + type = (u32) request.type; + + if (!(memory = DRM(alloc_agp)(pages, type))) { + DRM(free)(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return -ENOMEM; + } + + entry->handle = (unsigned long)memory->memory; + entry->memory = memory; + entry->bound = 0; + entry->pages = pages; + entry->prev = NULL; + entry->next = dev->agp->memory; + if (dev->agp->memory) dev->agp->memory->prev = entry; + dev->agp->memory = entry; + + request.handle = entry->handle; + request.physical = memory->physical; + + if (copy_to_user((drm_agp_buffer_t *)arg, &request, sizeof(request))) { + dev->agp->memory = entry->next; + dev->agp->memory->prev = NULL; + DRM(free_agp)(memory, pages); + DRM(free)(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return -EFAULT; + } + return 0; +} + +static drm_agp_mem_t *DRM(agp_lookup_entry)(drm_device_t *dev, + unsigned long handle) +{ + drm_agp_mem_t *entry; + + for (entry = dev->agp->memory; entry; entry = entry->next) { + if (entry->handle == handle) return entry; + } + return NULL; +} + +int DRM(agp_unbind)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_binding_t request; + drm_agp_mem_t *entry; + + if (!dev->agp || !dev->agp->acquired) return -EINVAL; + if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = DRM(agp_lookup_entry)(dev, request.handle))) + return -EINVAL; + if (!entry->bound) return -EINVAL; + return DRM(unbind_agp)(entry->memory); +} + +int DRM(agp_bind)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_binding_t request; + drm_agp_mem_t *entry; + int retcode; + int page; + + if (!dev->agp || !dev->agp->acquired || !drm_agp->bind_memory) + return -EINVAL; + if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = DRM(agp_lookup_entry)(dev, request.handle))) + return -EINVAL; + if (entry->bound) return -EINVAL; + page = (request.offset + PAGE_SIZE - 1) / PAGE_SIZE; + if ((retcode = DRM(bind_agp)(entry->memory, page))) return retcode; + entry->bound = dev->agp->base + (page << PAGE_SHIFT); + DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n", + dev->agp->base, entry->bound); + return 0; +} + +int DRM(agp_free)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_buffer_t request; + drm_agp_mem_t *entry; + + if (!dev->agp || !dev->agp->acquired) return -EINVAL; + if (copy_from_user(&request, (drm_agp_buffer_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = DRM(agp_lookup_entry)(dev, request.handle))) + return -EINVAL; + if (entry->bound) DRM(unbind_agp)(entry->memory); + + if (entry->prev) entry->prev->next = entry->next; + else dev->agp->memory = entry->next; + if (entry->next) entry->next->prev = entry->prev; + DRM(free_agp)(entry->memory, entry->pages); + DRM(free)(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return 0; +} + +drm_agp_head_t *DRM(agp_init)(void) +{ + drm_agp_head_t *head = NULL; + + drm_agp = DRM_AGP_GET; + if (drm_agp) { + if (!(head = DRM(alloc)(sizeof(*head), DRM_MEM_AGPLISTS))) + return NULL; + memset((void *)head, 0, sizeof(*head)); + drm_agp->copy_info(&head->agp_info); + if (head->agp_info.chipset == NOT_SUPPORTED) { + DRM(free)(head, sizeof(*head), DRM_MEM_AGPLISTS); + return NULL; + } + head->memory = NULL; + switch (head->agp_info.chipset) { + case INTEL_GENERIC: head->chipset = "Intel"; break; + case INTEL_LX: head->chipset = "Intel 440LX"; break; + case INTEL_BX: head->chipset = "Intel 440BX"; break; + case INTEL_GX: head->chipset = "Intel 440GX"; break; + case INTEL_I810: head->chipset = "Intel i810"; break; + +#if LINUX_VERSION_CODE >= 0x020400 + case INTEL_I840: head->chipset = "Intel i840"; break; +#endif + + case VIA_GENERIC: head->chipset = "VIA"; break; + case VIA_VP3: head->chipset = "VIA VP3"; break; + case VIA_MVP3: head->chipset = "VIA MVP3"; break; + +#if LINUX_VERSION_CODE >= 0x020400 + case VIA_MVP4: head->chipset = "VIA MVP4"; break; + case VIA_APOLLO_KX133: head->chipset = "VIA Apollo KX133"; + break; + case VIA_APOLLO_KT133: head->chipset = "VIA Apollo KT133"; + break; +#endif + + case VIA_APOLLO_PRO: head->chipset = "VIA Apollo Pro"; + break; + case SIS_GENERIC: head->chipset = "SiS"; break; + case AMD_GENERIC: head->chipset = "AMD"; break; + case AMD_IRONGATE: head->chipset = "AMD Irongate"; break; + case ALI_GENERIC: head->chipset = "ALi"; break; + case ALI_M1541: head->chipset = "ALi M1541"; break; + default: head->chipset = "Unknown"; break; + } + DRM_INFO("AGP %d.%d on %s @ 0x%08lx %ZuMB\n", + head->agp_info.version.major, + head->agp_info.version.minor, + head->chipset, + head->agp_info.aper_base, + head->agp_info.aper_size); + } + return head; +} + +void DRM(agp_uninit)(void) +{ + DRM_AGP_PUT; + drm_agp = NULL; +} + +agp_memory *DRM(agp_allocate_memory)(size_t pages, u32 type) +{ + if (!drm_agp->allocate_memory) return NULL; + return drm_agp->allocate_memory(pages, type); +} + +int DRM(agp_free_memory)(agp_memory *handle) +{ + if (!handle || !drm_agp->free_memory) return 0; + drm_agp->free_memory(handle); + return 1; +} + +int DRM(agp_bind_memory)(agp_memory *handle, off_t start) +{ + if (!handle || !drm_agp->bind_memory) return -EINVAL; + return drm_agp->bind_memory(handle, start); +} + +int DRM(agp_unbind_memory)(agp_memory *handle) +{ + if (!handle || !drm_agp->unbind_memory) return -EINVAL; + return drm_agp->unbind_memory(handle); +} + +#endif /* __REALLY_HAVE_AGP */ diff --git a/linux-core/drm_bufs.c b/linux-core/drm_bufs.c new file mode 100644 index 00000000..16af7bd5 --- /dev/null +++ b/linux-core/drm_bufs.c @@ -0,0 +1,1023 @@ +/* drm_bufs.h -- Generic buffer template -*- linux-c -*- + * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com + * + * Copyright 1999, 2000 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 + * VA LINUX SYSTEMS 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 <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include <linux/vmalloc.h> +#include "drmP.h" + +#ifndef __HAVE_PCI_DMA +#define __HAVE_PCI_DMA 0 +#endif + +#ifndef __HAVE_SG +#define __HAVE_SG 0 +#endif + +#ifndef DRIVER_BUF_PRIV_T +#define DRIVER_BUF_PRIV_T u32 +#endif +#ifndef DRIVER_AGP_BUFFERS_MAP +#if __HAVE_AGP && __HAVE_DMA +#error "You must define DRIVER_AGP_BUFFERS_MAP()" +#else +#define DRIVER_AGP_BUFFERS_MAP( dev ) NULL +#endif +#endif + +/* + * Compute order. Can be made faster. + */ +int DRM(order)( unsigned long size ) +{ + int order; + unsigned long tmp; + + for ( order = 0, tmp = size ; tmp >>= 1 ; ++order ); + + if ( size & ~(1 << order) ) + ++order; + + return order; +} + +int DRM(addmap)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map; + drm_map_list_t *list; + + if ( !(filp->f_mode & 3) ) return -EACCES; /* Require read/write */ + + map = DRM(alloc)( sizeof(*map), DRM_MEM_MAPS ); + if ( !map ) + return -ENOMEM; + + if ( copy_from_user( map, (drm_map_t *)arg, sizeof(*map) ) ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -EFAULT; + } + + /* Only allow shared memory to be removable since we only keep enough + * book keeping information about shared memory to allow for removal + * when processes fork. + */ + if ( (map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -EINVAL; + } + DRM_DEBUG( "offset = 0x%08lx, size = 0x%08lx, type = %d\n", + map->offset, map->size, map->type ); + if ( (map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK)) ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -EINVAL; + } + map->mtrr = -1; + map->handle = 0; + + switch ( map->type ) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#if !defined(__sparc__) && !defined(__alpha__) + if ( map->offset + map->size < map->offset || + map->offset < virt_to_phys(high_memory) ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -EINVAL; + } +#endif +#ifdef __alpha__ + map->offset += dev->hose->mem_space->start; +#endif +#if __REALLY_HAVE_MTRR + if ( map->type == _DRM_FRAME_BUFFER || + (map->flags & _DRM_WRITE_COMBINING) ) { + map->mtrr = mtrr_add( map->offset, map->size, + MTRR_TYPE_WRCOMB, 1 ); + } +#endif + map->handle = DRM(ioremap)( map->offset, map->size ); + break; + + case _DRM_SHM: + map->handle = vmalloc_32(map->size); + DRM_DEBUG( "%ld %d %p\n", + map->size, DRM(order)( map->size ), map->handle ); + if ( !map->handle ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -ENOMEM; + } + map->offset = (unsigned long)map->handle; + if ( map->flags & _DRM_CONTAINS_LOCK ) { + dev->lock.hw_lock = map->handle; /* Pointer to lock */ + } + break; +#if __REALLY_HAVE_AGP + case _DRM_AGP: +#ifdef __alpha__ + map->offset += dev->hose->mem_space->start; +#endif + map->offset = map->offset + dev->agp->base; + map->mtrr = dev->agp->agp_mtrr; /* for getmap */ + break; +#endif + case _DRM_SCATTER_GATHER: + if (!dev->sg) { + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } + map->offset = map->offset + dev->sg->handle; + break; + + default: + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -EINVAL; + } + + list = DRM(alloc)(sizeof(*list), DRM_MEM_MAPS); + if(!list) { + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } + memset(list, 0, sizeof(*list)); + list->map = map; + + down(&dev->struct_sem); + list_add(&list->head, &dev->maplist->head); + up(&dev->struct_sem); + + if ( copy_to_user( (drm_map_t *)arg, map, sizeof(*map) ) ) + return -EFAULT; + if ( map->type != _DRM_SHM ) { + if ( copy_to_user( &((drm_map_t *)arg)->handle, + &map->offset, + sizeof(map->offset) ) ) + return -EFAULT; + } + return 0; +} + + +/* Remove a map private from list and deallocate resources if the mapping + * isn't in use. + */ + +int DRM(rmmap)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + struct list_head *list; + drm_map_list_t *r_list; + drm_vma_entry_t *pt, *prev; + drm_map_t *map; + drm_map_t request; + int found_maps = 0; + + if (copy_from_user(&request, (drm_map_t *)arg, + sizeof(request))) { + return -EFAULT; + } + + down(&dev->struct_sem); + list = &dev->maplist->head; + list_for_each(list, &dev->maplist->head) { + r_list = (drm_map_list_t *) list; + + if(r_list->map && + r_list->map->handle == request.handle && + r_list->map->flags & _DRM_REMOVABLE) break; + } + + /* List has wrapped around to the head pointer, or its empty we didn't + * find anything. + */ + if(list == (&dev->maplist->head)) { + up(&dev->struct_sem); + return -EINVAL; + } + map = r_list->map; + list_del(list); + DRM(free)(list, sizeof(*list), DRM_MEM_MAPS); + + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { +#if LINUX_VERSION_CODE >= 0x020300 + if (pt->vma->vm_private_data == map) found_maps++; +#else + if (pt->vma->vm_pte == map) found_maps++; +#endif + } + + if(!found_maps) { + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#if __REALLY_HAVE_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + DRM(ioremapfree)(map->handle, map->size); + break; + case _DRM_SHM: + vfree(map->handle); + break; + case _DRM_AGP: + case _DRM_SCATTER_GATHER: + break; + } + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + } + up(&dev->struct_sem); + return 0; +} + +#if __HAVE_DMA + +#if __REALLY_HAVE_AGP +int DRM(addbufs_agp)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + + if ( !dma ) return -EINVAL; + + if ( copy_from_user( &request, (drm_buf_desc_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + count = request.count; + order = DRM(order)( request.size ); + size = 1 << order; + + alignment = (request.flags & _DRM_PAGE_ALIGN) + ? PAGE_ALIGN(size) : size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + byte_count = 0; + agp_offset = dev->agp->base + request.agp_start; + + DRM_DEBUG( "count: %d\n", count ); + DRM_DEBUG( "order: %d\n", order ); + DRM_DEBUG( "size: %d\n", size ); + DRM_DEBUG( "agp_offset: %ld\n", agp_offset ); + DRM_DEBUG( "alignment: %d\n", alignment ); + DRM_DEBUG( "page_order: %d\n", page_order ); + DRM_DEBUG( "total: %d\n", total ); + + if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL; + if ( dev->queue_count ) return -EBUSY; /* Not while in use */ + + spin_lock( &dev->count_lock ); + if ( dev->buf_use ) { + spin_unlock( &dev->count_lock ); + return -EBUSY; + } + atomic_inc( &dev->buf_alloc ); + spin_unlock( &dev->count_lock ); + + down( &dev->struct_sem ); + entry = &dma->bufs[order]; + if ( entry->buf_count ) { + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = DRM(alloc)( count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + if ( !entry->buflist ) { + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; + } + memset( entry->buflist, 0, count * sizeof(*entry->buflist) ); + + entry->buf_size = size; + entry->page_order = page_order; + + offset = 0; + + while ( entry->buf_count < count ) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + + buf->offset = (dma->byte_count + offset); + buf->bus_address = agp_offset + offset; + buf->address = (void *)(agp_offset + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head( &buf->dma_wait ); + buf->pid = 0; + + buf->dev_priv_size = sizeof(DRIVER_BUF_PRIV_T); + buf->dev_private = DRM(alloc)( sizeof(DRIVER_BUF_PRIV_T), + DRM_MEM_BUFS ); + memset( buf->dev_private, 0, buf->dev_priv_size ); + +#if __HAVE_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG( "buffer %d @ %p\n", + entry->buf_count, buf->address ); + + offset += alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + } + + DRM_DEBUG( "byte_count: %d\n", byte_count ); + + dma->buflist = DRM(realloc)( dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + } + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + + DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count ); + DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count ); + +#if __HAVE_DMA_FREELIST + DRM(freelist_create)( &entry->freelist, entry->buf_count ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + DRM(freelist_put)( dev, &entry->freelist, &entry->buflist[i] ); + } +#endif + up( &dev->struct_sem ); + + request.count = entry->buf_count; + request.size = size; + + if ( copy_to_user( (drm_buf_desc_t *)arg, &request, sizeof(request) ) ) + return -EFAULT; + + dma->flags = _DRM_DMA_USE_AGP; + + atomic_dec( &dev->buf_alloc ); + return 0; +} +#endif /* __REALLY_HAVE_AGP */ + +#if __HAVE_PCI_DMA +int DRM(addbufs_pci)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int count; + int order; + int size; + int total; + int page_order; + drm_buf_entry_t *entry; + unsigned long page; + drm_buf_t *buf; + int alignment; + unsigned long offset; + int i; + int byte_count; + int page_count; + + if ( !dma ) return -EINVAL; + + if ( copy_from_user( &request, (drm_buf_desc_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + count = request.count; + order = DRM(order)( request.size ); + size = 1 << order; + + DRM_DEBUG( "count=%d, size=%d (%d), order=%d, queue_count=%d\n", + request.count, request.size, size, + order, dev->queue_count ); + + if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL; + if ( dev->queue_count ) return -EBUSY; /* Not while in use */ + + alignment = (request.flags & _DRM_PAGE_ALIGN) + ? PAGE_ALIGN(size) : size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + spin_lock( &dev->count_lock ); + if ( dev->buf_use ) { + spin_unlock( &dev->count_lock ); + return -EBUSY; + } + atomic_inc( &dev->buf_alloc ); + spin_unlock( &dev->count_lock ); + + down( &dev->struct_sem ); + entry = &dma->bufs[order]; + if ( entry->buf_count ) { + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = DRM(alloc)( count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + if ( !entry->buflist ) { + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; + } + memset( entry->buflist, 0, count * sizeof(*entry->buflist) ); + + entry->seglist = DRM(alloc)( count * sizeof(*entry->seglist), + DRM_MEM_SEGS ); + if ( !entry->seglist ) { + DRM(free)( entry->buflist, + count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; + } + memset( entry->seglist, 0, count * sizeof(*entry->seglist) ); + + dma->pagelist = DRM(realloc)( dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + (dma->page_count + (count << page_order)) + * sizeof(*dma->pagelist), + DRM_MEM_PAGES ); + DRM_DEBUG( "pagelist: %d entries\n", + dma->page_count + (count << page_order) ); + + entry->buf_size = size; + entry->page_order = page_order; + byte_count = 0; + page_count = 0; + + while ( entry->buf_count < count ) { + page = DRM(alloc_pages)( page_order, DRM_MEM_DMA ); + if ( !page ) break; + entry->seglist[entry->seg_count++] = page; + for ( i = 0 ; i < (1 << page_order) ; i++ ) { + DRM_DEBUG( "page %d @ 0x%08lx\n", + dma->page_count + page_count, + page + PAGE_SIZE * i ); + dma->pagelist[dma->page_count + page_count++] + = page + PAGE_SIZE * i; + } + for ( offset = 0 ; + offset + size <= total && entry->buf_count < count ; + offset += alignment, ++entry->buf_count ) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = (dma->byte_count + byte_count + offset); + buf->address = (void *)(page + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head( &buf->dma_wait ); + buf->pid = 0; +#if __HAVE_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG( "buffer %d @ %p\n", + entry->buf_count, buf->address ); + } + byte_count += PAGE_SIZE << page_order; + } + + dma->buflist = DRM(realloc)( dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + } + + dma->buf_count += entry->buf_count; + dma->seg_count += entry->seg_count; + dma->page_count += entry->seg_count << page_order; + dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); + +#if __HAVE_DMA_FREELIST + DRM(freelist_create)( &entry->freelist, entry->buf_count ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + DRM(freelist_put)( dev, &entry->freelist, &entry->buflist[i] ); + } +#endif + up( &dev->struct_sem ); + + request.count = entry->buf_count; + request.size = size; + + if ( copy_to_user( (drm_buf_desc_t *)arg, &request, sizeof(request) ) ) + return -EFAULT; + + atomic_dec( &dev->buf_alloc ); + return 0; +} +#endif /* __HAVE_PCI_DMA */ + +#ifdef __HAVE_SG +int DRM(addbufs_sg)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + + if ( !dma ) return -EINVAL; + + if ( copy_from_user( &request, (drm_buf_desc_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + count = request.count; + order = DRM(order)( request.size ); + size = 1 << order; + + alignment = (request.flags & _DRM_PAGE_ALIGN) + ? PAGE_ALIGN(size) : size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + byte_count = 0; + agp_offset = request.agp_start; + + DRM_DEBUG( "count: %d\n", count ); + DRM_DEBUG( "order: %d\n", order ); + DRM_DEBUG( "size: %d\n", size ); + DRM_DEBUG( "agp_offset: %ld\n", agp_offset ); + DRM_DEBUG( "alignment: %d\n", alignment ); + DRM_DEBUG( "page_order: %d\n", page_order ); + DRM_DEBUG( "total: %d\n", total ); + + if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL; + if ( dev->queue_count ) return -EBUSY; /* Not while in use */ + + spin_lock( &dev->count_lock ); + if ( dev->buf_use ) { + spin_unlock( &dev->count_lock ); + return -EBUSY; + } + atomic_inc( &dev->buf_alloc ); + spin_unlock( &dev->count_lock ); + + down( &dev->struct_sem ); + entry = &dma->bufs[order]; + if ( entry->buf_count ) { + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = DRM(alloc)( count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + if ( !entry->buflist ) { + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; + } + memset( entry->buflist, 0, count * sizeof(*entry->buflist) ); + + entry->buf_size = size; + entry->page_order = page_order; + + offset = 0; + + while ( entry->buf_count < count ) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + + buf->offset = (dma->byte_count + offset); + buf->bus_address = agp_offset + offset; + buf->address = (void *)(agp_offset + offset + dev->sg->handle); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head( &buf->dma_wait ); + buf->pid = 0; + + buf->dev_priv_size = sizeof(DRIVER_BUF_PRIV_T); + buf->dev_private = DRM(alloc)( sizeof(DRIVER_BUF_PRIV_T), + DRM_MEM_BUFS ); + memset( buf->dev_private, 0, buf->dev_priv_size ); + +#if __HAVE_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG( "buffer %d @ %p\n", + entry->buf_count, buf->address ); + + offset += alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + } + + DRM_DEBUG( "byte_count: %d\n", byte_count ); + + dma->buflist = DRM(realloc)( dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + } + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + + DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count ); + DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count ); + +#if __HAVE_DMA_FREELIST + DRM(freelist_create)( &entry->freelist, entry->buf_count ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + DRM(freelist_put)( dev, &entry->freelist, &entry->buflist[i] ); + } +#endif + up( &dev->struct_sem ); + + request.count = entry->buf_count; + request.size = size; + + if ( copy_to_user( (drm_buf_desc_t *)arg, &request, sizeof(request) ) ) + return -EFAULT; + + dma->flags = _DRM_DMA_USE_SG; + + atomic_dec( &dev->buf_alloc ); + return 0; +} +#endif /* __HAVE_SG */ + +int DRM(addbufs)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_buf_desc_t request; + + if ( copy_from_user( &request, (drm_buf_desc_t *)arg, + sizeof(request) ) ) + return -EFAULT; + +#if __REALLY_HAVE_AGP + if ( request.flags & _DRM_AGP_BUFFER ) + return DRM(addbufs_agp)( inode, filp, cmd, arg ); + else +#endif +#if __HAVE_SG + if ( request.flags & _DRM_SG_BUFFER ) + return DRM(addbufs_sg)( inode, filp, cmd, arg ); + else +#endif +#if __HAVE_PCI_DMA + return DRM(addbufs_pci)( inode, filp, cmd, arg ); +#else + return -EINVAL; +#endif +} + +int DRM(infobufs)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_info_t request; + int i; + int count; + + if ( !dma ) return -EINVAL; + + spin_lock( &dev->count_lock ); + if ( atomic_read( &dev->buf_alloc ) ) { + spin_unlock( &dev->count_lock ); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock( &dev->count_lock ); + + if ( copy_from_user( &request, + (drm_buf_info_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) { + if ( dma->bufs[i].buf_count ) ++count; + } + + DRM_DEBUG( "count = %d\n", count ); + + if ( request.count >= count ) { + for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) { + if ( dma->bufs[i].buf_count ) { + drm_buf_desc_t *to = &request.list[count]; + drm_buf_entry_t *from = &dma->bufs[i]; + drm_freelist_t *list = &dma->bufs[i].freelist; + if ( copy_to_user( &to->count, + &from->buf_count, + sizeof(from->buf_count) ) || + copy_to_user( &to->size, + &from->buf_size, + sizeof(from->buf_size) ) || + copy_to_user( &to->low_mark, + &list->low_mark, + sizeof(list->low_mark) ) || + copy_to_user( &to->high_mark, + &list->high_mark, + sizeof(list->high_mark) ) ) + return -EFAULT; + + DRM_DEBUG( "%d %d %d %d %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].buf_size, + dma->bufs[i].freelist.low_mark, + dma->bufs[i].freelist.high_mark ); + ++count; + } + } + } + request.count = count; + + if ( copy_to_user( (drm_buf_info_t *)arg, + &request, + sizeof(request) ) ) + return -EFAULT; + + return 0; +} + +int DRM(markbufs)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int order; + drm_buf_entry_t *entry; + + if ( !dma ) return -EINVAL; + + if ( copy_from_user( &request, + (drm_buf_desc_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + DRM_DEBUG( "%d, %d, %d\n", + request.size, request.low_mark, request.high_mark ); + order = DRM(order)( request.size ); + if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL; + entry = &dma->bufs[order]; + + if ( request.low_mark < 0 || request.low_mark > entry->buf_count ) + return -EINVAL; + if ( request.high_mark < 0 || request.high_mark > entry->buf_count ) + return -EINVAL; + + entry->freelist.low_mark = request.low_mark; + entry->freelist.high_mark = request.high_mark; + + return 0; +} + +int DRM(freebufs)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_free_t request; + int i; + int idx; + drm_buf_t *buf; + + if ( !dma ) return -EINVAL; + + if ( copy_from_user( &request, + (drm_buf_free_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + DRM_DEBUG( "%d\n", request.count ); + for ( i = 0 ; i < request.count ; i++ ) { + if ( copy_from_user( &idx, + &request.list[i], + sizeof(idx) ) ) + return -EFAULT; + if ( idx < 0 || idx >= dma->buf_count ) { + DRM_ERROR( "Index %d (of %d max)\n", + idx, dma->buf_count - 1 ); + return -EINVAL; + } + buf = dma->buflist[idx]; + if ( buf->pid != current->pid ) { + DRM_ERROR( "Process %d freeing buffer owned by %d\n", + current->pid, buf->pid ); + return -EINVAL; + } + DRM(free_buffer)( dev, buf ); + } + + return 0; +} + +int DRM(mapbufs)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + const int zero = 0; + unsigned long virtual; + unsigned long address; + drm_buf_map_t request; + int i; + + if ( !dma ) return -EINVAL; + + spin_lock( &dev->count_lock ); + if ( atomic_read( &dev->buf_alloc ) ) { + spin_unlock( &dev->count_lock ); + return -EBUSY; + } + dev->buf_use++; /* Can't allocate more after this call */ + spin_unlock( &dev->count_lock ); + + if ( copy_from_user( &request, (drm_buf_map_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + if ( request.count >= dma->buf_count ) { + if ( (__HAVE_AGP && (dma->flags & _DRM_DMA_USE_AGP)) || + (__HAVE_SG && (dma->flags & _DRM_DMA_USE_SG)) ) { + drm_map_t *map = DRIVER_AGP_BUFFERS_MAP( dev ); + + if ( !map ) { + retcode = -EINVAL; + goto done; + } + +#if LINUX_VERSION_CODE <= 0x020402 + down( ¤t->mm->mmap_sem ); +#else + down_write( ¤t->mm->mmap_sem ); +#endif + virtual = do_mmap( filp, 0, map->size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + (unsigned long)map->offset ); +#if LINUX_VERSION_CODE <= 0x020402 + up( ¤t->mm->mmap_sem ); +#else + up_write( ¤t->mm->mmap_sem ); +#endif + } else { +#if LINUX_VERSION_CODE <= 0x020402 + down( ¤t->mm->mmap_sem ); +#else + down_write( ¤t->mm->mmap_sem ); +#endif + virtual = do_mmap( filp, 0, dma->byte_count, + PROT_READ | PROT_WRITE, + MAP_SHARED, 0 ); +#if LINUX_VERSION_CODE <= 0x020402 + up( ¤t->mm->mmap_sem ); +#else + up_write( ¤t->mm->mmap_sem ); +#endif + } + if ( virtual > -1024UL ) { + /* Real error */ + retcode = (signed long)virtual; + goto done; + } + request.virtual = (void *)virtual; + + for ( i = 0 ; i < dma->buf_count ; i++ ) { + if ( copy_to_user( &request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx) ) ) { + retcode = -EFAULT; + goto done; + } + if ( copy_to_user( &request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total) ) ) { + retcode = -EFAULT; + goto done; + } + if ( copy_to_user( &request.list[i].used, + &zero, + sizeof(zero) ) ) { + retcode = -EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; /* *** */ + if ( copy_to_user( &request.list[i].address, + &address, + sizeof(address) ) ) { + retcode = -EFAULT; + goto done; + } + } + } + done: + request.count = dma->buf_count; + DRM_DEBUG( "%d buffers, retcode = %d\n", request.count, retcode ); + + if ( copy_to_user( (drm_buf_map_t *)arg, &request, sizeof(request) ) ) + return -EFAULT; + + return retcode; +} + +#endif /* __HAVE_DMA */ diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 7447ca6d..5337646f 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -81,6 +81,9 @@ #ifndef __HAVE_COUNTERS #define __HAVE_COUNTERS 0 #endif +#ifndef __HAVE_SG +#define __HAVE_SG 0 +#endif #ifndef DRIVER_PREINIT #define DRIVER_PREINIT() @@ -178,6 +181,11 @@ static drm_ioctl_desc_t DRM(ioctls)[] = { [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { DRM(agp_unbind), 1, 1 }, #endif +#if __HAVE_SG + [DRM_IOCTL_NR(DRM_IOCTL_SG_ALLOC)] = { DRM(sg_alloc), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)] = { DRM(sg_free), 1, 1 }, +#endif + DRIVER_IOCTLS }; @@ -415,6 +423,17 @@ static int DRM(takedown)( drm_device_t *dev ) * handled in the AGP/GART driver. */ break; + case _DRM_SCATTER_GATHER: + /* Handle it, but do nothing, if HAVE_SG + * isn't defined. + */ +#if __HAVE_SG + if(dev->sg) { + DRM(sg_cleanup)(dev->sg); + dev->sg = NULL; + } +#endif + break; } DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); } diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c new file mode 100644 index 00000000..75752b3a --- /dev/null +++ b/linux-core/drm_fops.c @@ -0,0 +1,223 @@ +/* drm_fops.h -- File operations for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.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 + * VA LINUX SYSTEMS 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 <faith@valinux.com> + * Daryll Strauss <daryll@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include <linux/poll.h> + +/* drm_open is called whenever a process opens /dev/drm. */ + +int DRM(open_helper)(struct inode *inode, struct file *filp, drm_device_t *dev) +{ + kdev_t minor = MINOR(inode->i_rdev); + drm_file_t *priv; + + if (filp->f_flags & O_EXCL) return -EBUSY; /* No exclusive opens */ + if (!DRM(cpu_valid)()) return -EINVAL; + + DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor); + + priv = DRM(alloc)(sizeof(*priv), DRM_MEM_FILES); + memset(priv, 0, sizeof(*priv)); + filp->private_data = priv; + priv->uid = current->euid; + priv->pid = current->pid; + priv->minor = minor; + priv->dev = dev; + priv->ioctl_count = 0; + priv->authenticated = capable(CAP_SYS_ADMIN); + + down(&dev->struct_sem); + if (!dev->file_last) { + priv->next = NULL; + priv->prev = NULL; + dev->file_first = priv; + dev->file_last = priv; + } else { + priv->next = NULL; + priv->prev = dev->file_last; + dev->file_last->next = priv; + dev->file_last = priv; + } + up(&dev->struct_sem); + +#ifdef __alpha__ + /* + * Default the hose + */ + if (!dev->hose) { + struct pci_dev *pci_dev; + pci_dev = pci_find_class(PCI_CLASS_DISPLAY_VGA << 8, NULL); + if (pci_dev) dev->hose = pci_dev->sysdata; + if (!dev->hose) { + struct pci_bus *b = pci_bus_b(pci_root_buses.next); + if (b) dev->hose = b->sysdata; + } + } +#endif + + return 0; +} + +int DRM(flush)(struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + return 0; +} + +int DRM(fasync)(int fd, struct file *filp, int on) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode; + + DRM_DEBUG("fd = %d, device = 0x%x\n", fd, dev->device); + retcode = fasync_helper(fd, filp, on, &dev->buf_async); + if (retcode < 0) return retcode; + return 0; +} + + +/* The drm_read and drm_write_string code (especially that which manages + the circular buffer), is based on Alessandro Rubini's LINUX DEVICE + DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */ + +ssize_t DRM(read)(struct file *filp, char *buf, size_t count, loff_t *off) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int left; + int avail; + int send; + int cur; + + DRM_DEBUG("%p, %p\n", dev->buf_rp, dev->buf_wp); + + while (dev->buf_rp == dev->buf_wp) { + DRM_DEBUG(" sleeping\n"); + if (filp->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + interruptible_sleep_on(&dev->buf_readers); + if (signal_pending(current)) { + DRM_DEBUG(" interrupted\n"); + return -ERESTARTSYS; + } + DRM_DEBUG(" awake\n"); + } + + left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + avail = DRM_BSZ - left; + send = DRM_MIN(avail, count); + + while (send) { + if (dev->buf_wp > dev->buf_rp) { + cur = DRM_MIN(send, dev->buf_wp - dev->buf_rp); + } else { + cur = DRM_MIN(send, dev->buf_end - dev->buf_rp); + } + if (copy_to_user(buf, dev->buf_rp, cur)) + return -EFAULT; + dev->buf_rp += cur; + if (dev->buf_rp == dev->buf_end) dev->buf_rp = dev->buf; + send -= cur; + } + + wake_up_interruptible(&dev->buf_writers); + return DRM_MIN(avail, count);; +} + +int DRM(write_string)(drm_device_t *dev, const char *s) +{ + int left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + int send = strlen(s); + int count; + + DRM_DEBUG("%d left, %d to send (%p, %p)\n", + left, send, dev->buf_rp, dev->buf_wp); + + if (left == 1 || dev->buf_wp != dev->buf_rp) { + DRM_ERROR("Buffer not empty (%d left, wp = %p, rp = %p)\n", + left, + dev->buf_wp, + dev->buf_rp); + } + + while (send) { + if (dev->buf_wp >= dev->buf_rp) { + count = DRM_MIN(send, dev->buf_end - dev->buf_wp); + if (count == left) --count; /* Leave a hole */ + } else { + count = DRM_MIN(send, dev->buf_rp - dev->buf_wp - 1); + } + strncpy(dev->buf_wp, s, count); + dev->buf_wp += count; + if (dev->buf_wp == dev->buf_end) dev->buf_wp = dev->buf; + send -= count; + } + +#if LINUX_VERSION_CODE < 0x020315 && !defined(KILLFASYNCHASTHREEPARAMETERS) + /* The extra parameter to kill_fasync was added in 2.3.21, and is + _not_ present in _stock_ 2.2.14 and 2.2.15. However, some + distributions patch 2.2.x kernels to add this parameter. The + Makefile.linux attempts to detect this addition and defines + KILLFASYNCHASTHREEPARAMETERS if three parameters are found. */ + if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO); +#else + + /* Parameter added in 2.3.21. */ +#if LINUX_VERSION_CODE < 0x020400 + if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO, POLL_IN); +#else + /* Type of first parameter changed in + Linux 2.4.0-test2... */ + if (dev->buf_async) kill_fasync(&dev->buf_async, SIGIO, POLL_IN); +#endif +#endif + DRM_DEBUG("waking\n"); + wake_up_interruptible(&dev->buf_readers); + return 0; +} + +unsigned int DRM(poll)(struct file *filp, struct poll_table_struct *wait) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + poll_wait(filp, &dev->buf_readers, wait); + if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM; + return 0; +} diff --git a/linux-core/drm_init.c b/linux-core/drm_init.c new file mode 100644 index 00000000..d9d8e3a2 --- /dev/null +++ b/linux-core/drm_init.c @@ -0,0 +1,116 @@ +/* drm_init.h -- Setup/Cleanup for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.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 + * VA LINUX SYSTEMS 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 <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +#if 0 +int DRM(flags) = DRM_FLAG_DEBUG; +#else +int DRM(flags) = 0; +#endif + +/* drm_parse_option parses a single option. See description for + * drm_parse_options for details. + */ +static void DRM(parse_option)(char *s) +{ + char *c, *r; + + DRM_DEBUG("\"%s\"\n", s); + if (!s || !*s) return; + for (c = s; *c && *c != ':'; c++); /* find : or \0 */ + if (*c) r = c + 1; else r = NULL; /* remember remainder */ + *c = '\0'; /* terminate */ + if (!strcmp(s, "noctx")) { + DRM(flags) |= DRM_FLAG_NOCTX; + DRM_INFO("Server-mediated context switching OFF\n"); + return; + } + if (!strcmp(s, "debug")) { + DRM(flags) |= DRM_FLAG_DEBUG; + DRM_INFO("Debug messages ON\n"); + return; + } + DRM_ERROR("\"%s\" is not a valid option\n", s); + return; +} + +/* drm_parse_options parse the insmod "drm=" options, or the command-line + * options passed to the kernel via LILO. The grammar of the format is as + * follows: + * + * drm ::= 'drm=' option_list + * option_list ::= option [ ';' option_list ] + * option ::= 'device:' major + * | 'debug' + * | 'noctx' + * major ::= INTEGER + * + * Note that 's' contains option_list without the 'drm=' part. + * + * device=major,minor specifies the device number used for /dev/drm + * if major == 0 then the misc device is used + * if major == 0 and minor == 0 then dynamic misc allocation is used + * debug=on specifies that debugging messages will be printk'd + * debug=trace specifies that each function call will be logged via printk + * debug=off turns off all debugging options + * + */ + +void DRM(parse_options)(char *s) +{ + char *h, *t, *n; + + DRM_DEBUG("\"%s\"\n", s ?: ""); + if (!s || !*s) return; + + for (h = t = n = s; h && *h; h = n) { + for (; *t && *t != ';'; t++); /* find ; or \0 */ + if (*t) n = t + 1; else n = NULL; /* remember next */ + *t = '\0'; /* terminate */ + DRM(parse_option)(h); /* parse */ + } +} + +/* drm_cpu_valid returns non-zero if the DRI will run on this CPU, and 0 + * otherwise. + */ +int DRM(cpu_valid)(void) +{ +#if defined(__i386__) + if (boot_cpu_data.x86 == 3) return 0; /* No cmpxchg on a 386 */ +#endif +#if defined(__sparc__) && !defined(__sparc_v9__) + return 0; /* No cmpxchg before v9 sparc. */ +#endif + return 1; +} diff --git a/linux-core/drm_ioctl.c b/linux-core/drm_ioctl.c new file mode 100644 index 00000000..1cc8f31f --- /dev/null +++ b/linux-core/drm_ioctl.c @@ -0,0 +1,231 @@ +/* drm_ioctl.h -- IOCTL processing for DRM -*- linux-c -*- + * Created: Fri Jan 8 09:01:26 1999 by faith@valinux.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 + * VA LINUX SYSTEMS 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 <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int DRM(irq_busid)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_irq_busid_t p; + struct pci_dev *dev; + + if (copy_from_user(&p, (drm_irq_busid_t *)arg, sizeof(p))) + return -EFAULT; + dev = pci_find_slot(p.busnum, PCI_DEVFN(p.devnum, p.funcnum)); + if (dev) p.irq = dev->irq; + else p.irq = 0; + DRM_DEBUG("%d:%d:%d => IRQ %d\n", + p.busnum, p.devnum, p.funcnum, p.irq); + if (copy_to_user((drm_irq_busid_t *)arg, &p, sizeof(p))) + return -EFAULT; + return 0; +} + +int DRM(getunique)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_unique_t u; + + if (copy_from_user(&u, (drm_unique_t *)arg, sizeof(u))) + return -EFAULT; + if (u.unique_len >= dev->unique_len) { + if (copy_to_user(u.unique, dev->unique, dev->unique_len)) + return -EFAULT; + } + u.unique_len = dev->unique_len; + if (copy_to_user((drm_unique_t *)arg, &u, sizeof(u))) + return -EFAULT; + return 0; +} + +int DRM(setunique)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_unique_t u; + + if (dev->unique_len || dev->unique) + return -EBUSY; + + if (copy_from_user(&u, (drm_unique_t *)arg, sizeof(u))) + return -EFAULT; + + if (!u.unique_len) + return -EINVAL; + + dev->unique_len = u.unique_len; + dev->unique = DRM(alloc)(u.unique_len + 1, DRM_MEM_DRIVER); + if (copy_from_user(dev->unique, u.unique, dev->unique_len)) + return -EFAULT; + dev->unique[dev->unique_len] = '\0'; + + dev->devname = DRM(alloc)(strlen(dev->name) + strlen(dev->unique) + 2, + DRM_MEM_DRIVER); + sprintf(dev->devname, "%s@%s", dev->name, dev->unique); + +#ifdef __alpha__ + do { + struct pci_dev *pci_dev; + int b, d, f; + char *p; + + for(p = dev->unique; p && *p && *p != ':'; p++); + if (!p || !*p) break; + b = (int)simple_strtoul(p+1, &p, 10); + if (*p != ':') break; + d = (int)simple_strtoul(p+1, &p, 10); + if (*p != ':') break; + f = (int)simple_strtoul(p+1, &p, 10); + if (*p) break; + + pci_dev = pci_find_slot(b, PCI_DEVFN(d,f)); + if (pci_dev) + dev->hose = pci_dev->sysdata; + } while(0); +#endif + + return 0; +} + + +int DRM(getmap)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t map; + drm_map_list_t *r_list = NULL; + struct list_head *list; + int idx; + int i; + + if (copy_from_user(&map, (drm_map_t *)arg, sizeof(map))) + return -EFAULT; + idx = map.offset; + + down(&dev->struct_sem); + if (idx < 0 || idx >= dev->map_count) { + up(&dev->struct_sem); + return -EINVAL; + } + + i = 0; + list_for_each(list, &dev->maplist->head) { + if(i == idx) { + r_list = (drm_map_list_t *)list; + break; + } + i++; + } + if(!r_list || !r_list->map) { + up(&dev->struct_sem); + return -EINVAL; + } + + map.offset = r_list->map->offset; + map.size = r_list->map->size; + map.type = r_list->map->type; + map.flags = r_list->map->flags; + map.handle = r_list->map->handle; + map.mtrr = r_list->map->mtrr; + up(&dev->struct_sem); + + if (copy_to_user((drm_map_t *)arg, &map, sizeof(map))) return -EFAULT; + return 0; +} + +int DRM(getclient)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_client_t client; + drm_file_t *pt; + int idx; + int i; + + if (copy_from_user(&client, (drm_client_t *)arg, sizeof(client))) + return -EFAULT; + idx = client.idx; + down(&dev->struct_sem); + for (i = 0, pt = dev->file_first; i < idx && pt; i++, pt = pt->next) + ; + + if (!pt) { + up(&dev->struct_sem); + return -EINVAL; + } + client.auth = pt->authenticated; + client.pid = pt->pid; + client.uid = pt->uid; + client.magic = pt->magic; + client.iocs = pt->ioctl_count; + up(&dev->struct_sem); + + if (copy_to_user((drm_client_t *)arg, &client, sizeof(client))) + return -EFAULT; + return 0; +} + +int DRM(getstats)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_stats_t stats; + int i; + + memset(&stats, 0, sizeof(stats)); + + down(&dev->struct_sem); + + for (i = 0; i < dev->counters; i++) { + if (dev->types[i] == _DRM_STAT_LOCK) + stats.data[i].value + = (dev->lock.hw_lock + ? dev->lock.hw_lock->lock : 0); + else + stats.data[i].value = atomic_read(&dev->counts[i]); + stats.data[i].type = dev->types[i]; + } + + stats.count = dev->counters; + + up(&dev->struct_sem); + + if (copy_to_user((drm_stats_t *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; +} diff --git a/linux-core/drm_memory.h b/linux-core/drm_memory.h new file mode 100644 index 00000000..498937d4 --- /dev/null +++ b/linux-core/drm_memory.h @@ -0,0 +1,459 @@ +/* drm_memory.h -- Memory management wrappers for DRM -*- linux-c -*- + * Created: Thu Feb 4 14:00:34 1999 by faith@valinux.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 + * VA LINUX SYSTEMS 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 <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include <linux/config.h> +#include "drmP.h" +#include <linux/wrapper.h> + +typedef struct drm_mem_stats { + const char *name; + int succeed_count; + int free_count; + int fail_count; + unsigned long bytes_allocated; + unsigned long bytes_freed; +} drm_mem_stats_t; + +static spinlock_t DRM(mem_lock) = SPIN_LOCK_UNLOCKED; +static unsigned long DRM(ram_available) = 0; /* In pages */ +static unsigned long DRM(ram_used) = 0; +static drm_mem_stats_t DRM(mem_stats)[] = { + [DRM_MEM_DMA] = { "dmabufs" }, + [DRM_MEM_SAREA] = { "sareas" }, + [DRM_MEM_DRIVER] = { "driver" }, + [DRM_MEM_MAGIC] = { "magic" }, + [DRM_MEM_IOCTLS] = { "ioctltab" }, + [DRM_MEM_MAPS] = { "maplist" }, + [DRM_MEM_VMAS] = { "vmalist" }, + [DRM_MEM_BUFS] = { "buflist" }, + [DRM_MEM_SEGS] = { "seglist" }, + [DRM_MEM_PAGES] = { "pagelist" }, + [DRM_MEM_FILES] = { "files" }, + [DRM_MEM_QUEUES] = { "queues" }, + [DRM_MEM_CMDS] = { "commands" }, + [DRM_MEM_MAPPINGS] = { "mappings" }, + [DRM_MEM_BUFLISTS] = { "buflists" }, + [DRM_MEM_AGPLISTS] = { "agplist" }, + [DRM_MEM_SGLISTS] = { "sglist" }, + [DRM_MEM_TOTALAGP] = { "totalagp" }, + [DRM_MEM_BOUNDAGP] = { "boundagp" }, + [DRM_MEM_CTXBITMAP] = { "ctxbitmap"}, + [DRM_MEM_STUB] = { "stub" }, + { NULL, 0, } /* Last entry must be null */ +}; + +void DRM(mem_init)(void) +{ + drm_mem_stats_t *mem; + struct sysinfo si; + + for (mem = DRM(mem_stats); mem->name; ++mem) { + mem->succeed_count = 0; + mem->free_count = 0; + mem->fail_count = 0; + mem->bytes_allocated = 0; + mem->bytes_freed = 0; + } + + si_meminfo(&si); +#if LINUX_VERSION_CODE < 0x020317 + /* Changed to page count in 2.3.23 */ + DRM(ram_available) = si.totalram >> PAGE_SHIFT; +#else + DRM(ram_available) = si.totalram; +#endif + DRM(ram_used) = 0; +} + +/* drm_mem_info is called whenever a process reads /dev/drm/mem. */ + +static int DRM(_mem_info)(char *buf, char **start, off_t offset, + int request, int *eof, void *data) +{ + drm_mem_stats_t *pt; + int len = 0; + + if (offset > DRM_PROC_LIMIT) { + *eof = 1; + return 0; + } + + *eof = 0; + *start = &buf[offset]; + + DRM_PROC_PRINT(" total counts " + " | outstanding \n"); + DRM_PROC_PRINT("type alloc freed fail bytes freed" + " | allocs bytes\n\n"); + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB |\n", + "system", 0, 0, 0, + DRM(ram_available) << (PAGE_SHIFT - 10)); + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB |\n", + "locked", 0, 0, 0, DRM(ram_used) >> 10); + DRM_PROC_PRINT("\n"); + for (pt = DRM(mem_stats); pt->name; pt++) { + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu %10lu | %6d %10ld\n", + pt->name, + pt->succeed_count, + pt->free_count, + pt->fail_count, + pt->bytes_allocated, + pt->bytes_freed, + pt->succeed_count - pt->free_count, + (long)pt->bytes_allocated + - (long)pt->bytes_freed); + } + + if (len > request + offset) return request; + *eof = 1; + return len - offset; +} + +int DRM(mem_info)(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + int ret; + + spin_lock(&DRM(mem_lock)); + ret = DRM(_mem_info)(buf, start, offset, len, eof, data); + spin_unlock(&DRM(mem_lock)); + return ret; +} + +void *DRM(alloc)(size_t size, int area) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(area, "Allocating 0 bytes\n"); + return NULL; + } + + if (!(pt = kmalloc(size, GFP_KERNEL))) { + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[area].fail_count; + spin_unlock(&DRM(mem_lock)); + return NULL; + } + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[area].succeed_count; + DRM(mem_stats)[area].bytes_allocated += size; + spin_unlock(&DRM(mem_lock)); + return pt; +} + +void *DRM(realloc)(void *oldpt, size_t oldsize, size_t size, int area) +{ + void *pt; + + if (!(pt = DRM(alloc)(size, area))) return NULL; + if (oldpt && oldsize) { + memcpy(pt, oldpt, oldsize); + DRM(free)(oldpt, oldsize, area); + } + return pt; +} + +char *DRM(strdup)(const char *s, int area) +{ + char *pt; + int length = s ? strlen(s) : 0; + + if (!(pt = DRM(alloc)(length+1, area))) return NULL; + strcpy(pt, s); + return pt; +} + +void DRM(strfree)(const char *s, int area) +{ + unsigned int size; + + if (!s) return; + + size = 1 + (s ? strlen(s) : 0); + DRM(free)((void *)s, size, area); +} + +void DRM(free)(void *pt, size_t size, int area) +{ + int alloc_count; + int free_count; + + if (!pt) DRM_MEM_ERROR(area, "Attempt to free NULL pointer\n"); + else kfree(pt); + spin_lock(&DRM(mem_lock)); + DRM(mem_stats)[area].bytes_freed += size; + free_count = ++DRM(mem_stats)[area].free_count; + alloc_count = DRM(mem_stats)[area].succeed_count; + spin_unlock(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +unsigned long DRM(alloc_pages)(int order, int area) +{ + unsigned long address; + unsigned long bytes = PAGE_SIZE << order; + unsigned long addr; + unsigned int sz; + + spin_lock(&DRM(mem_lock)); + if ((DRM(ram_used) >> PAGE_SHIFT) + > (DRM_RAM_PERCENT * DRM(ram_available)) / 100) { + spin_unlock(&DRM(mem_lock)); + return 0; + } + spin_unlock(&DRM(mem_lock)); + + address = __get_free_pages(GFP_KERNEL, order); + if (!address) { + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[area].fail_count; + spin_unlock(&DRM(mem_lock)); + return 0; + } + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[area].succeed_count; + DRM(mem_stats)[area].bytes_allocated += bytes; + DRM(ram_used) += bytes; + spin_unlock(&DRM(mem_lock)); + + + /* Zero outside the lock */ + memset((void *)address, 0, bytes); + + /* Reserve */ + for (addr = address, sz = bytes; + sz > 0; + addr += PAGE_SIZE, sz -= PAGE_SIZE) { +#if LINUX_VERSION_CODE >= 0x020400 + /* Argument type changed in 2.4.0-test6/pre8 */ + mem_map_reserve(virt_to_page(addr)); +#else + mem_map_reserve(MAP_NR(addr)); +#endif + } + + return address; +} + +void DRM(free_pages)(unsigned long address, int order, int area) +{ + unsigned long bytes = PAGE_SIZE << order; + int alloc_count; + int free_count; + unsigned long addr; + unsigned int sz; + + if (!address) { + DRM_MEM_ERROR(area, "Attempt to free address 0\n"); + } else { + /* Unreserve */ + for (addr = address, sz = bytes; + sz > 0; + addr += PAGE_SIZE, sz -= PAGE_SIZE) { +#if LINUX_VERSION_CODE >= 0x020400 + /* Argument type changed in 2.4.0-test6/pre8 */ + mem_map_unreserve(virt_to_page(addr)); +#else + mem_map_unreserve(MAP_NR(addr)); +#endif + } + free_pages(address, order); + } + + spin_lock(&DRM(mem_lock)); + free_count = ++DRM(mem_stats)[area].free_count; + alloc_count = DRM(mem_stats)[area].succeed_count; + DRM(mem_stats)[area].bytes_freed += bytes; + DRM(ram_used) -= bytes; + spin_unlock(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +void *DRM(ioremap)(unsigned long offset, unsigned long size) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Mapping 0 bytes at 0x%08lx\n", offset); + return NULL; + } + + if (!(pt = ioremap(offset, size))) { + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_MAPPINGS].fail_count; + spin_unlock(&DRM(mem_lock)); + return NULL; + } + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_MAPPINGS].succeed_count; + DRM(mem_stats)[DRM_MEM_MAPPINGS].bytes_allocated += size; + spin_unlock(&DRM(mem_lock)); + return pt; +} + +void DRM(ioremapfree)(void *pt, unsigned long size) +{ + int alloc_count; + int free_count; + + if (!pt) + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Attempt to free NULL pointer\n"); + else + iounmap(pt); + + spin_lock(&DRM(mem_lock)); + DRM(mem_stats)[DRM_MEM_MAPPINGS].bytes_freed += size; + free_count = ++DRM(mem_stats)[DRM_MEM_MAPPINGS].free_count; + alloc_count = DRM(mem_stats)[DRM_MEM_MAPPINGS].succeed_count; + spin_unlock(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +#if __REALLY_HAVE_AGP + +agp_memory *DRM(alloc_agp)(int pages, u32 type) +{ + agp_memory *handle; + + if (!pages) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, "Allocating 0 pages\n"); + return NULL; + } + + if ((handle = DRM(agp_allocate_memory)(pages, type))) { + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_TOTALAGP].succeed_count; + DRM(mem_stats)[DRM_MEM_TOTALAGP].bytes_allocated + += pages << PAGE_SHIFT; + spin_unlock(&DRM(mem_lock)); + return handle; + } + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_TOTALAGP].fail_count; + spin_unlock(&DRM(mem_lock)); + return NULL; +} + +int DRM(free_agp)(agp_memory *handle, int pages) +{ + int alloc_count; + int free_count; + int retval = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, + "Attempt to free NULL AGP handle\n"); + return retval;; + } + + if (DRM(agp_free_memory)(handle)) { + spin_lock(&DRM(mem_lock)); + free_count = ++DRM(mem_stats)[DRM_MEM_TOTALAGP].free_count; + alloc_count = DRM(mem_stats)[DRM_MEM_TOTALAGP].succeed_count; + DRM(mem_stats)[DRM_MEM_TOTALAGP].bytes_freed + += pages << PAGE_SHIFT; + spin_unlock(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } + return 0; + } + return retval; +} + +int DRM(bind_agp)(agp_memory *handle, unsigned int start) +{ + int retcode = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Attempt to bind NULL AGP handle\n"); + return retcode; + } + + if (!(retcode = DRM(agp_bind_memory)(handle, start))) { + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_BOUNDAGP].succeed_count; + DRM(mem_stats)[DRM_MEM_BOUNDAGP].bytes_allocated + += handle->page_count << PAGE_SHIFT; + spin_unlock(&DRM(mem_lock)); + return retcode; + } + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_BOUNDAGP].fail_count; + spin_unlock(&DRM(mem_lock)); + return retcode; +} + +int DRM(unbind_agp)(agp_memory *handle) +{ + int alloc_count; + int free_count; + int retcode = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Attempt to unbind NULL AGP handle\n"); + return retcode; + } + + if ((retcode = DRM(agp_unbind_memory)(handle))) return retcode; + spin_lock(&DRM(mem_lock)); + free_count = ++DRM(mem_stats)[DRM_MEM_BOUNDAGP].free_count; + alloc_count = DRM(mem_stats)[DRM_MEM_BOUNDAGP].succeed_count; + DRM(mem_stats)[DRM_MEM_BOUNDAGP].bytes_freed + += handle->page_count << PAGE_SHIFT; + spin_unlock(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } + return retcode; +} +#endif diff --git a/linux-core/drm_scatter.c b/linux-core/drm_scatter.c new file mode 100644 index 00000000..a0371074 --- /dev/null +++ b/linux-core/drm_scatter.c @@ -0,0 +1,218 @@ +/* drm_scatter.h -- IOCTLs to manage scatter/gather memory -*- linux-c -*- + * Created: Mon Dec 18 23:20:54 2000 by gareth@valinux.com + * + * 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: + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include <linux/config.h> +#include <linux/vmalloc.h> +#include "drmP.h" + +#define DEBUG_SCATTER 0 + +void DRM(sg_cleanup)( drm_sg_mem_t *entry ) +{ + struct page *page; + int i; + + for ( i = 0 ; i < entry->pages ; i++ ) { + page = entry->pagelist[i]; + if ( page ) + ClearPageReserved( page ); + } + + vfree( entry->virtual ); + + DRM(free)( entry->pagelist, + entry->pages * sizeof(*entry->pagelist), + DRM_MEM_PAGES ); + DRM(free)( entry, + sizeof(*entry), + DRM_MEM_SGLISTS ); +} + +int DRM(sg_alloc)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_scatter_gather_t request; + drm_sg_mem_t *entry; + unsigned long pages, i, j; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( dev->sg ) + return -EINVAL; + + if ( copy_from_user( &request, + (drm_scatter_gather_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + entry = DRM(alloc)( sizeof(*entry), DRM_MEM_SGLISTS ); + if ( !entry ) + return -ENOMEM; + + memset( entry, 0, sizeof(*entry) ); + + pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE; + DRM_DEBUG( "sg size=%ld pages=%ld\n", request.size, pages ); + + entry->pages = pages; + entry->pagelist = DRM(alloc)( pages * sizeof(*entry->pagelist), + DRM_MEM_PAGES ); + if ( !entry->pagelist ) { + DRM(free)( entry, sizeof(*entry), DRM_MEM_SGLISTS ); + return -ENOMEM; + } + + entry->virtual = vmalloc_32( pages << PAGE_SHIFT ); + if ( !entry->virtual ) { + DRM(free)( entry->pagelist, + entry->pages * sizeof(*entry->pagelist), + DRM_MEM_PAGES ); + DRM(free)( entry, + sizeof(*entry), + DRM_MEM_SGLISTS ); + return -ENOMEM; + } + + /* This also forces the mapping of COW pages, so our page list + * will be valid. Please don't remove it... + */ + memset( entry->virtual, 0, pages << PAGE_SHIFT ); + + entry->handle = (unsigned long)entry->virtual; + + DRM_DEBUG( "sg alloc handle = %08lx\n", entry->handle ); + DRM_DEBUG( "sg alloc virtual = %p\n", entry->virtual ); + + for ( i = entry->handle, j = 0 ; j < pages ; i += PAGE_SIZE, j++ ) { + pgd = pgd_offset_k( i ); + if ( !pgd_present( *pgd ) ) + goto failed; + + pmd = pmd_offset( pgd, i ); + if ( !pmd_present( *pmd ) ) + goto failed; + + pte = pte_offset( pmd, i ); + if ( !pte_present( *pte ) ) + goto failed; + + entry->pagelist[j] = pte_page( *pte ); + + SetPageReserved( entry->pagelist[j] ); + } + + request.handle = entry->handle; + + if ( copy_to_user( (drm_scatter_gather_t *)arg, + &request, + sizeof(request) ) ) { + DRM(sg_cleanup)( entry ); + return -EFAULT; + } + + dev->sg = entry; + +#if DEBUG_SCATTER + /* Verify that each page points to its virtual address, and vice + * versa. + */ + { + int error = 0; + + for ( i = 0 ; i < pages ; i++ ) { + unsigned long *tmp; + + tmp = (unsigned long *)entry->pagelist[i]->virtual; + for ( j = 0 ; + j < PAGE_SIZE / sizeof(unsigned long) ; + j++, tmp++ ) { + *tmp = 0xcafebabe; + } + tmp = (unsigned long *)((u8 *)entry->virtual + + (PAGE_SIZE * i)); + for( j = 0 ; + j < PAGE_SIZE / sizeof(unsigned long) ; + j++, tmp++ ) { + if ( *tmp != 0xcafebabe && error == 0 ) { + error = 1; + DRM_ERROR( "Scatter allocation error, " + "pagelist does not match " + "virtual mapping\n" ); + } + } + tmp = (unsigned long *)entry->pagelist[i]->virtual; + for(j = 0 ; + j < PAGE_SIZE / sizeof(unsigned long) ; + j++, tmp++) { + *tmp = 0; + } + } + if (error == 0) + DRM_ERROR( "Scatter allocation matches pagelist\n" ); + } +#endif + + return 0; + + failed: + DRM(sg_cleanup)( entry ); + return -ENOMEM; +} + +int DRM(sg_free)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_scatter_gather_t request; + drm_sg_mem_t *entry; + + if ( copy_from_user( &request, + (drm_scatter_gather_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + entry = dev->sg; + dev->sg = NULL; + + if ( !entry || entry->handle != request.handle ) + return -EINVAL; + + DRM_DEBUG( "sg free virtual = %p\n", entry->virtual ); + + DRM(sg_cleanup)( entry ); + + return 0; +} diff --git a/linux-core/drm_vm.c b/linux-core/drm_vm.c new file mode 100644 index 00000000..771c11bd --- /dev/null +++ b/linux-core/drm_vm.c @@ -0,0 +1,482 @@ +/* drm_vm.h -- Memory mapping for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.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 + * VA LINUX SYSTEMS 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 <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +struct vm_operations_struct drm_vm_ops = { + nopage: DRM(vm_nopage), + open: DRM(vm_open), + close: DRM(vm_close), +}; + +struct vm_operations_struct drm_vm_shm_ops = { + nopage: DRM(vm_shm_nopage), + open: DRM(vm_open), + close: DRM(vm_shm_close), +}; + +struct vm_operations_struct drm_vm_dma_ops = { + nopage: DRM(vm_dma_nopage), + open: DRM(vm_open), + close: DRM(vm_close), +}; + +struct vm_operations_struct drm_vm_sg_ops = { + nopage: DRM(vm_sg_nopage), + open: DRM(vm_open), + close: DRM(vm_close), +}; + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long DRM(vm_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *DRM(vm_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + return NOPAGE_SIGBUS; /* Disallow mremap */ +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long DRM(vm_shm_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *DRM(vm_shm_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ +#if LINUX_VERSION_CODE >= 0x020300 + drm_map_t *map = (drm_map_t *)vma->vm_private_data; +#else + drm_map_t *map = (drm_map_t *)vma->vm_pte; +#endif + unsigned long physical; + unsigned long offset; + unsigned long i; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!map) return NOPAGE_OOM; /* Nothing allocated */ + + offset = address - vma->vm_start; + i = (unsigned long)map->handle + offset; + /* We have to walk page tables here because we need large SAREA's, and + * they need to be virtually contiguous in kernel space. + */ + pgd = pgd_offset_k( i ); + if( !pgd_present( *pgd ) ) return NOPAGE_OOM; + pmd = pmd_offset( pgd, i ); + if( !pmd_present( *pmd ) ) return NOPAGE_OOM; + pte = pte_offset( pmd, i ); + if( !pte_present( *pte ) ) return NOPAGE_OOM; + physical = (unsigned long)pte_page( *pte )->virtual; + atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx => 0x%08lx\n", address, physical); +#if LINUX_VERSION_CODE < 0x020317 + return physical; +#else + return virt_to_page(physical); +#endif +} + +/* Special close routine which deletes map information if we are the last + * person to close a mapping and its not in the global maplist. + */ + +void DRM(vm_shm_close)(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_vma_entry_t *pt, *prev; + drm_map_t *map; + drm_map_list_t *r_list; + struct list_head *list; + int found_maps = 0; + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_dec(&dev->vma_count); + +#if LINUX_VERSION_CODE >= 0x020300 + map = vma->vm_private_data; +#else + map = vma->vm_pte; +#endif + + down(&dev->struct_sem); + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { +#if LINUX_VERSION_CODE >= 0x020300 + if (pt->vma->vm_private_data == map) found_maps++; +#else + if (pt->vma->vm_pte == map) found_maps++; +#endif + if (pt->vma == vma) { + if (prev) { + prev->next = pt->next; + } else { + dev->vmalist = pt->next; + } + DRM(free)(pt, sizeof(*pt), DRM_MEM_VMAS); + } + } + /* We were the only map that was found */ + if(found_maps == 1 && + map->flags & _DRM_REMOVABLE) { + /* Check to see if we are in the maplist, if we are not, then + * we delete this mappings information. + */ + found_maps = 0; + list = &dev->maplist->head; + list_for_each(list, &dev->maplist->head) { + r_list = (drm_map_list_t *) list; + if (r_list->map == map) found_maps++; + } + + if(!found_maps) { + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#if __REALLY_HAVE_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + DRM(ioremapfree)(map->handle, map->size); + break; + case _DRM_SHM: + vfree(map->handle); + break; + case _DRM_AGP: + case _DRM_SCATTER_GATHER: + break; + } + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + } + } + up(&dev->struct_sem); +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long DRM(vm_dma_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *DRM(vm_dma_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + unsigned long physical; + unsigned long offset; + unsigned long page; + + if (!dma) return NOPAGE_SIGBUS; /* Error */ + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!dma->pagelist) return NOPAGE_OOM ; /* Nothing allocated */ + + offset = address - vma->vm_start; /* vm_[pg]off[set] should be 0 */ + page = offset >> PAGE_SHIFT; + physical = dma->pagelist[page] + (offset & (~PAGE_MASK)); + atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical); +#if LINUX_VERSION_CODE < 0x020317 + return physical; +#else + return virt_to_page(physical); +#endif +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long DRM(vm_sg_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *DRM(vm_sg_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ +#if LINUX_VERSION_CODE >= 0x020300 + drm_map_t *map = (drm_map_t *)vma->vm_private_data; +#else + drm_map_t *map = (drm_map_t *)vma->vm_pte; +#endif + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_sg_mem_t *entry = dev->sg; + unsigned long offset; + unsigned long map_offset; + unsigned long page_offset; + struct page *page; + + if (!entry) return NOPAGE_SIGBUS; /* Error */ + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!entry->pagelist) return NOPAGE_OOM ; /* Nothing allocated */ + + + offset = address - vma->vm_start; + map_offset = map->offset - dev->sg->handle; + page_offset = (offset >> PAGE_SHIFT) + (map_offset >> PAGE_SHIFT); + page = entry->pagelist[page_offset]; + atomic_inc(&page->count); /* Dec. by kernel */ + +#if LINUX_VERSION_CODE < 0x020317 + return (unsigned long)virt_to_phys(page->virtual); +#else + return page; +#endif +} + +void DRM(vm_open)(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_vma_entry_t *vma_entry; + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); + atomic_inc(&dev->vma_count); +#if LINUX_VERSION_CODE < 0x020333 + /* The map can exist after the fd is closed. */ + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + + vma_entry = DRM(alloc)(sizeof(*vma_entry), DRM_MEM_VMAS); + if (vma_entry) { + down(&dev->struct_sem); + vma_entry->vma = vma; + vma_entry->next = dev->vmalist; + vma_entry->pid = current->pid; + dev->vmalist = vma_entry; + up(&dev->struct_sem); + } +} + +void DRM(vm_close)(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_vma_entry_t *pt, *prev; + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_dec(&dev->vma_count); + + down(&dev->struct_sem); + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { + if (pt->vma == vma) { + if (prev) { + prev->next = pt->next; + } else { + dev->vmalist = pt->next; + } + DRM(free)(pt, sizeof(*pt), DRM_MEM_VMAS); + break; + } + } + up(&dev->struct_sem); +} + +int DRM(mmap_dma)(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + drm_device_dma_t *dma; + unsigned long length = vma->vm_end - vma->vm_start; + + lock_kernel(); + dev = priv->dev; + dma = dev->dma; + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + /* Length must match exact page count */ + if (!dma || (length >> PAGE_SHIFT) != dma->page_count) { + unlock_kernel(); + return -EINVAL; + } + unlock_kernel(); + + vma->vm_ops = &drm_vm_dma_ops; + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + +#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ + /* In Linux 2.2.3 and above, this is + handled in do_mmap() in mm/mmap.c. */ + ++filp->f_count; +#endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ + DRM(vm_open)(vma); + return 0; +} + +int DRM(mmap)(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map = NULL; + drm_map_list_t *r_list; + unsigned long offset = 0; + struct list_head *list; + + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + if (!VM_OFFSET(vma)) return DRM(mmap_dma)(filp, vma); + + /* A sequential search of a linked list is + fine here because: 1) there will only be + about 5-10 entries in the list and, 2) a + DRI client only has to do this mapping + once, so it doesn't have to be optimized + for performance, even if the list was a + bit longer. */ + list_for_each(list, &dev->maplist->head) { + r_list = (drm_map_list_t *)list; + map = r_list->map; + if (!map) continue; + if (map->offset == VM_OFFSET(vma)) break; + } + + if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) + return -EPERM; + + /* Check for valid size. */ + if (map->size != vma->vm_end - vma->vm_start) return -EINVAL; + + if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) { + vma->vm_flags &= VM_MAYWRITE; +#if defined(__i386__) + pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW; +#else + /* Ye gads this is ugly. With more thought + we could move this up higher and use + `protection_map' instead. */ + vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect( + __pte(pgprot_val(vma->vm_page_prot))))); +#endif + } + + switch (map->type) { + case _DRM_FRAME_BUFFER: + case _DRM_REGISTERS: + case _DRM_AGP: + if (VM_OFFSET(vma) >= __pa(high_memory)) { +#if defined(__i386__) + if (boot_cpu_data.x86 > 3 && map->type != _DRM_AGP) { + pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; + pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT; + } +#elif defined(__ia64__) + if (map->type != _DRM_AGP) + vma->vm_page_prot = + pgprot_writecombine(vma->vm_page_prot); +#elif defined(__powerpc__) + pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE | _PAGE_GUARDED; +#endif + vma->vm_flags |= VM_IO; /* not in core dump */ + } +#ifdef __alpha__ + offset = dev->hose->dense_mem_base - + dev->hose->mem_space->start; +#endif + if (remap_page_range(vma->vm_start, + VM_OFFSET(vma) + offset, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + DRM_DEBUG(" Type = %d; start = 0x%lx, end = 0x%lx," + " offset = 0x%lx\n", + map->type, + vma->vm_start, vma->vm_end, VM_OFFSET(vma) + offset); + vma->vm_ops = &drm_vm_ops; + break; + case _DRM_SHM: + vma->vm_ops = &drm_vm_shm_ops; +#if LINUX_VERSION_CODE >= 0x020300 + vma->vm_private_data = (void *)map; +#else + vma->vm_pte = (unsigned long)map; +#endif + /* Don't let this area swap. Change when + DRM_KERNEL advisory is supported. */ + vma->vm_flags |= VM_LOCKED; + break; + case _DRM_SCATTER_GATHER: + vma->vm_ops = &drm_vm_sg_ops; +#if LINUX_VERSION_CODE >= 0x020300 + vma->vm_private_data = (void *)map; +#else + vma->vm_pte = (unsigned long)map; +#endif + vma->vm_flags |= VM_LOCKED; + break; + default: + return -EINVAL; /* This should never happen. */ + } + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + +#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ + /* In Linux 2.2.3 and above, this is + handled in do_mmap() in mm/mmap.c. */ + ++filp->f_count; +#endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ + DRM(vm_open)(vma); + return 0; +} diff --git a/linux-core/mga_drv.c b/linux-core/mga_drv.c index 42794e82..bea65cfa 100644 --- a/linux-core/mga_drv.c +++ b/linux-core/mga_drv.c @@ -38,11 +38,11 @@ #define DRIVER_NAME "mga" #define DRIVER_DESC "Matrox G200/G400" -#define DRIVER_DATE "20010319" +#define DRIVER_DATE "20010321" #define DRIVER_MAJOR 3 #define DRIVER_MINOR 0 -#define DRIVER_PATCHLEVEL 1 +#define DRIVER_PATCHLEVEL 2 #define DRIVER_IOCTLS \ [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { mga_dma_buffers, 1, 0 }, \ diff --git a/linux-core/r128_drv.c b/linux-core/r128_drv.c index daae0f87..584cb29c 100644 --- a/linux-core/r128_drv.c +++ b/linux-core/r128_drv.c @@ -33,16 +33,17 @@ #include "r128.h" #include "drmP.h" #include "r128_drv.h" +#include "ati_pcigart.h" #define DRIVER_AUTHOR "Gareth Hughes, VA Linux Systems Inc." #define DRIVER_NAME "r128" #define DRIVER_DESC "ATI Rage 128" -#define DRIVER_DATE "20010308" +#define DRIVER_DATE "20010405" #define DRIVER_MAJOR 2 #define DRIVER_MINOR 1 -#define DRIVER_PATCHLEVEL 5 +#define DRIVER_PATCHLEVEL 6 #define DRIVER_IOCTLS \ [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { r128_cce_buffers, 1, 0 }, \ @@ -88,3 +89,4 @@ #include "drm_proc.h" #include "drm_vm.h" #include "drm_stub.h" +#include "drm_scatter.h" diff --git a/linux-core/radeon_drv.c b/linux-core/radeon_drv.c index dba2037f..d7632667 100644 --- a/linux-core/radeon_drv.c +++ b/linux-core/radeon_drv.c @@ -31,15 +31,16 @@ #include "radeon.h" #include "drmP.h" #include "radeon_drv.h" +#include "ati_pcigart.h" #define DRIVER_AUTHOR "Gareth Hughes, VA Linux Systems Inc." #define DRIVER_NAME "radeon" #define DRIVER_DESC "ATI Radeon" -#define DRIVER_DATE "20010308" +#define DRIVER_DATE "20010405" #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 0 +#define DRIVER_MINOR 1 #define DRIVER_PATCHLEVEL 1 #define DRIVER_IOCTLS \ @@ -85,3 +86,4 @@ #include "drm_proc.h" #include "drm_vm.h" #include "drm_stub.h" +#include "drm_scatter.h" diff --git a/linux/Makefile.linux b/linux/Makefile.linux index 470c25b3..95044e6f 100644 --- a/linux/Makefile.linux +++ b/linux/Makefile.linux @@ -47,7 +47,7 @@ # **** End of SMP/MODVERSIONS detection -MODS = gamma.o tdfx.o +MODS = gamma.o tdfx.o r128.o radeon.o LIBS = DRMTEMPLATES = drm_auth.h drm_bufs.h drm_context.h drm_dma.h drm_drawable.h \ @@ -61,6 +61,13 @@ GAMMAHEADERS = gamma_drv.h $(DRMHEADERS) $(DRMTEMPLATES) TDFXOBJS = tdfx_drv.o TDFXHEADERS = tdfx.h $(DRMHEADERS) +R128OBJS = r128_drv.o r128_cce.o r128_state.o +R128HEADERS = r128.h r128_drv.h r128_drm.h $(DRMHEADERS) $(DRMTEMPLATES) + +RADEONOBJS = radeon_drv.o radeon_cp.o radeon_state.o +RADEONHEADERS = radeon.h radeon_drv.h radeon_drm.h $(DRMHEADERS) \ + $(DRMTEMPLATES) + INC = /usr/include CFLAGS = -O2 $(WARNINGS) @@ -83,9 +90,10 @@ PRGLIBS = # This may not be correct, but it is the best assumption we can make. VERSION := $(shell uname -r) -A := /usr/src/linux-$(VERSION)/include -B := /usr/src/linux/include -C := /usr/include +A := /lib/modules/$(VERSION)/build/include +B := /usr/src/linux-$(VERSION)/include +C := /usr/src/linux/include +D := /usr/include V := $(shell gcc -E -nostdinc -I$A picker.c 2>/dev/null \ | grep -s 'RELEASE = ' | cut -d' ' -f3) @@ -100,15 +108,21 @@ else V := $(shell gcc -E -nostdinc -I$C picker.c 2>/dev/null \ | grep -s 'RELEASE = ' | cut -d' ' -f3) ifeq ($(V),"$(VERSION)") - TREE := $C + TREE := $C +else + V := $(shell gcc -E -nostdinc -I$D picker.c 2>/dev/null \ + | grep -s 'RELEASE = ' | cut -d' ' -f3) +ifeq ($(V),"$(VERSION)") + TREE := $D else TREE := 0 endif endif endif +endif ifeq ($(TREE),0) -all:; @echo Error: Could not locate kernel tree in $A $B $C +all:; @echo Error: Could not locate kernel tree in $A $B $C $D else SMP := $(shell gcc -E -nostdinc -I$(TREE) picker.c 2>/dev/null \ | grep -s 'SMP = ' | cut -d' ' -f3) @@ -132,7 +146,7 @@ ifeq ($(AGP),1) MODCFLAGS += -DCONFIG_AGP -DCONFIG_AGP_MODULE DRMTEMPLATES += drm_agpsupport.h DRMHEADERS += agpsupport-pre24.h -MODS += mga.o r128.o radeon.o +MODS += mga.o ifeq ($(MACHINE),i386) MODS += i810.o endif @@ -140,19 +154,16 @@ ifeq ($(MACHINE),i686) MODS += i810.o endif - MGAOBJS = mga_drv.o mga_dma.o mga_state.o mga_warp.o MGAHEADERS = mga.h mga_drv.h mga_drm.h $(DRMHEADERS) $(DRMTEMPLATES) I810OBJS = i810_drv.o i810_dma.o I810HEADERS = i810.h i810_drv.h i810_drm.h $(DRMHEADERS) $(DRMTEMPLATES) -R128OBJS = r128_drv.o r128_cce.o r128_state.o -R128HEADERS = r128.h r128_drv.h r128_drm.h $(DRMHEADERS) $(DRMTEMPLATES) +endif -RADEONOBJS = radeon_drv.o radeon_cp.o radeon_state.o -RADEONHEADERS = radeon.h radeon_drv.h radeon_drm.h $(DRMHEADERS) \ - $(DRMTEMPLATES) +ifeq ($(MACHINE),alpha) +MODCFLAGS+= -ffixed-8 -mno-fp-regs -mcpu=ev56 -Wa,-mev6 endif ifeq ($(SIS),1) @@ -218,26 +229,27 @@ tdfx.o: $(TDFXOBJS) $(LIBS) sis.o: $(SISOBJS) $(LIBS) $(LD) -r $^ -o $@ -ifeq ($(AGP),1) -mga_drv.o: mga_drv.c +r128_drv.o: r128_drv.c $(CC) $(MODCFLAGS) -DEXPORT_SYMTAB -I$(TREE) -c $< -o $@ -mga.o: $(MGAOBJS) +r128.o: $(R128OBJS) $(LIBS) $(LD) -r $^ -o $@ -i810_drv.o: i810_drv.c +radeon_drv.o: radeon_drv.c $(CC) $(MODCFLAGS) -DEXPORT_SYMTAB -I$(TREE) -c $< -o $@ -i810.o: $(I810OBJS) $(LIBS) +radeon.o: $(RADEONOBJS) $(LIBS) $(LD) -r $^ -o $@ -r128_drv.o: r128_drv.c +ifeq ($(AGP),1) +mga_drv.o: mga_drv.c $(CC) $(MODCFLAGS) -DEXPORT_SYMTAB -I$(TREE) -c $< -o $@ -r128.o: $(R128OBJS) $(LIBS) +mga.o: $(MGAOBJS) $(LD) -r $^ -o $@ -radeon_drv.o: radeon_drv.c +i810_drv.o: i810_drv.c $(CC) $(MODCFLAGS) -DEXPORT_SYMTAB -I$(TREE) -c $< -o $@ -radeon.o: $(RADEONOBJS) $(LIBS) +i810.o: $(I810OBJS) $(LIBS) $(LD) -r $^ -o $@ + endif .PHONY: ChangeLog diff --git a/linux/ati_pcigart.h b/linux/ati_pcigart.h new file mode 100644 index 00000000..93c5148f --- /dev/null +++ b/linux/ati_pcigart.h @@ -0,0 +1,138 @@ +/* ati_pcigart.h -- ATI PCI GART support -*- linux-c -*- + * Created: Wed Dec 13 21:52:19 2000 by gareth@valinux.com + * + * 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: + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +#if PAGE_SIZE == 8192 +# define ATI_PCIGART_TABLE_ORDER 2 +# define ATI_PCIGART_TABLE_PAGES (1 << 2) +#elif PAGE_SIZE == 4096 +# define ATI_PCIGART_TABLE_ORDER 3 +# define ATI_PCIGART_TABLE_PAGES (1 << 3) +#elif +# error - PAGE_SIZE not 8K or 4K +#endif + +# define ATI_MAX_PCIGART_PAGES 8192 /* 32 MB aperture, 4K pages */ +# define ATI_PCIGART_PAGE_SIZE 4096 /* PCI GART page size */ + +static unsigned long DRM(ati_alloc_pcigart_table)( void ) +{ + unsigned long address; + struct page *page; + int i; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + address = __get_free_pages( GFP_KERNEL, ATI_PCIGART_TABLE_ORDER ); + if ( address == 0UL ) { + return 0; + } + + page = virt_to_page( address ); + + for ( i = 0 ; i <= ATI_PCIGART_TABLE_PAGES ; i++, page++ ) { + atomic_inc( &page->count ); + SetPageReserved( page ); + } + + DRM_DEBUG( "%s: returning 0x%08lx\n", __FUNCTION__, address ); + return address; +} + +static void DRM(ati_free_pcigart_table)( unsigned long address ) +{ + struct page *page; + int i; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( !address ) return; + + page = virt_to_page( address ); + + for ( i = 0 ; i <= ATI_PCIGART_TABLE_PAGES ; i++, page++ ) { + atomic_dec( &page->count ); + ClearPageReserved( page ); + } + + free_pages( address, ATI_PCIGART_TABLE_ORDER ); +} + +unsigned long DRM(ati_pcigart_init)( drm_device_t *dev ) +{ + drm_sg_mem_t *entry = dev->sg; + unsigned long address; + unsigned long pages; + u32 *pci_gart, page_base; + int i, j; + + if ( !entry ) { + DRM_ERROR( "no scatter/gather memory!\n" ); + return 0; + } + + address = DRM(ati_alloc_pcigart_table)(); + if ( !address ) { + DRM_ERROR( "cannot allocate PCI GART page!\n" ); + return 0; + } + + pci_gart = (u32 *)address; + + pages = ( entry->pages <= ATI_MAX_PCIGART_PAGES ) + ? entry->pages : ATI_MAX_PCIGART_PAGES; + + memset( pci_gart, 0, ATI_MAX_PCIGART_PAGES * sizeof(u32) ); + + for ( i = 0 ; i < pages ; i++ ) { + page_base = virt_to_bus( entry->pagelist[i]->virtual ); + for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) { + *pci_gart++ = cpu_to_le32( page_base ); + page_base += ATI_PCIGART_PAGE_SIZE; + } + } + +#if __i386__ + asm volatile ( "wbinvd" ::: "memory" ); +#else + mb(); +#endif + + return address; +} + +int DRM(ati_pcigart_cleanup)( unsigned long address ) +{ + + if ( address ) { + DRM(ati_free_pcigart_table)( address ); + } + + return 0; +} diff --git a/linux/drm.h b/linux/drm.h index f1abaabf..3def97f7 100644 --- a/linux/drm.h +++ b/linux/drm.h @@ -126,10 +126,11 @@ typedef struct drm_control { } drm_control_t; typedef enum drm_map_type { - _DRM_FRAME_BUFFER = 0, /* WC (no caching), no core dump */ - _DRM_REGISTERS = 1, /* no caching, no core dump */ - _DRM_SHM = 2, /* shared, cached */ - _DRM_AGP = 3 /* AGP/GART */ + _DRM_FRAME_BUFFER = 0, /* WC (no caching), no core dump */ + _DRM_REGISTERS = 1, /* no caching, no core dump */ + _DRM_SHM = 2, /* shared, cached */ + _DRM_AGP = 3, /* AGP/GART */ + _DRM_SCATTER_GATHER = 4 /* Scatter/gather memory for PCI DMA */ } drm_map_type_t; typedef enum drm_map_flags { @@ -238,7 +239,8 @@ typedef struct drm_buf_desc { int high_mark; /* High water mark */ enum { _DRM_PAGE_ALIGN = 0x01, /* Align on page boundaries for DMA */ - _DRM_AGP_BUFFER = 0x02 /* Buffer is in agp space */ + _DRM_AGP_BUFFER = 0x02, /* Buffer is in agp space */ + _DRM_SG_BUFFER = 0x04 /* Scatter/gather memory buffer */ } flags; unsigned long agp_start; /* Start address of where the agp buffers * are in the agp aperture */ @@ -344,6 +346,11 @@ typedef struct drm_agp_info { unsigned short id_device; } drm_agp_info_t; +typedef struct drm_scatter_gather { + unsigned long size; /* In bytes -- will round to page boundary */ + unsigned long handle; /* Used for mapping / unmapping */ +} drm_scatter_gather_t; + #define DRM_IOCTL_BASE 'd' #define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) #define DRM_IOR(nr,size) _IOR(DRM_IOCTL_BASE,nr,size) @@ -399,6 +406,9 @@ typedef struct drm_agp_info { #define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, drm_agp_binding_t) #define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, drm_agp_binding_t) +#define DRM_IOCTL_SG_ALLOC DRM_IOW( 0x38, drm_scatter_gather_t) +#define DRM_IOCTL_SG_FREE DRM_IOW( 0x39, drm_scatter_gather_t) + /* MGA specific ioctls */ #define DRM_IOCTL_MGA_INIT DRM_IOW( 0x40, drm_mga_init_t) #define DRM_IOCTL_MGA_FLUSH DRM_IOW( 0x41, drm_lock_t) diff --git a/linux/drmP.h b/linux/drmP.h index 274e318a..227ec35a 100644 --- a/linux/drmP.h +++ b/linux/drmP.h @@ -53,7 +53,7 @@ #include <linux/sched.h> #include <linux/smp_lock.h> /* For (un)lock_kernel */ #include <linux/mm.h> -#ifdef __alpha__ +#if defined(__alpha__) || defined(__powerpc__) #include <asm/pgtable.h> /* For pte_wrprotect */ #endif #include <asm/io.h> @@ -145,6 +145,7 @@ #define DRM_MEM_BOUNDAGP 17 #define DRM_MEM_CTXBITMAP 18 #define DRM_MEM_STUB 19 +#define DRM_MEM_SGLISTS 20 #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) @@ -199,7 +200,7 @@ __cmpxchg_u32(volatile int *m, int old, int new) unsigned long prev, cmp; __asm__ __volatile__( - "1: ldl_l %0,%2\n" + "1: ldl_l %0,%5\n" " cmpeq %0,%3,%1\n" " beq %1,2f\n" " mov %4,%1\n" @@ -210,7 +211,8 @@ __cmpxchg_u32(volatile int *m, int old, int new) "3: br 1b\n" ".previous" : "=&r"(prev), "=&r"(cmp), "=m"(*m) - : "r"((long) old), "r"(new), "m"(*m)); + : "r"((long) old), "r"(new), "m"(*m) + : "memory" ); return prev; } @@ -221,7 +223,7 @@ __cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new) unsigned long prev, cmp; __asm__ __volatile__( - "1: ldq_l %0,%2\n" + "1: ldq_l %0,%5\n" " cmpeq %0,%3,%1\n" " beq %1,2f\n" " mov %4,%1\n" @@ -232,7 +234,8 @@ __cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new) "3: br 1b\n" ".previous" : "=&r"(prev), "=&r"(cmp), "=m"(*m) - : "r"((long) old), "r"(new), "m"(*m)); + : "r"((long) old), "r"(new), "m"(*m) + : "memory" ); return prev; } @@ -284,12 +287,43 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, return old; } +#elif defined(__powerpc__) +extern void __cmpxchg_called_with_bad_pointer(void); +static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, + unsigned long new, int size) +{ + unsigned long prev; + + switch (size) { + case 4: + __asm__ __volatile__( + "sync;" + "0: lwarx %0,0,%1 ;" + " cmpl 0,%0,%3;" + " bne 1f;" + " stwcx. %2,0,%1;" + " bne- 0b;" + "1: " + "sync;" + : "=&r"(prev) + : "r"(ptr), "r"(new), "r"(old) + : "cr0", "memory"); + return prev; + } + __cmpxchg_called_with_bad_pointer(); + return old; +} + +#endif /* i386, powerpc & alpha */ + +#ifndef __alpha__ #define cmpxchg(ptr,o,n) \ ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o), \ (unsigned long)(n),sizeof(*(ptr)))) -#endif /* i386 & alpha */ #endif +#endif /* !__HAVE_ARCH_CMPXCHG */ + /* Macros to make printk easier */ #define DRM_ERROR(fmt, arg...) \ printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ "] *ERROR* " fmt , ##arg) @@ -547,7 +581,8 @@ typedef struct drm_device_dma { unsigned long *pagelist; unsigned long byte_count; enum { - _DRM_DMA_USE_AGP = 0x01 + _DRM_DMA_USE_AGP = 0x01, + _DRM_DMA_USE_SG = 0x02 } flags; /* DMA support */ @@ -579,6 +614,13 @@ typedef struct drm_agp_head { } drm_agp_head_t; #endif +typedef struct drm_sg_mem { + unsigned long handle; + void *virtual; + int pages; + struct page **pagelist; +} drm_sg_mem_t; + typedef struct drm_sigdata { int context; drm_hw_lock_t *lock; @@ -667,6 +709,10 @@ typedef struct drm_device { #if __REALLY_HAVE_AGP drm_agp_head_t *agp; #endif +#ifdef __alpha__ + struct pci_controler *hose; +#endif + drm_sg_mem_t *sg; /* Scatter gather memory */ unsigned long *ctx_bitmap; void *dev_private; drm_sigdata_t sigdata; /* For block_all_signals */ @@ -718,6 +764,9 @@ extern unsigned long DRM(vm_shm_nopage)(struct vm_area_struct *vma, extern unsigned long DRM(vm_dma_nopage)(struct vm_area_struct *vma, unsigned long address, int write_access); +extern unsigned long DRM(vm_sg_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access); #else /* Return type changed in 2.3.23 */ extern struct page *DRM(vm_nopage)(struct vm_area_struct *vma, @@ -729,6 +778,9 @@ extern struct page *DRM(vm_shm_nopage)(struct vm_area_struct *vma, extern struct page *DRM(vm_dma_nopage)(struct vm_area_struct *vma, unsigned long address, int write_access); +extern struct page *DRM(vm_sg_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access); #endif extern void DRM(vm_open)(struct vm_area_struct *vma); extern void DRM(vm_close)(struct vm_area_struct *vma); @@ -947,5 +999,18 @@ extern int DRM(proc_cleanup)(int minor, struct proc_dir_entry *root, struct proc_dir_entry *dev_root); +#if __HAVE_SG + /* Scatter Gather Support (drm_scatter.h) */ +extern void DRM(sg_cleanup)(drm_sg_mem_t *entry); +extern int DRM(sg_alloc)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int DRM(sg_free)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +#endif + + /* ATI PCIGART support (ati_pcigart.h) */ +extern unsigned long DRM(ati_pcigart_init)(drm_device_t *dev); +extern int DRM(ati_pcigart_cleanup)(unsigned long address); + #endif /* __KERNEL__ */ #endif diff --git a/linux/drm_agpsupport.h b/linux/drm_agpsupport.h new file mode 100644 index 00000000..9b056c75 --- /dev/null +++ b/linux/drm_agpsupport.h @@ -0,0 +1,340 @@ +/* drm_agpsupport.h -- DRM support for AGP/GART backend -*- linux-c -*- + * Created: Mon Dec 13 09:56:45 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 + * VA LINUX SYSTEMS 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. + * + * Author: + * Rickard E. (Rik) Faith <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include <linux/module.h> + +#if __REALLY_HAVE_AGP + +#if LINUX_VERSION_CODE < 0x020400 +#include "agpsupport-pre24.h" +#else +#define DRM_AGP_GET (drm_agp_t *)inter_module_get("drm_agp") +#define DRM_AGP_PUT inter_module_put("drm_agp") +#endif + +static const drm_agp_t *drm_agp = NULL; + +int DRM(agp_info)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + agp_kern_info *kern; + drm_agp_info_t info; + + if (!dev->agp || !dev->agp->acquired || !drm_agp->copy_info) + return -EINVAL; + + kern = &dev->agp->agp_info; + info.agp_version_major = kern->version.major; + info.agp_version_minor = kern->version.minor; + info.mode = kern->mode; + info.aperture_base = kern->aper_base; + info.aperture_size = kern->aper_size * 1024 * 1024; + info.memory_allowed = kern->max_memory << PAGE_SHIFT; + info.memory_used = kern->current_memory << PAGE_SHIFT; + info.id_vendor = kern->device->vendor; + info.id_device = kern->device->device; + + if (copy_to_user((drm_agp_info_t *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; +} + +int DRM(agp_acquire)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode; + + if (!dev->agp || dev->agp->acquired || !drm_agp->acquire) + return -EINVAL; + if ((retcode = drm_agp->acquire())) return retcode; + dev->agp->acquired = 1; + return 0; +} + +int DRM(agp_release)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + if (!dev->agp || !dev->agp->acquired || !drm_agp->release) + return -EINVAL; + drm_agp->release(); + dev->agp->acquired = 0; + return 0; + +} + +void DRM(agp_do_release)(void) +{ + if (drm_agp->release) drm_agp->release(); +} + +int DRM(agp_enable)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_mode_t mode; + + if (!dev->agp || !dev->agp->acquired || !drm_agp->enable) + return -EINVAL; + + if (copy_from_user(&mode, (drm_agp_mode_t *)arg, sizeof(mode))) + return -EFAULT; + + dev->agp->mode = mode.mode; + drm_agp->enable(mode.mode); + dev->agp->base = dev->agp->agp_info.aper_base; + dev->agp->enabled = 1; + return 0; +} + +int DRM(agp_alloc)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_buffer_t request; + drm_agp_mem_t *entry; + agp_memory *memory; + unsigned long pages; + u32 type; + + if (!dev->agp || !dev->agp->acquired) return -EINVAL; + if (copy_from_user(&request, (drm_agp_buffer_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = DRM(alloc)(sizeof(*entry), DRM_MEM_AGPLISTS))) + return -ENOMEM; + + memset(entry, 0, sizeof(*entry)); + + pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE; + type = (u32) request.type; + + if (!(memory = DRM(alloc_agp)(pages, type))) { + DRM(free)(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return -ENOMEM; + } + + entry->handle = (unsigned long)memory->memory; + entry->memory = memory; + entry->bound = 0; + entry->pages = pages; + entry->prev = NULL; + entry->next = dev->agp->memory; + if (dev->agp->memory) dev->agp->memory->prev = entry; + dev->agp->memory = entry; + + request.handle = entry->handle; + request.physical = memory->physical; + + if (copy_to_user((drm_agp_buffer_t *)arg, &request, sizeof(request))) { + dev->agp->memory = entry->next; + dev->agp->memory->prev = NULL; + DRM(free_agp)(memory, pages); + DRM(free)(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return -EFAULT; + } + return 0; +} + +static drm_agp_mem_t *DRM(agp_lookup_entry)(drm_device_t *dev, + unsigned long handle) +{ + drm_agp_mem_t *entry; + + for (entry = dev->agp->memory; entry; entry = entry->next) { + if (entry->handle == handle) return entry; + } + return NULL; +} + +int DRM(agp_unbind)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_binding_t request; + drm_agp_mem_t *entry; + + if (!dev->agp || !dev->agp->acquired) return -EINVAL; + if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = DRM(agp_lookup_entry)(dev, request.handle))) + return -EINVAL; + if (!entry->bound) return -EINVAL; + return DRM(unbind_agp)(entry->memory); +} + +int DRM(agp_bind)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_binding_t request; + drm_agp_mem_t *entry; + int retcode; + int page; + + if (!dev->agp || !dev->agp->acquired || !drm_agp->bind_memory) + return -EINVAL; + if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = DRM(agp_lookup_entry)(dev, request.handle))) + return -EINVAL; + if (entry->bound) return -EINVAL; + page = (request.offset + PAGE_SIZE - 1) / PAGE_SIZE; + if ((retcode = DRM(bind_agp)(entry->memory, page))) return retcode; + entry->bound = dev->agp->base + (page << PAGE_SHIFT); + DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n", + dev->agp->base, entry->bound); + return 0; +} + +int DRM(agp_free)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_buffer_t request; + drm_agp_mem_t *entry; + + if (!dev->agp || !dev->agp->acquired) return -EINVAL; + if (copy_from_user(&request, (drm_agp_buffer_t *)arg, sizeof(request))) + return -EFAULT; + if (!(entry = DRM(agp_lookup_entry)(dev, request.handle))) + return -EINVAL; + if (entry->bound) DRM(unbind_agp)(entry->memory); + + if (entry->prev) entry->prev->next = entry->next; + else dev->agp->memory = entry->next; + if (entry->next) entry->next->prev = entry->prev; + DRM(free_agp)(entry->memory, entry->pages); + DRM(free)(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return 0; +} + +drm_agp_head_t *DRM(agp_init)(void) +{ + drm_agp_head_t *head = NULL; + + drm_agp = DRM_AGP_GET; + if (drm_agp) { + if (!(head = DRM(alloc)(sizeof(*head), DRM_MEM_AGPLISTS))) + return NULL; + memset((void *)head, 0, sizeof(*head)); + drm_agp->copy_info(&head->agp_info); + if (head->agp_info.chipset == NOT_SUPPORTED) { + DRM(free)(head, sizeof(*head), DRM_MEM_AGPLISTS); + return NULL; + } + head->memory = NULL; + switch (head->agp_info.chipset) { + case INTEL_GENERIC: head->chipset = "Intel"; break; + case INTEL_LX: head->chipset = "Intel 440LX"; break; + case INTEL_BX: head->chipset = "Intel 440BX"; break; + case INTEL_GX: head->chipset = "Intel 440GX"; break; + case INTEL_I810: head->chipset = "Intel i810"; break; + +#if LINUX_VERSION_CODE >= 0x020400 + case INTEL_I840: head->chipset = "Intel i840"; break; +#endif + + case VIA_GENERIC: head->chipset = "VIA"; break; + case VIA_VP3: head->chipset = "VIA VP3"; break; + case VIA_MVP3: head->chipset = "VIA MVP3"; break; + +#if LINUX_VERSION_CODE >= 0x020400 + case VIA_MVP4: head->chipset = "VIA MVP4"; break; + case VIA_APOLLO_KX133: head->chipset = "VIA Apollo KX133"; + break; + case VIA_APOLLO_KT133: head->chipset = "VIA Apollo KT133"; + break; +#endif + + case VIA_APOLLO_PRO: head->chipset = "VIA Apollo Pro"; + break; + case SIS_GENERIC: head->chipset = "SiS"; break; + case AMD_GENERIC: head->chipset = "AMD"; break; + case AMD_IRONGATE: head->chipset = "AMD Irongate"; break; + case ALI_GENERIC: head->chipset = "ALi"; break; + case ALI_M1541: head->chipset = "ALi M1541"; break; + default: head->chipset = "Unknown"; break; + } + DRM_INFO("AGP %d.%d on %s @ 0x%08lx %ZuMB\n", + head->agp_info.version.major, + head->agp_info.version.minor, + head->chipset, + head->agp_info.aper_base, + head->agp_info.aper_size); + } + return head; +} + +void DRM(agp_uninit)(void) +{ + DRM_AGP_PUT; + drm_agp = NULL; +} + +agp_memory *DRM(agp_allocate_memory)(size_t pages, u32 type) +{ + if (!drm_agp->allocate_memory) return NULL; + return drm_agp->allocate_memory(pages, type); +} + +int DRM(agp_free_memory)(agp_memory *handle) +{ + if (!handle || !drm_agp->free_memory) return 0; + drm_agp->free_memory(handle); + return 1; +} + +int DRM(agp_bind_memory)(agp_memory *handle, off_t start) +{ + if (!handle || !drm_agp->bind_memory) return -EINVAL; + return drm_agp->bind_memory(handle, start); +} + +int DRM(agp_unbind_memory)(agp_memory *handle) +{ + if (!handle || !drm_agp->unbind_memory) return -EINVAL; + return drm_agp->unbind_memory(handle); +} + +#endif /* __REALLY_HAVE_AGP */ diff --git a/linux/drm_bufs.h b/linux/drm_bufs.h new file mode 100644 index 00000000..16af7bd5 --- /dev/null +++ b/linux/drm_bufs.h @@ -0,0 +1,1023 @@ +/* drm_bufs.h -- Generic buffer template -*- linux-c -*- + * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com + * + * Copyright 1999, 2000 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 + * VA LINUX SYSTEMS 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 <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include <linux/vmalloc.h> +#include "drmP.h" + +#ifndef __HAVE_PCI_DMA +#define __HAVE_PCI_DMA 0 +#endif + +#ifndef __HAVE_SG +#define __HAVE_SG 0 +#endif + +#ifndef DRIVER_BUF_PRIV_T +#define DRIVER_BUF_PRIV_T u32 +#endif +#ifndef DRIVER_AGP_BUFFERS_MAP +#if __HAVE_AGP && __HAVE_DMA +#error "You must define DRIVER_AGP_BUFFERS_MAP()" +#else +#define DRIVER_AGP_BUFFERS_MAP( dev ) NULL +#endif +#endif + +/* + * Compute order. Can be made faster. + */ +int DRM(order)( unsigned long size ) +{ + int order; + unsigned long tmp; + + for ( order = 0, tmp = size ; tmp >>= 1 ; ++order ); + + if ( size & ~(1 << order) ) + ++order; + + return order; +} + +int DRM(addmap)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map; + drm_map_list_t *list; + + if ( !(filp->f_mode & 3) ) return -EACCES; /* Require read/write */ + + map = DRM(alloc)( sizeof(*map), DRM_MEM_MAPS ); + if ( !map ) + return -ENOMEM; + + if ( copy_from_user( map, (drm_map_t *)arg, sizeof(*map) ) ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -EFAULT; + } + + /* Only allow shared memory to be removable since we only keep enough + * book keeping information about shared memory to allow for removal + * when processes fork. + */ + if ( (map->flags & _DRM_REMOVABLE) && map->type != _DRM_SHM ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -EINVAL; + } + DRM_DEBUG( "offset = 0x%08lx, size = 0x%08lx, type = %d\n", + map->offset, map->size, map->type ); + if ( (map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK)) ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -EINVAL; + } + map->mtrr = -1; + map->handle = 0; + + switch ( map->type ) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#if !defined(__sparc__) && !defined(__alpha__) + if ( map->offset + map->size < map->offset || + map->offset < virt_to_phys(high_memory) ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -EINVAL; + } +#endif +#ifdef __alpha__ + map->offset += dev->hose->mem_space->start; +#endif +#if __REALLY_HAVE_MTRR + if ( map->type == _DRM_FRAME_BUFFER || + (map->flags & _DRM_WRITE_COMBINING) ) { + map->mtrr = mtrr_add( map->offset, map->size, + MTRR_TYPE_WRCOMB, 1 ); + } +#endif + map->handle = DRM(ioremap)( map->offset, map->size ); + break; + + case _DRM_SHM: + map->handle = vmalloc_32(map->size); + DRM_DEBUG( "%ld %d %p\n", + map->size, DRM(order)( map->size ), map->handle ); + if ( !map->handle ) { + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -ENOMEM; + } + map->offset = (unsigned long)map->handle; + if ( map->flags & _DRM_CONTAINS_LOCK ) { + dev->lock.hw_lock = map->handle; /* Pointer to lock */ + } + break; +#if __REALLY_HAVE_AGP + case _DRM_AGP: +#ifdef __alpha__ + map->offset += dev->hose->mem_space->start; +#endif + map->offset = map->offset + dev->agp->base; + map->mtrr = dev->agp->agp_mtrr; /* for getmap */ + break; +#endif + case _DRM_SCATTER_GATHER: + if (!dev->sg) { + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } + map->offset = map->offset + dev->sg->handle; + break; + + default: + DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); + return -EINVAL; + } + + list = DRM(alloc)(sizeof(*list), DRM_MEM_MAPS); + if(!list) { + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } + memset(list, 0, sizeof(*list)); + list->map = map; + + down(&dev->struct_sem); + list_add(&list->head, &dev->maplist->head); + up(&dev->struct_sem); + + if ( copy_to_user( (drm_map_t *)arg, map, sizeof(*map) ) ) + return -EFAULT; + if ( map->type != _DRM_SHM ) { + if ( copy_to_user( &((drm_map_t *)arg)->handle, + &map->offset, + sizeof(map->offset) ) ) + return -EFAULT; + } + return 0; +} + + +/* Remove a map private from list and deallocate resources if the mapping + * isn't in use. + */ + +int DRM(rmmap)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + struct list_head *list; + drm_map_list_t *r_list; + drm_vma_entry_t *pt, *prev; + drm_map_t *map; + drm_map_t request; + int found_maps = 0; + + if (copy_from_user(&request, (drm_map_t *)arg, + sizeof(request))) { + return -EFAULT; + } + + down(&dev->struct_sem); + list = &dev->maplist->head; + list_for_each(list, &dev->maplist->head) { + r_list = (drm_map_list_t *) list; + + if(r_list->map && + r_list->map->handle == request.handle && + r_list->map->flags & _DRM_REMOVABLE) break; + } + + /* List has wrapped around to the head pointer, or its empty we didn't + * find anything. + */ + if(list == (&dev->maplist->head)) { + up(&dev->struct_sem); + return -EINVAL; + } + map = r_list->map; + list_del(list); + DRM(free)(list, sizeof(*list), DRM_MEM_MAPS); + + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { +#if LINUX_VERSION_CODE >= 0x020300 + if (pt->vma->vm_private_data == map) found_maps++; +#else + if (pt->vma->vm_pte == map) found_maps++; +#endif + } + + if(!found_maps) { + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#if __REALLY_HAVE_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + DRM(ioremapfree)(map->handle, map->size); + break; + case _DRM_SHM: + vfree(map->handle); + break; + case _DRM_AGP: + case _DRM_SCATTER_GATHER: + break; + } + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + } + up(&dev->struct_sem); + return 0; +} + +#if __HAVE_DMA + +#if __REALLY_HAVE_AGP +int DRM(addbufs_agp)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + + if ( !dma ) return -EINVAL; + + if ( copy_from_user( &request, (drm_buf_desc_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + count = request.count; + order = DRM(order)( request.size ); + size = 1 << order; + + alignment = (request.flags & _DRM_PAGE_ALIGN) + ? PAGE_ALIGN(size) : size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + byte_count = 0; + agp_offset = dev->agp->base + request.agp_start; + + DRM_DEBUG( "count: %d\n", count ); + DRM_DEBUG( "order: %d\n", order ); + DRM_DEBUG( "size: %d\n", size ); + DRM_DEBUG( "agp_offset: %ld\n", agp_offset ); + DRM_DEBUG( "alignment: %d\n", alignment ); + DRM_DEBUG( "page_order: %d\n", page_order ); + DRM_DEBUG( "total: %d\n", total ); + + if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL; + if ( dev->queue_count ) return -EBUSY; /* Not while in use */ + + spin_lock( &dev->count_lock ); + if ( dev->buf_use ) { + spin_unlock( &dev->count_lock ); + return -EBUSY; + } + atomic_inc( &dev->buf_alloc ); + spin_unlock( &dev->count_lock ); + + down( &dev->struct_sem ); + entry = &dma->bufs[order]; + if ( entry->buf_count ) { + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = DRM(alloc)( count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + if ( !entry->buflist ) { + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; + } + memset( entry->buflist, 0, count * sizeof(*entry->buflist) ); + + entry->buf_size = size; + entry->page_order = page_order; + + offset = 0; + + while ( entry->buf_count < count ) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + + buf->offset = (dma->byte_count + offset); + buf->bus_address = agp_offset + offset; + buf->address = (void *)(agp_offset + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head( &buf->dma_wait ); + buf->pid = 0; + + buf->dev_priv_size = sizeof(DRIVER_BUF_PRIV_T); + buf->dev_private = DRM(alloc)( sizeof(DRIVER_BUF_PRIV_T), + DRM_MEM_BUFS ); + memset( buf->dev_private, 0, buf->dev_priv_size ); + +#if __HAVE_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG( "buffer %d @ %p\n", + entry->buf_count, buf->address ); + + offset += alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + } + + DRM_DEBUG( "byte_count: %d\n", byte_count ); + + dma->buflist = DRM(realloc)( dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + } + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + + DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count ); + DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count ); + +#if __HAVE_DMA_FREELIST + DRM(freelist_create)( &entry->freelist, entry->buf_count ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + DRM(freelist_put)( dev, &entry->freelist, &entry->buflist[i] ); + } +#endif + up( &dev->struct_sem ); + + request.count = entry->buf_count; + request.size = size; + + if ( copy_to_user( (drm_buf_desc_t *)arg, &request, sizeof(request) ) ) + return -EFAULT; + + dma->flags = _DRM_DMA_USE_AGP; + + atomic_dec( &dev->buf_alloc ); + return 0; +} +#endif /* __REALLY_HAVE_AGP */ + +#if __HAVE_PCI_DMA +int DRM(addbufs_pci)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int count; + int order; + int size; + int total; + int page_order; + drm_buf_entry_t *entry; + unsigned long page; + drm_buf_t *buf; + int alignment; + unsigned long offset; + int i; + int byte_count; + int page_count; + + if ( !dma ) return -EINVAL; + + if ( copy_from_user( &request, (drm_buf_desc_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + count = request.count; + order = DRM(order)( request.size ); + size = 1 << order; + + DRM_DEBUG( "count=%d, size=%d (%d), order=%d, queue_count=%d\n", + request.count, request.size, size, + order, dev->queue_count ); + + if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL; + if ( dev->queue_count ) return -EBUSY; /* Not while in use */ + + alignment = (request.flags & _DRM_PAGE_ALIGN) + ? PAGE_ALIGN(size) : size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + spin_lock( &dev->count_lock ); + if ( dev->buf_use ) { + spin_unlock( &dev->count_lock ); + return -EBUSY; + } + atomic_inc( &dev->buf_alloc ); + spin_unlock( &dev->count_lock ); + + down( &dev->struct_sem ); + entry = &dma->bufs[order]; + if ( entry->buf_count ) { + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = DRM(alloc)( count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + if ( !entry->buflist ) { + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; + } + memset( entry->buflist, 0, count * sizeof(*entry->buflist) ); + + entry->seglist = DRM(alloc)( count * sizeof(*entry->seglist), + DRM_MEM_SEGS ); + if ( !entry->seglist ) { + DRM(free)( entry->buflist, + count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; + } + memset( entry->seglist, 0, count * sizeof(*entry->seglist) ); + + dma->pagelist = DRM(realloc)( dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + (dma->page_count + (count << page_order)) + * sizeof(*dma->pagelist), + DRM_MEM_PAGES ); + DRM_DEBUG( "pagelist: %d entries\n", + dma->page_count + (count << page_order) ); + + entry->buf_size = size; + entry->page_order = page_order; + byte_count = 0; + page_count = 0; + + while ( entry->buf_count < count ) { + page = DRM(alloc_pages)( page_order, DRM_MEM_DMA ); + if ( !page ) break; + entry->seglist[entry->seg_count++] = page; + for ( i = 0 ; i < (1 << page_order) ; i++ ) { + DRM_DEBUG( "page %d @ 0x%08lx\n", + dma->page_count + page_count, + page + PAGE_SIZE * i ); + dma->pagelist[dma->page_count + page_count++] + = page + PAGE_SIZE * i; + } + for ( offset = 0 ; + offset + size <= total && entry->buf_count < count ; + offset += alignment, ++entry->buf_count ) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = (dma->byte_count + byte_count + offset); + buf->address = (void *)(page + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head( &buf->dma_wait ); + buf->pid = 0; +#if __HAVE_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG( "buffer %d @ %p\n", + entry->buf_count, buf->address ); + } + byte_count += PAGE_SIZE << page_order; + } + + dma->buflist = DRM(realloc)( dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + } + + dma->buf_count += entry->buf_count; + dma->seg_count += entry->seg_count; + dma->page_count += entry->seg_count << page_order; + dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); + +#if __HAVE_DMA_FREELIST + DRM(freelist_create)( &entry->freelist, entry->buf_count ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + DRM(freelist_put)( dev, &entry->freelist, &entry->buflist[i] ); + } +#endif + up( &dev->struct_sem ); + + request.count = entry->buf_count; + request.size = size; + + if ( copy_to_user( (drm_buf_desc_t *)arg, &request, sizeof(request) ) ) + return -EFAULT; + + atomic_dec( &dev->buf_alloc ); + return 0; +} +#endif /* __HAVE_PCI_DMA */ + +#ifdef __HAVE_SG +int DRM(addbufs_sg)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + + if ( !dma ) return -EINVAL; + + if ( copy_from_user( &request, (drm_buf_desc_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + count = request.count; + order = DRM(order)( request.size ); + size = 1 << order; + + alignment = (request.flags & _DRM_PAGE_ALIGN) + ? PAGE_ALIGN(size) : size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + byte_count = 0; + agp_offset = request.agp_start; + + DRM_DEBUG( "count: %d\n", count ); + DRM_DEBUG( "order: %d\n", order ); + DRM_DEBUG( "size: %d\n", size ); + DRM_DEBUG( "agp_offset: %ld\n", agp_offset ); + DRM_DEBUG( "alignment: %d\n", alignment ); + DRM_DEBUG( "page_order: %d\n", page_order ); + DRM_DEBUG( "total: %d\n", total ); + + if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL; + if ( dev->queue_count ) return -EBUSY; /* Not while in use */ + + spin_lock( &dev->count_lock ); + if ( dev->buf_use ) { + spin_unlock( &dev->count_lock ); + return -EBUSY; + } + atomic_inc( &dev->buf_alloc ); + spin_unlock( &dev->count_lock ); + + down( &dev->struct_sem ); + entry = &dma->bufs[order]; + if ( entry->buf_count ) { + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = DRM(alloc)( count * sizeof(*entry->buflist), + DRM_MEM_BUFS ); + if ( !entry->buflist ) { + up( &dev->struct_sem ); + atomic_dec( &dev->buf_alloc ); + return -ENOMEM; + } + memset( entry->buflist, 0, count * sizeof(*entry->buflist) ); + + entry->buf_size = size; + entry->page_order = page_order; + + offset = 0; + + while ( entry->buf_count < count ) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + + buf->offset = (dma->byte_count + offset); + buf->bus_address = agp_offset + offset; + buf->address = (void *)(agp_offset + offset + dev->sg->handle); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head( &buf->dma_wait ); + buf->pid = 0; + + buf->dev_priv_size = sizeof(DRIVER_BUF_PRIV_T); + buf->dev_private = DRM(alloc)( sizeof(DRIVER_BUF_PRIV_T), + DRM_MEM_BUFS ); + memset( buf->dev_private, 0, buf->dev_priv_size ); + +#if __HAVE_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG( "buffer %d @ %p\n", + entry->buf_count, buf->address ); + + offset += alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + } + + DRM_DEBUG( "byte_count: %d\n", byte_count ); + + dma->buflist = DRM(realloc)( dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + dma->buflist[i + dma->buf_count] = &entry->buflist[i]; + } + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + + DRM_DEBUG( "dma->buf_count : %d\n", dma->buf_count ); + DRM_DEBUG( "entry->buf_count : %d\n", entry->buf_count ); + +#if __HAVE_DMA_FREELIST + DRM(freelist_create)( &entry->freelist, entry->buf_count ); + for ( i = 0 ; i < entry->buf_count ; i++ ) { + DRM(freelist_put)( dev, &entry->freelist, &entry->buflist[i] ); + } +#endif + up( &dev->struct_sem ); + + request.count = entry->buf_count; + request.size = size; + + if ( copy_to_user( (drm_buf_desc_t *)arg, &request, sizeof(request) ) ) + return -EFAULT; + + dma->flags = _DRM_DMA_USE_SG; + + atomic_dec( &dev->buf_alloc ); + return 0; +} +#endif /* __HAVE_SG */ + +int DRM(addbufs)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_buf_desc_t request; + + if ( copy_from_user( &request, (drm_buf_desc_t *)arg, + sizeof(request) ) ) + return -EFAULT; + +#if __REALLY_HAVE_AGP + if ( request.flags & _DRM_AGP_BUFFER ) + return DRM(addbufs_agp)( inode, filp, cmd, arg ); + else +#endif +#if __HAVE_SG + if ( request.flags & _DRM_SG_BUFFER ) + return DRM(addbufs_sg)( inode, filp, cmd, arg ); + else +#endif +#if __HAVE_PCI_DMA + return DRM(addbufs_pci)( inode, filp, cmd, arg ); +#else + return -EINVAL; +#endif +} + +int DRM(infobufs)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_info_t request; + int i; + int count; + + if ( !dma ) return -EINVAL; + + spin_lock( &dev->count_lock ); + if ( atomic_read( &dev->buf_alloc ) ) { + spin_unlock( &dev->count_lock ); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock( &dev->count_lock ); + + if ( copy_from_user( &request, + (drm_buf_info_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) { + if ( dma->bufs[i].buf_count ) ++count; + } + + DRM_DEBUG( "count = %d\n", count ); + + if ( request.count >= count ) { + for ( i = 0, count = 0 ; i < DRM_MAX_ORDER + 1 ; i++ ) { + if ( dma->bufs[i].buf_count ) { + drm_buf_desc_t *to = &request.list[count]; + drm_buf_entry_t *from = &dma->bufs[i]; + drm_freelist_t *list = &dma->bufs[i].freelist; + if ( copy_to_user( &to->count, + &from->buf_count, + sizeof(from->buf_count) ) || + copy_to_user( &to->size, + &from->buf_size, + sizeof(from->buf_size) ) || + copy_to_user( &to->low_mark, + &list->low_mark, + sizeof(list->low_mark) ) || + copy_to_user( &to->high_mark, + &list->high_mark, + sizeof(list->high_mark) ) ) + return -EFAULT; + + DRM_DEBUG( "%d %d %d %d %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].buf_size, + dma->bufs[i].freelist.low_mark, + dma->bufs[i].freelist.high_mark ); + ++count; + } + } + } + request.count = count; + + if ( copy_to_user( (drm_buf_info_t *)arg, + &request, + sizeof(request) ) ) + return -EFAULT; + + return 0; +} + +int DRM(markbufs)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int order; + drm_buf_entry_t *entry; + + if ( !dma ) return -EINVAL; + + if ( copy_from_user( &request, + (drm_buf_desc_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + DRM_DEBUG( "%d, %d, %d\n", + request.size, request.low_mark, request.high_mark ); + order = DRM(order)( request.size ); + if ( order < DRM_MIN_ORDER || order > DRM_MAX_ORDER ) return -EINVAL; + entry = &dma->bufs[order]; + + if ( request.low_mark < 0 || request.low_mark > entry->buf_count ) + return -EINVAL; + if ( request.high_mark < 0 || request.high_mark > entry->buf_count ) + return -EINVAL; + + entry->freelist.low_mark = request.low_mark; + entry->freelist.high_mark = request.high_mark; + + return 0; +} + +int DRM(freebufs)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_free_t request; + int i; + int idx; + drm_buf_t *buf; + + if ( !dma ) return -EINVAL; + + if ( copy_from_user( &request, + (drm_buf_free_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + DRM_DEBUG( "%d\n", request.count ); + for ( i = 0 ; i < request.count ; i++ ) { + if ( copy_from_user( &idx, + &request.list[i], + sizeof(idx) ) ) + return -EFAULT; + if ( idx < 0 || idx >= dma->buf_count ) { + DRM_ERROR( "Index %d (of %d max)\n", + idx, dma->buf_count - 1 ); + return -EINVAL; + } + buf = dma->buflist[idx]; + if ( buf->pid != current->pid ) { + DRM_ERROR( "Process %d freeing buffer owned by %d\n", + current->pid, buf->pid ); + return -EINVAL; + } + DRM(free_buffer)( dev, buf ); + } + + return 0; +} + +int DRM(mapbufs)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + const int zero = 0; + unsigned long virtual; + unsigned long address; + drm_buf_map_t request; + int i; + + if ( !dma ) return -EINVAL; + + spin_lock( &dev->count_lock ); + if ( atomic_read( &dev->buf_alloc ) ) { + spin_unlock( &dev->count_lock ); + return -EBUSY; + } + dev->buf_use++; /* Can't allocate more after this call */ + spin_unlock( &dev->count_lock ); + + if ( copy_from_user( &request, (drm_buf_map_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + if ( request.count >= dma->buf_count ) { + if ( (__HAVE_AGP && (dma->flags & _DRM_DMA_USE_AGP)) || + (__HAVE_SG && (dma->flags & _DRM_DMA_USE_SG)) ) { + drm_map_t *map = DRIVER_AGP_BUFFERS_MAP( dev ); + + if ( !map ) { + retcode = -EINVAL; + goto done; + } + +#if LINUX_VERSION_CODE <= 0x020402 + down( ¤t->mm->mmap_sem ); +#else + down_write( ¤t->mm->mmap_sem ); +#endif + virtual = do_mmap( filp, 0, map->size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + (unsigned long)map->offset ); +#if LINUX_VERSION_CODE <= 0x020402 + up( ¤t->mm->mmap_sem ); +#else + up_write( ¤t->mm->mmap_sem ); +#endif + } else { +#if LINUX_VERSION_CODE <= 0x020402 + down( ¤t->mm->mmap_sem ); +#else + down_write( ¤t->mm->mmap_sem ); +#endif + virtual = do_mmap( filp, 0, dma->byte_count, + PROT_READ | PROT_WRITE, + MAP_SHARED, 0 ); +#if LINUX_VERSION_CODE <= 0x020402 + up( ¤t->mm->mmap_sem ); +#else + up_write( ¤t->mm->mmap_sem ); +#endif + } + if ( virtual > -1024UL ) { + /* Real error */ + retcode = (signed long)virtual; + goto done; + } + request.virtual = (void *)virtual; + + for ( i = 0 ; i < dma->buf_count ; i++ ) { + if ( copy_to_user( &request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx) ) ) { + retcode = -EFAULT; + goto done; + } + if ( copy_to_user( &request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total) ) ) { + retcode = -EFAULT; + goto done; + } + if ( copy_to_user( &request.list[i].used, + &zero, + sizeof(zero) ) ) { + retcode = -EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; /* *** */ + if ( copy_to_user( &request.list[i].address, + &address, + sizeof(address) ) ) { + retcode = -EFAULT; + goto done; + } + } + } + done: + request.count = dma->buf_count; + DRM_DEBUG( "%d buffers, retcode = %d\n", request.count, retcode ); + + if ( copy_to_user( (drm_buf_map_t *)arg, &request, sizeof(request) ) ) + return -EFAULT; + + return retcode; +} + +#endif /* __HAVE_DMA */ diff --git a/linux/drm_drv.h b/linux/drm_drv.h index 7447ca6d..5337646f 100644 --- a/linux/drm_drv.h +++ b/linux/drm_drv.h @@ -81,6 +81,9 @@ #ifndef __HAVE_COUNTERS #define __HAVE_COUNTERS 0 #endif +#ifndef __HAVE_SG +#define __HAVE_SG 0 +#endif #ifndef DRIVER_PREINIT #define DRIVER_PREINIT() @@ -178,6 +181,11 @@ static drm_ioctl_desc_t DRM(ioctls)[] = { [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { DRM(agp_unbind), 1, 1 }, #endif +#if __HAVE_SG + [DRM_IOCTL_NR(DRM_IOCTL_SG_ALLOC)] = { DRM(sg_alloc), 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)] = { DRM(sg_free), 1, 1 }, +#endif + DRIVER_IOCTLS }; @@ -415,6 +423,17 @@ static int DRM(takedown)( drm_device_t *dev ) * handled in the AGP/GART driver. */ break; + case _DRM_SCATTER_GATHER: + /* Handle it, but do nothing, if HAVE_SG + * isn't defined. + */ +#if __HAVE_SG + if(dev->sg) { + DRM(sg_cleanup)(dev->sg); + dev->sg = NULL; + } +#endif + break; } DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); } diff --git a/linux/drm_fops.h b/linux/drm_fops.h new file mode 100644 index 00000000..75752b3a --- /dev/null +++ b/linux/drm_fops.h @@ -0,0 +1,223 @@ +/* drm_fops.h -- File operations for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.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 + * VA LINUX SYSTEMS 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 <faith@valinux.com> + * Daryll Strauss <daryll@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include <linux/poll.h> + +/* drm_open is called whenever a process opens /dev/drm. */ + +int DRM(open_helper)(struct inode *inode, struct file *filp, drm_device_t *dev) +{ + kdev_t minor = MINOR(inode->i_rdev); + drm_file_t *priv; + + if (filp->f_flags & O_EXCL) return -EBUSY; /* No exclusive opens */ + if (!DRM(cpu_valid)()) return -EINVAL; + + DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor); + + priv = DRM(alloc)(sizeof(*priv), DRM_MEM_FILES); + memset(priv, 0, sizeof(*priv)); + filp->private_data = priv; + priv->uid = current->euid; + priv->pid = current->pid; + priv->minor = minor; + priv->dev = dev; + priv->ioctl_count = 0; + priv->authenticated = capable(CAP_SYS_ADMIN); + + down(&dev->struct_sem); + if (!dev->file_last) { + priv->next = NULL; + priv->prev = NULL; + dev->file_first = priv; + dev->file_last = priv; + } else { + priv->next = NULL; + priv->prev = dev->file_last; + dev->file_last->next = priv; + dev->file_last = priv; + } + up(&dev->struct_sem); + +#ifdef __alpha__ + /* + * Default the hose + */ + if (!dev->hose) { + struct pci_dev *pci_dev; + pci_dev = pci_find_class(PCI_CLASS_DISPLAY_VGA << 8, NULL); + if (pci_dev) dev->hose = pci_dev->sysdata; + if (!dev->hose) { + struct pci_bus *b = pci_bus_b(pci_root_buses.next); + if (b) dev->hose = b->sysdata; + } + } +#endif + + return 0; +} + +int DRM(flush)(struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + return 0; +} + +int DRM(fasync)(int fd, struct file *filp, int on) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode; + + DRM_DEBUG("fd = %d, device = 0x%x\n", fd, dev->device); + retcode = fasync_helper(fd, filp, on, &dev->buf_async); + if (retcode < 0) return retcode; + return 0; +} + + +/* The drm_read and drm_write_string code (especially that which manages + the circular buffer), is based on Alessandro Rubini's LINUX DEVICE + DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */ + +ssize_t DRM(read)(struct file *filp, char *buf, size_t count, loff_t *off) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int left; + int avail; + int send; + int cur; + + DRM_DEBUG("%p, %p\n", dev->buf_rp, dev->buf_wp); + + while (dev->buf_rp == dev->buf_wp) { + DRM_DEBUG(" sleeping\n"); + if (filp->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + interruptible_sleep_on(&dev->buf_readers); + if (signal_pending(current)) { + DRM_DEBUG(" interrupted\n"); + return -ERESTARTSYS; + } + DRM_DEBUG(" awake\n"); + } + + left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + avail = DRM_BSZ - left; + send = DRM_MIN(avail, count); + + while (send) { + if (dev->buf_wp > dev->buf_rp) { + cur = DRM_MIN(send, dev->buf_wp - dev->buf_rp); + } else { + cur = DRM_MIN(send, dev->buf_end - dev->buf_rp); + } + if (copy_to_user(buf, dev->buf_rp, cur)) + return -EFAULT; + dev->buf_rp += cur; + if (dev->buf_rp == dev->buf_end) dev->buf_rp = dev->buf; + send -= cur; + } + + wake_up_interruptible(&dev->buf_writers); + return DRM_MIN(avail, count);; +} + +int DRM(write_string)(drm_device_t *dev, const char *s) +{ + int left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + int send = strlen(s); + int count; + + DRM_DEBUG("%d left, %d to send (%p, %p)\n", + left, send, dev->buf_rp, dev->buf_wp); + + if (left == 1 || dev->buf_wp != dev->buf_rp) { + DRM_ERROR("Buffer not empty (%d left, wp = %p, rp = %p)\n", + left, + dev->buf_wp, + dev->buf_rp); + } + + while (send) { + if (dev->buf_wp >= dev->buf_rp) { + count = DRM_MIN(send, dev->buf_end - dev->buf_wp); + if (count == left) --count; /* Leave a hole */ + } else { + count = DRM_MIN(send, dev->buf_rp - dev->buf_wp - 1); + } + strncpy(dev->buf_wp, s, count); + dev->buf_wp += count; + if (dev->buf_wp == dev->buf_end) dev->buf_wp = dev->buf; + send -= count; + } + +#if LINUX_VERSION_CODE < 0x020315 && !defined(KILLFASYNCHASTHREEPARAMETERS) + /* The extra parameter to kill_fasync was added in 2.3.21, and is + _not_ present in _stock_ 2.2.14 and 2.2.15. However, some + distributions patch 2.2.x kernels to add this parameter. The + Makefile.linux attempts to detect this addition and defines + KILLFASYNCHASTHREEPARAMETERS if three parameters are found. */ + if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO); +#else + + /* Parameter added in 2.3.21. */ +#if LINUX_VERSION_CODE < 0x020400 + if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO, POLL_IN); +#else + /* Type of first parameter changed in + Linux 2.4.0-test2... */ + if (dev->buf_async) kill_fasync(&dev->buf_async, SIGIO, POLL_IN); +#endif +#endif + DRM_DEBUG("waking\n"); + wake_up_interruptible(&dev->buf_readers); + return 0; +} + +unsigned int DRM(poll)(struct file *filp, struct poll_table_struct *wait) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + poll_wait(filp, &dev->buf_readers, wait); + if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM; + return 0; +} diff --git a/linux/drm_init.h b/linux/drm_init.h new file mode 100644 index 00000000..d9d8e3a2 --- /dev/null +++ b/linux/drm_init.h @@ -0,0 +1,116 @@ +/* drm_init.h -- Setup/Cleanup for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.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 + * VA LINUX SYSTEMS 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 <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +#if 0 +int DRM(flags) = DRM_FLAG_DEBUG; +#else +int DRM(flags) = 0; +#endif + +/* drm_parse_option parses a single option. See description for + * drm_parse_options for details. + */ +static void DRM(parse_option)(char *s) +{ + char *c, *r; + + DRM_DEBUG("\"%s\"\n", s); + if (!s || !*s) return; + for (c = s; *c && *c != ':'; c++); /* find : or \0 */ + if (*c) r = c + 1; else r = NULL; /* remember remainder */ + *c = '\0'; /* terminate */ + if (!strcmp(s, "noctx")) { + DRM(flags) |= DRM_FLAG_NOCTX; + DRM_INFO("Server-mediated context switching OFF\n"); + return; + } + if (!strcmp(s, "debug")) { + DRM(flags) |= DRM_FLAG_DEBUG; + DRM_INFO("Debug messages ON\n"); + return; + } + DRM_ERROR("\"%s\" is not a valid option\n", s); + return; +} + +/* drm_parse_options parse the insmod "drm=" options, or the command-line + * options passed to the kernel via LILO. The grammar of the format is as + * follows: + * + * drm ::= 'drm=' option_list + * option_list ::= option [ ';' option_list ] + * option ::= 'device:' major + * | 'debug' + * | 'noctx' + * major ::= INTEGER + * + * Note that 's' contains option_list without the 'drm=' part. + * + * device=major,minor specifies the device number used for /dev/drm + * if major == 0 then the misc device is used + * if major == 0 and minor == 0 then dynamic misc allocation is used + * debug=on specifies that debugging messages will be printk'd + * debug=trace specifies that each function call will be logged via printk + * debug=off turns off all debugging options + * + */ + +void DRM(parse_options)(char *s) +{ + char *h, *t, *n; + + DRM_DEBUG("\"%s\"\n", s ?: ""); + if (!s || !*s) return; + + for (h = t = n = s; h && *h; h = n) { + for (; *t && *t != ';'; t++); /* find ; or \0 */ + if (*t) n = t + 1; else n = NULL; /* remember next */ + *t = '\0'; /* terminate */ + DRM(parse_option)(h); /* parse */ + } +} + +/* drm_cpu_valid returns non-zero if the DRI will run on this CPU, and 0 + * otherwise. + */ +int DRM(cpu_valid)(void) +{ +#if defined(__i386__) + if (boot_cpu_data.x86 == 3) return 0; /* No cmpxchg on a 386 */ +#endif +#if defined(__sparc__) && !defined(__sparc_v9__) + return 0; /* No cmpxchg before v9 sparc. */ +#endif + return 1; +} diff --git a/linux/drm_ioctl.h b/linux/drm_ioctl.h new file mode 100644 index 00000000..1cc8f31f --- /dev/null +++ b/linux/drm_ioctl.h @@ -0,0 +1,231 @@ +/* drm_ioctl.h -- IOCTL processing for DRM -*- linux-c -*- + * Created: Fri Jan 8 09:01:26 1999 by faith@valinux.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 + * VA LINUX SYSTEMS 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 <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int DRM(irq_busid)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_irq_busid_t p; + struct pci_dev *dev; + + if (copy_from_user(&p, (drm_irq_busid_t *)arg, sizeof(p))) + return -EFAULT; + dev = pci_find_slot(p.busnum, PCI_DEVFN(p.devnum, p.funcnum)); + if (dev) p.irq = dev->irq; + else p.irq = 0; + DRM_DEBUG("%d:%d:%d => IRQ %d\n", + p.busnum, p.devnum, p.funcnum, p.irq); + if (copy_to_user((drm_irq_busid_t *)arg, &p, sizeof(p))) + return -EFAULT; + return 0; +} + +int DRM(getunique)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_unique_t u; + + if (copy_from_user(&u, (drm_unique_t *)arg, sizeof(u))) + return -EFAULT; + if (u.unique_len >= dev->unique_len) { + if (copy_to_user(u.unique, dev->unique, dev->unique_len)) + return -EFAULT; + } + u.unique_len = dev->unique_len; + if (copy_to_user((drm_unique_t *)arg, &u, sizeof(u))) + return -EFAULT; + return 0; +} + +int DRM(setunique)(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_unique_t u; + + if (dev->unique_len || dev->unique) + return -EBUSY; + + if (copy_from_user(&u, (drm_unique_t *)arg, sizeof(u))) + return -EFAULT; + + if (!u.unique_len) + return -EINVAL; + + dev->unique_len = u.unique_len; + dev->unique = DRM(alloc)(u.unique_len + 1, DRM_MEM_DRIVER); + if (copy_from_user(dev->unique, u.unique, dev->unique_len)) + return -EFAULT; + dev->unique[dev->unique_len] = '\0'; + + dev->devname = DRM(alloc)(strlen(dev->name) + strlen(dev->unique) + 2, + DRM_MEM_DRIVER); + sprintf(dev->devname, "%s@%s", dev->name, dev->unique); + +#ifdef __alpha__ + do { + struct pci_dev *pci_dev; + int b, d, f; + char *p; + + for(p = dev->unique; p && *p && *p != ':'; p++); + if (!p || !*p) break; + b = (int)simple_strtoul(p+1, &p, 10); + if (*p != ':') break; + d = (int)simple_strtoul(p+1, &p, 10); + if (*p != ':') break; + f = (int)simple_strtoul(p+1, &p, 10); + if (*p) break; + + pci_dev = pci_find_slot(b, PCI_DEVFN(d,f)); + if (pci_dev) + dev->hose = pci_dev->sysdata; + } while(0); +#endif + + return 0; +} + + +int DRM(getmap)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t map; + drm_map_list_t *r_list = NULL; + struct list_head *list; + int idx; + int i; + + if (copy_from_user(&map, (drm_map_t *)arg, sizeof(map))) + return -EFAULT; + idx = map.offset; + + down(&dev->struct_sem); + if (idx < 0 || idx >= dev->map_count) { + up(&dev->struct_sem); + return -EINVAL; + } + + i = 0; + list_for_each(list, &dev->maplist->head) { + if(i == idx) { + r_list = (drm_map_list_t *)list; + break; + } + i++; + } + if(!r_list || !r_list->map) { + up(&dev->struct_sem); + return -EINVAL; + } + + map.offset = r_list->map->offset; + map.size = r_list->map->size; + map.type = r_list->map->type; + map.flags = r_list->map->flags; + map.handle = r_list->map->handle; + map.mtrr = r_list->map->mtrr; + up(&dev->struct_sem); + + if (copy_to_user((drm_map_t *)arg, &map, sizeof(map))) return -EFAULT; + return 0; +} + +int DRM(getclient)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_client_t client; + drm_file_t *pt; + int idx; + int i; + + if (copy_from_user(&client, (drm_client_t *)arg, sizeof(client))) + return -EFAULT; + idx = client.idx; + down(&dev->struct_sem); + for (i = 0, pt = dev->file_first; i < idx && pt; i++, pt = pt->next) + ; + + if (!pt) { + up(&dev->struct_sem); + return -EINVAL; + } + client.auth = pt->authenticated; + client.pid = pt->pid; + client.uid = pt->uid; + client.magic = pt->magic; + client.iocs = pt->ioctl_count; + up(&dev->struct_sem); + + if (copy_to_user((drm_client_t *)arg, &client, sizeof(client))) + return -EFAULT; + return 0; +} + +int DRM(getstats)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_stats_t stats; + int i; + + memset(&stats, 0, sizeof(stats)); + + down(&dev->struct_sem); + + for (i = 0; i < dev->counters; i++) { + if (dev->types[i] == _DRM_STAT_LOCK) + stats.data[i].value + = (dev->lock.hw_lock + ? dev->lock.hw_lock->lock : 0); + else + stats.data[i].value = atomic_read(&dev->counts[i]); + stats.data[i].type = dev->types[i]; + } + + stats.count = dev->counters; + + up(&dev->struct_sem); + + if (copy_to_user((drm_stats_t *)arg, &stats, sizeof(stats))) + return -EFAULT; + return 0; +} diff --git a/linux/drm_memory.h b/linux/drm_memory.h new file mode 100644 index 00000000..498937d4 --- /dev/null +++ b/linux/drm_memory.h @@ -0,0 +1,459 @@ +/* drm_memory.h -- Memory management wrappers for DRM -*- linux-c -*- + * Created: Thu Feb 4 14:00:34 1999 by faith@valinux.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 + * VA LINUX SYSTEMS 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 <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include <linux/config.h> +#include "drmP.h" +#include <linux/wrapper.h> + +typedef struct drm_mem_stats { + const char *name; + int succeed_count; + int free_count; + int fail_count; + unsigned long bytes_allocated; + unsigned long bytes_freed; +} drm_mem_stats_t; + +static spinlock_t DRM(mem_lock) = SPIN_LOCK_UNLOCKED; +static unsigned long DRM(ram_available) = 0; /* In pages */ +static unsigned long DRM(ram_used) = 0; +static drm_mem_stats_t DRM(mem_stats)[] = { + [DRM_MEM_DMA] = { "dmabufs" }, + [DRM_MEM_SAREA] = { "sareas" }, + [DRM_MEM_DRIVER] = { "driver" }, + [DRM_MEM_MAGIC] = { "magic" }, + [DRM_MEM_IOCTLS] = { "ioctltab" }, + [DRM_MEM_MAPS] = { "maplist" }, + [DRM_MEM_VMAS] = { "vmalist" }, + [DRM_MEM_BUFS] = { "buflist" }, + [DRM_MEM_SEGS] = { "seglist" }, + [DRM_MEM_PAGES] = { "pagelist" }, + [DRM_MEM_FILES] = { "files" }, + [DRM_MEM_QUEUES] = { "queues" }, + [DRM_MEM_CMDS] = { "commands" }, + [DRM_MEM_MAPPINGS] = { "mappings" }, + [DRM_MEM_BUFLISTS] = { "buflists" }, + [DRM_MEM_AGPLISTS] = { "agplist" }, + [DRM_MEM_SGLISTS] = { "sglist" }, + [DRM_MEM_TOTALAGP] = { "totalagp" }, + [DRM_MEM_BOUNDAGP] = { "boundagp" }, + [DRM_MEM_CTXBITMAP] = { "ctxbitmap"}, + [DRM_MEM_STUB] = { "stub" }, + { NULL, 0, } /* Last entry must be null */ +}; + +void DRM(mem_init)(void) +{ + drm_mem_stats_t *mem; + struct sysinfo si; + + for (mem = DRM(mem_stats); mem->name; ++mem) { + mem->succeed_count = 0; + mem->free_count = 0; + mem->fail_count = 0; + mem->bytes_allocated = 0; + mem->bytes_freed = 0; + } + + si_meminfo(&si); +#if LINUX_VERSION_CODE < 0x020317 + /* Changed to page count in 2.3.23 */ + DRM(ram_available) = si.totalram >> PAGE_SHIFT; +#else + DRM(ram_available) = si.totalram; +#endif + DRM(ram_used) = 0; +} + +/* drm_mem_info is called whenever a process reads /dev/drm/mem. */ + +static int DRM(_mem_info)(char *buf, char **start, off_t offset, + int request, int *eof, void *data) +{ + drm_mem_stats_t *pt; + int len = 0; + + if (offset > DRM_PROC_LIMIT) { + *eof = 1; + return 0; + } + + *eof = 0; + *start = &buf[offset]; + + DRM_PROC_PRINT(" total counts " + " | outstanding \n"); + DRM_PROC_PRINT("type alloc freed fail bytes freed" + " | allocs bytes\n\n"); + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB |\n", + "system", 0, 0, 0, + DRM(ram_available) << (PAGE_SHIFT - 10)); + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB |\n", + "locked", 0, 0, 0, DRM(ram_used) >> 10); + DRM_PROC_PRINT("\n"); + for (pt = DRM(mem_stats); pt->name; pt++) { + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu %10lu | %6d %10ld\n", + pt->name, + pt->succeed_count, + pt->free_count, + pt->fail_count, + pt->bytes_allocated, + pt->bytes_freed, + pt->succeed_count - pt->free_count, + (long)pt->bytes_allocated + - (long)pt->bytes_freed); + } + + if (len > request + offset) return request; + *eof = 1; + return len - offset; +} + +int DRM(mem_info)(char *buf, char **start, off_t offset, + int len, int *eof, void *data) +{ + int ret; + + spin_lock(&DRM(mem_lock)); + ret = DRM(_mem_info)(buf, start, offset, len, eof, data); + spin_unlock(&DRM(mem_lock)); + return ret; +} + +void *DRM(alloc)(size_t size, int area) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(area, "Allocating 0 bytes\n"); + return NULL; + } + + if (!(pt = kmalloc(size, GFP_KERNEL))) { + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[area].fail_count; + spin_unlock(&DRM(mem_lock)); + return NULL; + } + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[area].succeed_count; + DRM(mem_stats)[area].bytes_allocated += size; + spin_unlock(&DRM(mem_lock)); + return pt; +} + +void *DRM(realloc)(void *oldpt, size_t oldsize, size_t size, int area) +{ + void *pt; + + if (!(pt = DRM(alloc)(size, area))) return NULL; + if (oldpt && oldsize) { + memcpy(pt, oldpt, oldsize); + DRM(free)(oldpt, oldsize, area); + } + return pt; +} + +char *DRM(strdup)(const char *s, int area) +{ + char *pt; + int length = s ? strlen(s) : 0; + + if (!(pt = DRM(alloc)(length+1, area))) return NULL; + strcpy(pt, s); + return pt; +} + +void DRM(strfree)(const char *s, int area) +{ + unsigned int size; + + if (!s) return; + + size = 1 + (s ? strlen(s) : 0); + DRM(free)((void *)s, size, area); +} + +void DRM(free)(void *pt, size_t size, int area) +{ + int alloc_count; + int free_count; + + if (!pt) DRM_MEM_ERROR(area, "Attempt to free NULL pointer\n"); + else kfree(pt); + spin_lock(&DRM(mem_lock)); + DRM(mem_stats)[area].bytes_freed += size; + free_count = ++DRM(mem_stats)[area].free_count; + alloc_count = DRM(mem_stats)[area].succeed_count; + spin_unlock(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +unsigned long DRM(alloc_pages)(int order, int area) +{ + unsigned long address; + unsigned long bytes = PAGE_SIZE << order; + unsigned long addr; + unsigned int sz; + + spin_lock(&DRM(mem_lock)); + if ((DRM(ram_used) >> PAGE_SHIFT) + > (DRM_RAM_PERCENT * DRM(ram_available)) / 100) { + spin_unlock(&DRM(mem_lock)); + return 0; + } + spin_unlock(&DRM(mem_lock)); + + address = __get_free_pages(GFP_KERNEL, order); + if (!address) { + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[area].fail_count; + spin_unlock(&DRM(mem_lock)); + return 0; + } + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[area].succeed_count; + DRM(mem_stats)[area].bytes_allocated += bytes; + DRM(ram_used) += bytes; + spin_unlock(&DRM(mem_lock)); + + + /* Zero outside the lock */ + memset((void *)address, 0, bytes); + + /* Reserve */ + for (addr = address, sz = bytes; + sz > 0; + addr += PAGE_SIZE, sz -= PAGE_SIZE) { +#if LINUX_VERSION_CODE >= 0x020400 + /* Argument type changed in 2.4.0-test6/pre8 */ + mem_map_reserve(virt_to_page(addr)); +#else + mem_map_reserve(MAP_NR(addr)); +#endif + } + + return address; +} + +void DRM(free_pages)(unsigned long address, int order, int area) +{ + unsigned long bytes = PAGE_SIZE << order; + int alloc_count; + int free_count; + unsigned long addr; + unsigned int sz; + + if (!address) { + DRM_MEM_ERROR(area, "Attempt to free address 0\n"); + } else { + /* Unreserve */ + for (addr = address, sz = bytes; + sz > 0; + addr += PAGE_SIZE, sz -= PAGE_SIZE) { +#if LINUX_VERSION_CODE >= 0x020400 + /* Argument type changed in 2.4.0-test6/pre8 */ + mem_map_unreserve(virt_to_page(addr)); +#else + mem_map_unreserve(MAP_NR(addr)); +#endif + } + free_pages(address, order); + } + + spin_lock(&DRM(mem_lock)); + free_count = ++DRM(mem_stats)[area].free_count; + alloc_count = DRM(mem_stats)[area].succeed_count; + DRM(mem_stats)[area].bytes_freed += bytes; + DRM(ram_used) -= bytes; + spin_unlock(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +void *DRM(ioremap)(unsigned long offset, unsigned long size) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Mapping 0 bytes at 0x%08lx\n", offset); + return NULL; + } + + if (!(pt = ioremap(offset, size))) { + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_MAPPINGS].fail_count; + spin_unlock(&DRM(mem_lock)); + return NULL; + } + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_MAPPINGS].succeed_count; + DRM(mem_stats)[DRM_MEM_MAPPINGS].bytes_allocated += size; + spin_unlock(&DRM(mem_lock)); + return pt; +} + +void DRM(ioremapfree)(void *pt, unsigned long size) +{ + int alloc_count; + int free_count; + + if (!pt) + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Attempt to free NULL pointer\n"); + else + iounmap(pt); + + spin_lock(&DRM(mem_lock)); + DRM(mem_stats)[DRM_MEM_MAPPINGS].bytes_freed += size; + free_count = ++DRM(mem_stats)[DRM_MEM_MAPPINGS].free_count; + alloc_count = DRM(mem_stats)[DRM_MEM_MAPPINGS].succeed_count; + spin_unlock(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +#if __REALLY_HAVE_AGP + +agp_memory *DRM(alloc_agp)(int pages, u32 type) +{ + agp_memory *handle; + + if (!pages) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, "Allocating 0 pages\n"); + return NULL; + } + + if ((handle = DRM(agp_allocate_memory)(pages, type))) { + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_TOTALAGP].succeed_count; + DRM(mem_stats)[DRM_MEM_TOTALAGP].bytes_allocated + += pages << PAGE_SHIFT; + spin_unlock(&DRM(mem_lock)); + return handle; + } + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_TOTALAGP].fail_count; + spin_unlock(&DRM(mem_lock)); + return NULL; +} + +int DRM(free_agp)(agp_memory *handle, int pages) +{ + int alloc_count; + int free_count; + int retval = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, + "Attempt to free NULL AGP handle\n"); + return retval;; + } + + if (DRM(agp_free_memory)(handle)) { + spin_lock(&DRM(mem_lock)); + free_count = ++DRM(mem_stats)[DRM_MEM_TOTALAGP].free_count; + alloc_count = DRM(mem_stats)[DRM_MEM_TOTALAGP].succeed_count; + DRM(mem_stats)[DRM_MEM_TOTALAGP].bytes_freed + += pages << PAGE_SHIFT; + spin_unlock(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } + return 0; + } + return retval; +} + +int DRM(bind_agp)(agp_memory *handle, unsigned int start) +{ + int retcode = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Attempt to bind NULL AGP handle\n"); + return retcode; + } + + if (!(retcode = DRM(agp_bind_memory)(handle, start))) { + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_BOUNDAGP].succeed_count; + DRM(mem_stats)[DRM_MEM_BOUNDAGP].bytes_allocated + += handle->page_count << PAGE_SHIFT; + spin_unlock(&DRM(mem_lock)); + return retcode; + } + spin_lock(&DRM(mem_lock)); + ++DRM(mem_stats)[DRM_MEM_BOUNDAGP].fail_count; + spin_unlock(&DRM(mem_lock)); + return retcode; +} + +int DRM(unbind_agp)(agp_memory *handle) +{ + int alloc_count; + int free_count; + int retcode = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Attempt to unbind NULL AGP handle\n"); + return retcode; + } + + if ((retcode = DRM(agp_unbind_memory)(handle))) return retcode; + spin_lock(&DRM(mem_lock)); + free_count = ++DRM(mem_stats)[DRM_MEM_BOUNDAGP].free_count; + alloc_count = DRM(mem_stats)[DRM_MEM_BOUNDAGP].succeed_count; + DRM(mem_stats)[DRM_MEM_BOUNDAGP].bytes_freed + += handle->page_count << PAGE_SHIFT; + spin_unlock(&DRM(mem_lock)); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } + return retcode; +} +#endif diff --git a/linux/drm_scatter.h b/linux/drm_scatter.h new file mode 100644 index 00000000..a0371074 --- /dev/null +++ b/linux/drm_scatter.h @@ -0,0 +1,218 @@ +/* drm_scatter.h -- IOCTLs to manage scatter/gather memory -*- linux-c -*- + * Created: Mon Dec 18 23:20:54 2000 by gareth@valinux.com + * + * 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: + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include <linux/config.h> +#include <linux/vmalloc.h> +#include "drmP.h" + +#define DEBUG_SCATTER 0 + +void DRM(sg_cleanup)( drm_sg_mem_t *entry ) +{ + struct page *page; + int i; + + for ( i = 0 ; i < entry->pages ; i++ ) { + page = entry->pagelist[i]; + if ( page ) + ClearPageReserved( page ); + } + + vfree( entry->virtual ); + + DRM(free)( entry->pagelist, + entry->pages * sizeof(*entry->pagelist), + DRM_MEM_PAGES ); + DRM(free)( entry, + sizeof(*entry), + DRM_MEM_SGLISTS ); +} + +int DRM(sg_alloc)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_scatter_gather_t request; + drm_sg_mem_t *entry; + unsigned long pages, i, j; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + if ( dev->sg ) + return -EINVAL; + + if ( copy_from_user( &request, + (drm_scatter_gather_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + entry = DRM(alloc)( sizeof(*entry), DRM_MEM_SGLISTS ); + if ( !entry ) + return -ENOMEM; + + memset( entry, 0, sizeof(*entry) ); + + pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE; + DRM_DEBUG( "sg size=%ld pages=%ld\n", request.size, pages ); + + entry->pages = pages; + entry->pagelist = DRM(alloc)( pages * sizeof(*entry->pagelist), + DRM_MEM_PAGES ); + if ( !entry->pagelist ) { + DRM(free)( entry, sizeof(*entry), DRM_MEM_SGLISTS ); + return -ENOMEM; + } + + entry->virtual = vmalloc_32( pages << PAGE_SHIFT ); + if ( !entry->virtual ) { + DRM(free)( entry->pagelist, + entry->pages * sizeof(*entry->pagelist), + DRM_MEM_PAGES ); + DRM(free)( entry, + sizeof(*entry), + DRM_MEM_SGLISTS ); + return -ENOMEM; + } + + /* This also forces the mapping of COW pages, so our page list + * will be valid. Please don't remove it... + */ + memset( entry->virtual, 0, pages << PAGE_SHIFT ); + + entry->handle = (unsigned long)entry->virtual; + + DRM_DEBUG( "sg alloc handle = %08lx\n", entry->handle ); + DRM_DEBUG( "sg alloc virtual = %p\n", entry->virtual ); + + for ( i = entry->handle, j = 0 ; j < pages ; i += PAGE_SIZE, j++ ) { + pgd = pgd_offset_k( i ); + if ( !pgd_present( *pgd ) ) + goto failed; + + pmd = pmd_offset( pgd, i ); + if ( !pmd_present( *pmd ) ) + goto failed; + + pte = pte_offset( pmd, i ); + if ( !pte_present( *pte ) ) + goto failed; + + entry->pagelist[j] = pte_page( *pte ); + + SetPageReserved( entry->pagelist[j] ); + } + + request.handle = entry->handle; + + if ( copy_to_user( (drm_scatter_gather_t *)arg, + &request, + sizeof(request) ) ) { + DRM(sg_cleanup)( entry ); + return -EFAULT; + } + + dev->sg = entry; + +#if DEBUG_SCATTER + /* Verify that each page points to its virtual address, and vice + * versa. + */ + { + int error = 0; + + for ( i = 0 ; i < pages ; i++ ) { + unsigned long *tmp; + + tmp = (unsigned long *)entry->pagelist[i]->virtual; + for ( j = 0 ; + j < PAGE_SIZE / sizeof(unsigned long) ; + j++, tmp++ ) { + *tmp = 0xcafebabe; + } + tmp = (unsigned long *)((u8 *)entry->virtual + + (PAGE_SIZE * i)); + for( j = 0 ; + j < PAGE_SIZE / sizeof(unsigned long) ; + j++, tmp++ ) { + if ( *tmp != 0xcafebabe && error == 0 ) { + error = 1; + DRM_ERROR( "Scatter allocation error, " + "pagelist does not match " + "virtual mapping\n" ); + } + } + tmp = (unsigned long *)entry->pagelist[i]->virtual; + for(j = 0 ; + j < PAGE_SIZE / sizeof(unsigned long) ; + j++, tmp++) { + *tmp = 0; + } + } + if (error == 0) + DRM_ERROR( "Scatter allocation matches pagelist\n" ); + } +#endif + + return 0; + + failed: + DRM(sg_cleanup)( entry ); + return -ENOMEM; +} + +int DRM(sg_free)( struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg ) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_scatter_gather_t request; + drm_sg_mem_t *entry; + + if ( copy_from_user( &request, + (drm_scatter_gather_t *)arg, + sizeof(request) ) ) + return -EFAULT; + + entry = dev->sg; + dev->sg = NULL; + + if ( !entry || entry->handle != request.handle ) + return -EINVAL; + + DRM_DEBUG( "sg free virtual = %p\n", entry->virtual ); + + DRM(sg_cleanup)( entry ); + + return 0; +} diff --git a/linux/drm_vm.h b/linux/drm_vm.h new file mode 100644 index 00000000..771c11bd --- /dev/null +++ b/linux/drm_vm.h @@ -0,0 +1,482 @@ +/* drm_vm.h -- Memory mapping for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.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 + * VA LINUX SYSTEMS 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 <faith@valinux.com> + * Gareth Hughes <gareth@valinux.com> + */ + +#define __NO_VERSION__ +#include "drmP.h" + +struct vm_operations_struct drm_vm_ops = { + nopage: DRM(vm_nopage), + open: DRM(vm_open), + close: DRM(vm_close), +}; + +struct vm_operations_struct drm_vm_shm_ops = { + nopage: DRM(vm_shm_nopage), + open: DRM(vm_open), + close: DRM(vm_shm_close), +}; + +struct vm_operations_struct drm_vm_dma_ops = { + nopage: DRM(vm_dma_nopage), + open: DRM(vm_open), + close: DRM(vm_close), +}; + +struct vm_operations_struct drm_vm_sg_ops = { + nopage: DRM(vm_sg_nopage), + open: DRM(vm_open), + close: DRM(vm_close), +}; + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long DRM(vm_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *DRM(vm_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + return NOPAGE_SIGBUS; /* Disallow mremap */ +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long DRM(vm_shm_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *DRM(vm_shm_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ +#if LINUX_VERSION_CODE >= 0x020300 + drm_map_t *map = (drm_map_t *)vma->vm_private_data; +#else + drm_map_t *map = (drm_map_t *)vma->vm_pte; +#endif + unsigned long physical; + unsigned long offset; + unsigned long i; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!map) return NOPAGE_OOM; /* Nothing allocated */ + + offset = address - vma->vm_start; + i = (unsigned long)map->handle + offset; + /* We have to walk page tables here because we need large SAREA's, and + * they need to be virtually contiguous in kernel space. + */ + pgd = pgd_offset_k( i ); + if( !pgd_present( *pgd ) ) return NOPAGE_OOM; + pmd = pmd_offset( pgd, i ); + if( !pmd_present( *pmd ) ) return NOPAGE_OOM; + pte = pte_offset( pmd, i ); + if( !pte_present( *pte ) ) return NOPAGE_OOM; + physical = (unsigned long)pte_page( *pte )->virtual; + atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx => 0x%08lx\n", address, physical); +#if LINUX_VERSION_CODE < 0x020317 + return physical; +#else + return virt_to_page(physical); +#endif +} + +/* Special close routine which deletes map information if we are the last + * person to close a mapping and its not in the global maplist. + */ + +void DRM(vm_shm_close)(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_vma_entry_t *pt, *prev; + drm_map_t *map; + drm_map_list_t *r_list; + struct list_head *list; + int found_maps = 0; + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_dec(&dev->vma_count); + +#if LINUX_VERSION_CODE >= 0x020300 + map = vma->vm_private_data; +#else + map = vma->vm_pte; +#endif + + down(&dev->struct_sem); + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { +#if LINUX_VERSION_CODE >= 0x020300 + if (pt->vma->vm_private_data == map) found_maps++; +#else + if (pt->vma->vm_pte == map) found_maps++; +#endif + if (pt->vma == vma) { + if (prev) { + prev->next = pt->next; + } else { + dev->vmalist = pt->next; + } + DRM(free)(pt, sizeof(*pt), DRM_MEM_VMAS); + } + } + /* We were the only map that was found */ + if(found_maps == 1 && + map->flags & _DRM_REMOVABLE) { + /* Check to see if we are in the maplist, if we are not, then + * we delete this mappings information. + */ + found_maps = 0; + list = &dev->maplist->head; + list_for_each(list, &dev->maplist->head) { + r_list = (drm_map_list_t *) list; + if (r_list->map == map) found_maps++; + } + + if(!found_maps) { + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#if __REALLY_HAVE_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + DRM(ioremapfree)(map->handle, map->size); + break; + case _DRM_SHM: + vfree(map->handle); + break; + case _DRM_AGP: + case _DRM_SCATTER_GATHER: + break; + } + DRM(free)(map, sizeof(*map), DRM_MEM_MAPS); + } + } + up(&dev->struct_sem); +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long DRM(vm_dma_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *DRM(vm_dma_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + unsigned long physical; + unsigned long offset; + unsigned long page; + + if (!dma) return NOPAGE_SIGBUS; /* Error */ + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!dma->pagelist) return NOPAGE_OOM ; /* Nothing allocated */ + + offset = address - vma->vm_start; /* vm_[pg]off[set] should be 0 */ + page = offset >> PAGE_SHIFT; + physical = dma->pagelist[page] + (offset & (~PAGE_MASK)); + atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical); +#if LINUX_VERSION_CODE < 0x020317 + return physical; +#else + return virt_to_page(physical); +#endif +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long DRM(vm_sg_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *DRM(vm_sg_nopage)(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ +#if LINUX_VERSION_CODE >= 0x020300 + drm_map_t *map = (drm_map_t *)vma->vm_private_data; +#else + drm_map_t *map = (drm_map_t *)vma->vm_pte; +#endif + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_sg_mem_t *entry = dev->sg; + unsigned long offset; + unsigned long map_offset; + unsigned long page_offset; + struct page *page; + + if (!entry) return NOPAGE_SIGBUS; /* Error */ + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!entry->pagelist) return NOPAGE_OOM ; /* Nothing allocated */ + + + offset = address - vma->vm_start; + map_offset = map->offset - dev->sg->handle; + page_offset = (offset >> PAGE_SHIFT) + (map_offset >> PAGE_SHIFT); + page = entry->pagelist[page_offset]; + atomic_inc(&page->count); /* Dec. by kernel */ + +#if LINUX_VERSION_CODE < 0x020317 + return (unsigned long)virt_to_phys(page->virtual); +#else + return page; +#endif +} + +void DRM(vm_open)(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_vma_entry_t *vma_entry; + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); + atomic_inc(&dev->vma_count); +#if LINUX_VERSION_CODE < 0x020333 + /* The map can exist after the fd is closed. */ + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + + vma_entry = DRM(alloc)(sizeof(*vma_entry), DRM_MEM_VMAS); + if (vma_entry) { + down(&dev->struct_sem); + vma_entry->vma = vma; + vma_entry->next = dev->vmalist; + vma_entry->pid = current->pid; + dev->vmalist = vma_entry; + up(&dev->struct_sem); + } +} + +void DRM(vm_close)(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_vma_entry_t *pt, *prev; + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_dec(&dev->vma_count); + + down(&dev->struct_sem); + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { + if (pt->vma == vma) { + if (prev) { + prev->next = pt->next; + } else { + dev->vmalist = pt->next; + } + DRM(free)(pt, sizeof(*pt), DRM_MEM_VMAS); + break; + } + } + up(&dev->struct_sem); +} + +int DRM(mmap_dma)(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + drm_device_dma_t *dma; + unsigned long length = vma->vm_end - vma->vm_start; + + lock_kernel(); + dev = priv->dev; + dma = dev->dma; + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + /* Length must match exact page count */ + if (!dma || (length >> PAGE_SHIFT) != dma->page_count) { + unlock_kernel(); + return -EINVAL; + } + unlock_kernel(); + + vma->vm_ops = &drm_vm_dma_ops; + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + +#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ + /* In Linux 2.2.3 and above, this is + handled in do_mmap() in mm/mmap.c. */ + ++filp->f_count; +#endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ + DRM(vm_open)(vma); + return 0; +} + +int DRM(mmap)(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map = NULL; + drm_map_list_t *r_list; + unsigned long offset = 0; + struct list_head *list; + + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + if (!VM_OFFSET(vma)) return DRM(mmap_dma)(filp, vma); + + /* A sequential search of a linked list is + fine here because: 1) there will only be + about 5-10 entries in the list and, 2) a + DRI client only has to do this mapping + once, so it doesn't have to be optimized + for performance, even if the list was a + bit longer. */ + list_for_each(list, &dev->maplist->head) { + r_list = (drm_map_list_t *)list; + map = r_list->map; + if (!map) continue; + if (map->offset == VM_OFFSET(vma)) break; + } + + if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) + return -EPERM; + + /* Check for valid size. */ + if (map->size != vma->vm_end - vma->vm_start) return -EINVAL; + + if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) { + vma->vm_flags &= VM_MAYWRITE; +#if defined(__i386__) + pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW; +#else + /* Ye gads this is ugly. With more thought + we could move this up higher and use + `protection_map' instead. */ + vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect( + __pte(pgprot_val(vma->vm_page_prot))))); +#endif + } + + switch (map->type) { + case _DRM_FRAME_BUFFER: + case _DRM_REGISTERS: + case _DRM_AGP: + if (VM_OFFSET(vma) >= __pa(high_memory)) { +#if defined(__i386__) + if (boot_cpu_data.x86 > 3 && map->type != _DRM_AGP) { + pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; + pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT; + } +#elif defined(__ia64__) + if (map->type != _DRM_AGP) + vma->vm_page_prot = + pgprot_writecombine(vma->vm_page_prot); +#elif defined(__powerpc__) + pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE | _PAGE_GUARDED; +#endif + vma->vm_flags |= VM_IO; /* not in core dump */ + } +#ifdef __alpha__ + offset = dev->hose->dense_mem_base - + dev->hose->mem_space->start; +#endif + if (remap_page_range(vma->vm_start, + VM_OFFSET(vma) + offset, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + DRM_DEBUG(" Type = %d; start = 0x%lx, end = 0x%lx," + " offset = 0x%lx\n", + map->type, + vma->vm_start, vma->vm_end, VM_OFFSET(vma) + offset); + vma->vm_ops = &drm_vm_ops; + break; + case _DRM_SHM: + vma->vm_ops = &drm_vm_shm_ops; +#if LINUX_VERSION_CODE >= 0x020300 + vma->vm_private_data = (void *)map; +#else + vma->vm_pte = (unsigned long)map; +#endif + /* Don't let this area swap. Change when + DRM_KERNEL advisory is supported. */ + vma->vm_flags |= VM_LOCKED; + break; + case _DRM_SCATTER_GATHER: + vma->vm_ops = &drm_vm_sg_ops; +#if LINUX_VERSION_CODE >= 0x020300 + vma->vm_private_data = (void *)map; +#else + vma->vm_pte = (unsigned long)map; +#endif + vma->vm_flags |= VM_LOCKED; + break; + default: + return -EINVAL; /* This should never happen. */ + } + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + +#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ + /* In Linux 2.2.3 and above, this is + handled in do_mmap() in mm/mmap.c. */ + ++filp->f_count; +#endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ + DRM(vm_open)(vma); + return 0; +} diff --git a/linux/mga_dma.c b/linux/mga_dma.c index 8b0a67a2..66c9df9f 100644 --- a/linux/mga_dma.c +++ b/linux/mga_dma.c @@ -42,6 +42,7 @@ #include <linux/delay.h> #define MGA_DEFAULT_USEC_TIMEOUT 10000 +#define MGA_FREELIST_DEBUG 0 /* ================================================================ @@ -63,7 +64,10 @@ int mga_do_wait_for_idle( drm_mga_private_t *dev_priv ) udelay( 1 ); } - DRM_DEBUG( "failed! status=0x%08x\n", status ); +#if MGA_DMA_DEBUG + DRM_ERROR( "failed!\n" ); + DRM_INFO( " status=0x%08x\n", status ); +#endif return -EBUSY; } @@ -79,7 +83,9 @@ int mga_do_dma_idle( drm_mga_private_t *dev_priv ) udelay( 1 ); } - DRM_DEBUG( "failed! status=0x%08x\n", status ); +#if MGA_DMA_DEBUG + DRM_ERROR( "failed! status=0x%08x\n", status ); +#endif return -EBUSY; } @@ -208,7 +214,7 @@ void mga_do_dma_wrap_start( drm_mga_private_t *dev_priv ) DMA_BLOCK( MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000, - MGA_DWGSYNC, 0x12345678 ); + MGA_DMAPAD, 0x00000000 ); ADVANCE_DMA(); @@ -235,11 +241,13 @@ void mga_do_dma_wrap_start( drm_mga_private_t *dev_priv ) mga_flush_write_combine(); MGA_WRITE( MGA_PRIMEND, tail | MGA_PAGPXFER ); + set_bit( 0, &primary->wrapped ); DRM_DEBUG( "%s: done.\n", __FUNCTION__ ); } void mga_do_dma_wrap_end( drm_mga_private_t *dev_priv ) { + drm_mga_primary_buffer_t *primary = &dev_priv->prim; drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; u32 head = dev_priv->primary->offset; DRM_DEBUG( "%s:\n", __FUNCTION__ ); @@ -250,6 +258,7 @@ void mga_do_dma_wrap_end( drm_mga_private_t *dev_priv ) mga_flush_write_combine(); MGA_WRITE( MGA_PRIMADDRESS, head | MGA_DMA_GENERAL ); + clear_bit( 0, &primary->wrapped ); DRM_DEBUG( "%s: done.\n", __FUNCTION__ ); } @@ -261,6 +270,7 @@ void mga_do_dma_wrap_end( drm_mga_private_t *dev_priv ) #define MGA_BUFFER_USED ~0 #define MGA_BUFFER_FREE 0 +#if MGA_FREELIST_DEBUG static void mga_freelist_print( drm_device_t *dev ) { drm_mga_private_t *dev_priv = dev->dev_private; @@ -280,6 +290,7 @@ static void mga_freelist_print( drm_device_t *dev ) } DRM_INFO( "\n" ); } +#endif static int mga_freelist_init( drm_device_t *dev ) { @@ -348,6 +359,9 @@ static void mga_freelist_cleanup( drm_device_t *dev ) dev_priv->head = dev_priv->tail = NULL; } +#if 0 +/* FIXME: Still needed? + */ static void mga_freelist_reset( drm_device_t *dev ) { drm_device_dma_t *dma = dev->dma; @@ -362,6 +376,7 @@ static void mga_freelist_reset( drm_device_t *dev ) MGA_BUFFER_FREE, 0 ); } } +#endif static drm_buf_t *mga_freelist_get( drm_device_t *dev ) { @@ -468,6 +483,11 @@ static int mga_do_init_dma( drm_device_t *dev, drm_mga_init_t *init ) dev_priv->depth_offset = init->depth_offset; dev_priv->depth_pitch = init->depth_pitch; + /* FIXME: Need to support AGP textures... + */ + dev_priv->texture_offset = init->texture_offset[0]; + dev_priv->texture_size = init->texture_size[0]; + list_for_each( list, &dev->maplist->head ) { drm_map_list_t *entry = (drm_map_list_t *)list; if ( entry->map && @@ -530,6 +550,7 @@ static int mga_do_init_dma( drm_device_t *dev, drm_mga_init_t *init ) dev_priv->prim.tail = 0; dev_priv->prim.space = dev_priv->prim.size; + dev_priv->prim.wrapped = 0; dev_priv->prim.last_flush = 0; dev_priv->prim.last_wrap = 0; @@ -621,15 +642,21 @@ int mga_dma_flush( struct inode *inode, struct file *filp, (lock.flags & _DRM_LOCK_FLUSH_ALL) ? "flush all, " : "", (lock.flags & _DRM_LOCK_QUIESCENT) ? "idle, " : "" ); - WRAP_TEST_WITH_RETURN( dev_priv ); + WRAP_WAIT_WITH_RETURN( dev_priv ); -#if 0 if ( lock.flags & (_DRM_LOCK_FLUSH | _DRM_LOCK_FLUSH_ALL) ) { mga_do_dma_flush( dev_priv ); } -#endif + if ( lock.flags & _DRM_LOCK_QUIESCENT ) { +#if MGA_DMA_DEBUG + int ret = mga_do_wait_for_idle( dev_priv ); + if ( ret < 0 ) + DRM_INFO( __FUNCTION__": -EBUSY\n" ); + return ret; +#else return mga_do_wait_for_idle( dev_priv ); +#endif } else { return 0; } @@ -681,6 +708,7 @@ int mga_dma_buffers( struct inode *inode, struct file *filp, drm_file_t *priv = filp->private_data; drm_device_t *dev = priv->dev; drm_device_dma_t *dma = dev->dma; + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; drm_dma_t d; int ret = 0; @@ -705,6 +733,8 @@ int mga_dma_buffers( struct inode *inode, struct file *filp, return -EINVAL; } + WRAP_TEST_WITH_RETURN( dev_priv ); + d.granted_count = 0; if ( d.request_count ) { diff --git a/linux/mga_drm.h b/linux/mga_drm.h index 066b4e48..4af2ca2e 100644 --- a/linux/mga_drm.h +++ b/linux/mga_drm.h @@ -237,7 +237,7 @@ typedef struct drm_mga_init { MGA_CLEANUP_DMA = 0x02 } func; - int sarea_priv_offset; + unsigned long sarea_priv_offset; int chipset; int sgram; @@ -254,12 +254,12 @@ typedef struct drm_mga_init { unsigned int texture_offset[MGA_NR_TEX_HEAPS]; unsigned int texture_size[MGA_NR_TEX_HEAPS]; - unsigned int fb_offset; - unsigned int mmio_offset; - unsigned int status_offset; - unsigned int warp_offset; - unsigned int primary_offset; - unsigned int buffers_offset; + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long status_offset; + unsigned long warp_offset; + unsigned long primary_offset; + unsigned long buffers_offset; } drm_mga_init_t; typedef struct drm_mga_fullscreen { diff --git a/linux/mga_drv.c b/linux/mga_drv.c index 42794e82..bea65cfa 100644 --- a/linux/mga_drv.c +++ b/linux/mga_drv.c @@ -38,11 +38,11 @@ #define DRIVER_NAME "mga" #define DRIVER_DESC "Matrox G200/G400" -#define DRIVER_DATE "20010319" +#define DRIVER_DATE "20010321" #define DRIVER_MAJOR 3 #define DRIVER_MINOR 0 -#define DRIVER_PATCHLEVEL 1 +#define DRIVER_PATCHLEVEL 2 #define DRIVER_IOCTLS \ [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { mga_dma_buffers, 1, 0 }, \ diff --git a/linux/mga_drv.h b/linux/mga_drv.h index bd33d9d1..bc70d750 100644 --- a/linux/mga_drv.h +++ b/linux/mga_drv.h @@ -38,6 +38,7 @@ typedef struct drm_mga_primary_buffer { u32 tail; int space; + volatile int wrapped; volatile u32 *status; @@ -123,7 +124,6 @@ extern void mga_do_dma_wrap_end( drm_mga_private_t *dev_priv ); extern int mga_freelist_put( drm_device_t *dev, drm_buf_t *buf ); - /* mga_state.c */ extern int mga_dma_clear( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ); @@ -193,10 +193,26 @@ do { \ #define WRAP_TEST_WITH_RETURN( dev_priv ) \ do { \ - if ( dev_priv->sarea_priv->last_wrap < \ - dev_priv->prim.last_wrap ) { \ - if ( mga_do_wait_for_idle( dev_priv ) < 0 ) \ + if ( test_bit( 0, &dev_priv->prim.wrapped ) ) { \ + if ( mga_is_idle( dev_priv ) ) { \ + mga_do_dma_wrap_end( dev_priv ); \ + } else if ( dev_priv->prim.space < \ + dev_priv->prim.high_mark ) { \ + if ( MGA_DMA_DEBUG ) \ + DRM_INFO( __FUNCTION__": wrap...\n" ); \ + return -EBUSY; \ + } \ + } \ +} while (0) + +#define WRAP_WAIT_WITH_RETURN( dev_priv ) \ +do { \ + if ( test_bit( 0, &dev_priv->prim.wrapped ) ) { \ + if ( mga_do_wait_for_idle( dev_priv ) < 0 ) { \ + if ( MGA_DMA_DEBUG ) \ + DRM_INFO( __FUNCTION__": wrap...\n" ); \ return -EBUSY; \ + } \ mga_do_dma_wrap_end( dev_priv ); \ } \ } while (0) @@ -252,10 +268,13 @@ do { \ MGA_READ( MGA_PRIMADDRESS ) - \ dev_priv->primary->offset ); \ } \ - if ( dev_priv->prim.space < dev_priv->prim.high_mark ) { \ - mga_do_dma_wrap_start( dev_priv ); \ - } else { \ - mga_do_dma_flush( dev_priv ); \ + if ( !test_bit( 0, &dev_priv->prim.wrapped ) ) { \ + if ( dev_priv->prim.space < \ + dev_priv->prim.high_mark ) { \ + mga_do_dma_wrap_start( dev_priv ); \ + } else { \ + mga_do_dma_flush( dev_priv ); \ + } \ } \ } while (0) @@ -317,7 +336,7 @@ do { \ #define MGA_DMA_IDLE_MASK (MGA_SOFTRAPEN | \ MGA_ENDPRDMASTS) -#define MGA_DMA_SOFTRAP_SIZE 32 * DMA_BLOCK_SIZE +#define MGA_DMA_DEBUG 0 @@ -599,4 +618,12 @@ do { \ MGA_BLTMOD_BFCOL | \ MGA_CLIPDIS) +/* Simple idle test. + */ +static inline int mga_is_idle( drm_mga_private_t *dev_priv ) +{ + u32 status = MGA_READ( MGA_STATUS ) & MGA_ENGINE_IDLE_MASK; + return ( status == MGA_ENDPRDMASTS ); +} + #endif diff --git a/linux/mga_state.c b/linux/mga_state.c index 4c9c9c05..99778c58 100644 --- a/linux/mga_state.c +++ b/linux/mga_state.c @@ -408,7 +408,7 @@ static int mga_verify_context( drm_mga_private_t *dev_priv ) if ( ctx->dstorg != dev_priv->front_offset && ctx->dstorg != dev_priv->back_offset ) { - DRM_DEBUG( "*** bad DSTORG: %x (front %x, back %x)\n\n", + DRM_ERROR( "*** bad DSTORG: %x (front %x, back %x)\n\n", ctx->dstorg, dev_priv->front_offset, dev_priv->back_offset ); ctx->dstorg = 0; @@ -429,7 +429,7 @@ static int mga_verify_tex( drm_mga_private_t *dev_priv, int unit ) org = tex->texorg & (MGA_TEXORGMAP_MASK | MGA_TEXORGACC_MASK); if ( org == (MGA_TEXORGMAP_SYSMEM | MGA_TEXORGACC_PCI) ) { - DRM_DEBUG( "*** bad TEXORG: 0x%x, unit %d\n", + DRM_ERROR( "*** bad TEXORG: 0x%x, unit %d\n", tex->texorg, unit ); tex->texorg = 0; return -EINVAL; @@ -473,10 +473,13 @@ static int mga_verify_iload( drm_mga_private_t *dev_priv, if ( dstorg < dev_priv->texture_offset || dstorg + length > (dev_priv->texture_offset + dev_priv->texture_size) ) { + DRM_ERROR( "*** bad iload DSTORG: 0x%x\n", dstorg ); return -EINVAL; } if ( length & MGA_ILOAD_MASK ) { + DRM_ERROR( "*** bad iload length: 0x%x\n", + length & MGA_ILOAD_MASK ); return -EINVAL; } @@ -488,6 +491,8 @@ static int mga_verify_blit( drm_mga_private_t *dev_priv, { if ( (srcorg & 0x3) == (MGA_SRCACC_PCI | MGA_SRCMAP_SYSMEM) || (dstorg & 0x3) == (MGA_SRCACC_PCI | MGA_SRCMAP_SYSMEM) ) { + DRM_ERROR( "*** bad blit: src=0x%x dst=0x%x\n", + srcorg, dstorg ); return -EINVAL; } return 0; @@ -510,6 +515,15 @@ static void mga_dma_dispatch_clear( drm_device_t *dev, DMA_LOCALS; DRM_DEBUG( __FUNCTION__ ":\n" ); + BEGIN_DMA( 1 ); + + DMA_BLOCK( MGA_DMAPAD, 0x00000000, + MGA_DMAPAD, 0x00000000, + MGA_DWGSYNC, 0x00007100, + MGA_DWGSYNC, 0x00007000 ); + + ADVANCE_DMA(); + for ( i = 0 ; i < nbox ; i++ ) { drm_clip_rect_t *box = &pbox[i]; u32 height = box->y2 - box->y1; @@ -752,7 +766,12 @@ static void mga_dma_dispatch_iload( drm_device_t *dev, drm_buf_t *buf, y2 = length / 64; - BEGIN_DMA( 4 ); + BEGIN_DMA( 5 ); + + DMA_BLOCK( MGA_DMAPAD, 0x00000000, + MGA_DMAPAD, 0x00000000, + MGA_DWGSYNC, 0x00007100, + MGA_DWGSYNC, 0x00007000 ); DMA_BLOCK( MGA_DSTORG, dstorg, MGA_MACCESS, 0x00000000, @@ -1004,20 +1023,21 @@ int mga_dma_iload( struct inode *inode, struct file *filp, if ( copy_from_user( &iload, (drm_mga_iload_t *)arg, sizeof(iload) ) ) return -EFAULT; - if ( mga_do_wait_for_idle( dev_priv ) < 0 ) +#if 0 + if ( mga_do_wait_for_idle( dev_priv ) < 0 ) { + if ( MGA_DMA_DEBUG ) + DRM_INFO( __FUNCTION__": -EBUSY\n" ); return -EBUSY; + } +#endif buf = dma->buflist[iload.idx]; buf_priv = buf->dev_private; -#if 0 - DRM_INFO( " verifying iload...\n" ); if ( mga_verify_iload( dev_priv, iload.dstorg, iload.length ) ) { mga_freelist_put( dev, buf ); return -EINVAL; } - DRM_INFO( " verifying iload... done.\n" ); -#endif WRAP_TEST_WITH_RETURN( dev_priv ); diff --git a/linux/r128_cce.c b/linux/r128_cce.c index c0e20c21..1ced05fe 100644 --- a/linux/r128_cce.c +++ b/linux/r128_cce.c @@ -178,6 +178,8 @@ static void r128_cce_load_microcode( drm_r128_private_t *dev_priv ) { int i; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + r128_do_wait_for_idle( dev_priv ); R128_WRITE( R128_PM4_MICROCODE_ADDR, 0 ); @@ -208,7 +210,7 @@ int r128_do_cce_idle( drm_r128_private_t *dev_priv ) int i; for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) { - if ( *dev_priv->ring.head == dev_priv->ring.tail ) { + if ( GET_RING_HEAD( &dev_priv->ring ) == dev_priv->ring.tail ) { int pm4stat = R128_READ( R128_PM4_STAT ); if ( ( (pm4stat & R128_PM4_FIFOCNT_MASK) >= dev_priv->cce_fifo_size ) && @@ -249,7 +251,7 @@ static void r128_do_cce_reset( drm_r128_private_t *dev_priv ) { R128_WRITE( R128_PM4_BUFFER_DL_WPTR, 0 ); R128_WRITE( R128_PM4_BUFFER_DL_RPTR, 0 ); - *dev_priv->ring.head = 0; + SET_RING_HEAD( &dev_priv->ring, 0 ); dev_priv->ring.tail = 0; } @@ -312,19 +314,43 @@ static void r128_cce_init_ring_buffer( drm_device_t *dev ) u32 ring_start; u32 tmp; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + /* The manual (p. 2) says this address is in "VM space". This * means it's an offset from the start of AGP space. */ - ring_start = dev_priv->cce_ring->offset - dev->agp->base; +#if __REALLY_HAVE_AGP + if ( !dev_priv->is_pci ) + ring_start = dev_priv->cce_ring->offset - dev->agp->base; + else +#endif + ring_start = dev_priv->cce_ring->offset - dev->sg->handle; + R128_WRITE( R128_PM4_BUFFER_OFFSET, ring_start | R128_AGP_OFFSET ); R128_WRITE( R128_PM4_BUFFER_DL_WPTR, 0 ); R128_WRITE( R128_PM4_BUFFER_DL_RPTR, 0 ); /* DL_RPTR_ADDR is a physical address in AGP space. */ - *dev_priv->ring.head = 0; - R128_WRITE( R128_PM4_BUFFER_DL_RPTR_ADDR, - dev_priv->ring_rptr->offset ); + SET_RING_HEAD( &dev_priv->ring, 0 ); + + if ( !dev_priv->is_pci ) { + R128_WRITE( R128_PM4_BUFFER_DL_RPTR_ADDR, + dev_priv->ring_rptr->offset ); + } else { + drm_sg_mem_t *entry = dev->sg; + unsigned long tmp_ofs, page_ofs; + + tmp_ofs = dev_priv->ring_rptr->offset - dev->sg->handle; + page_ofs = tmp_ofs >> PAGE_SHIFT; + + R128_WRITE( R128_PM4_BUFFER_DL_RPTR_ADDR, + virt_to_bus(entry->pagelist[page_ofs]->virtual)); + + DRM_DEBUG( "ring rptr: offset=0x%08lx handle=0x%08lx\n", + virt_to_bus(entry->pagelist[page_ofs]->virtual), + entry->handle + tmp_ofs ); + } /* Set watermark control */ R128_WRITE( R128_PM4_BUFFER_WM_CNTL, @@ -346,6 +372,8 @@ static int r128_do_init_cce( drm_device_t *dev, drm_r128_init_t *init ) drm_r128_private_t *dev_priv; struct list_head *list; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + dev_priv = DRM(alloc)( sizeof(drm_r128_private_t), DRM_MEM_DRIVER ); if ( dev_priv == NULL ) return -ENOMEM; @@ -355,11 +383,9 @@ static int r128_do_init_cce( drm_device_t *dev, drm_r128_init_t *init ) dev_priv->is_pci = init->is_pci; - /* GH: We don't support PCI cards until PCI GART is implemented. - * Fail here so we can remove all checks for PCI cards around - * the CCE ring code. - */ - if ( dev_priv->is_pci ) { + if ( dev_priv->is_pci && !dev->sg ) { + DRM_DEBUG( "PCI GART memory not allocated!\n" ); + DRM_ERROR( "PCI GART memory not allocated!\n" ); DRM(free)( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); dev->dev_private = NULL; return -EINVAL; @@ -368,6 +394,7 @@ static int r128_do_init_cce( drm_device_t *dev, drm_r128_init_t *init ) dev_priv->usec_timeout = init->usec_timeout; if ( dev_priv->usec_timeout < 1 || dev_priv->usec_timeout > R128_MAX_USEC_TIMEOUT ) { + DRM_DEBUG( "TIMEOUT problem!\n" ); DRM(free)( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); dev->dev_private = NULL; return -EINVAL; @@ -387,6 +414,7 @@ static int r128_do_init_cce( drm_device_t *dev, drm_r128_init_t *init ) ( init->cce_mode != R128_PM4_128BM_64INDBM ) && ( init->cce_mode != R128_PM4_64BM_128INDBM ) && ( init->cce_mode != R128_PM4_64BM_64VCBM_64INDBM ) ) { + DRM_DEBUG( "Bad cce_mode!\n" ); DRM(free)( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); dev->dev_private = NULL; return -EINVAL; @@ -476,9 +504,24 @@ static int r128_do_init_cce( drm_device_t *dev, drm_r128_init_t *init ) (drm_r128_sarea_t *)((u8 *)dev_priv->sarea->handle + init->sarea_priv_offset); - DRM_IOREMAP( dev_priv->cce_ring ); - DRM_IOREMAP( dev_priv->ring_rptr ); - DRM_IOREMAP( dev_priv->buffers ); + if ( !dev_priv->is_pci ) { + DRM_IOREMAP( dev_priv->cce_ring ); + DRM_IOREMAP( dev_priv->ring_rptr ); + DRM_IOREMAP( dev_priv->buffers ); + } else { + dev_priv->cce_ring->handle = + (void *)dev_priv->cce_ring->offset; + dev_priv->ring_rptr->handle = + (void *)dev_priv->ring_rptr->offset; + dev_priv->buffers->handle = (void *)dev_priv->buffers->offset; + } + +#if __REALLY_HAVE_AGP + if ( !dev_priv->is_pci ) + dev_priv->cce_buffers_offset = dev->agp->base; + else +#endif + dev_priv->cce_buffers_offset = dev->sg->handle; dev_priv->ring.head = ((__volatile__ u32 *) dev_priv->ring_rptr->handle); @@ -501,6 +544,20 @@ static int r128_do_init_cce( drm_device_t *dev, drm_r128_init_t *init ) R128_WRITE( R128_LAST_DISPATCH_REG, dev_priv->sarea_priv->last_dispatch ); + if ( dev_priv->is_pci ) { + dev_priv->phys_pci_gart = DRM(ati_pcigart_init)( dev ); + if ( !dev_priv->phys_pci_gart ) { + DRM_DEBUG( "failed to init PCI GART!\n" ); + DRM_ERROR( "failed to init PCI GART!\n" ); + DRM(free)( dev_priv, sizeof(*dev_priv), + DRM_MEM_DRIVER ); + dev->dev_private = NULL; + return -EINVAL; + } + R128_WRITE( R128_PCI_GART_PAGE, + virt_to_bus( (void *)dev_priv->phys_pci_gart ) ); + } + r128_cce_init_ring_buffer( dev ); r128_cce_load_microcode( dev_priv ); r128_do_engine_reset( dev ); @@ -513,9 +570,11 @@ int r128_do_cleanup_cce( drm_device_t *dev ) if ( dev->dev_private ) { drm_r128_private_t *dev_priv = dev->dev_private; - DRM_IOREMAPFREE( dev_priv->cce_ring ); - DRM_IOREMAPFREE( dev_priv->ring_rptr ); - DRM_IOREMAPFREE( dev_priv->buffers ); + if ( !dev_priv->is_pci ) { + DRM_IOREMAPFREE( dev_priv->cce_ring ); + DRM_IOREMAPFREE( dev_priv->ring_rptr ); + DRM_IOREMAPFREE( dev_priv->buffers ); + } DRM(free)( dev->dev_private, sizeof(drm_r128_private_t), DRM_MEM_DRIVER ); @@ -532,6 +591,8 @@ int r128_cce_init( struct inode *inode, struct file *filp, drm_device_t *dev = priv->dev; drm_r128_init_t init; + DRM_DEBUG( "%s\n", __FUNCTION__ ); + if ( copy_from_user( &init, (drm_r128_init_t *)arg, sizeof(init) ) ) return -EFAULT; diff --git a/linux/r128_drm.h b/linux/r128_drm.h index 86aba175..fc1261ea 100644 --- a/linux/r128_drm.h +++ b/linux/r128_drm.h @@ -175,7 +175,7 @@ typedef struct drm_r128_init { R128_INIT_CCE = 0x01, R128_CLEANUP_CCE = 0x02 } func; - int sarea_priv_offset; + unsigned long sarea_priv_offset; int is_pci; int cce_mode; int cce_secure; @@ -189,12 +189,12 @@ typedef struct drm_r128_init { unsigned int depth_offset, depth_pitch; unsigned int span_offset; - unsigned int fb_offset; - unsigned int mmio_offset; - unsigned int ring_offset; - unsigned int ring_rptr_offset; - unsigned int buffers_offset; - unsigned int agp_textures_offset; + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long ring_offset; + unsigned long ring_rptr_offset; + unsigned long buffers_offset; + unsigned long agp_textures_offset; } drm_r128_init_t; typedef struct drm_r128_cce_stop { diff --git a/linux/r128_drv.c b/linux/r128_drv.c index daae0f87..584cb29c 100644 --- a/linux/r128_drv.c +++ b/linux/r128_drv.c @@ -33,16 +33,17 @@ #include "r128.h" #include "drmP.h" #include "r128_drv.h" +#include "ati_pcigart.h" #define DRIVER_AUTHOR "Gareth Hughes, VA Linux Systems Inc." #define DRIVER_NAME "r128" #define DRIVER_DESC "ATI Rage 128" -#define DRIVER_DATE "20010308" +#define DRIVER_DATE "20010405" #define DRIVER_MAJOR 2 #define DRIVER_MINOR 1 -#define DRIVER_PATCHLEVEL 5 +#define DRIVER_PATCHLEVEL 6 #define DRIVER_IOCTLS \ [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { r128_cce_buffers, 1, 0 }, \ @@ -88,3 +89,4 @@ #include "drm_proc.h" #include "drm_vm.h" #include "drm_stub.h" +#include "drm_scatter.h" diff --git a/linux/r128_drv.h b/linux/r128_drv.h index 6216b9a1..0ef94e45 100644 --- a/linux/r128_drv.h +++ b/linux/r128_drv.h @@ -28,11 +28,15 @@ * Rickard E. (Rik) Faith <faith@valinux.com> * Kevin E. Martin <martin@valinux.com> * Gareth Hughes <gareth@valinux.com> + * Michel Dänzer <daenzerm@student.ethz.ch> */ #ifndef __R128_DRV_H__ #define __R128_DRV_H__ +#define GET_RING_HEAD( ring ) le32_to_cpu( *(ring)->head ) +#define SET_RING_HEAD( ring, val ) *(ring)->head = cpu_to_le32( val ) + typedef struct drm_r128_freelist { unsigned int age; drm_buf_t *buf; @@ -67,6 +71,8 @@ typedef struct drm_r128_private { int usec_timeout; int is_pci; + unsigned long phys_pci_gart; + unsigned long cce_buffers_offset; atomic_t idle_count; @@ -134,7 +140,7 @@ extern int r128_wait_ring( drm_r128_private_t *dev_priv, int n ); static inline void r128_update_ring_snapshot( drm_r128_ring_buffer_t *ring ) { - ring->space = (*(volatile int *)ring->head - ring->tail) * sizeof(u32); + ring->space = (GET_RING_HEAD( ring ) - ring->tail) * sizeof(u32); if ( ring->space <= 0 ) ring->space += ring->size; } @@ -248,6 +254,7 @@ extern int r128_cce_indirect( struct inode *inode, struct file *filp, # define R128_PC_FLUSH_ALL 0x00ff # define R128_PC_BUSY (1 << 31) +#define R128_PCI_GART_PAGE 0x017c #define R128_PRIM_TEX_CNTL_C 0x1cb0 #define R128_SCALE_3D_CNTL 0x1a00 @@ -365,7 +372,7 @@ extern int r128_cce_indirect( struct inode *inode, struct file *filp, #define R128_LAST_FRAME_REG R128_GUI_SCRATCH_REG0 #define R128_LAST_DISPATCH_REG R128_GUI_SCRATCH_REG1 -#define R128_MAX_VB_AGE 0xffffffff +#define R128_MAX_VB_AGE 0x7fffffff #define R128_MAX_VB_VERTS (0xffff) #define R128_RING_HIGH_MARK 128 @@ -373,22 +380,53 @@ extern int r128_cce_indirect( struct inode *inode, struct file *filp, #define R128_PERFORMANCE_BOXES 0 -#define R128_BASE(reg) ((u32)(dev_priv->mmio->handle)) +#define R128_BASE(reg) ((unsigned long)(dev_priv->mmio->handle)) #define R128_ADDR(reg) (R128_BASE( reg ) + reg) #define R128_DEREF(reg) *(volatile u32 *)R128_ADDR( reg ) -#define R128_READ(reg) R128_DEREF( reg ) -#define R128_WRITE(reg,val) do { R128_DEREF( reg ) = val; } while (0) +#ifdef __alpha__ +#define R128_READ(reg) (_R128_READ((u32 *)R128_ADDR(reg))) +static inline u32 _R128_READ(u32 *addr) +{ + mb(); + return *(volatile u32 *)addr; +} +#define R128_WRITE(reg,val) \ +do { \ + wmb(); \ + R128_DEREF(reg) = val; \ +} while (0) +#else +#define R128_READ(reg) le32_to_cpu( R128_DEREF( reg ) ) +#define R128_WRITE(reg,val) \ +do { \ + R128_DEREF( reg ) = cpu_to_le32( val ); \ +} while (0) +#endif #define R128_DEREF8(reg) *(volatile u8 *)R128_ADDR( reg ) +#ifdef __alpha__ +#define R128_READ8(reg) _R128_READ8((u8 *)R128_ADDR(reg)) +static inline u8 _R128_READ8(u8 *addr) +{ + mb(); + return *(volatile u8 *)addr; +} +#define R128_WRITE8(reg,val) \ +do { \ + wmb(); \ + R128_DEREF8(reg) = val; \ +} while (0) +#else #define R128_READ8(reg) R128_DEREF8( reg ) #define R128_WRITE8(reg,val) do { R128_DEREF8( reg ) = val; } while (0) +#endif - -#define R128_WRITE_PLL(addr,val) \ -do { \ - R128_WRITE8(R128_CLOCK_CNTL_INDEX, ((addr) & 0x1f) | R128_PLL_WR_EN); \ - R128_WRITE(R128_CLOCK_CNTL_DATA, (val)); \ +#define R128_WRITE_PLL(addr,val) \ +do { \ + R128_WRITE8(R128_CLOCK_CNTL_INDEX, \ + ((addr) & 0x1f) | R128_PLL_WR_EN); \ + R128_WRITE(R128_CLOCK_CNTL_DATA, (val)); \ } while (0) extern int R128_READ_PLL(drm_device_t *dev, int addr); @@ -485,7 +523,7 @@ do { \ #define ADVANCE_RING() do { \ if ( R128_VERBOSE ) { \ - DRM_INFO( "ADVANCE_RING() tail=0x%06x wr=0x%06x\n", \ + DRM_INFO( "ADVANCE_RING() wr=0x%06x tail=0x%06x\n", \ write, dev_priv->ring.tail ); \ } \ if ( R128_BROKEN_CCE && write < 32 ) { \ @@ -503,7 +541,7 @@ do { \ DRM_INFO( " OUT_RING( 0x%08x ) at 0x%x\n", \ (unsigned int)(x), write ); \ } \ - ring[write++] = (x); \ + ring[write++] = cpu_to_le32( x ); \ write &= tail_mask; \ } while (0) diff --git a/linux/r128_state.c b/linux/r128_state.c index 9fc6b485..7eafd72d 100644 --- a/linux/r128_state.c +++ b/linux/r128_state.c @@ -660,7 +660,7 @@ static void r128_cce_dispatch_indirect( drm_device_t *dev, u32 *data = (u32 *) ((char *)dev_priv->buffers->handle + buf->offset + start); - data[dwords++] = R128_CCE_PACKET2; + data[dwords++] = cpu_to_le32( R128_CCE_PACKET2 ); } buf_priv->dispatched = 1; @@ -704,7 +704,7 @@ static void r128_cce_dispatch_indices( drm_device_t *dev, drm_r128_buf_priv_t *buf_priv = buf->dev_private; drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv; int format = sarea_priv->vc_format; - int offset = dev_priv->buffers->offset - dev->agp->base; + int offset = dev_priv->buffers->offset - dev_priv->cce_buffers_offset; int prim = buf_priv->prim; u32 *data; int dwords; @@ -727,16 +727,21 @@ static void r128_cce_dispatch_indices( drm_device_t *dev, data = (u32 *)((char *)dev_priv->buffers->handle + buf->offset + start); - data[0] = CCE_PACKET3( R128_3D_RNDR_GEN_INDX_PRIM, dwords-2 ); + data[0] = cpu_to_le32( CCE_PACKET3( R128_3D_RNDR_GEN_INDX_PRIM, + dwords-2 ) ); - data[1] = offset; - data[2] = R128_MAX_VB_VERTS; - data[3] = format; - data[4] = (prim | R128_CCE_VC_CNTL_PRIM_WALK_IND | - (count << 16)); + data[1] = cpu_to_le32( offset ); + data[2] = cpu_to_le32( R128_MAX_VB_VERTS ); + data[3] = cpu_to_le32( format ); + data[4] = cpu_to_le32( (prim | R128_CCE_VC_CNTL_PRIM_WALK_IND | + (count << 16)) ); if ( count & 0x1 ) { +#ifdef __LITTLE_ENDIAN data[dwords-1] &= 0x0000ffff; +#else + data[dwords-1] &= 0xffff0000; +#endif } do { @@ -842,23 +847,23 @@ static int r128_cce_dispatch_blit( drm_device_t *dev, data = (u32 *)((char *)dev_priv->buffers->handle + buf->offset); - data[0] = CCE_PACKET3( R128_CNTL_HOSTDATA_BLT, dwords + 6 ); - data[1] = (R128_GMC_DST_PITCH_OFFSET_CNTL | - R128_GMC_BRUSH_NONE | - (blit->format << 8) | - R128_GMC_SRC_DATATYPE_COLOR | - R128_ROP3_S | - R128_DP_SRC_SOURCE_HOST_DATA | - R128_GMC_CLR_CMP_CNTL_DIS | - R128_GMC_AUX_CLIP_DIS | - R128_GMC_WR_MSK_DIS); - - data[2] = (blit->pitch << 21) | (blit->offset >> 5); - data[3] = 0xffffffff; - data[4] = 0xffffffff; - data[5] = (blit->y << 16) | blit->x; - data[6] = (blit->height << 16) | blit->width; - data[7] = dwords; + data[0] = cpu_to_le32( CCE_PACKET3( R128_CNTL_HOSTDATA_BLT, dwords + 6 ) ); + data[1] = cpu_to_le32( (R128_GMC_DST_PITCH_OFFSET_CNTL | + R128_GMC_BRUSH_NONE | + (blit->format << 8) | + R128_GMC_SRC_DATATYPE_COLOR | + R128_ROP3_S | + R128_DP_SRC_SOURCE_HOST_DATA | + R128_GMC_CLR_CMP_CNTL_DIS | + R128_GMC_AUX_CLIP_DIS | + R128_GMC_WR_MSK_DIS) ); + + data[2] = cpu_to_le32( (blit->pitch << 21) | (blit->offset >> 5) ); + data[3] = cpu_to_le32( 0xffffffff ); + data[4] = cpu_to_le32( 0xffffffff ); + data[5] = cpu_to_le32( (blit->y << 16) | blit->x ); + data[6] = cpu_to_le32( (blit->height << 16) | blit->width ); + data[7] = cpu_to_le32( dwords ); buf->used = (dwords + 8) * sizeof(u32); @@ -1299,8 +1304,8 @@ int r128_cce_vertex( struct inode *inode, struct file *filp, LOCK_TEST_WITH_RETURN( dev ); - if ( !dev_priv || dev_priv->is_pci ) { - DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ ); + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); return -EINVAL; } @@ -1362,8 +1367,8 @@ int r128_cce_indices( struct inode *inode, struct file *filp, LOCK_TEST_WITH_RETURN( dev ); - if ( !dev_priv || dev_priv->is_pci ) { - DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ ); + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); return -EINVAL; } diff --git a/linux/radeon_cp.c b/linux/radeon_cp.c index ed8b1bbc..c255a3e0 100644 --- a/linux/radeon_cp.c +++ b/linux/radeon_cp.c @@ -318,6 +318,16 @@ static void radeon_status( drm_radeon_private_t *dev_priv ) (unsigned int)RADEON_READ( RADEON_CP_RB_RPTR ) ); printk( "CP_RB_WTPR = 0x%08x\n", (unsigned int)RADEON_READ( RADEON_CP_RB_WPTR ) ); + printk( "AIC_CNTL = 0x%08x\n", + (unsigned int)RADEON_READ( RADEON_AIC_CNTL ) ); + printk( "AIC_STAT = 0x%08x\n", + (unsigned int)RADEON_READ( RADEON_AIC_STAT ) ); + printk( "AIC_PT_BASE = 0x%08x\n", + (unsigned int)RADEON_READ( RADEON_AIC_PT_BASE ) ); + printk( "TLB_ADDR = 0x%08x\n", + (unsigned int)RADEON_READ( RADEON_AIC_TLB_ADDR ) ); + printk( "TLB_DATA = 0x%08x\n", + (unsigned int)RADEON_READ( RADEON_AIC_TLB_DATA ) ); } #endif @@ -401,6 +411,7 @@ static int radeon_do_wait_for_idle( drm_radeon_private_t *dev_priv ) static void radeon_cp_load_microcode( drm_radeon_private_t *dev_priv ) { int i; + DRM_DEBUG( "%s\n", __FUNCTION__ ); radeon_do_wait_for_idle( dev_priv ); @@ -419,6 +430,7 @@ static void radeon_cp_load_microcode( drm_radeon_private_t *dev_priv ) */ static void radeon_do_cp_flush( drm_radeon_private_t *dev_priv ) { + DRM_DEBUG( "%s\n", __FUNCTION__ ); #if 0 u32 tmp; @@ -432,6 +444,7 @@ static void radeon_do_cp_flush( drm_radeon_private_t *dev_priv ) int radeon_do_cp_idle( drm_radeon_private_t *dev_priv ) { RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); BEGIN_RING( 6 ); @@ -449,6 +462,7 @@ int radeon_do_cp_idle( drm_radeon_private_t *dev_priv ) static void radeon_do_cp_start( drm_radeon_private_t *dev_priv ) { RING_LOCALS; + DRM_DEBUG( "%s\n", __FUNCTION__ ); radeon_do_wait_for_idle( dev_priv ); @@ -472,6 +486,7 @@ static void radeon_do_cp_start( drm_radeon_private_t *dev_priv ) static void radeon_do_cp_reset( drm_radeon_private_t *dev_priv ) { u32 cur_read_ptr; + DRM_DEBUG( "%s\n", __FUNCTION__ ); cur_read_ptr = RADEON_READ( RADEON_CP_RB_RPTR ); RADEON_WRITE( RADEON_CP_RB_WPTR, cur_read_ptr ); @@ -485,6 +500,8 @@ static void radeon_do_cp_reset( drm_radeon_private_t *dev_priv ) */ static void radeon_do_cp_stop( drm_radeon_private_t *dev_priv ) { + DRM_DEBUG( "%s\n", __FUNCTION__ ); + RADEON_WRITE( RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIDIS_INDDIS ); dev_priv->cp_running = 0; @@ -551,18 +568,29 @@ static void radeon_cp_init_ring_buffer( drm_device_t *dev ) drm_radeon_private_t *dev_priv = dev->dev_private; u32 ring_start, cur_read_ptr; u32 tmp; + DRM_DEBUG( "%s\n", __FUNCTION__ ); /* Initialize the memory controller */ RADEON_WRITE( RADEON_MC_FB_LOCATION, (dev_priv->agp_vm_start - 1) & 0xffff0000 ); - RADEON_WRITE( RADEON_MC_AGP_LOCATION, - (((dev_priv->agp_vm_start - 1 + - dev_priv->agp_size) & 0xffff0000) | - (dev_priv->agp_vm_start >> 16)) ); - ring_start = (dev_priv->cp_ring->offset - - dev->agp->base - + dev_priv->agp_vm_start); + if ( !dev_priv->is_pci ) { + RADEON_WRITE( RADEON_MC_AGP_LOCATION, + (((dev_priv->agp_vm_start - 1 + + dev_priv->agp_size) & 0xffff0000) | + (dev_priv->agp_vm_start >> 16)) ); + } + +#if __REALLY_HAVE_AGP + if ( !dev_priv->is_pci ) + ring_start = (dev_priv->cp_ring->offset + - dev->agp->base + + dev_priv->agp_vm_start); + else +#endif + ring_start = (dev_priv->cp_ring->offset + - dev->sg->handle + + dev_priv->agp_vm_start); RADEON_WRITE( RADEON_CP_RB_BASE, ring_start ); @@ -575,17 +603,29 @@ static void radeon_cp_init_ring_buffer( drm_device_t *dev ) *dev_priv->ring.head = cur_read_ptr; dev_priv->ring.tail = cur_read_ptr; - RADEON_WRITE( RADEON_CP_RB_RPTR_ADDR, dev_priv->ring_rptr->offset ); + if ( !dev_priv->is_pci ) { + RADEON_WRITE( RADEON_CP_RB_RPTR_ADDR, + dev_priv->ring_rptr->offset ); + } else { + drm_sg_mem_t *entry = dev->sg; + unsigned long tmp_ofs, page_ofs; + + tmp_ofs = dev_priv->ring_rptr->offset - dev->sg->handle; + page_ofs = tmp_ofs >> PAGE_SHIFT; + + RADEON_WRITE( RADEON_CP_RB_RPTR_ADDR, + virt_to_bus(entry->pagelist[page_ofs]->virtual)); + + DRM_DEBUG( "ring rptr: offset=0x%08lx handle=0x%08lx\n", + virt_to_bus(entry->pagelist[page_ofs]->virtual), + entry->handle + tmp_ofs ); + } /* Set ring buffer size */ RADEON_WRITE( RADEON_CP_RB_CNTL, dev_priv->ring.size_l2qw ); radeon_do_wait_for_idle( dev_priv ); - /* Turn off PCI GART */ - tmp = RADEON_READ( RADEON_AIC_CNTL ) & ~RADEON_PCIGART_TRANSLATE_EN; - RADEON_WRITE( RADEON_AIC_CNTL, tmp ); - /* Turn on bus mastering */ tmp = RADEON_READ( RADEON_BUS_CNTL ) & ~RADEON_BUS_MASTER_DIS; RADEON_WRITE( RADEON_BUS_CNTL, tmp ); @@ -602,6 +642,8 @@ static int radeon_do_init_cp( drm_device_t *dev, drm_radeon_init_t *init ) { drm_radeon_private_t *dev_priv; struct list_head *list; + u32 tmp; + DRM_DEBUG( "%s\n", __FUNCTION__ ); dev_priv = DRM(alloc)( sizeof(drm_radeon_private_t), DRM_MEM_DRIVER ); if ( dev_priv == NULL ) @@ -612,11 +654,20 @@ static int radeon_do_init_cp( drm_device_t *dev, drm_radeon_init_t *init ) dev_priv->is_pci = init->is_pci; - /* We don't support PCI cards until PCI GART is implemented. - * Fail here so we can remove all checks for PCI cards around - * the CP ring code. +#if 1 + /* PCI support is not 100% working, so we disable it here. */ if ( dev_priv->is_pci ) { + DRM_ERROR( "PCI GART not yet supported for Radeon!\n" ); + DRM(free)( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); + dev->dev_private = NULL; + return -EINVAL; + } +#endif + + if ( dev_priv->is_pci && !dev->sg ) { + DRM_DEBUG( "PCI GART memory not allocated!\n" ); + DRM_ERROR( "PCI GART memory not allocated!\n" ); DRM(free)( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); dev->dev_private = NULL; return -EINVAL; @@ -625,6 +676,7 @@ static int radeon_do_init_cp( drm_device_t *dev, drm_radeon_init_t *init ) dev_priv->usec_timeout = init->usec_timeout; if ( dev_priv->usec_timeout < 1 || dev_priv->usec_timeout > RADEON_MAX_USEC_TIMEOUT ) { + DRM_DEBUG( "TIMEOUT problem!\n" ); DRM(free)( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); dev->dev_private = NULL; return -EINVAL; @@ -642,6 +694,7 @@ static int radeon_do_init_cp( drm_device_t *dev, drm_radeon_init_t *init ) */ if ( ( init->cp_mode != RADEON_CSQ_PRIBM_INDDIS ) && ( init->cp_mode != RADEON_CSQ_PRIBM_INDBM ) ) { + DRM_DEBUG( "BAD cp_mode (%x)!\n", init->cp_mode ); DRM(free)( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); dev->dev_private = NULL; return -EINVAL; @@ -735,15 +788,45 @@ static int radeon_do_init_cp( drm_device_t *dev, drm_radeon_init_t *init ) (drm_radeon_sarea_t *)((u8 *)dev_priv->sarea->handle + init->sarea_priv_offset); - DRM_IOREMAP( dev_priv->cp_ring ); - DRM_IOREMAP( dev_priv->ring_rptr ); - DRM_IOREMAP( dev_priv->buffers ); + if ( !dev_priv->is_pci ) { + DRM_IOREMAP( dev_priv->cp_ring ); + DRM_IOREMAP( dev_priv->ring_rptr ); + DRM_IOREMAP( dev_priv->buffers ); + } else { + dev_priv->cp_ring->handle = + (void *)dev_priv->cp_ring->offset; + dev_priv->ring_rptr->handle = + (void *)dev_priv->ring_rptr->offset; + dev_priv->buffers->handle = (void *)dev_priv->buffers->offset; + + DRM_DEBUG( "dev_priv->cp_ring->handle %p\n", + dev_priv->cp_ring->handle ); + DRM_DEBUG( "dev_priv->ring_rptr->handle %p\n", + dev_priv->ring_rptr->handle ); + DRM_DEBUG( "dev_priv->buffers->handle %p\n", + dev_priv->buffers->handle ); + } + dev_priv->agp_size = init->agp_size; dev_priv->agp_vm_start = RADEON_READ( RADEON_CONFIG_APER_SIZE ); - dev_priv->agp_buffers_offset = (dev_priv->buffers->offset - - dev->agp->base - + dev_priv->agp_vm_start); +#if __REALLY_HAVE_AGP + if ( !dev_priv->is_pci ) + dev_priv->agp_buffers_offset = (dev_priv->buffers->offset + - dev->agp->base + + dev_priv->agp_vm_start); + else +#endif + dev_priv->agp_buffers_offset = (dev_priv->buffers->offset + - dev->sg->handle + + dev_priv->agp_vm_start); + + DRM_DEBUG( "dev_priv->agp_size %d\n", + dev_priv->agp_size ); + DRM_DEBUG( "dev_priv->agp_vm_start 0x%x\n", + dev_priv->agp_vm_start ); + DRM_DEBUG( "dev_priv->agp_buffers_offset 0x%lx\n", + dev_priv->agp_buffers_offset ); dev_priv->ring.head = ((__volatile__ u32 *) dev_priv->ring_rptr->handle); @@ -787,6 +870,44 @@ static int radeon_do_init_cp( drm_device_t *dev, drm_radeon_init_t *init ) RADEON_WRITE( RADEON_LAST_CLEAR_REG, dev_priv->sarea_priv->last_clear ); + if ( dev_priv->is_pci ) { + dev_priv->phys_pci_gart = DRM(ati_pcigart_init)( dev ); + if ( !dev_priv->phys_pci_gart ) { + DRM_DEBUG( "failed to init PCI GART!\n" ); + DRM_ERROR( "failed to init PCI GART!\n" ); + DRM(free)( dev_priv, sizeof(*dev_priv), + DRM_MEM_DRIVER ); + dev->dev_private = NULL; + return -EINVAL; + } + /* Turn on PCI GART + */ + tmp = RADEON_READ( RADEON_AIC_CNTL ) + | RADEON_PCIGART_TRANSLATE_EN; + RADEON_WRITE( RADEON_AIC_CNTL, tmp ); + + /* set PCI GART page-table base address + */ + RADEON_WRITE( RADEON_AIC_PT_BASE, + virt_to_bus( (void *)dev_priv->phys_pci_gart ) ); + + /* set address range for PCI address translate + */ + RADEON_WRITE( RADEON_AIC_LO_ADDR, dev_priv->agp_vm_start ); + RADEON_WRITE( RADEON_AIC_HI_ADDR, dev_priv->agp_vm_start + + dev_priv->agp_size - 1); + + /* Turn off AGP aperture -- is this required for PCIGART? + */ + RADEON_WRITE( RADEON_MC_AGP_LOCATION, 0 ); + } else { + /* Turn off PCI GART + */ + tmp = RADEON_READ( RADEON_AIC_CNTL ) + & ~RADEON_PCIGART_TRANSLATE_EN; + RADEON_WRITE( RADEON_AIC_CNTL, tmp ); + } + radeon_cp_load_microcode( dev_priv ); radeon_cp_init_ring_buffer( dev ); radeon_do_engine_reset( dev ); @@ -800,12 +921,16 @@ static int radeon_do_init_cp( drm_device_t *dev, drm_radeon_init_t *init ) int radeon_do_cleanup_cp( drm_device_t *dev ) { + DRM_DEBUG( "%s\n", __FUNCTION__ ); + if ( dev->dev_private ) { drm_radeon_private_t *dev_priv = dev->dev_private; - DRM_IOREMAPFREE( dev_priv->cp_ring ); - DRM_IOREMAPFREE( dev_priv->ring_rptr ); - DRM_IOREMAPFREE( dev_priv->buffers ); + if ( !dev_priv->is_pci ) { + DRM_IOREMAPFREE( dev_priv->cp_ring ); + DRM_IOREMAPFREE( dev_priv->ring_rptr ); + DRM_IOREMAPFREE( dev_priv->buffers ); + } DRM(free)( dev->dev_private, sizeof(drm_radeon_private_t), DRM_MEM_DRIVER ); @@ -1173,7 +1298,10 @@ int radeon_wait_ring( drm_radeon_private_t *dev_priv, int n ) } /* FIXME: This return value is ignored in the BEGIN_RING macro! */ +#if RADEON_FIFO_DEBUG + radeon_status( dev_priv ); DRM_ERROR( "failed!\n" ); +#endif return -EBUSY; } diff --git a/linux/radeon_drm.h b/linux/radeon_drm.h index 50a7d6ed..4ae387ff 100644 --- a/linux/radeon_drm.h +++ b/linux/radeon_drm.h @@ -236,7 +236,7 @@ typedef struct drm_radeon_init { RADEON_INIT_CP = 0x01, RADEON_CLEANUP_CP = 0x02 } func; - int sarea_priv_offset; + unsigned long sarea_priv_offset; int is_pci; int cp_mode; int agp_size; @@ -249,12 +249,12 @@ typedef struct drm_radeon_init { unsigned int depth_bpp; unsigned int depth_offset, depth_pitch; - unsigned int fb_offset; - unsigned int mmio_offset; - unsigned int ring_offset; - unsigned int ring_rptr_offset; - unsigned int buffers_offset; - unsigned int agp_textures_offset; + unsigned long fb_offset; + unsigned long mmio_offset; + unsigned long ring_offset; + unsigned long ring_rptr_offset; + unsigned long buffers_offset; + unsigned long agp_textures_offset; } drm_radeon_init_t; typedef struct drm_radeon_cp_stop { diff --git a/linux/radeon_drv.c b/linux/radeon_drv.c index dba2037f..d7632667 100644 --- a/linux/radeon_drv.c +++ b/linux/radeon_drv.c @@ -31,15 +31,16 @@ #include "radeon.h" #include "drmP.h" #include "radeon_drv.h" +#include "ati_pcigart.h" #define DRIVER_AUTHOR "Gareth Hughes, VA Linux Systems Inc." #define DRIVER_NAME "radeon" #define DRIVER_DESC "ATI Radeon" -#define DRIVER_DATE "20010308" +#define DRIVER_DATE "20010405" #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 0 +#define DRIVER_MINOR 1 #define DRIVER_PATCHLEVEL 1 #define DRIVER_IOCTLS \ @@ -85,3 +86,4 @@ #include "drm_proc.h" #include "drm_vm.h" #include "drm_stub.h" +#include "drm_scatter.h" diff --git a/linux/radeon_drv.h b/linux/radeon_drv.h index f176bb56..ee5e113b 100644 --- a/linux/radeon_drv.h +++ b/linux/radeon_drv.h @@ -64,7 +64,7 @@ typedef struct drm_radeon_private { int agp_size; u32 agp_vm_start; - u32 agp_buffers_offset; + unsigned long agp_buffers_offset; int cp_mode; int cp_running; @@ -83,6 +83,7 @@ typedef struct drm_radeon_private { int usec_timeout; int is_pci; + unsigned long phys_pci_gart; atomic_t idle_count; @@ -433,6 +434,12 @@ extern int radeon_cp_indirect( struct inode *inode, struct file *filp, #define RADEON_AIC_CNTL 0x01d0 # define RADEON_PCIGART_TRANSLATE_EN (1 << 0) +#define RADEON_AIC_STAT 0x01d4 +#define RADEON_AIC_PT_BASE 0x01d8 +#define RADEON_AIC_LO_ADDR 0x01dc +#define RADEON_AIC_HI_ADDR 0x01e0 +#define RADEON_AIC_TLB_ADDR 0x01e4 +#define RADEON_AIC_TLB_DATA 0x01e8 /* CP command packets */ #define RADEON_CP_PACKET0 0x00000000 @@ -508,18 +515,47 @@ extern int radeon_cp_indirect( struct inode *inode, struct file *filp, #define RADEON_RING_HIGH_MARK 128 -#define RADEON_BASE(reg) ((u32)(dev_priv->mmio->handle)) +#define RADEON_BASE(reg) ((unsigned long)(dev_priv->mmio->handle)) #define RADEON_ADDR(reg) (RADEON_BASE( reg ) + reg) #define RADEON_DEREF(reg) *(volatile u32 *)RADEON_ADDR( reg ) +#ifdef __alpha__ +#define RADEON_READ(reg) (_RADEON_READ((u32 *)RADEON_ADDR( reg ))) +static inline u32 _RADEON_READ(u32 *addr) +{ + mb(); + return *(volatile u32 *)addr; +} +#define RADEON_WRITE(reg,val) \ +do { \ + wmb(); \ + RADEON_DEREF(reg) = val; \ +} while (0) +#else #define RADEON_READ(reg) RADEON_DEREF( reg ) #define RADEON_WRITE(reg, val) do { RADEON_DEREF( reg ) = val; } while (0) +#endif #define RADEON_DEREF8(reg) *(volatile u8 *)RADEON_ADDR( reg ) +#ifdef __alpha__ +#define RADEON_READ8(reg) _RADEON_READ8((u8 *)RADEON_ADDR( reg )) +static inline u8 _RADEON_READ8(u8 *addr) +{ + mb(); + return *(volatile u8 *)addr; +} +#define RADEON_WRITE8(reg,val) \ +do { \ + wmb(); \ + RADEON_DEREF8( reg ) = val; \ +} while (0) +#else #define RADEON_READ8(reg) RADEON_DEREF8( reg ) #define RADEON_WRITE8(reg, val) do { RADEON_DEREF8( reg ) = val; } while (0) +#endif -#define RADEON_WRITE_PLL( addr, val ) do { \ +#define RADEON_WRITE_PLL( addr, val ) \ +do { \ RADEON_WRITE8( RADEON_CLOCK_CNTL_INDEX, \ ((addr) & 0x1f) | RADEON_PLL_WR_EN ); \ RADEON_WRITE( RADEON_CLOCK_CNTL_DATA, (val) ); \ @@ -673,7 +709,7 @@ do { \ #define ADVANCE_RING() do { \ if ( RADEON_VERBOSE ) { \ - DRM_INFO( "ADVANCE_RING() tail=0x%06x wr=0x%06x\n", \ + DRM_INFO( "ADVANCE_RING() wr=0x%06x tail=0x%06x\n", \ write, dev_priv->ring.tail ); \ } \ radeon_flush_write_combine(); \ diff --git a/linux/radeon_state.c b/linux/radeon_state.c index 9360c43b..0a209245 100644 --- a/linux/radeon_state.c +++ b/linux/radeon_state.c @@ -1227,8 +1227,8 @@ int radeon_cp_vertex( struct inode *inode, struct file *filp, LOCK_TEST_WITH_RETURN( dev ); - if ( !dev_priv || dev_priv->is_pci ) { - DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ ); + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); return -EINVAL; } @@ -1290,8 +1290,8 @@ int radeon_cp_indices( struct inode *inode, struct file *filp, LOCK_TEST_WITH_RETURN( dev ); - if ( !dev_priv || dev_priv->is_pci ) { - DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ ); + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); return -EINVAL; } @@ -1420,8 +1420,8 @@ int radeon_cp_indirect( struct inode *inode, struct file *filp, LOCK_TEST_WITH_RETURN( dev ); - if ( !dev_priv || dev_priv->is_pci ) { - DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ ); + if ( !dev_priv ) { + DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ ); return -EINVAL; } diff --git a/shared-core/drm.h b/shared-core/drm.h index f1abaabf..3def97f7 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -126,10 +126,11 @@ typedef struct drm_control { } drm_control_t; typedef enum drm_map_type { - _DRM_FRAME_BUFFER = 0, /* WC (no caching), no core dump */ - _DRM_REGISTERS = 1, /* no caching, no core dump */ - _DRM_SHM = 2, /* shared, cached */ - _DRM_AGP = 3 /* AGP/GART */ + _DRM_FRAME_BUFFER = 0, /* WC (no caching), no core dump */ + _DRM_REGISTERS = 1, /* no caching, no core dump */ + _DRM_SHM = 2, /* shared, cached */ + _DRM_AGP = 3, /* AGP/GART */ + _DRM_SCATTER_GATHER = 4 /* Scatter/gather memory for PCI DMA */ } drm_map_type_t; typedef enum drm_map_flags { @@ -238,7 +239,8 @@ typedef struct drm_buf_desc { int high_mark; /* High water mark */ enum { _DRM_PAGE_ALIGN = 0x01, /* Align on page boundaries for DMA */ - _DRM_AGP_BUFFER = 0x02 /* Buffer is in agp space */ + _DRM_AGP_BUFFER = 0x02, /* Buffer is in agp space */ + _DRM_SG_BUFFER = 0x04 /* Scatter/gather memory buffer */ } flags; unsigned long agp_start; /* Start address of where the agp buffers * are in the agp aperture */ @@ -344,6 +346,11 @@ typedef struct drm_agp_info { unsigned short id_device; } drm_agp_info_t; +typedef struct drm_scatter_gather { + unsigned long size; /* In bytes -- will round to page boundary */ + unsigned long handle; /* Used for mapping / unmapping */ +} drm_scatter_gather_t; + #define DRM_IOCTL_BASE 'd' #define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) #define DRM_IOR(nr,size) _IOR(DRM_IOCTL_BASE,nr,size) @@ -399,6 +406,9 @@ typedef struct drm_agp_info { #define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, drm_agp_binding_t) #define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, drm_agp_binding_t) +#define DRM_IOCTL_SG_ALLOC DRM_IOW( 0x38, drm_scatter_gather_t) +#define DRM_IOCTL_SG_FREE DRM_IOW( 0x39, drm_scatter_gather_t) + /* MGA specific ioctls */ #define DRM_IOCTL_MGA_INIT DRM_IOW( 0x40, drm_mga_init_t) #define DRM_IOCTL_MGA_FLUSH DRM_IOW( 0x41, drm_lock_t) diff --git a/shared/drm.h b/shared/drm.h index f1abaabf..3def97f7 100644 --- a/shared/drm.h +++ b/shared/drm.h @@ -126,10 +126,11 @@ typedef struct drm_control { } drm_control_t; typedef enum drm_map_type { - _DRM_FRAME_BUFFER = 0, /* WC (no caching), no core dump */ - _DRM_REGISTERS = 1, /* no caching, no core dump */ - _DRM_SHM = 2, /* shared, cached */ - _DRM_AGP = 3 /* AGP/GART */ + _DRM_FRAME_BUFFER = 0, /* WC (no caching), no core dump */ + _DRM_REGISTERS = 1, /* no caching, no core dump */ + _DRM_SHM = 2, /* shared, cached */ + _DRM_AGP = 3, /* AGP/GART */ + _DRM_SCATTER_GATHER = 4 /* Scatter/gather memory for PCI DMA */ } drm_map_type_t; typedef enum drm_map_flags { @@ -238,7 +239,8 @@ typedef struct drm_buf_desc { int high_mark; /* High water mark */ enum { _DRM_PAGE_ALIGN = 0x01, /* Align on page boundaries for DMA */ - _DRM_AGP_BUFFER = 0x02 /* Buffer is in agp space */ + _DRM_AGP_BUFFER = 0x02, /* Buffer is in agp space */ + _DRM_SG_BUFFER = 0x04 /* Scatter/gather memory buffer */ } flags; unsigned long agp_start; /* Start address of where the agp buffers * are in the agp aperture */ @@ -344,6 +346,11 @@ typedef struct drm_agp_info { unsigned short id_device; } drm_agp_info_t; +typedef struct drm_scatter_gather { + unsigned long size; /* In bytes -- will round to page boundary */ + unsigned long handle; /* Used for mapping / unmapping */ +} drm_scatter_gather_t; + #define DRM_IOCTL_BASE 'd' #define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) #define DRM_IOR(nr,size) _IOR(DRM_IOCTL_BASE,nr,size) @@ -399,6 +406,9 @@ typedef struct drm_agp_info { #define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, drm_agp_binding_t) #define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, drm_agp_binding_t) +#define DRM_IOCTL_SG_ALLOC DRM_IOW( 0x38, drm_scatter_gather_t) +#define DRM_IOCTL_SG_FREE DRM_IOW( 0x39, drm_scatter_gather_t) + /* MGA specific ioctls */ #define DRM_IOCTL_MGA_INIT DRM_IOW( 0x40, drm_mga_init_t) #define DRM_IOCTL_MGA_FLUSH DRM_IOW( 0x41, drm_lock_t) |