Completeley rewritten Savage DRM which can be considered secure (modulo
authorFelix Kuehling <fxkuehl@gmx.de>
Sat, 1 Jan 2005 20:22:58 +0000 (20:22 +0000)
committerFelix Kuehling <fxkuehl@gmx.de>
Sat, 1 Jan 2005 20:22:58 +0000 (20:22 +0000)
    implementation errors). Direct hardware (MMIO, BCI) access is no longer
    needed in the Mesa driver. Bumped version to 2.0.0. Corresponding
    changes to the DDX and Mesa drivers are being committed.

linux-core/Makefile
linux-core/Makefile.kernel
linux-core/savage_dma.c [deleted file]
linux-core/savage_drm.h [deleted file]
linux-core/savage_drv.c
linux-core/savage_drv.h [deleted file]
shared-core/savage_bci.c [new file with mode: 0644]
shared-core/savage_drm.h [new file with mode: 0644]
shared-core/savage_drv.h [new file with mode: 0644]
shared-core/savage_state.c [new file with mode: 0644]

index 05854ea..1b1bc04 100644 (file)
@@ -88,6 +88,7 @@ I915SHARED  =   i915_drv.h i915_drm.h i915_irq.c i915_mem.c i915_dma.c
 SISHEADERS=     sis_drv.h sis_drm.h $(DRMHEADERS)
 SISSHARED=      sis_drv.h sis_drm.h sis_ds.c sis_ds.h sis_mm.c
 SAVAGEHEADERS=  savage_drv.h savage_drm.h $(DRMHEADERS)
+SAVAGESHARED=  savage_drv.h savage_drm.h savage_bci.c savage_state.c
 VIAHEADERS =   via_drm.h via_drv.h via_mm.h via_ds.h \
                via_3d_reg.h $(DRMHEADERS)
 VIASHARED      = via_drm.h via_drv.h via_mm.h via_ds.h \
@@ -100,7 +101,7 @@ FFBHEADERS =        ffb_drv.h $(DRMHEADERS)
 
 SHAREDSRC = $(DRMSHARED) $(MGASHARED) $(R128SHARED) $(RADEONSHARED) \
        $(SISSHARED) $(TDFXSHARED) $(VIASHARED) $(MACH64SHARED) \
-       $(I915SHARED)
+       $(I915SHARED) $(SAVAGESHARED)
 
 PROGS = dristat drmstat
 
index 34cbf53..2899583 100644 (file)
@@ -21,7 +21,7 @@ i915-objs   := i915_drv.o i915_dma.o i915_irq.o i915_mem.o
 radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o radeon_i2c.o
 sis-objs    := sis_drv.o sis_ds.o sis_mm.o
 ffb-objs    := ffb_drv.o ffb_context.o
-savage-objs := savage_drv.o savage_dma.o
+savage-objs := savage_drv.o savage_bci.o savage_state.o
 via-objs    := via_irq.o via_drv.o via_ds.o via_map.o via_mm.o via_dma.o via_verifier.o
 mach64-objs := mach64_drv.o mach64_dma.o mach64_irq.o mach64_state.o 
 
diff --git a/linux-core/savage_dma.c b/linux-core/savage_dma.c
deleted file mode 100644 (file)
index eb21b09..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
- * Copyright 2001-2003 S3 Graphics, Inc. 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, sub license,
- * 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 NON-INFRINGEMENT. IN NO EVENT SHALL
- * VIA, S3 GRAPHICS, 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.
- */
-
-/*=========================================================*/
-#include <linux/interrupt.h>   /* For task queue support */
-#include <linux/delay.h>
-
-#include "drmP.h"
-#include "savage_drm.h"
-#include "savage_drv.h"
-
-#define SAVAGE_DEFAULT_USEC_TIMEOUT    10000
-#define SAVAGE_FREELIST_DEBUG          0
-
-int savage_preinit(drm_device_t * dev, unsigned long chipset)
-{
-       drm_savage_private_t *dev_priv;
-       unsigned mmioBase, fbBase, fbSize, apertureBase;
-       int ret = 0;
-
-       dev_priv = drm_alloc(sizeof(drm_savage_private_t), DRM_MEM_DRIVER);
-       if (dev_priv == NULL)
-               return DRM_ERR(ENOMEM);
-
-       memset(dev_priv, 0, sizeof(drm_savage_private_t));
-       dev->dev_private = (void *)dev_priv;
-       dev_priv->chipset = (enum savage_family)chipset;
-
-       if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
-               fbBase = pci_resource_start(dev->pdev, 0);
-               fbSize = SAVAGE_FB_SIZE_S3;
-               mmioBase = fbBase + fbSize;
-               apertureBase = fbBase + SAVAGE_APERTURE_OFFSET;
-       } else if (chipset != S3_SUPERSAVAGE) {
-               mmioBase = pci_resource_start(dev->pdev, 0);
-               fbBase = pci_resource_start(dev->pdev, 1);
-               fbSize = SAVAGE_FB_SIZE_S4;
-               apertureBase = fbBase + SAVAGE_APERTURE_OFFSET;
-       } else {
-               mmioBase = pci_resource_start(dev->pdev, 0);
-               fbBase = pci_resource_start(dev->pdev, 1);
-               fbSize = pci_resource_len(dev->pdev, 1);
-               apertureBase = pci_resource_start(dev->pdev, 2);
-       }
-
-       if ((ret = drm_initmap(dev, mmioBase, SAVAGE_MMIO_SIZE,
-                              _DRM_REGISTERS, 0)))
-               return ret;
-
-       if ((ret = drm_initmap(dev, fbBase, fbSize,
-                              _DRM_FRAME_BUFFER, _DRM_WRITE_COMBINING)))
-               return ret;
-
-       if ((ret = drm_initmap(dev, apertureBase, SAVAGE_APERTURE_SIZE,
-                              _DRM_FRAME_BUFFER, _DRM_WRITE_COMBINING)))
-               return ret;
-
-       return ret;
-}
diff --git a/linux-core/savage_drm.h b/linux-core/savage_drm.h
deleted file mode 100644 (file)
index ee06d68..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
- * Copyright 2001-2003 S3 Graphics, Inc. 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, sub license,
- * 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 NON-INFRINGEMENT. IN NO EVENT SHALL
- * VIA, S3 GRAPHICS, 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.
- */
-
-#ifndef __SAVAGE_DRM_H__
-#define __SAVAGE_DRM_H__
-
-#ifndef __SAVAGE_SAREA_DEFINES__
-#define __SAVAGE_SAREA_DEFINES__
-
-#define DRM_SAVAGE_MEM_PAGE (1UL<<12)
-#define DRM_SAVAGE_MEM_WORK 32
-#define DRM_SAVAGE_MEM_LOCATION_PCI 1
-#define DRM_SAVAGE_MEM_LOCATION_AGP 2
-#define DRM_SAVAGE_DMA_AGP_SIZE (16*1024*1024)
-
-typedef struct drm_savage_alloc_cont_mem {
-       size_t size;            /*size of buffer */
-       unsigned long type;     /*4k page or word */
-       unsigned long alignment;
-       unsigned long location; /*agp or pci */
-
-       unsigned long phyaddress;
-       unsigned long linear;
-} drm_savage_alloc_cont_mem_t;
-
-typedef struct drm_savage_get_physcis_address {
-       unsigned long v_address;
-       unsigned long p_address;
-} drm_savage_get_physcis_address_t;
-
-/*ioctl number*/
-#define DRM_IOCTL_SAVAGE_ALLOC_CONTINUOUS_MEM \
-       DRM_IOWR(0x40,drm_savage_alloc_cont_mem_t)
-#define DRM_IOCTL_SAVAGE_GET_PHYSICS_ADDRESS \
-       DRM_IOWR(0x41, drm_savage_get_physcis_address_t)
-#define DRM_IOCTL_SAVAGE_FREE_CONTINUOUS_MEM \
-       DRM_IOWR(0x42, drm_savage_alloc_cont_mem_t)
-
-#define SAVAGE_FRONT           0x1
-#define SAVAGE_BACK            0x2
-#define SAVAGE_DEPTH           0x4
-#define SAVAGE_STENCIL         0x8
-
-/* What needs to be changed for the current vertex dma buffer?
- */
-#define SAVAGE_UPLOAD_CTX      0x1
-#define SAVAGE_UPLOAD_TEX0     0x2
-#define SAVAGE_UPLOAD_TEX1     0x4
-#define SAVAGE_UPLOAD_PIPE     0x8     /* <- seems should be removed, Jiayo Hsu */
-#define SAVAGE_UPLOAD_TEX0IMAGE        0x10    /* handled client-side */
-#define SAVAGE_UPLOAD_TEX1IMAGE        0x20    /* handled client-side */
-#define SAVAGE_UPLOAD_2D       0x40
-#define SAVAGE_WAIT_AGE                0x80    /* handled client-side */
-#define SAVAGE_UPLOAD_CLIPRECTS        0x100   /* handled client-side */
-/*frank:add Buffer state 2001/11/15*/
-#define SAVAGE_UPLOAD_BUFFERS 0x200
-/* original marked off in MGA drivers , Jiayo Hsu Oct.23,2001 */
-
-/* Keep these small for testing.
- */
-#define SAVAGE_NR_SAREA_CLIPRECTS      8
-
-/* 2 heaps (1 for card, 1 for agp), each divided into upto 128
- * regions, subject to a minimum region size of (1<<16) == 64k.
- *
- * Clients may subdivide regions internally, but when sharing between
- * clients, the region size is the minimum granularity.
- */
-
-#define SAVAGE_CARD_HEAP               0
-#define SAVAGE_AGP_HEAP                        1
-#define SAVAGE_NR_TEX_HEAPS            2
-#define SAVAGE_NR_TEX_REGIONS          16      /* num. of global texture manage list element */
-#define SAVAGE_LOG_MIN_TEX_REGION_SIZE 16      /* each region 64K, Jiayo Hsu */
-
-#endif                         /* __SAVAGE_SAREA_DEFINES__ */
-
-/* drm_tex_region_t define in drm.h */
-
-typedef drm_tex_region_t drm_savage_tex_region_t;
-
-/* Setup registers for 2D, X server
- */
-typedef struct {
-       unsigned int pitch;
-} drm_savage_server_regs_t;
-
-typedef struct _drm_savage_sarea {
-       /* The channel for communication of state information to the kernel
-        * on firing a vertex dma buffer.
-        */
-       unsigned int setup[28]; /* 3D context registers */
-       drm_savage_server_regs_t server_state;
-
-       unsigned int dirty;
-
-       unsigned int vertsize;  /* vertext  size */
-
-       /* The current cliprects, or a subset thereof.
-        */
-       drm_clip_rect_t boxes[SAVAGE_NR_SAREA_CLIPRECTS];
-       unsigned int nbox;
-
-       /* Information about the most recently used 3d drawable.  The
-        * client fills in the req_* fields, the server fills in the
-        * exported_ fields and puts the cliprects into boxes, above.
-        *
-        * The client clears the exported_drawable field before
-        * clobbering the boxes data.
-        */
-       unsigned int req_drawable;      /* the X drawable id */
-       unsigned int req_draw_buffer;   /* SAVAGE_FRONT or SAVAGE_BACK */
-
-       unsigned int exported_drawable;
-       unsigned int exported_index;
-       unsigned int exported_stamp;
-       unsigned int exported_buffers;
-       unsigned int exported_nfront;
-       unsigned int exported_nback;
-       int exported_back_x, exported_front_x, exported_w;
-       int exported_back_y, exported_front_y, exported_h;
-       drm_clip_rect_t exported_boxes[SAVAGE_NR_SAREA_CLIPRECTS];
-
-       /* Counters for aging textures and for client-side throttling.
-        */
-       unsigned int status[4];
-
-       /* LRU lists for texture memory in agp space and on the card.
-        */
-       drm_tex_region_t texList[SAVAGE_NR_TEX_HEAPS][SAVAGE_NR_TEX_REGIONS +
-                                                     1];
-       unsigned int texAge[SAVAGE_NR_TEX_HEAPS];
-
-       /* Mechanism to validate card state.
-        */
-       int ctxOwner;
-       unsigned long shadow_status[64];        /*too big? */
-
-       /*agp offset */
-       unsigned long agp_offset;
-} drm_savage_sarea_t, *drm_savage_sarea_ptr;
-
-typedef struct drm_savage_init {
-
-       unsigned long sarea_priv_offset;
-
-       int chipset;
-       int sgram;
-
-       unsigned int maccess;
-
-       unsigned int fb_cpp;
-       unsigned int front_offset, front_pitch;
-       unsigned int back_offset, back_pitch;
-
-       unsigned int depth_cpp;
-       unsigned int depth_offset, depth_pitch;
-
-       unsigned int texture_offset[SAVAGE_NR_TEX_HEAPS];
-       unsigned int texture_size[SAVAGE_NR_TEX_HEAPS];
-
-       unsigned long fb_offset;
-       unsigned long mmio_offset;
-       unsigned long status_offset;
-} drm_savage_init_t;
-
-typedef struct drm_savage_fullscreen {
-       enum {
-               SAVAGE_INIT_FULLSCREEN = 0x01,
-               SAVAGE_CLEANUP_FULLSCREEN = 0x02
-       } func;
-} drm_savage_fullscreen_t;
-
-typedef struct drm_savage_clear {
-       unsigned int flags;
-       unsigned int clear_color;
-       unsigned int clear_depth;
-       unsigned int color_mask;
-       unsigned int depth_mask;
-} drm_savage_clear_t;
-
-typedef struct drm_savage_vertex {
-       int idx;                /* buffer to queue */
-       int used;               /* bytes in use */
-       int discard;            /* client finished with buffer?  */
-} drm_savage_vertex_t;
-
-typedef struct drm_savage_indices {
-       int idx;                /* buffer to queue */
-       unsigned int start;
-       unsigned int end;
-       int discard;            /* client finished with buffer?  */
-} drm_savage_indices_t;
-
-typedef struct drm_savage_iload {
-       int idx;
-       unsigned int dstorg;
-       unsigned int length;
-} drm_savage_iload_t;
-
-typedef struct _drm_savage_blit {
-       unsigned int planemask;
-       unsigned int srcorg;
-       unsigned int dstorg;
-       int src_pitch, dst_pitch;
-       int delta_sx, delta_sy;
-       int delta_dx, delta_dy;
-       int height, ydir;       /* flip image vertically */
-       int source_pitch, dest_pitch;
-} drm_savage_blit_t;
-
-#endif
index e859168..fff5b68 100644 (file)
@@ -1,6 +1,7 @@
-/*
- * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
- * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved.
+/* savage_drv.c -- Savage driver for Linux
+ *
+ * Copyright 2004  Felix Kuehling
+ * 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"),
  * 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 NON-INFRINGEMENT. IN NO EVENT SHALL
- * VIA, S3 GRAPHICS, 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.
+ * 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
+ * NON-INFRINGEMENT. IN NO EVENT SHALL FELIX KUEHLING 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.
  */
 
 #include <linux/config.h>
 #include "savage_drm.h"
 #include "savage_drv.h"
 
