diff options
-rw-r--r-- | linux-core/ati_pcigart.c | 97 | ||||
-rw-r--r-- | linux-core/drmP.h | 26 | ||||
-rw-r--r-- | linux-core/drm_bufs.c | 174 | ||||
-rw-r--r-- | linux-core/drm_drv.c | 15 | ||||
-rw-r--r-- | linux-core/drm_scatter.c | 210 | ||||
-rw-r--r-- | linux-core/drm_vm.c | 57 | ||||
-rw-r--r-- | linux/ati_pcigart.c | 123 | ||||
-rw-r--r-- | linux/ati_pcigart.h | 97 | ||||
-rw-r--r-- | linux/drmP.h | 26 | ||||
-rw-r--r-- | linux/drm_bufs.h | 174 | ||||
-rw-r--r-- | linux/drm_drv.h | 15 | ||||
-rw-r--r-- | linux/drm_scatter.h | 210 | ||||
-rw-r--r-- | linux/drm_vm.h | 57 | ||||
-rw-r--r-- | linux/r128.h | 1 | ||||
-rw-r--r-- | linux/r128_cce.c | 74 | ||||
-rw-r--r-- | linux/r128_state.c | 11 |
16 files changed, 1210 insertions, 157 deletions
diff --git a/linux-core/ati_pcigart.c b/linux-core/ati_pcigart.c index df574a10..e80f9944 100644 --- a/linux-core/ati_pcigart.c +++ b/linux-core/ati_pcigart.c @@ -1,5 +1,5 @@ /* ati_pcigart.h -- ATI PCI GART support -*- linux-c -*- - * Created: Tue Jan 23 21:52:19 2000 by jhartmann@valinux.com + * Created: Wed Dec 13 21:52:19 2000 by gareth@valinux.com * * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. @@ -27,13 +27,100 @@ * Gareth Hughes <gareth@valinux.com> */ -#ifndef _ATI_PCIGART_H_ -#define _ATI_PCIGART_H_ +#define __NO_VERSION__ +#include "drmP.h" #define ATI_PCIGART_TABLE_ORDER 3 #define ATI_PCIGART_TABLE_PAGES (1 << 3) #define ATI_MAX_PCIGART_PAGES 8192 /* 32 MB aperture */ -extern unsigned long ati_pcigart_init(drm_device_t *dev); -extern int ati_pcigart_cleanup(unsigned long address); +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; + int i; + + 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++ ) { + struct page *page = entry->pagelist[i]; + pci_gart[i] = cpu_to_le32( virt_to_bus( page->virtual ) ); + } + +#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 4032689a..37fc1661 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -572,6 +572,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; @@ -652,6 +659,7 @@ typedef struct drm_device { #if __REALLY_HAVE_AGP drm_agp_head_t *agp; #endif + drm_sg_mem_t *sg; /* Scatter gather memory */ unsigned long *ctx_bitmap; void *dev_private; drm_sigdata_t sigdata; /* For block_all_signals */ @@ -706,6 +714,9 @@ extern unsigned long DRM(vm_shm_nopage_lock)(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, @@ -720,6 +731,10 @@ extern struct page *DRM(vm_shm_nopage_lock)(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); @@ -930,5 +945,16 @@ extern int DRM(proc_cleanup)(int minor, struct proc_dir_entry *root, struct proc_dir_entry *dev_root); + /* 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); + + /* 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_bufs.c b/linux-core/drm_bufs.c index 38ea1ff0..c1a4862b 100644 --- a/linux-core/drm_bufs.c +++ b/linux-core/drm_bufs.c @@ -36,6 +36,10 @@ #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 @@ -130,6 +134,13 @@ int DRM(addmap)( struct inode *inode, struct file *filp, map->offset = map->offset + dev->agp->base; 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; @@ -483,6 +494,160 @@ int DRM(addbufs_pci)( struct inode *inode, struct file *filp, } #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 ) { @@ -495,7 +660,11 @@ int DRM(addbufs)( struct inode *inode, struct file *filp, #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 ); @@ -678,7 +847,8 @@ int DRM(mapbufs)( struct inode *inode, struct file *filp, return -EFAULT; if ( request.count >= dma->buf_count ) { - if ( __HAVE_AGP && (dma->flags & _DRM_DMA_USE_AGP) ) { + 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 ) { diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 71f52276..4e5d4c0c 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -81,7 +81,9 @@ #ifndef __HAVE_COUNTERS #define __HAVE_COUNTERS 0 #endif - +#ifndef __HAVE_SG +#define __HAVE_SG 0 +#endif #ifndef DRIVER_PREINIT #define DRIVER_PREINIT() #endif @@ -174,6 +176,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 }; @@ -402,6 +409,12 @@ static int DRM(takedown)( drm_device_t *dev ) * handled in the AGP/GART driver. */ break; + case _DRM_SCATTER_GATHER: + if(dev->sg) { + DRM(sg_cleanup)(dev->sg); + dev->sg = NULL; + } + break; } DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); } diff --git a/linux-core/drm_scatter.c b/linux-core/drm_scatter.c new file mode 100644 index 00000000..ec759084 --- /dev/null +++ b/linux-core/drm_scatter.c @@ -0,0 +1,210 @@ +/* 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; + printk("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) printk("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 index 347816ac..62d3d2af 100644 --- a/linux-core/drm_vm.c +++ b/linux-core/drm_vm.c @@ -56,6 +56,12 @@ struct vm_operations_struct drm_vm_dma_ops = { 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, @@ -172,6 +178,48 @@ struct page *DRM(vm_dma_nopage)(struct vm_area_struct *vma, #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; @@ -354,6 +402,15 @@ int DRM(mmap)(struct file *filp, struct vm_area_struct *vma) 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. */ } diff --git a/linux/ati_pcigart.c b/linux/ati_pcigart.c deleted file mode 100644 index 54762f1a..00000000 --- a/linux/ati_pcigart.c +++ /dev/null @@ -1,123 +0,0 @@ -/* ati_pcigart.c -- 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" -#include "ati_pcigart.h" - -static unsigned long 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 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 ati_pcigart_init( drm_device_t *dev ) -{ - drm_sg_mem_t *entry = dev->sg; - unsigned long address; - unsigned long pages; - u32 *pci_gart; - int i; - - if ( !entry ) { - DRM_ERROR( "no scatter/gather memory!\n" ); - return 0; - } - - address = 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++ ) { - struct page *page = entry->pagelist[i]; - pci_gart[i] = cpu_to_le32( virt_to_bus( page->virtual ) ); - } - -#if __i386__ - asm volatile ( "wbinvd" ::: "memory" ); -#else - mb(); -#endif - - return address; -} - -int ati_pcigart_cleanup( unsigned long address ) -{ - - if ( address ) { - ati_free_pcigart_table( address ); - } - - return 0; -} diff --git a/linux/ati_pcigart.h b/linux/ati_pcigart.h index df574a10..e80f9944 100644 --- a/linux/ati_pcigart.h +++ b/linux/ati_pcigart.h @@ -1,5 +1,5 @@ /* ati_pcigart.h -- ATI PCI GART support -*- linux-c -*- - * Created: Tue Jan 23 21:52:19 2000 by jhartmann@valinux.com + * Created: Wed Dec 13 21:52:19 2000 by gareth@valinux.com * * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * All Rights Reserved. @@ -27,13 +27,100 @@ * Gareth Hughes <gareth@valinux.com> */ -#ifndef _ATI_PCIGART_H_ -#define _ATI_PCIGART_H_ +#define __NO_VERSION__ +#include "drmP.h" #define ATI_PCIGART_TABLE_ORDER 3 #define ATI_PCIGART_TABLE_PAGES (1 << 3) #define ATI_MAX_PCIGART_PAGES 8192 /* 32 MB aperture */ -extern unsigned long ati_pcigart_init(drm_device_t *dev); -extern int ati_pcigart_cleanup(unsigned long address); +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; + int i; + + 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++ ) { + struct page *page = entry->pagelist[i]; + pci_gart[i] = cpu_to_le32( virt_to_bus( page->virtual ) ); + } + +#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/drmP.h b/linux/drmP.h index 4032689a..37fc1661 100644 --- a/linux/drmP.h +++ b/linux/drmP.h @@ -572,6 +572,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; @@ -652,6 +659,7 @@ typedef struct drm_device { #if __REALLY_HAVE_AGP drm_agp_head_t *agp; #endif + drm_sg_mem_t *sg; /* Scatter gather memory */ unsigned long *ctx_bitmap; void *dev_private; drm_sigdata_t sigdata; /* For block_all_signals */ @@ -706,6 +714,9 @@ extern unsigned long DRM(vm_shm_nopage_lock)(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, @@ -720,6 +731,10 @@ extern struct page *DRM(vm_shm_nopage_lock)(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); @@ -930,5 +945,16 @@ extern int DRM(proc_cleanup)(int minor, struct proc_dir_entry *root, struct proc_dir_entry *dev_root); + /* 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); + + /* 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_bufs.h b/linux/drm_bufs.h index 38ea1ff0..c1a4862b 100644 --- a/linux/drm_bufs.h +++ b/linux/drm_bufs.h @@ -36,6 +36,10 @@ #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 @@ -130,6 +134,13 @@ int DRM(addmap)( struct inode *inode, struct file *filp, map->offset = map->offset + dev->agp->base; 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; @@ -483,6 +494,160 @@ int DRM(addbufs_pci)( struct inode *inode, struct file *filp, } #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 ) { @@ -495,7 +660,11 @@ int DRM(addbufs)( struct inode *inode, struct file *filp, #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 ); @@ -678,7 +847,8 @@ int DRM(mapbufs)( struct inode *inode, struct file *filp, return -EFAULT; if ( request.count >= dma->buf_count ) { - if ( __HAVE_AGP && (dma->flags & _DRM_DMA_USE_AGP) ) { + 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 ) { diff --git a/linux/drm_drv.h b/linux/drm_drv.h index 71f52276..4e5d4c0c 100644 --- a/linux/drm_drv.h +++ b/linux/drm_drv.h @@ -81,7 +81,9 @@ #ifndef __HAVE_COUNTERS #define __HAVE_COUNTERS 0 #endif - +#ifndef __HAVE_SG +#define __HAVE_SG 0 +#endif #ifndef DRIVER_PREINIT #define DRIVER_PREINIT() #endif @@ -174,6 +176,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 }; @@ -402,6 +409,12 @@ static int DRM(takedown)( drm_device_t *dev ) * handled in the AGP/GART driver. */ break; + case _DRM_SCATTER_GATHER: + if(dev->sg) { + DRM(sg_cleanup)(dev->sg); + dev->sg = NULL; + } + break; } DRM(free)( map, sizeof(*map), DRM_MEM_MAPS ); } diff --git a/linux/drm_scatter.h b/linux/drm_scatter.h new file mode 100644 index 00000000..ec759084 --- /dev/null +++ b/linux/drm_scatter.h @@ -0,0 +1,210 @@ +/* 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; + printk("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) printk("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 index 347816ac..62d3d2af 100644 --- a/linux/drm_vm.h +++ b/linux/drm_vm.h @@ -56,6 +56,12 @@ struct vm_operations_struct drm_vm_dma_ops = { 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, @@ -172,6 +178,48 @@ struct page *DRM(vm_dma_nopage)(struct vm_area_struct *vma, #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; @@ -354,6 +402,15 @@ int DRM(mmap)(struct file *filp, struct vm_area_struct *vma) 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. */ } diff --git a/linux/r128.h b/linux/r128.h index 83e002af..d94b9256 100644 --- a/linux/r128.h +++ b/linux/r128.h @@ -40,6 +40,7 @@ #define __MUST_HAVE_AGP 1 #define __HAVE_MTRR 1 #define __HAVE_CTX_BITMAP 1 +#define __HAVE_SG 1 /* Driver customization: */ diff --git a/linux/r128_cce.c b/linux/r128_cce.c index d1790497..f01aecff 100644 --- a/linux/r128_cce.c +++ b/linux/r128_cce.c @@ -315,7 +315,13 @@ static void r128_cce_init_ring_buffer( drm_device_t *dev ) /* 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 ); @@ -323,8 +329,25 @@ static void r128_cce_init_ring_buffer( drm_device_t *dev ) /* 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 ); + + 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, @@ -355,15 +378,12 @@ 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 ) { - DRM(free)( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); + if ( dev_priv->is_pci && !dev->sg ) { + DRM_ERROR( "PCI GART memory not allocated!\n" ); + drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER ); dev->dev_private = NULL; return -EINVAL; - } + } dev_priv->usec_timeout = init->usec_timeout; if ( dev_priv->usec_timeout < 1 || @@ -475,10 +495,25 @@ static int r128_do_init_cce( drm_device_t *dev, drm_r128_init_t *init ) dev_priv->sarea_priv = (drm_r128_sarea_t *)((u8 *)dev_priv->sarea->handle + init->sarea_priv_offset); + 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; + } - DRM_IOREMAP( dev_priv->cce_ring ); - DRM_IOREMAP( dev_priv->ring_rptr ); - DRM_IOREMAP( dev_priv->buffers ); + +#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 +536,19 @@ 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_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 ); diff --git a/linux/r128_state.c b/linux/r128_state.c index fa16f3ce..c926b2e1 100644 --- a/linux/r128_state.c +++ b/linux/r128_state.c @@ -714,7 +714,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; @@ -1323,8 +1323,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; } @@ -1386,8 +1386,9 @@ 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; } |