summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux-core/ati_pcigart.c97
-rw-r--r--linux-core/drmP.h26
-rw-r--r--linux-core/drm_bufs.c174
-rw-r--r--linux-core/drm_drv.c15
-rw-r--r--linux-core/drm_scatter.c210
-rw-r--r--linux-core/drm_vm.c57
-rw-r--r--linux/ati_pcigart.c123
-rw-r--r--linux/ati_pcigart.h97
-rw-r--r--linux/drmP.h26
-rw-r--r--linux/drm_bufs.h174
-rw-r--r--linux/drm_drv.h15
-rw-r--r--linux/drm_scatter.h210
-rw-r--r--linux/drm_vm.h57
-rw-r--r--linux/r128.h1
-rw-r--r--linux/r128_cce.c74
-rw-r--r--linux/r128_state.c11
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;
}