-#define DRIVER_AUTHOR          "John Zhao, S3 Graphics Inc."
-
-#define DRIVER_NAME            "savage"
-#define DRIVER_DESC            "Savage4 Family"
-#define DRIVER_DATE            "20011023"
-
-#define DRIVER_MAJOR           1
-#define DRIVER_MINOR           0
-#define DRIVER_PATCHLEVEL      0
-
-/* Currently Savage4 not implement DMA */
-/* mark off by Jiayo Hsu, Oct. 23, 2001*/
-
-
-#if SAVAGE_CMD_DMA /* Check the 3D driver, and we need to fix this anyway */
-
-int savage_alloc_continuous_mem(struct inode *inode, struct file *filp,
-               unsigned int cmd, unsigned long arg)
-{
-  drm_savage_alloc_cont_mem_t cont_mem;
-  unsigned long size,addr;
-  void * ret;
-  int i;
-  mem_map_t *p;
-  pgprot_t flags;
-
-  /* add to list */
-  drm_file_t *priv = filp->private_data;
-  drm_device_t *dev = priv->dev;
-  drm_map_t *map;
-  drm_map_list_t *list;
-
-  if (copy_from_user(&cont_mem,(drm_savage_alloc_cont_mem_t *)arg,sizeof(cont_mem)))
-    return -EFAULT;
-
-  map = savage_alloc)( sizeof(*map), DRM_MEM_MAPS );
-  if ( !map )
-    return -ENOMEM;
-
-  /*check the parameters*/
-  if (cont_mem.size <= 0 )
-    return -EINVAL;
-
-  size = cont_mem.type * cont_mem.size;
-  printk("[drm]JTLIsize = %lu\n",size);
-
-  ret = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
-  if (ret == NULL)
-    {
-      printk("Kalloc error\n");
-      return 0;
-    }
-
-  /*set the reserverd flag so that the remap_page_range can map these page*/
-  for(i=0,p=virt_to_page(ret);i< size /PAGE_SIZE; i++,p++)
-    SetPageReserved(p);
-
-  cont_mem.phyaddress = virt_to_bus(ret);
-  cont_mem.location = DRM_SAVAGE_MEM_LOCATION_PCI; /* pci only at present*/
-
-
-  /*Map the memory to user space*/
-  down_write(&current->mm->mmap_sem);
-  addr=do_mmap(NULL,0,size,PROT_READ|PROT_WRITE,MAP_PRIVATE,cont_mem.phyaddress);
-  if (addr<=0)
-    return -EFAULT;
-  pgprot_val(flags)=_PAGE_PRESENT | _PAGE_RW |_PAGE_USER ;
-  if (remap_page_range(addr,cont_mem.phyaddress,size,flags))
-    return -EFAULT;
-  up_write(&current->mm->mmap_sem);
-
-  for(i=0,p=virt_to_page(ret);i< size /PAGE_SIZE; i++,p++)
-    ClearPageReserved(p);
-
-  cont_mem.linear = addr;
-
-  /*map list*/
-  map->handle =  ret; /* to distinguish with other*/
-  map->offset = cont_mem.phyaddress;
-  map->size = size;
-  map->mtrr=-1;
-  /*map-flags,type??*/
-
-  list = savage_alloc)(sizeof(*list), DRM_MEM_MAPS);
-  if(!list) {
-    savage_free)(map, sizeof(*map), DRM_MEM_MAPS);
-    return -EINVAL;
-  }
-  memset(list, 0, sizeof(*list));
-  list->map = map;
-
-  down(&dev->struct_sem);
-  list_add(&list->head, &dev->maplist->head);
-  up(&dev->struct_sem);
-
-  if (copy_to_user((drm_savage_alloc_cont_mem_t *)arg,&cont_mem,sizeof(cont_mem)))
-    return -EFAULT;
-
-  for(i=0,p=virt_to_page(ret);i< size /PAGE_SIZE; i++,p++)
-    atomic_set(&p->count,1);
-
-  return 1;/*success*/
-}
-
-int savage_get_physics_address(struct inode *inode, struct file *filp,
-                              unsigned int cmd, unsigned long arg)
-{
-
-  drm_savage_get_physcis_address_t req;
-  unsigned long buf;
-  pgd_t *pgd;
-  pmd_t *pmd;
-  pte_t *pte;
-  struct mm_struct *mm;
-
-  if (copy_from_user(&req,(drm_savage_get_physcis_address_t *)arg,sizeof(req)))
-    return -EFAULT;
-  buf = req.v_address;
-
-  /*What kind of virtual address ?*/
-  if (buf >= (unsigned long)high_memory)
-    mm = &init_mm;
-  else
-    mm = current->mm;
-
-#ifdef LINUX_24
-  spin_lock(&mm->page_table_lock);
-#endif
-
-  pgd=pgd_offset(mm,buf);
-  pmd=pmd_offset(pgd,buf);
-  pte=pte_offset_map(pmd,buf);
-
-  if (!pte_present(*pte))
-    return -EFAULT;
-
-  req.p_address = ((pte_val(*pte) &PAGE_MASK) |( buf&(PAGE_SIZE-1)));
-
-  if (copy_to_user((drm_savage_get_physcis_address_t  *)arg,&req,sizeof(req)))
-    return -EFAULT;
-  return 1;
-}
-
-/*free the continuous memory*/
-int savage_free_cont_mem(struct inode *inode, struct file *filp,
-                              unsigned int cmd, unsigned long arg)
-{
-  drm_savage_alloc_cont_mem_t cont_mem;
-  unsigned long size;
-
-  /*map  list */
-  drm_file_t *priv = filp->private_data;
-  drm_device_t *dev = priv->dev;
-  drm_map_t *map;
-  struct list_head *list;
-  drm_map_list_t *r_list = NULL;
-
-  if (copy_from_user(&cont_mem,(drm_savage_alloc_cont_mem_t *)arg,sizeof(cont_mem)))
-    return -EFAULT;
-  size = cont_mem.type * cont_mem.size;
-  if (size <= 0)
-    return -EINVAL;
-
-  /* find the map in the list */
-  list_for_each(list,&dev->maplist->head)
-    {
-      r_list = (drm_map_list_t *) list;
-
-      if(r_list->map &&
-        r_list->map->offset == cont_mem.phyaddress )
-        break;
-    }
-  /*find none*/
-  if(list == (&dev->maplist->head)) {
-    up(&dev->struct_sem);
-    return -EINVAL;
-  }
-  map = r_list->map;
-  list_del(list);
-  savage_free)(list, sizeof(*list), DRM_MEM_MAPS);
-
-  /*unmap the user space */
-#ifdef DO_MUNMAP_4_ARGS
-  if ( do_munmap(current->mm,cont_mem.linear,size,1)!=0)
-#else
-  if ( do_munmap(current->mm,cont_mem.linear,size)!=0)
-#endif
-    return -EFAULT;
-  /*free the page*/
-  free_pages((unsigned long)map->handle, get_order(size));
-
-  return 1;
-}
-#else /* SAVAGE_CMD_DMA */
-#define DRIVER_IOCTLS
-#endif /* SAVAGE_CMD_DMA */
-
-#if 0
-
-#define DRIVER_IOCTLS                                                     \
-       [DRM_IOCTL_NR(DRM_IOCTL_DMA)]         = { mga_dma_buffers, 1, 0 }, \
-       [DRM_IOCTL_NR(DRM_IOCTL_MGA_INIT)]    = { mga_dma_init,    1, 1 }, \
-       [DRM_IOCTL_NR(DRM_IOCTL_MGA_FLUSH)]   = { mga_dma_flush,   1, 0 }, \
-       [DRM_IOCTL_NR(DRM_IOCTL_MGA_RESET)]   = { mga_dma_reset,   1, 0 }, \
-       [DRM_IOCTL_NR(DRM_IOCTL_MGA_SWAP)]    = { mga_dma_swap,    1, 0 }, \
-       [DRM_IOCTL_NR(DRM_IOCTL_MGA_CLEAR)]   = { mga_dma_clear,   1, 0 }, \
-       [DRM_IOCTL_NR(DRM_IOCTL_MGA_VERTEX)]  = { mga_dma_vertex,  1, 0 }, \
-       [DRM_IOCTL_NR(DRM_IOCTL_MGA_INDICES)] = { mga_dma_indices, 1, 0 }, \
-       [DRM_IOCTL_NR(DRM_IOCTL_MGA_ILOAD)]   = { mga_dma_iload,   1, 0 }, \
-       [DRM_IOCTL_NR(DRM_IOCTL_MGA_BLIT)]    = { mga_dma_blit,    1, 0 },
-
-
-#endif /* end #if 0 */
-
 #include "drm_pciids.h"
 
 static int postinit( struct drm_device *dev, unsigned long flags )
