changeset 9545:d1bbeae9f46a

tdfx_vid a new kernel driver for tdfx wich let use agp move :)
author albeu
date Fri, 07 Mar 2003 18:42:08 +0000
parents 97f61ffa441e
children 8feb4bb5b334
files drivers/Makefile drivers/tdfx_vid.c drivers/tdfx_vid.h drivers/tdfx_vid_tst.c
diffstat 4 files changed, 877 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/drivers/Makefile	Fri Mar 07 12:59:28 2003 +0000
+++ b/drivers/Makefile	Fri Mar 07 18:42:08 2003 +0000
@@ -1,7 +1,7 @@
 
 KERNEL_INCLUDES = /usr/src/linux/include
 INCLUDES = -I$(KERNEL_INCLUDES)
-CFLAGS = -O2  -D__KERNEL__  -DMODULE  	 -include $(KERNEL_INCLUDES)/linux/modversions.h
+CFLAGS = -O2  -D__KERNEL__  -DMODULE  	 -include $(KERNEL_INCLUDES)/linux/modversions.h -Wall
 VERSION = $(shell grep UTS_RELEASE $(KERNEL_INCLUDES)/linux/version.h | cut -d '"' -f2)
 MDIR = /lib/modules/$(VERSION)/misc
 
@@ -17,6 +17,12 @@
 mga_vid_test: mga_vid_test.c
 	$(CC) -O $(INCLUDES) -o $@ $@.c
 
+tdfx_vid.o: tdfx_vid.c 3dfx.h
+	$(CC) $(CFLAGS) $(INCLUDES) -c $(basename $@).c
+
+tdfx_vid_tst: tdfx_vid_tst.c
+	$(CC) -O $(INCLUDES) -o $@ $@.c
+
 install: mga_vid.o
 	if test ! -d $(MDIR) ; then mkdir -p $(MDIR) ; fi
 	install -m 644 mga_vid.o $(MDIR)/mga_vid.o
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/tdfx_vid.c	Fri Mar 07 18:42:08 2003 +0000
@@ -0,0 +1,698 @@
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
+#include <linux/malloc.h>
+#else
+#include <linux/slab.h>
+#endif
+
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/agp_backend.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "tdfx_vid.h"
+#include "3dfx.h"
+
+
+#define TDFX_VID_MAJOR 178
+
+MODULE_AUTHOR("Albeu");
+
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+#ifndef min
+#define min(x,y) (((x)<(y))?(x):(y))
+#endif
+
+static struct pci_dev *pci_dev;
+
+static uint8_t *tdfx_mmio_base = 0;
+static uint32_t tdfx_mem_base = 0;
+static struct voodoo_2d_reg_t* tdfx_2d_regs = NULL;
+
+static int tdfx_ram_size = 0;
+
+static int tdfx_vid_in_use = 0;
+
+static drm_agp_t *drm_agp = NULL;
+static agp_kern_info agp_info;
+static agp_memory *agp_mem = NULL;
+
+
+
+static inline u32 tdfx_inl(unsigned int reg) {
+  return readl(tdfx_mmio_base + reg);
+}
+
+static inline void tdfx_outl(unsigned int reg, u32 val) {
+  writel(val,tdfx_mmio_base  + reg);
+}
+
+static inline void banshee_make_room(int size) {
+  while((tdfx_inl(STATUS) & 0x1f) < size);
+}
+ 
+static inline void banshee_wait_idle(void) {
+  int i = 0;
+
+  banshee_make_room(1);
+  tdfx_outl(COMMAND_3D, COMMAND_3D_NOP);
+
+  while(1) {
+    i = (tdfx_inl(STATUS) & STATUS_BUSY) ? 0 : i + 1;
+    if(i == 3) break;
+  }
+}
+
+static unsigned long get_lfb_size(void) {
+  u32 draminit0 = 0;
+  u32 draminit1 = 0;
+  //  u32 miscinit1 = 0;
+  u32 lfbsize   = 0;
+  int sgram_p     = 0;
+
+  draminit0 = tdfx_inl(DRAMINIT0);  
+  draminit1 = tdfx_inl(DRAMINIT1);
+
+  if ((pci_dev->device == PCI_DEVICE_ID_3DFX_BANSHEE) ||
+      (pci_dev->device == PCI_DEVICE_ID_3DFX_VOODOO3)) {
+    sgram_p = (draminit1 & DRAMINIT1_MEM_SDRAM) ? 0 : 1;
+  
+    lfbsize = sgram_p ?
+      (((draminit0 & DRAMINIT0_SGRAM_NUM)  ? 2 : 1) * 
+       ((draminit0 & DRAMINIT0_SGRAM_TYPE) ? 8 : 4) * 1024 * 1024) :
+      16 * 1024 * 1024;
+  } else {
+    /* Voodoo4/5 */
+    u32 chips, psize, banks;
+
+    chips = ((draminit0 & (1 << 26)) == 0) ? 4 : 8;
+    psize = 1 << ((draminit0 & 0x38000000) >> 28);
+    banks = ((draminit0 & (1 << 30)) == 0) ? 2 : 4;
+    lfbsize = chips * psize * banks;
+    lfbsize <<= 20;
+  }
+
+#if 0
+  /* disable block writes for SDRAM (why?) */
+  miscinit1 = tdfx_inl(MISCINIT1);
+  miscinit1 |= sgram_p ? 0 : MISCINIT1_2DBLOCK_DIS;
+  miscinit1 |= MISCINIT1_CLUT_INV;
+
+  banshee_make_room(1); 
+  tdfx_outl(MISCINIT1, miscinit1);
+#endif
+
+  return lfbsize;
+}
+
+static int tdfx_vid_find_card(void)
+{
+  struct pci_dev *dev = NULL;
+  //  unsigned int card_option;
+
+  if((dev = pci_find_device(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE, NULL)))
+    printk(KERN_INFO "tdfx_vid: Found VOODOO BANSHEE\n");
+  else if((dev = pci_find_device(PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3, NULL)))
+    printk(KERN_INFO "tdfx_vid: Found VOODOO 3 \n");
+  else
+    return 0;
+
+  
+  pci_dev = dev;
+
+#if LINUX_VERSION_CODE >= 0x020300
+  tdfx_mmio_base = ioremap_nocache(dev->resource[0].start,1 << 24);
+  tdfx_mem_base =  dev->resource[1].start;
+#else
+  tdfx_mmio_base = ioremap_nocache(dev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK,0x4000);
+  tdfx_mem_base =  dev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK;
+#endif
+  printk(KERN_INFO "tdfx_vid: MMIO at 0x%p\n", tdfx_mmio_base);
+  tdfx_2d_regs = (struct voodoo_2d_reg_t*)tdfx_mmio_base;
+  tdfx_ram_size = get_lfb_size();
+
+  printk(KERN_INFO "tdfx_vid: Found %d MB (%d bytes) of memory\n",
+	 tdfx_ram_size / 1024 / 1024,tdfx_ram_size);
+
+  
+#if 0
+  {
+    int temp;
+    printk("List resources -----------\n");
+    for(temp=0;temp<DEVICE_COUNT_RESOURCE;temp++){
+      struct resource *res=&pci_dev->resource[temp];
+      if(res->flags){
+	int size=(1+res->end-res->start)>>20;
+	printk(KERN_DEBUG "res %d:  start: 0x%X   end: 0x%X  (%d MB) flags=0x%X\n",temp,res->start,res->end,size,res->flags);
+	if(res->flags&(IORESOURCE_MEM|IORESOURCE_PREFETCH)){
+	  if(size>tdfx_ram_size && size<=64) tdfx_ram_size=size;
+	}
+      }
+    }
+  }
+#endif
+
+
+  return 1;
+}
+
+static int agp_init(void) {
+
+  drm_agp = (drm_agp_t*)inter_module_get("drm_agp");
+
+  if(!drm_agp) {
+    printk(KERN_ERR "tdfx_vid: Unable to get drm_agp pointer\n");
+    return 0;
+  }
+
+  if(drm_agp->acquire()) {
+    printk(KERN_ERR "tdfx_vid: Unable to acquire the agp backend\n");
+    drm_agp = NULL;
+    return 0;
+  }
+
+  drm_agp->copy_info(&agp_info);
+#if 0
+  printk(KERN_DEBUG "AGP Version : %d %d\n"
+	 "AGP Mode: %#X\nAperture Base: %p\nAperture Size: %d\n"
+	 "Max memory = %d\nCurrent mem = %d\nCan use perture : %s\n"
+	 "Page mask = %#X\n",
+	 agp_info.version.major,agp_info.version.minor,
+	 agp_info.mode,agp_info.aper_base,agp_info.aper_size,
+	 agp_info.max_memory,agp_info.current_memory,
+	 agp_info.cant_use_aperture ? "no" : "yes",
+	 agp_info.page_mask);
+#endif
+  drm_agp->enable(agp_info.mode);
+
+  
+  printk(KERN_INFO "AGP Enabled\n");
+
+  return 1;
+}
+    
+static void agp_close(void) {
+
+  if(!drm_agp) return;
+
+  if(agp_mem) {
+    drm_agp->unbind_memory(agp_mem);
+    drm_agp->free_memory(agp_mem);
+    agp_mem = NULL;
+  }
+      
+
+  drm_agp->release();
+
+}
+
+static int agp_move(tdfx_vid_agp_move_t* m) {
+  //  u32 mov = 0;
+  u32 src = 0;
+  u32 src_h,src_l;
+
+  if(!agp_mem)
+    return (-EAGAIN);
+
+  if(m->move2 > 3) {
+    printk(KERN_DEBUG "tdfx_vid: AGP move invalid destination %d\n",
+	   m->move2);
+    return (-EAGAIN);
+  }
+
+
+  src =  agp_info.aper_base +  m->src;
+
+  src_l = (u32)src;
+  src_h = (m->width | (m->src_stride << 14)) & 0x0FFFFFFF;
+
+  //  banshee_wait_idle();
+  banshee_make_room(6);
+  tdfx_outl(AGPHOSTADDRESSHIGH,src_h);
+  tdfx_outl(AGPHOSTADDRESSLOW,src_l);
+
+  tdfx_outl(AGPGRAPHICSADDRESS, m->dst);
+  tdfx_outl(AGPGRAPHICSSTRIDE, m->dst_stride);
+  tdfx_outl(AGPREQSIZE,m->src_stride*m->height);
+
+  tdfx_outl(AGPMOVECMD,m->move2 << 3);
+  banshee_wait_idle();
+
+  banshee_make_room(5);
+  tdfx_outl(AGPHOSTADDRESSHIGH,0);
+  tdfx_outl(AGPHOSTADDRESSLOW,0);
+
+  tdfx_outl(AGPGRAPHICSADDRESS,0);
+  tdfx_outl(AGPGRAPHICSSTRIDE,0);
+  tdfx_outl(AGPREQSIZE,0);
+  banshee_wait_idle();
+
+  return 0;
+}
+
+static void setup_fifo(u32 offset,ssize_t pages) {
+  long addr = agp_info.aper_base + offset;
+  u32 size = pages | 0x700; // fifo on, in agp mem, disable hole cnt
+
+  banshee_wait_idle();
+
+  tdfx_outl(CMDBASEADDR0,addr >> 4);
+  tdfx_outl(CMDRDPTRL0, addr << 4);
+  tdfx_outl(CMDRDPTRH0, addr >> 28);
+  tdfx_outl(CMDAMIN0, (addr - 4) & 0xFFFFFF);
+  tdfx_outl(CMDAMAX0, (addr - 4) & 0xFFFFFF);
+  tdfx_outl(CMDFIFODEPTH0, 0);
+  tdfx_outl(CMDHOLECNT0, 0);
+  tdfx_outl(CMDBASESIZE0,size);
+
+  banshee_wait_idle();
+  
+}
+
+static int bump_fifo(u16 size) {
+
+  banshee_wait_idle();
+  tdfx_outl(CMDBUMP0 , size);
+  banshee_wait_idle();
+
+  return 0;
+}
+
+static void tdfx_vid_get_config(tdfx_vid_config_t* cfg) {
+  u32 in;
+
+  cfg->version = TDFX_VID_VERSION;
+  cfg->ram_size = tdfx_ram_size;
+
+  in = tdfx_inl(VIDSCREENSIZE);
+  cfg->screen_width = in & 0xFFF;
+  cfg->screen_height = (in >> 12) & 0xFFF;
+  in = (tdfx_inl(VIDPROCCFG)>> 18)& 0x7;
+  switch(in) {
+  case 0:
+    cfg->screen_format = TDFX_VID_FORMAT_BGR8;
+    break;
+  case 1:
+    cfg->screen_format = TDFX_VID_FORMAT_BGR16;
+    break;
+  case 2:
+    cfg->screen_format = TDFX_VID_FORMAT_BGR24;
+    break;
+  case 3:
+    cfg->screen_format = TDFX_VID_FORMAT_BGR32;
+    break;
+  default:
+    printk(KERN_INFO "tdfx_vid: unknow screen format %d\n",in);
+    cfg->screen_format = 0;
+    break;
+  }
+  cfg->screen_stride = tdfx_inl(VIDDESKSTRIDE);
+  cfg->screen_start = tdfx_inl(VIDDESKSTART);
+}
+
+inline static u32 tdfx_vid_make_format(int src,u16 stride,u32 fmt) {
+  u32 r = stride & 0xFFF3;
+  u32 tdfx_fmt = 0;
+
+  // src and dest formats
+  switch(fmt) {
+  case TDFX_VID_FORMAT_BGR8:
+    tdfx_fmt = 1;
+    break;
+  case TDFX_VID_FORMAT_BGR16:
+    tdfx_fmt = 3;
+    break;
+  case TDFX_VID_FORMAT_BGR24:
+    tdfx_fmt = 4;
+    break;
+  case TDFX_VID_FORMAT_BGR32:
+    tdfx_fmt = 5;
+    break;
+  }
+
+  if(!src && !tdfx_fmt) {
+    printk(KERN_INFO "tdfx_vid: Invalid destination format %#X\n",fmt);
+    return 0;
+  }
+
+  if(src && !tdfx_fmt) {
+    // src only format
+    switch(fmt){
+    case TDFX_VID_FORMAT_BGR1:
+      tdfx_fmt = 0;
+      break;
+    case TDFX_VID_FORMAT_YUY2:
+      tdfx_fmt = 8;
+      break;
+    case TDFX_VID_FORMAT_UYVY:
+      tdfx_fmt = 9;
+      break;
+    default:
+      printk(KERN_INFO "tdfx_vid: Invalid source format %#X\n",fmt);
+      return 0;
+    }
+  }
+
+  r |= tdfx_fmt << 16;
+
+  return r;
+}
+
+static int tdfx_vid_blit(tdfx_vid_blit_t* blit) {
+  u32 src_fmt,dst_fmt;
+  u32 cmin,cmax,srcbase,srcxy,srcfmt,srcsize;
+  u32 dstbase,dstxy,dstfmt,dstsize;
+  
+  //printk(KERN_INFO "tdfx_vid: Make src fmt 0x%x\n",blit->src_format);
+  src_fmt = tdfx_vid_make_format(1,blit->src_stride,blit->src_format);
+  if(!src_fmt)
+    return 0;
+  //printk(KERN_INFO "tdfx_vid: Make dst fmt 0x%x\n", blit->dst_format);
+  dst_fmt = tdfx_vid_make_format(0,blit->dst_stride,blit->dst_format);
+  if(!dst_fmt)
+    return 0;
+
+  // Save the regs otherwise fb get crazy
+  // we can perhaps avoid some ...
+  cmin = tdfx_inl(CLIP0MIN);
+  cmax = tdfx_inl(CLIP0MAX);
+  srcbase = tdfx_inl(SRCBASE);
+  srcxy = tdfx_inl(SRCXY);
+  srcfmt = tdfx_inl(SRCFORMAT);
+  srcsize = tdfx_inl(SRCSIZE);
+  dstbase = tdfx_inl(DSTBASE);
+  dstxy = tdfx_inl(DSTXY);
+  dstfmt = tdfx_inl(DSTFORMAT);
+  dstsize = tdfx_inl(DSTSIZE);
+
+  // Get rid of the clipping at the moment
+  banshee_make_room(11);
+  
+  tdfx_outl(CLIP0MIN,0);
+  tdfx_outl(CLIP0MAX,0x0fff0fff);
+
+  // Setup the src
+  tdfx_outl(SRCBASE,blit->src & 0x00FFFFFF);
+  tdfx_outl(SRCXY,XYREG(blit->src_x,blit->src_y));
+  tdfx_outl(SRCFORMAT,src_fmt);
+  tdfx_outl(SRCSIZE,XYREG(blit->src_w,blit->src_h));
+
+  // Setup the dst
+  tdfx_outl(DSTBASE,blit->dst & 0x00FFFFFF);
+  tdfx_outl(DSTXY,XYREG(blit->dst_x,blit->dst_y));
+  tdfx_outl(DSTFORMAT,dst_fmt);
+  tdfx_outl(DSTSIZE,XYREG(blit->dst_w,blit->dst_h));
+
+  // Send the command
+  tdfx_outl(COMMAND_2D,0xcc000102); // | (ROP_COPY << 24));
+  banshee_wait_idle();
+
+  // Now restore the regs to make fb happy
+  banshee_make_room(10);
+  tdfx_outl(CLIP0MIN, cmin);
+  tdfx_outl(CLIP0MAX, cmax);
+  tdfx_outl(SRCBASE, srcbase);
+  tdfx_outl(SRCXY, srcxy);
+  tdfx_outl(SRCFORMAT, srcfmt);
+  tdfx_outl(SRCSIZE, srcsize);
+  tdfx_outl(DSTBASE, dstbase);
+  tdfx_outl(DSTXY, dstxy);
+  tdfx_outl(DSTFORMAT, dstfmt);
+  tdfx_outl(DSTSIZE, dstsize);
+  banshee_wait_idle();
+  
+  return 1;
+}
+
+static int tdfx_vid_set_yuv(unsigned long arg) {
+  tdfx_vid_yuv_t yuv;
+
+  if(copy_from_user(&yuv,(tdfx_vid_yuv_t*)arg,sizeof(tdfx_vid_yuv_t))) {
+    printk(KERN_DEBUG "tdfx_vid:failed copy from userspace\n");
+    return(-EFAULT); 
+  }
+  banshee_make_room(2);
+  tdfx_outl(YUVBASEADDRESS,yuv.base & 0x01FFFFFF);
+  tdfx_outl(YUVSTRIDE, yuv.stride & 0x3FFF);
+  
+  banshee_wait_idle();
+  
+  return 0;
+}
+
+static int tdfx_vid_get_yuv(unsigned long arg) {
+  tdfx_vid_yuv_t yuv;
+
+  yuv.base = tdfx_inl(YUVBASEADDRESS) & 0x01FFFFFF;
+  yuv.stride = tdfx_inl(YUVSTRIDE) & 0x3FFF;
+
+  if(copy_to_user((tdfx_vid_yuv_t*)arg,&yuv,sizeof(tdfx_vid_yuv_t))) {
+      printk(KERN_INFO "tdfx_vid:failed copy to userspace\n");
+      return(-EFAULT); 
+  }
+
+  return 0;
+}
+
+static int tdfx_vid_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+  tdfx_vid_agp_move_t move;
+  tdfx_vid_config_t cfg;
+  tdfx_vid_blit_t blit;
+  u16 int16;
+
+  switch(cmd) {
+  case TDFX_VID_AGP_MOVE:
+    if(copy_from_user(&move,(tdfx_vid_agp_move_t*)arg,sizeof(tdfx_vid_agp_move_t))) {
+      printk(KERN_INFO "tdfx_vid:failed copy from userspace\n");
+      return(-EFAULT); 
+    }
+    return agp_move(&move);
+  case TDFX_VID_BUMP0:
+    if(copy_from_user(&int16,(u16*)arg,sizeof(u16))) {
+      printk(KERN_INFO "tdfx_vid:failed copy from userspace\n");
+      return(-EFAULT); 
+    }
+    return bump_fifo(int16);
+  case TDFX_VID_BLIT:
+    if(copy_from_user(&blit,(tdfx_vid_blit_t*)arg,sizeof(tdfx_vid_blit_t))) {
+      printk(KERN_INFO "tdfx_vid:failed copy from userspace\n");
+      return(-EFAULT); 
+    }
+    if(!tdfx_vid_blit(&blit)) {
+      printk(KERN_INFO "tdfx_vid: Blit failed\n");
+      return(-EFAULT); 
+    }
+    return 0;
+  case TDFX_VID_GET_CONFIG:
+    if(copy_from_user(&cfg,(tdfx_vid_config_t*)arg,sizeof(tdfx_vid_config_t))) {
+      printk(KERN_INFO "tdfx_vid:failed copy from userspace\n");
+      return(-EFAULT); 
+    }
+    tdfx_vid_get_config(&cfg);
+    if(copy_to_user((tdfx_vid_config_t*)arg,&cfg,sizeof(tdfx_vid_config_t))) {
+      printk(KERN_INFO "tdfx_vid:failed copy to userspace\n");
+      return(-EFAULT); 
+    }
+    return 0;
+  case TDFX_VID_SET_YUV:
+    return tdfx_vid_set_yuv(arg);
+  case TDFX_VID_GET_YUV:
+    return tdfx_vid_get_yuv(arg);
+  default:
+    printk(KERN_ERR "tdfx_vid: Invalid ioctl %d\n",cmd);
+    return (-EINVAL);
+  } 
+  return 0;
+}
+
+
+
+static ssize_t tdfx_vid_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	return 0;
+}
+
+static ssize_t tdfx_vid_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+
+	return 0;
+}
+
+
+static int tdfx_vid_mmap(struct file *file, struct vm_area_struct *vma)
+{
+  size_t size;
+  //u32 pages;
+#ifdef MP_DEBUG
+  printk(KERN_DEBUG "tdfx_vid: mapping agp memory into userspace\n");
+#endif
+
+  if(agp_mem)
+    return(-EAGAIN);
+	
+  size = (vma->vm_end-vma->vm_start + PAGE_SIZE - 1) / PAGE_SIZE;
+  
+  agp_mem = drm_agp->allocate_memory(size,AGP_NORMAL_MEMORY);
+  if(!agp_mem) {
+    printk(KERN_ERR "Failed to allocate AGP memory\n");
+    return(-ENOMEM);
+  }
+
+  if(drm_agp->bind_memory(agp_mem,0)) {
+    printk(KERN_ERR "Failed to bind the AGP memory\n");
+    drm_agp->free_memory(agp_mem);
+    agp_mem = NULL;
+    return(-ENOMEM);
+  }
+
+  printk(KERN_INFO "%d pages of AGP mem allocated (%ld/%ld bytes) :)))\n",
+	 size,vma->vm_end-vma->vm_start,size*PAGE_SIZE);
+
+  //setup_fifo(0,size);
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,3)
+  if(remap_page_range(vma, vma->vm_start,agp_info.aper_base,
+		      vma->vm_end - vma->vm_start, vma->vm_page_prot)) 
+#else
+  if(remap_page_range(vma->vm_start, (unsigned long)agp_info.aper_base,
+		      vma->vm_end - vma->vm_start, vma->vm_page_prot)) 
+#endif
+    {
+      printk(KERN_ERR "tdfx_vid: error mapping video memory\n");
+      return(-EAGAIN);
+    }
+
+  
+  printk(KERN_INFO "AGP Mem mapped in user space !!!!!\n");
+
+  return 0;
+}
+
+
+static int tdfx_vid_release(struct inode *inode, struct file *file)
+{
+  //Close the window just in case
+#ifdef MP_DEBUG
+  printk(KERN_DEBUG "tdfx_vid: Video OFF (release)\n");
+#endif
+
+  // Release the agp mem
+  if(agp_mem) {
+    drm_agp->unbind_memory(agp_mem);
+    drm_agp->free_memory(agp_mem);
+    agp_mem = NULL;
+  }
+  
+  tdfx_vid_in_use = 0;
+
+  MOD_DEC_USE_COUNT;
+  return 0;
+}
+
+static long long tdfx_vid_lseek(struct file *file, long long offset, int origin)
+{
+	return -ESPIPE;
+}					 
+
+static int tdfx_vid_open(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,2)
+	int minor = MINOR(inode->i_rdev.value);
+#else
+	int minor = MINOR(inode->i_rdev);
+#endif
+
+	if(minor != 0)
+	 return(-ENXIO);
+
+	if(tdfx_vid_in_use == 1) 
+		return(-EBUSY);
+
+	tdfx_vid_in_use = 1;
+	MOD_INC_USE_COUNT;
+	return(0);
+}
+
+#if LINUX_VERSION_CODE >= 0x020400
+static struct file_operations tdfx_vid_fops =
+{
+  llseek:  tdfx_vid_lseek,
+  read:	   tdfx_vid_read,
+  write:   tdfx_vid_write,
+  ioctl:   tdfx_vid_ioctl,
+  mmap:	   tdfx_vid_mmap,
+  open:    tdfx_vid_open,
+  release: tdfx_vid_release
+};
+#else
+static struct file_operations tdfx_vid_fops =
+{
+  tdfx_vid_lseek,
+  tdfx_vid_read,
+  tdfx_vid_write,
+  NULL,
+  NULL,
+  tdfx_vid_ioctl,
+  tdfx_vid_mmap,
+  tdfx_vid_open,
+  NULL,
+  tdfx_vid_release
+};
+#endif
+
+
+int init_module(void)
+{
+  tdfx_vid_in_use = 0;
+
+  if(register_chrdev(TDFX_VID_MAJOR, "tdfx_vid", &tdfx_vid_fops)) {
+    printk(KERN_ERR "tdfx_vid: unable to get major: %d\n", TDFX_VID_MAJOR);
+    return -EIO;
+  }
+
+  if(!agp_init()) {
+    printk(KERN_ERR "tdfx_vid: AGP init failed\n");
+    unregister_chrdev(TDFX_VID_MAJOR, "tdfx_vid");
+    return -EINVAL;
+  }
+
+  if (!tdfx_vid_find_card()) {
+    printk(KERN_ERR "tdfx_vid: no supported devices found\n");
+    agp_close();
+    unregister_chrdev(TDFX_VID_MAJOR, "tdfx_vid");
+    return -EINVAL;
+  }
+
+  
+
+  return (0);
+
+}
+
+void cleanup_module(void)
+{
+  if(tdfx_mmio_base)
+    iounmap(tdfx_mmio_base);
+  agp_close();
+  printk(KERN_INFO "tdfx_vid: Cleaning up module\n");
+  unregister_chrdev(TDFX_VID_MAJOR, "tdfx_vid");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/tdfx_vid.h	Fri Mar 07 18:42:08 2003 +0000
@@ -0,0 +1,74 @@
+
+
+#define TDFX_VID_VERSION 1
+
+#define TDFX_VID_MOVE_2_PACKED  0
+#define TDFX_VID_MOVE_2_YUV     1
+#define TDFX_VID_MOVE_2_3D      2
+#define TDFX_VID_MOVE_2_TEXTURE 3
+
+#define TDFX_VID_FORMAT_BGR1  (('B'<<24)|('G'<<16)|('R'<<8)|1)
+#define TDFX_VID_FORMAT_BGR8  (('B'<<24)|('G'<<16)|('R'<<8)|8)
+#define TDFX_VID_FORMAT_BGR16 (('B'<<24)|('G'<<16)|('R'<<8)|16)
+#define TDFX_VID_FORMAT_BGR24 (('B'<<24)|('G'<<16)|('R'<<8)|24)
+#define TDFX_VID_FORMAT_BGR32 (('B'<<24)|('G'<<16)|('R'<<8)|32)
+
+#define TDFX_VID_FORMAT_YUY2 (('2'<<24)|('Y'<<16)|('U'<<8)|'Y')
+#define TDFX_VID_FORMAT_UYVY (('U'<<24)|('Y'<<16)|('V'<<8)|'Y')
+
+#define TDFX_VID_FORMAT_YV12 0x32315659
+#define TDFX_VID_FORMAT_IYUV (('I'<<24)|('Y'<<16)|('U'<<8)|'V')
+#define TDFX_VID_FORMAT_I420 (('I'<<24)|('4'<<16)|('2'<<8)|'0')
+
+#define TDFX_VID_YUV_STRIDE        (1024)
+#define TDFX_VID_YUV_PLANE_SIZE    (0x0100000)
+                                 
+
+typedef struct tdfx_vid_blit_s {
+  uint32_t src;
+  uint32_t src_stride;
+  uint16_t src_x,src_y;
+  uint16_t src_w,src_h;
+  uint32_t src_format;
+
+  uint32_t  dst;
+  uint32_t dst_stride;
+  uint16_t dst_x,dst_y;
+  uint16_t dst_w,dst_h;
+  uint32_t dst_format;
+} tdfx_vid_blit_t;
+
+typedef struct tdfx_vid_config_s {
+  uint16_t version;
+  uint16_t card_type;
+  uint32_t ram_size;
+  uint16_t screen_width;
+  uint16_t screen_height;
+  uint16_t screen_stride;
+  uint32_t screen_format;
+  uint32_t screen_start;
+} tdfx_vid_config_t;
+
+typedef struct tdfx_vid_agp_move_s {
+  uint16_t move2;
+  uint16_t width,height;
+
+  uint32_t src;
+  uint32_t src_stride;
+
+  uint32_t dst;
+  uint32_t dst_stride;
+} tdfx_vid_agp_move_t;
+
+typedef struct tdfx_vid_yuv_s {
+  uint32_t base;
+  uint16_t stride;
+} tdfx_vid_yuv_t;
+
+#define TDFX_VID_GET_CONFIG _IOR('J', 1, tdfx_vid_config_t)
+#define TDFX_VID_AGP_MOVE _IOR('J', 2, tdfx_vid_agp_move_t)
+#define TDFX_VID_BLIT _IOR('J', 3, tdfx_vid_blit_t)
+#define TDFX_VID_SET_YUV _IOR('J', 4, tdfx_vid_blit_t)
+#define TDFX_VID_GET_YUV _IOR('J', 5, tdfx_vid_blit_t)
+
+#define TDFX_VID_BUMP0 _IOR('J', 6, u16)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/drivers/tdfx_vid_tst.c	Fri Mar 07 18:42:08 2003 +0000
@@ -0,0 +1,98 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <inttypes.h>
+
+#include "tdfx_vid.h"
+
+
+static void print_tdfd_vid_cfg(tdfx_vid_config_t* cfg) {
+     printf("tdfx_vid version %d\n"
+	    "  Ram: %d\n"
+	    "  Screen: %d x %d\n",
+	    cfg->version,
+	    cfg->ram_size,
+	    cfg->screen_width, cfg->screen_height);	    
+}
+     
+
+int main(int argc, char** argv) {
+  int fd,i;
+  unsigned char *mem,*ptr;
+  tdfx_vid_agp_move_t move;
+  tdfx_vid_config_t cfg;
+  tdfx_vid_blit_t blit;
+
+  fd = open("/dev/tdfx_vid", O_RDWR);
+
+  if(fd <= 0) {
+    printf("Can't open /dev/tdfx_vid\n");
+    return 1;
+  }
+
+  if(ioctl(fd,TDFX_VID_GET_CONFIG,&cfg)) {
+    printf("Ioctl GET_CONFIG error\n");
+    close(fd);
+    return 1;
+  }
+  
+  print_tdfd_vid_cfg(&cfg);
+
+  mem = mmap( NULL, 640*480*2, PROT_READ | PROT_WRITE, MAP_SHARED,
+	      fd, 0);
+
+  if(mem == MAP_FAILED) {
+    printf("Memmap failed !!!!!\n");
+    return 1;
+  }
+
+/*   for(ptr = mem, i = 0 ; i < 640*480 ; i++) { */
+/*     ptr[0] = i & 0xFF; */
+/*     ptr[1] = (i & 0xFF); */
+/*     ptr += 2; */
+/*   } */
+    
+  memset(mem,0xFF,640*480*2);
+  
+  memset(&move, 0, sizeof(tdfx_vid_agp_move_t));
+  move.width = 640;
+  move.height = 240;
+  move.src_stride = 640;
+  move.dst_stride = 640*2;
+
+  if(ioctl(fd,TDFX_VID_AGP_MOVE,&move)) {
+    printf("AGP Move failed !!!!\n");
+    return 0;
+  }
+  
+  printf("AGP Move ????\n");
+  sleep(1);
+
+  blit.src = 0;
+  blit.src_stride = 640*2;
+  blit.src_x = blit.src_y = 0;
+  blit.src_w = 320;
+  blit.src_h = 240;
+  blit.src_format = cfg.screen_format;
+
+  blit.dst = 240*640*2+320;
+  blit.dst_stride = 640*2;
+  blit.dst_x = blit.dst_y = 0;
+  blit.dst_w = 320;
+  blit.dst_h = 240;
+  blit.dst_format = cfg.screen_format;
+
+  if(ioctl(fd,TDFX_VID_BLIT,&blit)) {
+    printf("Blit failed !!!!\n");
+    return 0;
+  }
+  
+  close(fd);
+  return 1;
+}