@@ -275,24 +62,27 @@ static struct pci_device_id pciidlist[] = {
 };
 
 static drm_ioctl_desc_t ioctls[] = {
-#if SAVAGE_CMD_DMA /* Check the 3D driver, and we need to fix this anyway */
-       [DRM_IOCTL_NR(DRM_SAVAGE_ALLOC_CONTINUOUS_MEM)] = {savage_alloc_continuous_mem, 1, 0},
-       [DRM_IOCTL_NR(DRM_SAVAGE_GET_PHYSICS_ADDRESS)]  = {savage_get_physics_address,  1, 0},
-       [DRM_IOCTL_NR(DRM_SAVAGE_FREE_CONTINUOUS_MEM)]  = {savage_free_cont_mem,        1, 0},
-#endif
+       [DRM_IOCTL_NR(DRM_SAVAGE_BCI_INIT)] = {savage_bci_init, 1, 1},
+       [DRM_IOCTL_NR(DRM_SAVAGE_BCI_CMDBUF)] = {savage_bci_cmdbuf, 1, 0},
+       [DRM_IOCTL_NR(DRM_SAVAGE_BCI_EVENT_EMIT)] = {savage_bci_event_emit, 1, 0},
+       [DRM_IOCTL_NR(DRM_SAVAGE_BCI_EVENT_WAIT)] = {savage_bci_event_wait, 1, 0},
 };
 
 static int probe(struct pci_dev *pdev, const struct pci_device_id *ent);
 static struct drm_driver driver = {
-       .driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR,
-       .reclaim_buffers = drm_core_reclaim_buffers,
-       .get_map_ofs = drm_core_get_map_ofs,
-       .get_reg_ofs = drm_core_get_reg_ofs,
+       .driver_features =
+           DRIVER_USE_AGP | DRIVER_USE_MTRR |
+           DRIVER_HAVE_DMA | DRIVER_PCI_DMA,
+       .dev_priv_size = sizeof(drm_savage_buf_priv_t),
        .preinit = savage_preinit,
        .postinit = postinit,
+       .reclaim_buffers = savage_reclaim_buffers,
+       .get_map_ofs = drm_core_get_map_ofs,
+       .get_reg_ofs = drm_core_get_reg_ofs,
        .version = version,
        .ioctls = ioctls,
        .num_ioctls = DRM_ARRAY_SIZE(ioctls),
+       .dma_ioctl = savage_bci_buffers,
        .fops = {
                .owner   = THIS_MODULE,
                .open    = drm_open,
diff --git a/linux-core/savage_drv.h b/linux-core/savage_drv.h
deleted file mode 100644 (file)
index 69ae869..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
- * Copyright 2001-2003 S3 Graphics, Inc. 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, sub license,
- * 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 NON-INFRINGEMENT. IN NO EVENT SHALL
- * VIA, S3 GRAPHICS, 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.
- */
-#ifndef __SAVAGE_DRV_H__
-#define __SAVAGE_DRV_H__
-
-/* these chip tags should match the ones in the 2D driver in savage_regs.h. */
-enum savage_family {
-       S3_UNKNOWN = 0,
-       S3_SAVAGE3D,
-       S3_SAVAGE_MX,
-       S3_SAVAGE4,
-       S3_PROSAVAGE,
-       S3_TWISTER,
-       S3_PROSAVAGEDDR,
-       S3_SUPERSAVAGE,
-       S3_SAVAGE2000,
-       S3_LAST
-};
-
-extern int savage_preinit(drm_device_t * dev, unsigned long chipset);
-
-#define S3_SAVAGE3D_SERIES(chip)  ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE_MX))
-
-#define S3_SAVAGE4_SERIES(chip)  ((chip==S3_SAVAGE4)            \
-                                  || (chip==S3_PROSAVAGE)       \
-                                  || (chip==S3_TWISTER)         \
-                                  || (chip==S3_PROSAVAGEDDR))
-
-#define        S3_SAVAGE_MOBILE_SERIES(chip)   ((chip==S3_SAVAGE_MX) || (chip==S3_SUPERSAVAGE))
-
-#define S3_SAVAGE_SERIES(chip)    ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE2000))
-
-#define S3_MOBILE_TWISTER_SERIES(chip)   ((chip==S3_TWISTER)    \
-                                          ||(chip==S3_PROSAVAGEDDR))
-
-/* flags */
-#define SAVAGE_IS_AGP 1
-
-typedef struct drm_savage_private {
-       drm_savage_sarea_t *sarea_priv;
-
-       enum savage_family chipset;
-       unsigned flags;
-
-} drm_savage_private_t;
-
-#define SAVAGE_FB_SIZE_S3      0x01000000      /*  16MB */
-#define SAVAGE_FB_SIZE_S4      0x02000000      /*  32MB */
-#define SAVAGE_MMIO_SIZE        0x00080000     /* 512kB */
-#define SAVAGE_APERTURE_OFFSET  0x02000000     /*  32MB */
-#define SAVAGE_APERTURE_SIZE    0x05000000     /* 5 tiled surfaces, 16MB each */
-
-#endif                         /* end #ifndef __SAVAGE_DRV_ */
diff --git a/shared-core/savage_bci.c b/shared-core/savage_bci.c
new file mode 100644 (file)
index 0000000..55129ab
--- /dev/null
@@ -0,0 +1,657 @@
+/* savage_bci.c -- BCI support for Savage
+ *
+ * Copyright 2004  Felix Kuehling
+ * 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, sub license,
+ * 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
+ * NON-INFRINGEMENT. IN NO EVENT SHALL FELIX KUEHLING 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.
+ */
+#include "drmP.h"
+#include "savage_drm.h"
+#include "savage_drv.h"
+
+/* Need a long timeout for shadow status updates can take a while
+ * and so can waiting for events when the queue is full. */
+#define SAVAGE_DEFAULT_USEC_TIMEOUT    1000000 /* 1s */
+#define SAVAGE_EVENT_USEC_TIMEOUT      5000000 /* 5s */
+#define SAVAGE_FREELIST_DEBUG          0
+
+static int
+savage_bci_wait_fifo_shadow(drm_savage_private_t *dev_priv, unsigned int n)
+{
+       uint32_t mask = dev_priv->status_used_mask;
+       uint32_t threshold = dev_priv->bci_threshold_hi;
+       uint32_t status;
+       int i;
+
+#if SAVAGE_BCI_DEBUG
+       if (n > dev_priv->cob_size + SAVAGE_BCI_FIFO_SIZE - threshold)
+               DRM_ERROR("Trying to emit more than guaranteed space in COB");
+#endif
+
+       for (i = 0; i < SAVAGE_DEFAULT_USEC_TIMEOUT; i++) {
+               status = dev_priv->status_ptr[0];
+               if ((status & mask) <= threshold)
+                       return 0;
+               DRM_UDELAY(1);
+       }
+
+#if SAVAGE_BCI_DEBUG
+       DRM_ERROR("failed!\n");
+       DRM_INFO("   status=0x%08x, threshold=0x%08x\n", status, threshold);
+#endif
+       return DRM_ERR(EBUSY);
+}
+
+static int
+savage_bci_wait_fifo_s3d(drm_savage_private_t *dev_priv, unsigned int n)
+{
+       uint32_t maxUsed = dev_priv->cob_size + SAVAGE_BCI_FIFO_SIZE - n;
+       uint32_t status;
+       int i;
+
+       for (i = 0; i < SAVAGE_DEFAULT_USEC_TIMEOUT; i++) {
+               status = SAVAGE_READ(SAVAGE_STATUS_WORD0);
+               if ((status & SAVAGE_FIFO_USED_MASK_S3D) <= maxUsed)
+                       return 0;
+               DRM_UDELAY(1);
+       }
+
+#if SAVAGE_BCI_DEBUG
+       DRM_ERROR("failed!\n");
+       DRM_INFO("   status=0x%08x\n", status);
+#endif
+       return DRM_ERR(EBUSY);
+}
+
+static int
+savage_bci_wait_fifo_s4(drm_savage_private_t *dev_priv, unsigned int n)
+{
+       uint32_t maxUsed = dev_priv->cob_size + SAVAGE_BCI_FIFO_SIZE - n;
+       uint32_t status;
+       int i;
+
+       for (i = 0; i < SAVAGE_DEFAULT_USEC_TIMEOUT; i++) {
+               status = SAVAGE_READ(SAVAGE_ALT_STATUS_WORD0);
+               if ((status & SAVAGE_FIFO_USED_MASK_S4) <= maxUsed)
+                       return 0;
+               DRM_UDELAY(1);
+       }
+
+#if SAVAGE_BCI_DEBUG
+       DRM_ERROR("failed!\n");
+       DRM_INFO("   status=0x%08x\n", status);
+#endif
+       return DRM_ERR(EBUSY);
+}
+
+/*
+ * Waiting for events.
+ *
+ * The BIOSresets the event tag to 0 on mode changes. Therefore we
+ * never emit 0 to the event tag. If we find a 0 event tag we know the
+ * BIOS stomped on it and return success assuming that the BIOS waited
+ * for engine idle.
+ *
+ * Note: if the Xserver uses the event tag it has to follow the same
+ * rule. Otherwise there may be glitches every 2^16 events.
+ */
+static int
+savage_bci_wait_event_shadow(drm_savage_private_t *dev_priv, uint16_t e)
+{
+       uint32_t status;
+       int i;
+
+       for (i = 0; i < SAVAGE_EVENT_USEC_TIMEOUT; i++) {
+               status = dev_priv->status_ptr[1];
+               if ((((status & 0xffff) - e) & 0xffff) <= 0x7fff ||
+                   (status & 0xffff) == 0)
+                       return 0;
+               DRM_UDELAY(1);
+       }
+
+#if SAVAGE_BCI_DEBUG
+       DRM_ERROR("failed!\n");
+       DRM_INFO("   status=0x%08x, e=0x%04x\n", status, e);
+#endif
+
+       return DRM_ERR(EBUSY);
+}
+
+static int
+savage_bci_wait_event_reg(drm_savage_private_t *dev_priv, uint16_t e)
+{
+       uint32_t status;
+       int i;
+
+       for (i = 0; i < SAVAGE_EVENT_USEC_TIMEOUT; i++) {
+               status = SAVAGE_READ(SAVAGE_STATUS_WORD1);
+               if ((((status & 0xffff) - e) & 0xffff) <= 0x7fff ||
+                   (status & 0xffff) == 0)
+                       return 0;
+               DRM_UDELAY(1);
+       }
+
+#if SAVAGE_BCI_DEBUG
+       DRM_ERROR("failed!\n");
+       DRM_INFO("   status=0x%08x, e=0x%04x\n", status, e);
+#endif
+
+       return DRM_ERR(EBUSY);
+}
+
+uint16_t savage_bci_emit_event(drm_savage_private_t *dev_priv,
+                              unsigned int flags)
+{
+       BCI_LOCALS;
+       uint16_t count;
+
+       if (dev_priv->status_ptr) {
+               /* coordinate with Xserver */
+               count = dev_priv->status_ptr[1023];
+               if (count < dev_priv->event_counter)
+                       dev_priv->event_wrap++;
+       } else {
+               count = dev_priv->event_counter;
+       }
+       count = (count + 1) & 0xffff;
+       if (count == 0) {
+               count++; /* See the comment above savage_wait_event_*. */
+               dev_priv->event_wrap++;
+       }
+       dev_priv->event_counter = count;
+       if (dev_priv->status_ptr)
+               dev_priv->status_ptr[1023] = (uint32_t)count;
+
+       if ((flags & (SAVAGE_WAIT_2D | SAVAGE_WAIT_3D))) {
+               unsigned int wait_cmd = BCI_CMD_WAIT;
+               if ((flags & SAVAGE_WAIT_2D))
+                       wait_cmd |= BCI_CMD_WAIT_2D;
+               if ((flags & SAVAGE_WAIT_3D))
+                       wait_cmd |= BCI_CMD_WAIT_3D;
+               BEGIN_BCI(2);
+               BCI_WRITE(wait_cmd);
+       } else {
+               BEGIN_BCI(1);
+       }
+       BCI_WRITE(BCI_CMD_UPDATE_EVENT_TAG | (uint32_t)count);
+
+       return count;
+}
+
+/*
+ * Freelist management
+ */
+static int savage_freelist_init(drm_device_t *dev)
+{
+       drm_savage_private_t *dev_priv = dev->dev_private;
+       drm_device_dma_t *dma = dev->dma;
+       drm_buf_t *buf;
+       drm_savage_buf_priv_t *entry;
+       int i;
+       DRM_DEBUG("count=%d\n", dma->buf_count);
+
+       dev_priv->head.next = &dev_priv->tail;
+       dev_priv->head.prev = NULL;
+       dev_priv->head.buf = NULL;
+
+       dev_priv->tail.next = NULL;
+       dev_priv->tail.prev = &dev_priv->head;
+       dev_priv->tail.buf = NULL;
+
+       for (i = 0; i < dma->buf_count; i++) {
+               buf = dma->buflist[i];
+               entry = buf->dev_private;
+
+               SET_AGE(&entry->age, 0, 0);
+               entry->buf = buf;
+
+               entry->next = dev_priv->head.next;
+               entry->prev = &dev_priv->head;
+               dev_priv->head.next->prev = entry;
+               dev_priv->head.next = entry;
+       }
+
+       return 0;
+}
+
+static drm_buf_t *savage_freelist_get(drm_device_t *dev)
+{
+       drm_savage_private_t *dev_priv = dev->dev_private;
+       drm_savage_buf_priv_t *tail = dev_priv->tail.prev;
+       uint16_t event;
+       unsigned int wrap;
+       DRM_DEBUG("\n");
+
+       UPDATE_EVENT_COUNTER();
+       if (dev_priv->status_ptr)
+               event = dev_priv->status_ptr[1] & 0xffff;
+       else
+               event = SAVAGE_READ(SAVAGE_STATUS_WORD1) & 0xffff;
+       wrap = dev_priv->event_wrap;
+
+       DRM_DEBUG("   tail=0x%04x %d\n", tail->age.event, tail->age.wrap);
+       DRM_DEBUG("   head=0x%04x %d\n", event, wrap);
+
+       if (tail->buf && (TEST_AGE(&tail->age, event, wrap) || event == 0)) {
+               drm_savage_buf_priv_t *next = tail->next;
+               drm_savage_buf_priv_t *prev = tail->prev;
+               prev->next = next;
+               next->prev = prev;
+               tail->next = tail->prev = NULL;
+               return tail->buf;
+       }
+
+       DRM_DEBUG("returning NULL, tail->buf=%p!\n", tail->buf);
+       return NULL;
+}
+
+void savage_freelist_put(drm_device_t *dev, drm_buf_t *buf)
+{
+       drm_savage_private_t *dev_priv = dev->dev_private;
+       drm_savage_buf_priv_t *entry = buf->dev_private, *prev, *next;
+
+       DRM_DEBUG("age=0x%04x wrap=%d\n", entry->age.event, entry->age.wrap);
+
+       if (entry->next != NULL || entry->prev != NULL) {
+               DRM_ERROR("entry already on freelist.\n");
+               return;
+       }
+
+       prev = &dev_priv->head;
+       next = prev->next;
+       prev->next = entry;
+       next->prev = entry;
+       entry->prev = prev;
+       entry->next = next;
+}
+
+int savage_preinit(drm_device_t *dev, unsigned long chipset)
+{
+       drm_savage_private_t *dev_priv;
+       unsigned int mmio_base, fb_base, fb_size, aperture_base;
+       int ret = 0;
+
+       dev_priv = drm_alloc(sizeof(drm_savage_private_t), DRM_MEM_DRIVER);
+       if (dev_priv == NULL)
+               return DRM_ERR(ENOMEM);
+
+       memset(dev_priv, 0, sizeof(drm_savage_private_t));
+       dev->dev_private = (void *)dev_priv;
+       dev_priv->chipset = (enum savage_family)chipset;
+
+       if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
+               fb_base = pci_resource_start(dev->pdev, 0);
+               fb_size = SAVAGE_FB_SIZE_S3;
+               mmio_base = fb_base + SAVAGE_FB_SIZE_S3;
+               aperture_base = fb_base + SAVAGE_APERTURE_OFFSET;
+       } else if (chipset != S3_SUPERSAVAGE && chipset != S3_SAVAGE2000) {
+               mmio_base = pci_resource_start(dev->pdev, 0);
+               fb_base = pci_resource_start(dev->pdev, 1);
+               fb_size = SAVAGE_FB_SIZE_S4;
+               aperture_base = fb_base + SAVAGE_APERTURE_OFFSET;
+       } else {
+               mmio_base = pci_resource_start(dev->pdev, 0);
+               fb_base = pci_resource_start(dev->pdev, 1);
+               fb_size = pci_resource_len(dev->pdev, 1);
+               aperture_base = pci_resource_start(dev->pdev, 2);
+       }
+
+       if ((ret = drm_initmap(dev, mmio_base, SAVAGE_MMIO_SIZE,
+                              _DRM_REGISTERS, 0)))
+               return ret;
+       if (!(dev_priv->mmio = drm_core_findmap (dev, mmio_base)))
+               return DRM_ERR(ENOMEM);
+
+       if ((ret = drm_initmap(dev, fb_base, fb_size,
+                              _DRM_FRAME_BUFFER, _DRM_WRITE_COMBINING)))
+               return ret;
+       if (!(dev_priv->fb = drm_core_findmap (dev, fb_base)))
+               return DRM_ERR(ENOMEM);
+
+       if ((ret = drm_initmap(dev, aperture_base, SAVAGE_APERTURE_SIZE,
+                              _DRM_FRAME_BUFFER, _DRM_WRITE_COMBINING)))
+               return ret;
+       if (!(dev_priv->aperture = drm_core_findmap (dev, aperture_base)))
+               return DRM_ERR(ENOMEM);
+
+       return ret;
+}
+
+static int savage_do_init_bci(drm_device_t *dev, drm_savage_init_t *init)
+{
+       drm_savage_private_t *dev_priv = dev->dev_private;
+
+       if (init->fb_bpp != 16 && init->fb_bpp != 32) {
+               DRM_ERROR("invalid frame buffer bpp %d!\n", init->fb_bpp);
+               return DRM_ERR(EINVAL);
+       }
+       if (init->depth_bpp != 16 && init->depth_bpp != 32) {
+               DRM_ERROR("invalid depth buffer bpp %d!\n", init->fb_bpp);
+               return DRM_ERR(EINVAL);
+       }
+       if (init->dma_type != SAVAGE_DMA_AGP &&
+           init->dma_type != SAVAGE_DMA_PCI) {
+               DRM_ERROR("invalid dma memory type %d!\n", init->dma_type);
+               return DRM_ERR(EINVAL);
+       }
+
+       dev_priv->cob_size = init->cob_size;
+       dev_priv->bci_threshold_lo = init->bci_threshold_lo;
+       dev_priv->bci_threshold_hi = init->bci_threshold_hi;
+       dev_priv->dma_type = init->dma_type;
+
+       dev_priv->fb_bpp = init->fb_bpp;
+       dev_priv->front_offset = init->front_offset;
+       dev_priv->front_pitch = init->front_pitch;
+       dev_priv->back_offset = init->back_offset;
+       dev_priv->back_pitch = init->back_pitch;
+       dev_priv->depth_bpp = init->depth_bpp;
+       dev_priv->depth_offset = init->depth_offset;
+       dev_priv->depth_pitch = init->depth_pitch;
+
+       dev_priv->texture_offset = init->texture_offset;
+       dev_priv->texture_size = init->texture_size;
+
+       DRM_GETSAREA();
+       if (!dev_priv->sarea) {
+               DRM_ERROR("could not find sarea!\n");
+               savage_do_cleanup_bci(dev);
+               return DRM_ERR(EINVAL);
+       }
+       if (init->status_offset != 0) {
+               dev_priv->status = drm_core_findmap(dev, init->status_offset);
+               if (!dev_priv->status) {
+                       DRM_ERROR("could not find shadow status region!\n");
+                       savage_do_cleanup_bci(dev);
+                       return DRM_ERR(EINVAL);
+               }
+       } else {
+               dev_priv->status = NULL;
+       }
+       dev->agp_buffer_map = drm_core_findmap(dev, init->buffers_offset);
+       if (!dev->agp_buffer_map) {
+               DRM_ERROR("could not find dma buffer region!\n");
+               savage_do_cleanup_bci(dev);
+               return DRM_ERR(EINVAL);
+       }
+       if (init->agp_textures_offset) {
+               dev_priv->agp_textures =
+                       drm_core_findmap(dev, init->agp_textures_offset);
+               if (!dev_priv->agp_textures) {
+                       DRM_ERROR("could not find agp texture region!\n");
+                       savage_do_cleanup_bci(dev);
+                       return DRM_ERR(EINVAL);
+               }
+       } else {
+               dev_priv->agp_textures = NULL;
+       }
+       if (0 && !S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
+               /* command DMA not implemented yet */
+               dev_priv->cmd_dma = drm_core_findmap(dev, init->cmd_dma_offset);
+               if (!dev_priv->cmd_dma) {
+                       DRM_ERROR("could not find command DMA region!\n");
+                       savage_do_cleanup_bci(dev);
+                       return DRM_ERR(EINVAL);
+               }
+       } else {
+               dev_priv->cmd_dma = NULL;
+       }
+
+       if (dev_priv->cmd_dma && dev_priv->dma_type == SAVAGE_DMA_AGP) {
+               drm_core_ioremap(dev_priv->cmd_dma, dev);
+               if (!dev_priv->cmd_dma->handle) {
+                       DRM_ERROR("failed to ioremap command DMA region!\n");
+                       savage_do_cleanup_bci(dev);
+                       return DRM_ERR(ENOMEM);
+               }
+       }
+
+       dev_priv->sarea_priv =
+               (drm_savage_sarea_t *)((uint8_t *)dev_priv->sarea->handle +
+                                      init->sarea_priv_offset);
+
+       /* setup bitmap descriptors */
+       {
+               unsigned int color_tile_format;
+               unsigned int depth_tile_format;
+               unsigned int front_stride, back_stride, depth_stride;
+               if (dev_priv->chipset <= S3_SAVAGE4) {
+                       color_tile_format = dev_priv->fb_bpp == 16 ?
+                               SAVAGE_BD_TILE_16BPP : SAVAGE_BD_TILE_32BPP;
+                       depth_tile_format = dev_priv->depth_bpp == 16 ?
+                               SAVAGE_BD_TILE_16BPP : SAVAGE_BD_TILE_32BPP;
+               } else {
+                       color_tile_format = SAVAGE_BD_TILE_DEST;
+                       depth_tile_format = SAVAGE_BD_TILE_DEST;
+               }
+               front_stride = dev_priv->front_pitch / (dev_priv->fb_bpp/8);
+               back_stride  = dev_priv-> back_pitch / (dev_priv->fb_bpp/8);
+               depth_stride = dev_priv->depth_pitch / (dev_priv->depth_bpp/8);
+
+               dev_priv->front_bd = front_stride | SAVAGE_BD_BW_DISABLE |
+                       (dev_priv->fb_bpp << SAVAGE_BD_BPP_SHIFT) |
+                       (color_tile_format << SAVAGE_BD_TILE_SHIFT);
+
+               dev_priv-> back_bd =  back_stride | SAVAGE_BD_BW_DISABLE |
+                       (dev_priv->fb_bpp << SAVAGE_BD_BPP_SHIFT) |
+                       (color_tile_format << SAVAGE_BD_TILE_SHIFT);
+
+               dev_priv->depth_bd = depth_stride | SAVAGE_BD_BW_DISABLE |
+                       (dev_priv->depth_bpp << SAVAGE_BD_BPP_SHIFT) |
+                       (depth_tile_format << SAVAGE_BD_TILE_SHIFT);
+       }
+
+       /* setup status and bci ptr */
+       dev_priv->event_counter = 0;
+       dev_priv->event_wrap = 0;
+       dev_priv->bci_ptr = (volatile uint32_t *)
+           ((uint8_t *)dev_priv->mmio->handle + SAVAGE_BCI_OFFSET);
+       if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
+               dev_priv->status_used_mask = SAVAGE_FIFO_USED_MASK_S3D;
+       } else {
+               dev_priv->status_used_mask = SAVAGE_FIFO_USED_MASK_S4;
+       }
+       if (dev_priv->status != NULL) {
+               dev_priv->status_ptr =
+                       (volatile uint32_t *)dev_priv->status->handle;
+               dev_priv->wait_fifo = savage_bci_wait_fifo_shadow;
+               dev_priv->wait_evnt = savage_bci_wait_event_shadow;
+               dev_priv->status_ptr[1023] = dev_priv->event_counter;
+       } else {
+               dev_priv->status_ptr = NULL;
+               if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
+                       dev_priv->wait_fifo = savage_bci_wait_fifo_s3d;
+               } else {
+                       dev_priv->wait_fifo = savage_bci_wait_fifo_s4;
+               }
+               dev_priv->wait_evnt = savage_bci_wait_event_reg;
+       }
+
+       if (savage_freelist_init(dev) < 0) {
+               DRM_ERROR("could not initialize freelist\n");
+               savage_do_cleanup_bci(dev);
+               return DRM_ERR(ENOMEM);
+       }
+
+       return 0;
+}
+
+int savage_do_cleanup_bci(drm_device_t *dev)
+{
+       drm_savage_private_t *dev_priv = dev->dev_private;
+
+       if (dev_priv->cmd_dma && dev_priv->dma_type == SAVAGE_DMA_AGP)
+               drm_core_ioremapfree(dev_priv->cmd_dma, dev);
+
+       return 0;
+}
+
+int savage_bci_init(DRM_IOCTL_ARGS)
+{
+       DRM_DEVICE;
+       drm_savage_init_t init;
+
+       LOCK_TEST_WITH_RETURN(dev, filp);
+
+       DRM_COPY_FROM_USER_IOCTL(init, (drm_savage_init_t __user *)data,
+                                sizeof(init));
+
+       switch (init.func) {
+       case SAVAGE_INIT_BCI:
+               return savage_do_init_bci(dev, &init);
+       case SAVAGE_CLEANUP_BCI:
+               return savage_do_cleanup_bci(dev);
+       }
+
+       return DRM_ERR(EINVAL);
+}
+
+int savage_bci_event_emit(DRM_IOCTL_ARGS)
+{
+       DRM_DEVICE;
+       drm_savage_private_t *dev_priv = dev->dev_private;
+       drm_savage_event_emit_t event;
+
+       DRM_DEBUG("\n");
+
+       LOCK_TEST_WITH_RETURN(dev, filp);
+
+       DRM_COPY_FROM_USER_IOCTL(event, (drm_savage_event_emit_t __user *)data,
+                                sizeof(event));
+
+       event.count = savage_bci_emit_event(dev_priv, event.flags);
+       DRM_COPY_TO_USER_IOCTL(&((drm_savage_event_emit_t __user *)data)->count,
+                              event.count, sizeof(event.count));
+       return 0;
+}
+
+int savage_bci_event_wait(DRM_IOCTL_ARGS)
+{
+       DRM_DEVICE;
+       drm_savage_private_t *dev_priv = dev->dev_private;
+       drm_savage_event_wait_t event;
+
+       DRM_DEBUG("\n");
+
+       DRM_COPY_FROM_USER_IOCTL(event, (drm_savage_event_wait_t __user *)data,
+                                sizeof(event));
+
+       if (event.count > 0xffff)
+               return DRM_ERR(EINVAL);
+
+       return dev_priv->wait_evnt(dev_priv, event.count);
+}
+
+/*
+ * DMA buffer management
+ */
+
+static int savage_bci_get_buffers(DRMFILE filp, drm_device_t *dev, drm_dma_t *d)
+{
+       drm_buf_t *buf;
+       int i;
+
+       for (i = d->granted_count; i < d->request_count; i++) {
+               buf = savage_freelist_get(dev);
+               if (!buf)
+                       return DRM_ERR(EAGAIN);
+
+               buf->filp = filp;
+
+               if (DRM_COPY_TO_USER(&d->request_indices[i],
+                                    &buf->idx, sizeof(buf->idx)))
+                       return DRM_ERR(EFAULT);
+               if (DRM_COPY_TO_USER(&d->request_sizes[i],
+                                    &buf->total, sizeof(buf->total)))
+                       return DRM_ERR(EFAULT);
+
+               d->granted_count++;
+       }
+       return 0;
+}
+
+int savage_bci_buffers(DRM_IOCTL_ARGS)
+{
+       DRM_DEVICE;
+       drm_device_dma_t *dma = dev->dma;
+       drm_dma_t d;
+       int ret = 0;
+
+       LOCK_TEST_WITH_RETURN(dev, filp);
+
+       DRM_COPY_FROM_USER_IOCTL(d, (drm_dma_t __user *)data, sizeof(d));
+
+       /* Please don't send us buffers.
+        */
+       if (d.send_count != 0) {
+               DRM_ERROR("Process %d trying to send %d buffers via drmDMA\n",
+                         DRM_CURRENTPID, d.send_count);
+               return DRM_ERR(EINVAL);
+       }
+
+       /* We'll send you buffers.
+        */
+       if (d.request_count < 0 || d.request_count > dma->buf_count) {
+               DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n",
+                         DRM_CURRENTPID, d.request_count, dma->buf_count);
+               return DRM_ERR(EINVAL);
+       }
+
+       d.granted_count = 0;
+
+       if (d.request_count) {
+               ret = savage_bci_get_buffers(filp, dev, &d);
+       }
+
+       DRM_COPY_TO_USER_IOCTL((drm_dma_t __user *)data, d, sizeof(d));
+
+       return ret;
+}
+
+void savage_reclaim_buffers(drm_device_t *dev, DRMFILE filp) {
+       drm_device_dma_t *dma = dev->dma;
+       drm_savage_private_t *dev_priv = dev->dev_private;
+       int i;
+
+       if (!dma)
+               return;
+       if (!dev_priv)
+               return;
+       if (!dma->buflist)
+               return;
+
+       /*i830_flush_queue(dev);*/
+
+       for (i = 0; i < dma->buf_count; i++) {
+               drm_buf_t *buf = dma->buflist[i];
+               drm_savage_buf_priv_t *buf_priv = buf->dev_private;
+
+               if (buf->filp == filp && buf_priv &&
+                   buf_priv->next == NULL && buf_priv->prev == NULL) {
+                       uint16_t event;
+                       DRM_DEBUG("reclaimed from client\n");
+                       event = savage_bci_emit_event(dev_priv, SAVAGE_WAIT_3D);
+                       SET_AGE(&buf_priv->age, event, dev_priv->event_wrap);
+                       savage_freelist_put(dev, buf);
+               }
+       }
+
+       drm_core_reclaim_buffers(dev, filp);
+}
diff --git a/shared-core/savage_drm.h b/shared-core/savage_drm.h
new file mode 100644 (file)
index 0000000..6526c9a
--- /dev/null
@@ -0,0 +1,209 @@
+/* savage_drm.h -- Public header for the savage driver
+ *
+ * Copyright 2004  Felix Kuehling
+ * 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, sub license,
+ * 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
+ * NON-INFRINGEMENT. IN NO EVENT SHALL FELIX KUEHLING 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.
+ */
+
+#ifndef __SAVAGE_DRM_H__
+#define __SAVAGE_DRM_H__
+
+#ifndef __SAVAGE_SAREA_DEFINES__
+#define __SAVAGE_SAREA_DEFINES__
+
+/* 2 heaps (1 for card, 1 for agp), each divided into upto 128
+ * regions, subject to a minimum region size of (1<<16) == 64k.
+ *
+ * Clients may subdivide regions internally, but when sharing between
+ * clients, the region size is the minimum granularity.
+ */
+
+#define SAVAGE_CARD_HEAP               0
+#define SAVAGE_AGP_HEAP                        1
+#define SAVAGE_NR_TEX_HEAPS            2
+#define SAVAGE_NR_TEX_REGIONS          16
+#define SAVAGE_LOG_MIN_TEX_REGION_SIZE 16
+
+#endif /* __SAVAGE_SAREA_DEFINES__ */
+
+typedef struct _drm_savage_sarea {
+       /* LRU lists for texture memory in agp space and on the card.
+        */
+       drm_tex_region_t texList[SAVAGE_NR_TEX_HEAPS][SAVAGE_NR_TEX_REGIONS+1];
+       unsigned int texAge[SAVAGE_NR_TEX_HEAPS];
+
+       /* Mechanism to validate card state.
+        */
+       int ctxOwner;
+} drm_savage_sarea_t, *drm_savage_sarea_ptr;
+
+/* Savage-specific ioctls
+ */
+#define DRM_SAVAGE_BCI_INIT            0x00
+#define DRM_SAVAGE_BCI_CMDBUF           0x01
+#define DRM_SAVAGE_BCI_EVENT_EMIT      0x02
+#define DRM_SAVAGE_BCI_EVENT_WAIT      0x03
+
+#define DRM_IOCTL_SAVAGE_INIT          DRM_IOW( DRM_COMMAND_BASE + DRM_SAVAGE_BCI_INIT, drm_savage_init_t)
+#define DRM_IOCTL_SAVAGE_CMDBUF                DRM_IOW( DRM_COMMAND_BASE + DRM_SAVAGE_BCI_CMDBUF, drm_savage_cmdbuf_t)
+#define DRM_IOCTL_SAVAGE_EVENT_EMIT    DRM_IOWR(DRM_COMMAND_BASE + DRM_SAVAGE_BCI_EVENT_EMIT, drm_savage_event_emit_t)
+#define DRM_IOCTL_SAVAGE_EVENT_WAIT    DRM_IOW( DRM_COMMAND_BASE + DRM_SAVAGE_BCI_EVENT_WAIT, drm_savage_event_wait_t)
+
+#define SAVAGE_DMA_PCI 1
+#define SAVAGE_DMA_AGP 3
+typedef struct drm_savage_init {
+       enum {
+               SAVAGE_INIT_BCI = 1,
+               SAVAGE_CLEANUP_BCI = 2
+       } func;
+       unsigned int sarea_priv_offset;
+
+       /* some parameters */
+       unsigned int cob_size;
+       unsigned int bci_threshold_lo, bci_threshold_hi;
+       unsigned int dma_type;
+
+       /* frame buffer layout */
+       unsigned int fb_bpp;
+       unsigned int front_offset, front_pitch;
+       unsigned int back_offset, back_pitch;
+       unsigned int depth_bpp;
+       unsigned int depth_offset, depth_pitch;
+
+       /* local textures */
+       unsigned int texture_offset;
+       unsigned int texture_size;
+
+       /* physical locations of non-permanent maps */
+       unsigned long status_offset;
+       unsigned long buffers_offset;
+       unsigned long agp_textures_offset;
+       unsigned long cmd_dma_offset;
+} drm_savage_init_t;
+
+typedef union drm_savage_cmd_header drm_savage_cmd_header_t;
+typedef struct drm_savage_cmdbuf {
+                               /* command buffer in client's address space */
+       drm_savage_cmd_header_t __user *cmd_addr;
+       unsigned int size;      /* size of the command buffer in 64bit units */
+
+       unsigned int dma_idx;   /* DMA buffer index to use */
+       int discard;            /* discard DMA buffer when done */
+                               /* vertex buffer in client's address space */
+       unsigned int __user *vb_addr;
+       unsigned int vb_size;   /* size of client vertex buffer in bytes */
+       unsigned int vb_stride; /* stride of vertices in 32bit words */
+                               /* boxes in client's address space */
+       drm_clip_rect_t __user *box_addr;
+       unsigned int nbox;      /* number of clipping boxes */
+} drm_savage_cmdbuf_t;
+
+#define SAVAGE_WAIT_2D  0x1 /* wait for 2D idle before updating event tag */
+#define SAVAGE_WAIT_3D  0x2 /* wait for 3D idle before updating event tag */
+#define SAVAGE_WAIT_IRQ 0x4 /* emit or wait for IRQ, not implemented yet */
+typedef struct drm_savage_event {
+       unsigned int count;
+       unsigned int flags;
+} drm_savage_event_emit_t, drm_savage_event_wait_t;
+
+/* Commands for the cmdbuf ioctl
+ */
+#define SAVAGE_CMD_STATE       0  /* a range of state registers */
+#define SAVAGE_CMD_DMA_PRIM    1  /* vertices from DMA buffer */
+#define SAVAGE_CMD_VB_PRIM     2  /* vertices from client vertex buffer */
+#define SAVAGE_CMD_DMA_IDX     3  /* indexed vertices from DMA buffer */
+#define SAVAGE_CMD_VB_IDX      4  /* indexed vertices client vertex buffer */
+#define SAVAGE_CMD_CLEAR       5  /* clear buffers */
+#define SAVAGE_CMD_SWAP                6  /* swap buffers */
+
+/* Primitive types
+*/
+#define SAVAGE_PRIM_TRILIST    0  /* triangle list */
+#define SAVAGE_PRIM_TRISTRIP   1  /* triangle strip */
+#define SAVAGE_PRIM_TRIFAN     2  /* triangle fan */
+#define SAVAGE_PRIM_TRILIST_201        3  /* reorder verts for correct flat
+                                   * shading on s3d */
+
+/* Skip flags (vertex format)
+ */
+#define SAVAGE_SKIP_Z          0x01
+#define SAVAGE_SKIP_W          0x02
+#define SAVAGE_SKIP_C0         0x04
+#define SAVAGE_SKIP_C1         0x08
+#define SAVAGE_SKIP_S0         0x10
+#define SAVAGE_SKIP_T0         0x20
+#define SAVAGE_SKIP_ST0                0x30
+#define SAVAGE_SKIP_S1         0x40
+#define SAVAGE_SKIP_T1         0x80
+#define SAVAGE_SKIP_ST1                0xc0
+#define SAVAGE_SKIP_ALL_S3D    0x3f
+#define SAVAGE_SKIP_ALL_S4     0xff
+
+/* Buffer names for clear command
+ */
+#define SAVAGE_FRONT           0x1
+#define SAVAGE_BACK            0x2
+#define SAVAGE_DEPTH           0x4
+
+/* 64-bit command header
+ */
+union drm_savage_cmd_header {
+       struct {
+               unsigned char cmd;      /* command */
+               unsigned char pad0;
+               unsigned short pad1;
+               unsigned short pad2;
+               unsigned short pad3;
+       } cmd; /* generic */
+       struct {
+               unsigned char cmd;
+               unsigned char global;   /* need idle engine? */
+               unsigned short count;   /* number of consecutive registers */
+               unsigned short start;   /* first register */
+               unsigned short pad3;
+       } state; /* SAVAGE_CMD_STATE */
+       struct {
+               unsigned char cmd;
+               unsigned char prim;     /* primitive type */
+               unsigned short skip;    /* vertex format (skip flags) */
+               unsigned short count;   /* number of vertices */
+               unsigned short start;   /* first vertex in DMA/vertex buffer */
+       } prim; /* SAVAGE_CMD_DMA_PRIM, SAVAGE_CMD_VB_PRIM */
+       struct {
+               unsigned char cmd;
+               unsigned char prim;
+               unsigned short skip;
+               unsigned short count;   /* number of indices that follow */
+               unsigned short pad3;
+       } idx; /* SAVAGE_CMD_DMA_IDX, SAVAGE_CMD_VB_IDX */
+       struct {
+               unsigned char cmd;
+               unsigned char pad0;
+               unsigned short pad1;
+               unsigned int flags;
+       } clear0; /* SAVAGE_CMD_CLEAR */
+       struct {
+               unsigned int mask;
+               unsigned int value;
+       } clear1; /* SAVAGE_CMD_CLEAR data */
+};
+
+#endif
diff --git a/shared-core/savage_drv.h b/shared-core/savage_drv.h
new file mode 100644 (file)
index 0000000..f5d732a
--- /dev/null
@@ -0,0 +1,462 @@
+/* savage_drv.h -- Private header for the savage driver
+ *
+ * Copyright 2004  Felix Kuehling
+ * 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, sub license,
+ * 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
+ * NON-INFRINGEMENT. IN NO EVENT SHALL FELIX KUEHLING 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.
+ */
+
+#ifndef __SAVAGE_DRV_H__
+#define __SAVAGE_DRV_H__
+
+#define DRIVER_AUTHOR  "Felix Kuehling"
+
+#define DRIVER_NAME    "savage"
+#define DRIVER_DESC    "Savage3D/MX/IX, Savage4, SuperSavage, Twister, ProSavage[DDR]"
+#define DRIVER_DATE    "20050101"
+
+#define DRIVER_MAJOR           2
+#define DRIVER_MINOR           0
+#define DRIVER_PATCHLEVEL      0
+
+typedef struct drm_savage_age {
+       uint16_t event;
+       unsigned int wrap;
+} drm_savage_age_t;
+
+typedef struct drm_savage_buf_priv {
+       struct drm_savage_buf_priv *next;
+       struct drm_savage_buf_priv *prev;
+       drm_savage_age_t age;
+       drm_buf_t *buf;
+} drm_savage_buf_priv_t;
+
+/* interesting bits of hardware state that are saved in dev_priv */
+typedef union {
+       struct drm_savage_common_state {
+               uint32_t vbaddr;
+       } common;
+       struct {
+               unsigned char pad[sizeof(struct drm_savage_common_state)];
+               uint32_t texctrl;
+       } s3d;
+       struct {
+               unsigned char pad[sizeof(struct drm_savage_common_state)];
+               uint32_t texdescr;
+       } s4;
+} drm_savage_state_t;
+
+/* these chip tags should match the ones in the 2D driver in savage_regs.h. */
+enum savage_family {
+       S3_UNKNOWN = 0,
+       S3_SAVAGE3D,
+       S3_SAVAGE_MX,
+       S3_SAVAGE4,
+       S3_PROSAVAGE,
+       S3_TWISTER,
+       S3_PROSAVAGEDDR,
+       S3_SUPERSAVAGE,
+       S3_SAVAGE2000,
+       S3_LAST
+};
+
+#define S3_SAVAGE3D_SERIES(chip)  ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE_MX))
+
+#define S3_SAVAGE4_SERIES(chip)  ((chip==S3_SAVAGE4)            \
+                                  || (chip==S3_PROSAVAGE)       \
+                                  || (chip==S3_TWISTER)         \
+                                  || (chip==S3_PROSAVAGEDDR))
+
+#define        S3_SAVAGE_MOBILE_SERIES(chip)   ((chip==S3_SAVAGE_MX) || (chip==S3_SUPERSAVAGE))
+
+#define S3_SAVAGE_SERIES(chip)    ((chip>=S3_SAVAGE3D) && (chip<=S3_SAVAGE2000))
+
+#define S3_MOBILE_TWISTER_SERIES(chip)   ((chip==S3_TWISTER)    \
+                                          ||(chip==S3_PROSAVAGEDDR))
+
+/* flags */
+#define SAVAGE_IS_AGP 1
+
+typedef struct drm_savage_private {
+       drm_savage_sarea_t *sarea_priv;
+
+       drm_savage_buf_priv_t head, tail;
+
+       /* who am I? */
+       enum savage_family chipset;
+
+       unsigned int cob_size;
+       unsigned int bci_threshold_lo, bci_threshold_hi;
+       unsigned int dma_type;
+
+       /* frame buffer layout */
+       unsigned int fb_bpp;
+       unsigned int front_offset, front_pitch;
+       unsigned int back_offset, back_pitch;
+       unsigned int depth_bpp;
+       unsigned int depth_offset, depth_pitch;
+
+       /* bitmap descriptors for swap and clear */
+       unsigned int front_bd, back_bd, depth_bd;
+
+       /* local textures */
+       unsigned int texture_offset;
+       unsigned int texture_size;
+
+       /* memory regions in physical memory */
+       drm_local_map_t *sarea;
+       drm_local_map_t *mmio;
+       drm_local_map_t *fb;
+       drm_local_map_t *aperture;
+       drm_local_map_t *status;
+       drm_local_map_t *agp_textures;
+       drm_local_map_t *cmd_dma;
+
+       /* BCI and status-related stuff */
+       volatile uint32_t *status_ptr, *bci_ptr;
+       uint32_t status_used_mask;
+       uint16_t event_counter;
+       unsigned int event_wrap;
+
+       /* saved hw state for global/local check on S3D */
+       uint32_t hw_draw_ctrl, hw_zbuf_ctrl;
+       /* and for scissors (global, so don't emit if not changed) */
+       uint32_t hw_scissors_start, hw_scissors_end;
+
+       drm_savage_state_t state;
+
+       /* config/hardware-dependent function pointers */
+       int (*wait_fifo)(struct drm_savage_private *dev_priv, unsigned int n);
+       int (*wait_evnt)(struct drm_savage_private *dev_priv, uint16_t e);
+       /* Err, there is a macro wait_event in include/linux/wait.h.
+        * Avoid unwanted macro expansion. */
+} drm_savage_private_t;
+
+/* ioctls */
+extern int savage_bci_init(DRM_IOCTL_ARGS);
+extern int savage_bci_cmdbuf(DRM_IOCTL_ARGS);
+extern int savage_bci_event_emit(DRM_IOCTL_ARGS);
+extern int savage_bci_event_wait(DRM_IOCTL_ARGS);
+extern int savage_bci_buffers(DRM_IOCTL_ARGS);
+
+extern uint16_t savage_bci_emit_event(drm_savage_private_t *dev_priv,
+                                     unsigned int flags);
+extern void savage_freelist_put(drm_device_t *dev, drm_buf_t *buf);
+extern int savage_preinit(drm_device_t *dev, unsigned long chipset);
+extern int savage_do_cleanup_bci(drm_device_t *dev);
+extern void savage_reclaim_buffers(drm_device_t *dev, DRMFILE filp);
+
+#define SAVAGE_FB_SIZE_S3      0x01000000      /*  16MB */
+#define SAVAGE_FB_SIZE_S4      0x02000000      /*  32MB */
+#define SAVAGE_MMIO_SIZE        0x00080000     /* 512kB */
+#define SAVAGE_APERTURE_OFFSET  0x02000000     /*  32MB */
+#define SAVAGE_APERTURE_SIZE    0x05000000     /* 5 tiled surfaces, 16MB each */
+
+#define SAVAGE_BCI_OFFSET       0x00010000      /* offset of the BCI region
+                                                * inside the MMIO region */
+#define SAVAGE_BCI_FIFO_SIZE   32              /* number of entries in on-chip
+                                                * BCI FIFO */
+
+/*
+ * MMIO registers
+ */
+#define SAVAGE_STATUS_WORD0            0x48C00
+#define SAVAGE_STATUS_WORD1            0x48C04
+#define SAVAGE_ALT_STATUS_WORD0        0x48C60
+
+#define SAVAGE_FIFO_USED_MASK_S3D      0x0001ffff
+#define SAVAGE_FIFO_USED_MASK_S4       0x001fffff
+
+/* Copied from savage_bci.h in the 2D driver with some renaming. */
+
+/* Bitmap descriptors */
+#define SAVAGE_BD_STRIDE_SHIFT 0
+#define SAVAGE_BD_BPP_SHIFT   16
+#define SAVAGE_BD_TILE_SHIFT  24
+#define SAVAGE_BD_BW_DISABLE  (1<<28)
+/* common: */
+#define        SAVAGE_BD_TILE_LINEAR           0
+/* savage4, MX, IX, 3D */
+#define        SAVAGE_BD_TILE_16BPP            2
+#define        SAVAGE_BD_TILE_32BPP            3
+/* twister, prosavage, DDR, supersavage, 2000 */
+#define        SAVAGE_BD_TILE_DEST             1
+#define        SAVAGE_BD_TILE_TEXTURE          2
+/* GBD - BCI enable */
+/* savage4, MX, IX, 3D */
+#define SAVAGE_GBD_BCI_ENABLE                    8
+/* twister, prosavage, DDR, supersavage, 2000 */
+#define SAVAGE_GBD_BCI_ENABLE_TWISTER            0
+
+#define SAVAGE_GBD_BIG_ENDIAN                    4
+#define SAVAGE_GBD_LITTLE_ENDIAN                 0
+#define SAVAGE_GBD_64                            1
+
+/*  Global Bitmap Descriptor */
+#define SAVAGE_BCI_GLB_BD_LOW             0x8168
+#define SAVAGE_BCI_GLB_BD_HIGH            0x816C
+
+/*
+ * BCI registers
+ */
+/* Savage4/Twister/ProSavage 3D registers */
+#define SAVAGE_DRAWLOCALCTRL_S4                0x1e
+#define SAVAGE_TEXPALADDR_S4           0x1f
+#define SAVAGE_TEXCTRL0_S4             0x20
+#define SAVAGE_TEXCTRL1_S4             0x21
+#define SAVAGE_TEXADDR0_S4             0x22
+#define SAVAGE_TEXADDR1_S4             0x23
+#define SAVAGE_TEXBLEND0_S4            0x24
+#define SAVAGE_TEXBLEND1_S4            0x25
+#define SAVAGE_TEXXPRCLR_S4            0x26 /* never used */
+#define SAVAGE_TEXDESCR_S4             0x27
+#define SAVAGE_FOGTABLE_S4             0x28
+#define SAVAGE_FOGCTRL_S4              0x30
+#define SAVAGE_STENCILCTRL_S4          0x31
+#define SAVAGE_ZBUFCTRL_S4             0x32
+#define SAVAGE_ZBUFOFF_S4              0x33
+#define SAVAGE_DESTCTRL_S4             0x34
+#define SAVAGE_DRAWCTRLGLOBAL0_S4      0x35
+#define SAVAGE_DRAWCTRLGLOBAL1_S4      0x36
+#define SAVAGE_ZWATERMARK_S4           0x37
+#define SAVAGE_DESTTEXRWWATERMARK_S4   0x38
+#define SAVAGE_TEXBLENDCOLOR_S4                0x39
+/* Savage3D/MX/IX 3D registers */
+#define SAVAGE_TEXPALADDR_S3D          0x18
+#define SAVAGE_TEXXPRCLR_S3D           0x19 /* never used */
+#define SAVAGE_TEXADDR_S3D             0x1A
+#define SAVAGE_TEXDESCR_S3D            0x1B
+#define SAVAGE_TEXCTRL_S3D             0x1C
+#define SAVAGE_FOGTABLE_S3D            0x20
+#define SAVAGE_FOGCTRL_S3D             0x30
+#define SAVAGE_DRAWCTRL_S3D            0x31
+#define SAVAGE_ZBUFCTRL_S3D            0x32
+#define SAVAGE_ZBUFOFF_S3D             0x33
+#define SAVAGE_DESTCTRL_S3D            0x34
+#define SAVAGE_SCSTART_S3D             0x35
+#define SAVAGE_SCEND_S3D               0x36
+#define SAVAGE_ZWATERMARK_S3D          0x37 
+#define SAVAGE_DESTTEXRWWATERMARK_S3D  0x38
+/* common stuff */
+#define SAVAGE_VERTBUFADDR             0x3e
+#define SAVAGE_BITPLANEWTMASK          0xd7
+
+/* texture enable bits (needed for tex addr checking) */
+#define SAVAGE_TEXCTRL_TEXEN_MASK      0x00010000 /* S3D */
+#define SAVAGE_TEXDESCR_TEX0EN_MASK    0x02000000 /* S4 */
+#define SAVAGE_TEXDESCR_TEX1EN_MASK    0x04000000 /* S4 */
+
+/* Global fields in Savage4/Twister/ProSavage 3D registers:
+ *
+ * All texture registers and DrawLocalCtrl are local. All other
+ * registers are global. */
+
+/* Global fields in Savage3D/MX/IX 3D registers:
+ *
+ * All texture registers are local. DrawCtrl and ZBufCtrl are
+ * partially local. All other registers are global.
+ *
+ * DrawCtrl global fields: cullMode, alphaTestCmpFunc, alphaTestEn, alphaRefVal
+ * ZBufCtrl global fields: zCmpFunc, zBufEn
+ */
+#define SAVAGE_DRAWCTRL_S3D_GLOBAL     0x03f3c00c
+#define SAVAGE_ZBUFCTRL_S3D_GLOBAL     0x00000027
+
+/*
+ * BCI commands
+ */
+#define BCI_CMD_NOP                  0x40000000
+#define BCI_CMD_RECT                 0x48000000
+#define BCI_CMD_RECT_XP              0x01000000
+#define BCI_CMD_RECT_YP              0x02000000
+#define BCI_CMD_SCANLINE             0x50000000
+#define BCI_CMD_LINE                 0x5C000000
+#define BCI_CMD_LINE_LAST_PIXEL      0x58000000
+#define BCI_CMD_BYTE_TEXT            0x63000000
+#define BCI_CMD_NT_BYTE_TEXT         0x67000000
+#define BCI_CMD_BIT_TEXT             0x6C000000
+#define BCI_CMD_GET_ROP(cmd)         (((cmd) >> 16) & 0xFF)
+#define BCI_CMD_SET_ROP(cmd, rop)    ((cmd) |= ((rop & 0xFF) << 16))
+#define BCI_CMD_SEND_COLOR           0x00008000
+
+#define BCI_CMD_CLIP_NONE            0x00000000
+#define BCI_CMD_CLIP_CURRENT         0x00002000
+#define BCI_CMD_CLIP_LR              0x00004000
+#define BCI_CMD_CLIP_NEW             0x00006000
+
+#define BCI_CMD_DEST_GBD             0x00000000
+#define BCI_CMD_DEST_PBD             0x00000800
+#define BCI_CMD_DEST_PBD_NEW         0x00000C00
+#define BCI_CMD_DEST_SBD             0x00001000
+#define BCI_CMD_DEST_SBD_NEW         0x00001400
+
+#define BCI_CMD_SRC_TRANSPARENT      0x00000200
+#define BCI_CMD_SRC_SOLID            0x00000000
+#define BCI_CMD_SRC_GBD              0x00000020
+#define BCI_CMD_SRC_COLOR            0x00000040
+#define BCI_CMD_SRC_MONO             0x00000060
+#define BCI_CMD_SRC_PBD_COLOR        0x00000080
+#define BCI_CMD_SRC_PBD_MONO         0x000000A0
+#define BCI_CMD_SRC_PBD_COLOR_NEW    0x000000C0
+#define BCI_CMD_SRC_PBD_MONO_NEW     0x000000E0
+#define BCI_CMD_SRC_SBD_COLOR        0x00000100
+#define BCI_CMD_SRC_SBD_MONO         0x00000120
+#define BCI_CMD_SRC_SBD_COLOR_NEW    0x00000140
+#define BCI_CMD_SRC_SBD_MONO_NEW     0x00000160
+
+#define BCI_CMD_PAT_TRANSPARENT      0x00000010
+#define BCI_CMD_PAT_NONE             0x00000000
+#define BCI_CMD_PAT_COLOR            0x00000002
+#define BCI_CMD_PAT_MONO             0x00000003
+#define BCI_CMD_PAT_PBD_COLOR        0x00000004
+#define BCI_CMD_PAT_PBD_MONO         0x00000005
+#define BCI_CMD_PAT_PBD_COLOR_NEW    0x00000006
+#define BCI_CMD_PAT_PBD_MONO_NEW     0x00000007
+#define BCI_CMD_PAT_SBD_COLOR        0x00000008
+#define BCI_CMD_PAT_SBD_MONO         0x00000009
+#define BCI_CMD_PAT_SBD_COLOR_NEW    0x0000000A
+#define BCI_CMD_PAT_SBD_MONO_NEW     0x0000000B
+
+#define BCI_BD_BW_DISABLE            0x10000000
+#define BCI_BD_TILE_MASK             0x03000000
+#define BCI_BD_TILE_NONE             0x00000000
+#define BCI_BD_TILE_16               0x02000000
+#define BCI_BD_TILE_32               0x03000000
+#define BCI_BD_GET_BPP(bd)           (((bd) >> 16) & 0xFF)
+#define BCI_BD_SET_BPP(bd, bpp)      ((bd) |= (((bpp) & 0xFF) << 16))
+#define BCI_BD_GET_STRIDE(bd)        ((bd) & 0xFFFF)
+#define BCI_BD_SET_STRIDE(bd, st)    ((bd) |= ((st) & 0xFFFF))
+
+#define BCI_CMD_SET_REGISTER            0x96000000
+
+#define BCI_CMD_WAIT                    0xC0000000
+#define BCI_CMD_WAIT_3D                 0x00010000
+#define BCI_CMD_WAIT_2D                 0x00020000
+
+#define BCI_CMD_UPDATE_EVENT_TAG        0x98000000
+
+#define BCI_CMD_DRAW_PRIM               0x80000000
+#define BCI_CMD_DRAW_INDEXED_PRIM       0x88000000
+#define BCI_CMD_DRAW_CONT               0x01000000
+#define BCI_CMD_DRAW_TRILIST            0x00000000
+#define BCI_CMD_DRAW_TRISTRIP           0x02000000
+#define BCI_CMD_DRAW_TRIFAN             0x04000000
+#define BCI_CMD_DRAW_SKIPFLAGS          0x000000ff
+#define BCI_CMD_DRAW_NO_Z              0x00000001
+#define BCI_CMD_DRAW_NO_W              0x00000002
+#define BCI_CMD_DRAW_NO_CD             0x00000004
+#define BCI_CMD_DRAW_NO_CS             0x00000008
+#define BCI_CMD_DRAW_NO_U0             0x00000010
+#define BCI_CMD_DRAW_NO_V0             0x00000020
+#define BCI_CMD_DRAW_NO_UV0            0x00000030
+#define BCI_CMD_DRAW_NO_U1             0x00000040
+#define BCI_CMD_DRAW_NO_V1             0x00000080
+#define BCI_CMD_DRAW_NO_UV1            0x000000c0
+
+#define BCI_W_H(w, h)                ((((h) << 16) | (w)) & 0x0FFF0FFF)
+#define BCI_X_Y(x, y)                ((((y) << 16) | (x)) & 0x0FFF0FFF)
+#define BCI_X_W(x, y)                ((((w) << 16) | (x)) & 0x0FFF0FFF)
+#define BCI_CLIP_LR(l, r)            ((((r) << 16) | (l)) & 0x0FFF0FFF)
+#define BCI_CLIP_TL(t, l)            ((((t) << 16) | (l)) & 0x0FFF0FFF)
+#define BCI_CLIP_BR(b, r)            ((((b) << 16) | (r)) & 0x0FFF0FFF)
+
+#define BCI_LINE_X_Y(x, y)           (((y) << 16) | ((x) & 0xFFFF))
+#define BCI_LINE_STEPS(diag, axi)    (((axi) << 16) | ((diag) & 0xFFFF))
+#define BCI_LINE_MISC(maj, ym, xp, yp, err) \
+       (((maj) & 0x1FFF) | \
+       ((ym) ? 1<<13 : 0) | \
+       ((xp) ? 1<<14 : 0) | \
+       ((yp) ? 1<<15 : 0) | \
+       ((err) << 16))
+
+/*
+ * common commands
+ */
+#define BCI_SET_REGISTERS( first, n )                  \
+       BCI_WRITE(BCI_CMD_SET_REGISTER |                \
+                 ((uint32_t)(n) & 0xff) << 16 |        \
+                 ((uint32_t)(first) & 0xffff))
+
+#define BCI_DRAW_PRIMITIVE(n, type, skip)         \
+        BCI_WRITE(BCI_CMD_DRAW_PRIM | (type) | (skip) | \
+                 ((n) << 16))
+
+#define BCI_DRAW_INDICES_S3D(n, type, i0)         \
+        BCI_WRITE(BCI_CMD_DRAW_INDEXED_PRIM | (type) |  \
+                 ((n) << 16) | (i0))
+
+#define BCI_DRAW_INDICES_S4(n, type, skip)        \
+        BCI_WRITE(BCI_CMD_DRAW_INDEXED_PRIM | (type) |  \
+                  (skip) | ((n) << 16))
+
+/*
+ * access to MMIO
+ */
+#define SAVAGE_READ(reg)       DRM_READ32(  dev_priv->mmio, (reg) )
+#define SAVAGE_WRITE(reg)      DRM_WRITE32( dev_priv->mmio, (reg) )
+
+/*
+ * access to the burst command interface (BCI)
+ */
+#define SAVAGE_BCI_DEBUG 1
+
+#define BCI_LOCALS    volatile uint32_t *bci_ptr;
+
+#define BEGIN_BCI( n ) do {                    \
+       dev_priv->wait_fifo(dev_priv, (n));     \
+       bci_ptr = dev_priv->bci_ptr;            \
+} while(0)
+
+#define BCI_WRITE( val ) *bci_ptr++ = (uint32_t)(val)
+
+#define BCI_COPY_FROM_USER(src,n) do {                         \
+    unsigned int i;                                            \
+    for (i = 0; i < n; ++i) {                                  \
+       uint32_t val;                                           \
+       DRM_GET_USER_UNCHECKED(val, &((uint32_t*)(src))[i]);    \
+       BCI_WRITE(val);                                         \
+    }                                                          \
+} while(0)
+
+/* Buffer aging via event tag
+ */
+
+#define UPDATE_EVENT_COUNTER( ) do {                   \
+       if (dev_priv->status_ptr) {                     \
+               uint16_t count;                         \
+               /* coordinate with Xserver */           \
+               count = dev_priv->status_ptr[1023];     \
+               if (count < dev_priv->event_counter)    \
+                       dev_priv->event_wrap++;         \
+               dev_priv->event_counter = count;        \
+       }                                               \
+} while(0)
+
+#define SET_AGE( age, e, w ) do {      \
+       (age)->event = e;               \
+       (age)->wrap = w;                \
+} while(0)
+
+#define TEST_AGE( age, e, w )                                          \
+       ( (age)->wrap+1 < (w) ||                                        \
+         ( (age)->wrap+1 == (w) && (e) < 0x7fff ) ||                   \
+         (age)->event < (uint16_t)(e) )
+
+#endif /* __SAVAGE_DRV_H__ */
diff --git a/shared-core/savage_state.c b/shared-core/savage_state.c
new file mode 100644 (file)
index 0000000..84794cb
--- /dev/null
@@ -0,0 +1,636 @@
+/* savage_state.c -- State and drawing support for Savage
+ *
+ * Copyright 2004  Felix Kuehling
+ * 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, sub license,
+ * 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
+ * NON-INFRINGEMENT. IN NO EVENT SHALL FELIX KUEHLING 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.
+ */
+#include "drmP.h"
+#include "savage_drm.h"
+#include "savage_drv.h"
+
+void savage_emit_cliprect_s3d(drm_savage_private_t *dev_priv,
+                             drm_clip_rect_t *pbox)
+{
+       
+}
+
+void savage_emit_cliprect_s4(drm_savage_private_t *dev_priv,
+                            drm_clip_rect_t *pbox)
+{
+       
+}
+
+static int savage_verify_texaddr(drm_savage_private_t *dev_priv, int unit,
+                                uint32_t addr)
+{
+       if ((addr & 6) != 2) { /* reserved bits */
+               DRM_ERROR("bad texAddr%d %08x (reserved bits)\n", unit, addr);
+               return DRM_ERR(EINVAL);
+       }
+       if (!(addr & 1)) { /* local */
+               addr &= ~7;
+               if (addr <  dev_priv->texture_offset ||
+                   addr >= dev_priv->texture_offset+dev_priv->texture_size) {
+                       DRM_ERROR("bad texAddr%d %08x (local addr out of range)\n",
+                                 unit, addr);
+                       return DRM_ERR(EINVAL);
+               }
+       } else { /* AGP */
+               if (!dev_priv->agp_textures) {
+                       DRM_ERROR("bad texAddr%d %08x (AGP not available)\n",
+                                 unit, addr);
+                       return DRM_ERR(EINVAL);
+               }
+               addr &= ~7;
+               if (addr < dev_priv->agp_textures->offset ||
+                   addr >= (dev_priv->agp_textures->offset +
+                            dev_priv->agp_textures->size)) {
+                       DRM_ERROR("bad texAddr%d %08x (AGP addr out of range)\n",
+                                 unit, addr);
+                       return DRM_ERR(EINVAL);
+               }
+       }
+       return 0;
+}
+
+static int savage_verify_state_s3d(drm_savage_private_t *dev_priv,
+                                  unsigned int start, unsigned int count,
+                                  const uint32_t __user *regs)
+{
+       if (start < SAVAGE_TEXPALADDR_S3D ||
+           start+count-1 > SAVAGE_DESTTEXRWWATERMARK_S3D) {
+               DRM_ERROR("invalid register range (0x%04x-0x%04x)\n",
+                         start, start+count-1);
+               return DRM_ERR(EINVAL);
+       }
+
+       if (start <= SAVAGE_TEXCTRL_S3D && start+count > SAVAGE_TEXCTRL_S3D) {
+               DRM_GET_USER_UNCHECKED(dev_priv->state.s3d.texctrl,
+                                      &regs[SAVAGE_TEXCTRL_S3D-start]);
+       }
+
+       if (dev_priv->state.s3d.texctrl & SAVAGE_TEXCTRL_TEXEN_MASK &&
+           start <= SAVAGE_TEXADDR_S3D && start+count > SAVAGE_TEXADDR_S3D) {
+               uint32_t addr;
+               DRM_GET_USER_UNCHECKED(addr, &regs[SAVAGE_TEXADDR_S3D-start]);
+               return savage_verify_texaddr(dev_priv, 0, addr);
+       }
+
+       return 0;
+}
+
+static int savage_verify_state_s4(drm_savage_private_t *dev_priv,
+                                 unsigned int start, unsigned int count,
+                                 const uint32_t __user *regs)
+{
+       int ret = 0;
+
+       if (start < SAVAGE_DRAWLOCALCTRL_S4 ||
+           start+count-1 > SAVAGE_TEXBLENDCOLOR_S4) {
+               DRM_ERROR("invalid register range (0x%04x-0x%04x)\n",
+                         start, start+count-1);
+               return DRM_ERR(EINVAL);
+       }
+
+       if (start <= SAVAGE_TEXDESCR_S4 && start+count > SAVAGE_TEXDESCR_S4) {
+               DRM_GET_USER_UNCHECKED(dev_priv->state.s4.texdescr,
+                                      &regs[SAVAGE_TEXDESCR_S4-start]);
+       }
+
+       if (dev_priv->state.s4.texdescr & SAVAGE_TEXDESCR_TEX0EN_MASK &&
+           start <= SAVAGE_TEXADDR0_S4 && start+count > SAVAGE_TEXADDR0_S4) {
+               uint32_t addr;
+               DRM_GET_USER_UNCHECKED(addr, &regs[SAVAGE_TEXADDR0_S4-start]);
+               ret |= savage_verify_texaddr(dev_priv, 0, addr);
+       }
+       if (dev_priv->state.s4.texdescr & SAVAGE_TEXDESCR_TEX1EN_MASK &&
+           start <= SAVAGE_TEXADDR1_S4 && start+count > SAVAGE_TEXADDR1_S4) {
+               uint32_t addr;
+               DRM_GET_USER_UNCHECKED(addr, &regs[SAVAGE_TEXADDR1_S4-start]);
+               ret |= savage_verify_texaddr(dev_priv, 1, addr);
+       }
+
+       return ret;
+}
+
+static int savage_dispatch_state(drm_savage_private_t *dev_priv,
+                                const drm_savage_cmd_header_t *cmd_header,
+                                const uint32_t __user *regs)
+{
+       BCI_LOCALS;
+       unsigned int count = cmd_header->state.count;
+       unsigned int start = cmd_header->state.start;
+       unsigned int bci_size = count + (count+254)/255;
+       int ret;
+
+       if (DRM_VERIFYAREA_READ(regs, count*4))
+               return DRM_ERR(EFAULT);
+
+       if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
+               ret = savage_verify_state_s3d(dev_priv, start, count, regs);
+               if (ret != 0)
+                       return ret;
+       } else {
+               ret = savage_verify_state_s4(dev_priv, start, count, regs);
+               if (ret != 0)
+                       return ret;
+       }
+
+       if (cmd_header->state.global) {
+               BEGIN_BCI(bci_size+1);
+               BCI_WRITE(BCI_CMD_WAIT | BCI_CMD_WAIT_3D);
+       } else {
+               BEGIN_BCI(bci_size);
+       }
+
+       while (count > 0) {
+               unsigned int n = count < 255 ? count : 255;
+               BCI_SET_REGISTERS(start, n);
+               BCI_COPY_FROM_USER(regs, n);
+               count -= n;
+               start += n;
+               regs += n;
+       }
+
+       return 0;
+}
+
+static int savage_dispatch_dma_prim(drm_savage_private_t *dev_priv,
+                                   const drm_savage_cmd_header_t *cmd_header,
+                                   const drm_buf_t *dmabuf)
+{
+       BCI_LOCALS;
+       unsigned char reorder = 0;
+       unsigned char prim = cmd_header->prim.prim;
+       unsigned short skip = cmd_header->prim.skip;
+       unsigned int n = cmd_header->prim.count;
+       unsigned int start = cmd_header->prim.start;
+       unsigned int i;
+
+       switch (prim) {
+       case SAVAGE_PRIM_TRILIST_201:
+               reorder = 1;
+               prim = SAVAGE_PRIM_TRILIST;
+       case SAVAGE_PRIM_TRILIST:
+               if (n % 3 != 0) {
+                       DRM_ERROR("wrong number of vertices %u in TRILIST\n",
+                                 n);
+                       return DRM_ERR(EINVAL);
+               }
+               break;
+       case SAVAGE_PRIM_TRISTRIP:
+       case SAVAGE_PRIM_TRIFAN:
+               if (n < 3) {
+                       DRM_ERROR("wrong number of vertices %u in TRIFAN/STRIP\n",
+                                 n);
+                       return DRM_ERR(EINVAL);
+               }
+               break;
+       default:
+               DRM_ERROR("invalid primitive type %u\n", prim);
+               return DRM_ERR(EINVAL);
+       }
+
+       if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
+               if (skip != 0) {
+                       DRM_ERROR("invalid skip flags 0x%04x for DMA\n",
+                                 skip);
+                       return DRM_ERR(EINVAL);
+               }
+       } else {
+               unsigned int size = 10 - (skip & 1) - (skip >> 1 & 1) -
+                       (skip >> 2 & 1) - (skip >> 3 & 1) - (skip >> 4 & 1) -
+                       (skip >> 5 & 1) - (skip >> 6 & 1) - (skip >> 7 & 1);
+               if (skip > SAVAGE_SKIP_ALL_S4 || size != 8) {
+                       DRM_ERROR("invalid skip flags 0x%04x for DMA\n",
+                                 skip);
+                       return DRM_ERR(EINVAL);
+               }
+               if (reorder) {
+                       DRM_ERROR("TRILIST_201 used on Savage4 hardware\n");
+                       return DRM_ERR(EINVAL);
+               }
+       }
+
+       if (start + n > dmabuf->total/32) {
+               DRM_ERROR("vertex indices (%u-%u) out of range (0-%u)\n",
+                         start, start + n - 1, dmabuf->total/32);
+               return DRM_ERR(EINVAL);
+       }
+
+       if (dmabuf->bus_address != dev_priv->state.common.vbaddr) {
+               BEGIN_BCI(2);
+               BCI_SET_REGISTERS(SAVAGE_VERTBUFADDR, 1);
+               BCI_WRITE(dmabuf->bus_address | dev_priv->dma_type);
+               dev_priv->state.common.vbaddr = dmabuf->bus_address;
+       }
+       if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
+               /* Workaround for what looks like a hardware bug. If a
+                * WAIT_3D_IDLE was emitted some time before the
+                * indexed drawing command then the engine will lock
+                * up. There are two known workarounds:
+                * WAIT_IDLE_EMPTY or emit at least 63 NOPs. */
+               BEGIN_BCI(63);
+               for (i = 0; i < 63; ++i)
+                       BCI_WRITE(BCI_CMD_WAIT);
+       }
+
+       prim <<= 25;
+       while (n != 0) {
+               /* Can emit up to 255 indices (85 triangles) at once. */
+               unsigned int count = n > 255 ? 255 : n;
+               if (reorder) {
+                       /* Need to reorder indices for correct flat
+                        * shading while preserving the clock sense
+                        * for correct culling. Only on Savage3D. */
+                       int reorder[3] = {-1, -1, -1};
+                       reorder[start%3] = 2;
+
+                       BEGIN_BCI((count+1+1)/2);
+                       BCI_DRAW_INDICES_S3D(count, prim, start+2);
+
+                       for (i = start+1; i+1 < start+count; i += 2)
+                               BCI_WRITE((i + reorder[i % 3]) |
+                                         ((i+1 + reorder[(i+1) % 3]) << 16));
+                       if (i < start+count)
+                               BCI_WRITE(i + reorder[i%3]);
+               } else if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
+                       BEGIN_BCI((count+1+1)/2);
+                       BCI_DRAW_INDICES_S3D(count, prim, start);
+
+                       for (i = start+1; i+1 < start+count; i += 2)
+                               BCI_WRITE(i | ((i+1) << 16));
+                       if (i < start+count)
+                               BCI_WRITE(i);
+               } else {
+                       BEGIN_BCI((count+2+1)/2);
+                       BCI_DRAW_INDICES_S4(count, prim, skip);
+
+                       for (i = start; i+1 < start+count; i += 2)
+                               BCI_WRITE(i | ((i+1) << 16));
+                       if (i < start+count)
+                               BCI_WRITE(i);
+               }
+
+               start += count;
+               n -= count;
+
+               prim |= BCI_CMD_DRAW_CONT;
+       }
+
+       return 0;
+}
+
+static int savage_dispatch_vb_prim(drm_savage_private_t *dev_priv,
+                                  const drm_savage_cmd_header_t *cmd_header,
+                                  const uint32_t __user *vtxbuf,
+                                  unsigned int vb_size,
+                                  unsigned int vb_stride)
+{
+       BCI_LOCALS;
+       unsigned char reorder = 0;
+       unsigned char prim = cmd_header->prim.prim;
+       unsigned short skip = cmd_header->prim.skip;
+       unsigned int n = cmd_header->prim.count;
+       unsigned int start = cmd_header->prim.start;
+       unsigned int vtx_size;
+       unsigned int i;
+
+       switch (prim) {
+       case SAVAGE_PRIM_TRILIST_201:
+               reorder = 1;
+               prim = SAVAGE_PRIM_TRILIST;
+       case SAVAGE_PRIM_TRILIST:
+               if (n % 3 != 0) {
+                       DRM_ERROR("wrong number of vertices %u in TRILIST\n",
+                                 n);
+                       return DRM_ERR(EINVAL);
+               }
+               break;
+       case SAVAGE_PRIM_TRISTRIP:
+       case SAVAGE_PRIM_TRIFAN:
+               if (n < 3) {
+                       DRM_ERROR("wrong number of vertices %u in TRIFAN/STRIP\n",
+                                 n);
+                       return DRM_ERR(EINVAL);
+               }
+               break;
+       default:
+               DRM_ERROR("invalid primitive type %u\n", prim);
+               return DRM_ERR(EINVAL);
+       }
+
+       if (S3_SAVAGE3D_SERIES(dev_priv->chipset)) {
+               if (skip > SAVAGE_SKIP_ALL_S3D) {
+                       DRM_ERROR("invalid skip flags 0x%04x\n", skip);
+                       return DRM_ERR(EINVAL);
+               }
+               vtx_size = 8; /* full vertex */
+       } else {
+               if (skip > SAVAGE_SKIP_ALL_S4) {
+                       DRM_ERROR("invalid skip flags 0x%04x\n", skip);
+                       return DRM_ERR(EINVAL);
+               }
+               vtx_size = 10; /* full vertex */
+       }
+
+       vtx_size -= (skip & 1) + (skip >> 1 & 1) +
+               (skip >> 2 & 1) + (skip >> 3 & 1) + (skip >> 4 & 1) +
+               (skip >> 5 & 1) + (skip >> 6 & 1) + (skip >> 7 & 1);
+
+       if (vtx_size > vb_stride) {
+               DRM_ERROR("vertex size greater than vb stride (%u > %u)\n",
+                         vtx_size, vb_stride);
+               return DRM_ERR(EINVAL);
+       }
+
+       if (start + n > vb_size / (vtx_size*4)) {
+               DRM_ERROR("vertex indices (%u-%u) out of range (0-%u)\n",
+                         start, start + n - 1, vb_size / (vtx_size*4));
+               return DRM_ERR(EINVAL);
+       }
+
+       prim <<= 25;
+       while (n != 0) {
+               /* Can emit up to 255 vertices (85 triangles) at once. */
+               unsigned int count = n > 255 ? 255 : n;
+               if (reorder) {
+                       /* Need to reorder vertices for correct flat
+                        * shading while preserving the clock sense
+                        * for correct culling. Only on Savage3D. */
+                       int reorder[3] = {-1, -1, -1};
+                       reorder[start%3] = 2;
+
+                       BEGIN_BCI(vtx_size+1);
+                       BCI_DRAW_PRIMITIVE(count, prim, skip);
+
+                       for (i = start; i < start+count; ++i) {
+                               unsigned int j = i + reorder[i % 3];
+                               BCI_COPY_FROM_USER(&vtxbuf[vb_stride*j],
+                                                  vtx_size);
+                       }
+               } else {
+                       BEGIN_BCI(vtx_size+1);
+                       BCI_DRAW_PRIMITIVE(count, prim, skip);
+
+                       if (vb_stride == vtx_size) {
+                               BCI_COPY_FROM_USER(&vtxbuf[vtx_size*start],
+                                                  vtx_size*count);
+                       } else {
+                               for (i = start; i < start+count; ++i) {
+                                       BCI_COPY_FROM_USER(
+                                               &vtxbuf[vb_stride*i],
+                                               vtx_size);
+                               }
+                       }
+               }
+
+               start += count;
+               n -= count;
+
+               prim |= BCI_CMD_DRAW_CONT;
+       }
+
+       return 0;
+}
+
+static int savage_dispatch_clear(drm_savage_private_t *dev_priv,
+                                const drm_savage_cmd_header_t *cmd_header,
+                                const drm_savage_cmd_header_t __user *data,
+                                unsigned int nbox,
+                                const drm_clip_rect_t __user *usr_boxes)
+{
+       BCI_LOCALS;
+       unsigned int flags = cmd_header->clear0.flags, mask, value;
+       unsigned int clear_cmd;
+       unsigned int i;
+
+       if (nbox == 0)
+               return 0;
+
+       DRM_GET_USER_UNCHECKED(mask, &((drm_savage_cmd_header_t*)data)
+                              ->clear1.mask);
+       DRM_GET_USER_UNCHECKED(value, &((drm_savage_cmd_header_t*)data)
+                              ->clear1.value);
+
+       clear_cmd = BCI_CMD_RECT | BCI_CMD_RECT_XP | BCI_CMD_RECT_YP |
+               BCI_CMD_SEND_COLOR | BCI_CMD_DEST_PBD_NEW;
+       BCI_CMD_SET_ROP(clear_cmd,0xCC);
+
+       i = ((flags & SAVAGE_FRONT) ? 1 : 0) +
+               ((flags & SAVAGE_BACK) ? 1 : 0) +
+               ((flags & SAVAGE_DEPTH) ? 1 : 0);
+       if (i == 0)
+               return 0;
+
+       BEGIN_BCI(nbox * i * 6 + (mask != 0xffffffff ? 4 : 0));
+
+       if (mask != 0xffffffff) {
+               /* set mask */
+               BCI_SET_REGISTERS(SAVAGE_BITPLANEWTMASK, 1);
+               BCI_WRITE(mask);
+       }
+       for (i = 0; i < nbox; ++i) {
+               drm_clip_rect_t box;
+               unsigned int x, y, w, h;
+               unsigned int buf;
+               DRM_COPY_FROM_USER_UNCHECKED(&box, &usr_boxes[i], sizeof(box));
+               x = box.x1, y = box.y1;
+               w = box.x2 - box.x1;
+               h = box.y2 - box.y1;
+               for (buf = SAVAGE_FRONT; buf <= SAVAGE_DEPTH; buf <<= 1) {
+                       if (!(flags & buf))
+                               continue;
+                       BCI_WRITE(clear_cmd);
+                       switch(buf) {
+                       case SAVAGE_FRONT:
+                               BCI_WRITE(dev_priv->front_offset);
+                               BCI_WRITE(dev_priv->front_bd);
+                               break;
+                       case SAVAGE_BACK:
+                               BCI_WRITE(dev_priv->back_offset);
+                               BCI_WRITE(dev_priv->back_bd);
+                               break;
+                       case SAVAGE_DEPTH:
+                               BCI_WRITE(dev_priv->depth_offset);
+                               BCI_WRITE(dev_priv->depth_bd);
+                               break;
+                       }
+                       BCI_WRITE(value);
+                       BCI_WRITE(BCI_X_Y(x, y));
+                       BCI_WRITE(BCI_W_H(w, h));
+               }
+       }
+       if (mask != 0xffffffff) {
+               /* reset mask */
+               BCI_SET_REGISTERS(SAVAGE_BITPLANEWTMASK, 1);
+               BCI_WRITE(0xffffffff);
+       }
+
+       return 0;
+}
+
+static int savage_dispatch_swap(drm_savage_private_t *dev_priv,
+                               unsigned int nbox,
+                               const drm_clip_rect_t __user *usr_boxes)
+{
+       BCI_LOCALS;
+       unsigned int swap_cmd;
+       unsigned int i;
+
+       if (nbox == 0)
+               return 0;
+
+       swap_cmd = BCI_CMD_RECT | BCI_CMD_RECT_XP | BCI_CMD_RECT_YP |
+               BCI_CMD_SRC_PBD_COLOR_NEW | BCI_CMD_DEST_GBD;
+       BCI_CMD_SET_ROP(swap_cmd,0xCC);
+
+       BEGIN_BCI(nbox*6);
+       for (i = 0; i < nbox; ++i) {
+               drm_clip_rect_t box;
+               DRM_COPY_FROM_USER_UNCHECKED(&box, &usr_boxes[i], sizeof(box));
+
+               BCI_WRITE(swap_cmd);
+               BCI_WRITE(dev_priv->back_offset);
+               BCI_WRITE(dev_priv->back_bd);
+               BCI_WRITE(BCI_X_Y(box.x1, box.y1));
+               BCI_WRITE(BCI_X_Y(box.x1, box.y1));
+               BCI_WRITE(BCI_W_H(box.x2-box.x1, box.y2-box.y1));
+       }
+
+       return 0;
+}
+
+int savage_bci_cmdbuf(DRM_IOCTL_ARGS)
+{
+       DRM_DEVICE;
+       drm_savage_private_t *dev_priv = dev->dev_private;
+       drm_device_dma_t *dma = dev->dma;
+       drm_buf_t *dmabuf;
+       drm_savage_cmdbuf_t cmdbuf;
+       drm_savage_cmd_header_t __user *usr_cmdbuf;
+       unsigned int __user *usr_vtxbuf;
+       drm_clip_rect_t __user *usr_boxes;
+       unsigned int i, j;
+       int ret = 0;
+
+       DRM_DEBUG("\n");
+       
+       LOCK_TEST_WITH_RETURN(dev, filp);
+
+       DRM_COPY_FROM_USER_IOCTL(cmdbuf, (drm_savage_cmdbuf_t __user *)data,
+                                sizeof(cmdbuf));
+
+       if (cmdbuf.dma_idx > dma->buf_count) {
+               DRM_ERROR("vertex buffer index %u out of range (0-%u)\n",
+                         cmdbuf.dma_idx, dma->buf_count-1);
+               return DRM_ERR(EINVAL);
+       }
+       dmabuf = dma->buflist[cmdbuf.dma_idx];
+
+       usr_cmdbuf = (drm_savage_cmd_header_t __user *)cmdbuf.cmd_addr;
+       usr_vtxbuf = (unsigned int __user *)cmdbuf.vb_addr;
+       usr_boxes = (drm_clip_rect_t __user *)cmdbuf.box_addr;
+       if ((cmdbuf.size && DRM_VERIFYAREA_READ(usr_cmdbuf, cmdbuf.size*8)) ||
+           (cmdbuf.vb_size && DRM_VERIFYAREA_READ(
+                   usr_vtxbuf, cmdbuf.vb_size)) ||
+           (cmdbuf.nbox && DRM_VERIFYAREA_READ(
+                   usr_boxes, cmdbuf.nbox*sizeof(drm_clip_rect_t))))
+               return DRM_ERR(EFAULT);
+
+       /* Make sure writes to DMA buffers are finished before sending
+        * DMA commands to the graphics hardware. */
+       DRM_MEMORYBARRIER();
+
+       i = 0;
+       while (i < cmdbuf.size && ret == 0) {
+               drm_savage_cmd_header_t cmd_header;
+               DRM_COPY_FROM_USER_UNCHECKED(&cmd_header, usr_cmdbuf,
+                                            sizeof(cmd_header));
+               usr_cmdbuf++;
+               i++;
+
+               switch (cmd_header.cmd.cmd) {
+               case SAVAGE_CMD_STATE:
+                       ret = savage_dispatch_state(
+                               dev_priv, &cmd_header,
+                               (uint32_t __user *)usr_cmdbuf);
+                       j = (cmd_header.state.count + 1) / 2;
+                       usr_cmdbuf += j;
+                       i += j;
+                       break;
+               case SAVAGE_CMD_DMA_PRIM:
+                       ret = savage_dispatch_dma_prim(
+                               dev_priv, &cmd_header, dmabuf);
+                       break;
+               case SAVAGE_CMD_VB_PRIM:
+                       ret = savage_dispatch_vb_prim(
+                               dev_priv, &cmd_header,
+                               (uint32_t __user *)usr_vtxbuf,
+                               cmdbuf.vb_size, cmdbuf.vb_stride);
+                       break;
+#if 0 /* to be implemented */
+               case SAVAGE_CMD_DMA_IDX:
+                       ret = savage_dispatch_dma_idx(
+                               dev_priv, &cmd_header, usr_cmdbuf, dmabuf);
+                       j = (cmd_header.state.count + 3) / 4;
+                       usr_cmdbuf += j;
+                       i += j;
+                       break;
+               case SAVAGE_CMD_VB_IDX:
+                       ret = savage_dispatch_vtx_idx(
+                               dev_priv, &cmd_header, usr_cmdbuf, usr_vtxbuf,
+                               cmdbuf.vb_size, cmdbuf.vb_stride);
+                       j = (cmd_header.state.count + 3) / 4;
+                       usr_cmdbuf += j;
+                       i += j;
+                       break;
+#endif
+               case SAVAGE_CMD_CLEAR:
+                       ret = savage_dispatch_clear(dev_priv, &cmd_header,
+                                                   usr_cmdbuf,
+                                                   cmdbuf.nbox, usr_boxes);
+                       usr_cmdbuf++;
+                       i++;
+                       break;
+               case SAVAGE_CMD_SWAP:
+                       ret = savage_dispatch_swap(dev_priv,
+                                                  cmdbuf.nbox, usr_boxes);
+                       break;
+               default:
+                       DRM_ERROR("invalid command 0x%x\n", cmd_header.cmd.cmd);
+                       return DRM_ERR(EINVAL);
+               }
+
+               if (ret != 0)
+                       return ret;
+       }
+
+       if (cmdbuf.discard) {
+               drm_savage_buf_priv_t *buf_priv = dmabuf->dev_private;
+               uint16_t event;
+               event = savage_bci_emit_event(dev_priv, SAVAGE_WAIT_3D);
+               SET_AGE(&buf_priv->age, event, dev_priv->event_wrap);
+               savage_freelist_put(dev, dmabuf);
+       }
+
+       return 0;
+}