Mercurial > mplayer.hg
view drivers/tdfx_vid.c @ 27815:f92271dc5f17
Remove X11 backing store: this is now a useless flag.
Also, it is mandatory for Xserver 1.5.x (part of Xorg 7.4, shipped on all
Linux distributions starting from Oct. 08) and will be removed
from Xserver 1.6 anyhow ...
Patch by Stephane Marchesin (marchesin at icps dot u dash strasbg dot fr).
For more info, see long flame thread at:
http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/2008-August/058323.html
author | ben |
---|---|
date | Wed, 29 Oct 2008 22:03:36 +0000 |
parents | 60e75a146f45 |
children | 0f1b5b68af32 |
line wrap: on
line source
/* * Copyright (C) 2003 Alban Bedel * * This file is part of MPlayer. * * MPlayer is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * MPlayer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with MPlayer; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #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"); MODULE_DESCRIPTION("A driver for Banshee targeted for video app"); #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 uint32_t tdfx_io_base = 0; 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 __initdata int tdfx_map_io = 1; static __initdata unsigned long map_start = 0; //0x7300000; static __initdata unsigned long map_max = (10*1024*1024); MODULE_PARM(tdfx_map_io,"i"); MODULE_PARM_DESC(tdfx_map_io, "Set to 0 to use the page fault handler (you need to patch agpgart_be.c to allow the mapping in user space)\n"); MODULE_PARM(map_start,"l"); MODULE_PARM_DESC(map_start,"Use a block of physical mem instead of the agp arerture."); MODULE_PARM(map_max,"l"); MODULE_PARM_DESC(map_max, "Maximum amout of physical memory (in bytes) that can be used\n"); 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; tdfx_io_base = dev->resource[2].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; tdfx_io_base = dev->base_address[2] & PCI_BASE_ADDRESS_MEM_MASK; #endif printk(KERN_INFO "tdfx_vid: MMIO at 0x%p\n", 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(); inter_module_put("drm_agp"); } static int agp_move(tdfx_vid_agp_move_t* m) { u32 src = 0; u32 src_h,src_l; if(!(agp_mem||map_start)) return -EAGAIN; if(m->move2 > 3) { printk(KERN_DEBUG "tdfx_vid: AGP move invalid destination %d\n", m->move2); return -EAGAIN; } if(map_start) src = map_start + m->src; else 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(); return 0; } #if 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(); } #endif 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: unknown screen format %d\n",in); cfg->screen_format = 0; break; } cfg->screen_stride = tdfx_inl(VIDDESKSTRIDE) & 0x7FFF; 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_BGR15: // To check tdfx_fmt = 2; 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,cmd = 2; u32 cmin,cmax,srcbase,srcxy,srcfmt,srcsize; u32 dstbase,dstxy,dstfmt,dstsize = 0; u32 cmd_extra = 0,src_ck[2],dst_ck[2],rop123=0; //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; blit->colorkey &= 0x3; // Be nice if user just want a simple blit if((!blit->colorkey) && (!blit->rop[0])) blit->rop[0] = TDFX_VID_ROP_COPY; // No stretch : fix me the cmd should be 1 but it // doesn't work. Maybe some other regs need to be set // as non-stretch blit have more options if(((!blit->dst_w) && (!blit->dst_h)) || ((blit->dst_w == blit->src_w) && (blit->dst_h == blit->src_h))) cmd = 2; // Save the regs otherwise fb get crazy // we can perhaps avoid some ... banshee_wait_idle(); 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); if(cmd == 2) dstsize = tdfx_inl(DSTSIZE); if(blit->colorkey & TDFX_VID_SRC_COLORKEY) { src_ck[0] = tdfx_inl(SRCCOLORKEYMIN); src_ck[1] = tdfx_inl(SRCCOLORKEYMAX); tdfx_outl(SRCCOLORKEYMIN,blit->src_colorkey[0]); tdfx_outl(SRCCOLORKEYMAX,blit->src_colorkey[1]); } if(blit->colorkey & TDFX_VID_DST_COLORKEY) { dst_ck[0] = tdfx_inl(DSTCOLORKEYMIN); dst_ck[1] = tdfx_inl(DSTCOLORKEYMAX); tdfx_outl(SRCCOLORKEYMIN,blit->dst_colorkey[0]); tdfx_outl(SRCCOLORKEYMAX,blit->dst_colorkey[1]); } if(blit->colorkey) { cmd_extra = tdfx_inl(COMMANDEXTRA_2D); rop123 = tdfx_inl(ROP123); tdfx_outl(COMMANDEXTRA_2D, blit->colorkey); tdfx_outl(ROP123,(blit->rop[1] | (blit->rop[2] << 8) | blit->rop[3] << 16)); } // Get rid of the clipping at the moment 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); if(cmd == 2) tdfx_outl(DSTSIZE,XYREG(blit->dst_w,blit->dst_h)); // Send the command tdfx_outl(COMMAND_2D,cmd | 0x100 | (blit->rop[0] << 24)); banshee_wait_idle(); // Now restore the regs to make fb happy 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); if(cmd == 2) tdfx_outl(DSTSIZE, dstsize); if(blit->colorkey & TDFX_VID_SRC_COLORKEY) { tdfx_outl(SRCCOLORKEYMIN,src_ck[0]); tdfx_outl(SRCCOLORKEYMAX,src_ck[1]); } if(blit->colorkey & TDFX_VID_DST_COLORKEY) { tdfx_outl(SRCCOLORKEYMIN,dst_ck[0]); tdfx_outl(SRCCOLORKEYMAX,dst_ck[1]); } if(blit->colorkey) { tdfx_outl(COMMANDEXTRA_2D,cmd_extra); tdfx_outl(ROP123,rop123); } 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_set_overlay(unsigned long arg) { tdfx_vid_overlay_t ov; uint32_t screen_w,screen_h; uint32_t vidcfg,stride,vidbuf; int disp_w,disp_h; if(copy_from_user(&ov,(tdfx_vid_overlay_t*)arg,sizeof(tdfx_vid_overlay_t))) { printk(KERN_DEBUG "tdfx_vid:failed copy from userspace\n"); return -EFAULT; } if(ov.dst_y < 0) { int shift; if(-ov.dst_y >= ov.src_height) { printk(KERN_DEBUG "tdfx_vid: Overlay outside of the screen ????\n"); return -EFAULT; } shift = (-ov.dst_y)/(double)ov.dst_height*ov.src_height; ov.src[0] += shift*ov.src_stride; ov.src_height -= shift; ov.dst_height += ov.dst_y; ov.dst_y = 0; } if(ov.dst_x < 0) { int shift; if(-ov.dst_x >= ov.src_width) { printk(KERN_DEBUG "tdfx_vid: Overlay outside of the screen ????\n"); return -EFAULT; } shift = (-ov.dst_x)/(double)ov.dst_width*ov.src_width; shift = ((shift+3)/2)*2; ov.src[0] += shift*2; ov.src_width -= shift; ov.dst_width += ov.dst_x; ov.dst_x = 0; } vidcfg = tdfx_inl(VIDPROCCFG); // clear the overlay fmt vidcfg &= ~(7 << 21); switch(ov.format) { case TDFX_VID_FORMAT_BGR15: vidcfg |= (1 << 21); break; case TDFX_VID_FORMAT_BGR16: vidcfg |= (7 << 21); break; case TDFX_VID_FORMAT_YUY2: vidcfg |= (5 << 21); break; case TDFX_VID_FORMAT_UYVY: vidcfg |= (6 << 21); break; default: printk(KERN_DEBUG "tdfx_vid: Invalid overlay fmt 0x%x\n",ov.format); return -EFAULT; } // YUV422 need 4 bytes aligned stride and address if((ov.format == TDFX_VID_FORMAT_YUY2 || ov.format == TDFX_VID_FORMAT_UYVY)) { if((ov.src_stride & ~0x3) != ov.src_stride) { printk(KERN_DEBUG "tdfx_vid: YUV need a 4 bytes aligned stride %d\n",ov.src_stride); return -EFAULT; } if((ov.src[0] & ~0x3) != ov.src[0] || (ov.src[1] & ~0x3) != ov.src[1]){ printk(KERN_DEBUG "tdfx_vid: YUV need a 4 bytes aligned address 0x%x 0x%x\n",ov.src[0],ov.src[1]); return -EFAULT; } } // Now we have a good input format // but first get the screen size to check a bit // if the size/position is valid screen_w = tdfx_inl(VIDSCREENSIZE); screen_h = (screen_w >> 12) & 0xFFF; screen_w &= 0xFFF; disp_w = ov.dst_x + ov.dst_width >= screen_w ? screen_w - ov.dst_x : ov.dst_width; disp_h = ov.dst_y + ov.dst_height >= screen_h ? screen_h - ov.dst_y : ov.dst_height; if(ov.dst_x >= screen_w || ov.dst_y >= screen_h || disp_h <= 0 || disp_h > screen_h || disp_w <= 0 || disp_w > screen_w) { printk(KERN_DEBUG "tdfx_vid: Invalid overlay dimension and/or position\n"); return -EFAULT; } // Setup the vidproc // H scaling if(ov.src_width < ov.dst_width) vidcfg |= (1<<14); else vidcfg &= ~(1<<14); // V scaling if(ov.src_height < ov.dst_height) vidcfg |= (1<<15); else vidcfg &= ~(1<<15); // Filtering can only be used in 1x mode if(!(vidcfg | (1<<26))) vidcfg |= (3<<16); else vidcfg &= ~(3<<16); // disable overlay stereo mode vidcfg &= ~(1<<2); // Colorkey on/off if(ov.use_colorkey) { // Colorkey inversion if(ov.invert_colorkey) vidcfg |= (1<<6); else vidcfg &= ~(1<<6); vidcfg |= (1<<5); } else vidcfg &= ~(1<<5); // Overlay isn't VidIn vidcfg &= ~(1<<9); // vidcfg |= (1<<8); tdfx_outl(VIDPROCCFG,vidcfg); // Start coord //printk(KERN_DEBUG "tdfx_vid: start %dx%d\n",ov.dst_x & 0xFFF,ov.dst_y & 0xFFF); tdfx_outl(VIDOVRSTARTCRD,(ov.dst_x & 0xFFF)|((ov.dst_y & 0xFFF)<<12)); // End coord tdfx_outl(VIDOVRENDCRD, ((ov.dst_x + disp_w-1) & 0xFFF)| (((ov.dst_y + disp_h-1) & 0xFFF)<<12)); // H Scaling tdfx_outl(VIDOVRDUDX,( ((u32)ov.src_width) << 20) / ov.dst_width); // Src offset and width (in bytes) tdfx_outl(VIDOVRDUDXOFF,((ov.src_width<<1) & 0xFFF) << 19); // V Scaling tdfx_outl(VIDOVRDVDY, ( ((u32)ov.src_height) << 20) / ov.dst_height); //else // tdfx_outl(VIDOVRDVDY,0); // V Offset tdfx_outl(VIDOVRDVDYOFF,0); // Overlay stride stride = tdfx_inl(VIDDESKSTRIDE) & 0xFFFF; tdfx_outl(VIDDESKSTRIDE,stride | (((u32)ov.src_stride) << 16)); // Buffers address tdfx_outl(LEFTOVBUF, ov.src[0]); tdfx_outl(RIGHTOVBUF, ov.src[1]); // Send a swap buffer cmd if we are not on one of the 2 buffers vidbuf = tdfx_inl(VIDCUROVRSTART); if(vidbuf != ov.src[0] && vidbuf != ov.src[1]) { tdfx_outl(SWAPPENDING,0); tdfx_outl(SWAPBUFCMD, 1); } //printk(KERN_DEBUG "tdfx_vid: Buf0=0x%x Buf1=0x%x Current=0x%x\n", // ov.src[0],ov.src[1],tdfx_inl(VIDCUROVRSTART)); // Colorkey if(ov.use_colorkey) { tdfx_outl(VIDCHRMIN,ov.colorkey[0]); tdfx_outl(VIDCHRMAX,ov.colorkey[1]); } return 0; } static int tdfx_vid_overlay_on(void) { uint32_t vidcfg = tdfx_inl(VIDPROCCFG); //return 0; if(vidcfg & (1<<8)) { // Overlay is already on //printk(KERN_DEBUG "tdfx_vid: Overlay is already on.\n"); return -EFAULT; } vidcfg |= (1<<8); tdfx_outl(VIDPROCCFG,vidcfg); return 0; } static int tdfx_vid_overlay_off(void) { uint32_t vidcfg = tdfx_inl(VIDPROCCFG); if(vidcfg & (1<<8)) { vidcfg &= ~(1<<8); tdfx_outl(VIDPROCCFG,vidcfg); return 0; } printk(KERN_DEBUG "tdfx_vid: Overlay is already off.\n"); return -EFAULT; } 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); case TDFX_VID_SET_OVERLAY: return tdfx_vid_set_overlay(arg); case TDFX_VID_OVERLAY_ON: return tdfx_vid_overlay_on(); case TDFX_VID_OVERLAY_OFF: return tdfx_vid_overlay_off(); 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 void tdfx_vid_mopen(struct vm_area_struct *vma) { int i; struct page *page; unsigned long phys; printk(KERN_DEBUG "tdfx_vid: mopen\n"); for(i = 0 ; i < agp_mem->page_count ; i++) { phys = agp_mem->memory[i] & ~(0x00000fff); page = virt_to_page(phys_to_virt(phys)); if(!page) { printk(KERN_DEBUG "tdfx_vid: Can't get the page %d\%d\n",i,agp_mem->page_count); return; } get_page(page); } MOD_INC_USE_COUNT; } static void tdfx_vid_mclose(struct vm_area_struct *vma) { int i; struct page *page; unsigned long phys; printk(KERN_DEBUG "tdfx_vid: mclose\n"); for(i = 0 ; i < agp_mem->page_count ; i++) { phys = agp_mem->memory[i] & ~(0x00000fff); page = virt_to_page(phys_to_virt(phys)); if(!page) { printk(KERN_DEBUG "tdfx_vid: Can't get the page %d\%d\n",i,agp_mem->page_count); return; } put_page(page); } MOD_DEC_USE_COUNT; } static struct page *tdfx_vid_nopage(struct vm_area_struct *vma, unsigned long address, int write_access) { unsigned long off; uint32_t n; struct page *page; unsigned long phys; off = address - vma->vm_start + (vma->vm_pgoff<<PAGE_SHIFT); n = off / PAGE_SIZE; if(n >= agp_mem->page_count) { printk(KERN_DEBUG "tdfx_vid: Too far away\n"); return (struct page *)0UL; } phys = agp_mem->memory[n] & ~(0x00000fff); page = virt_to_page(phys_to_virt(phys)); if(!page) { printk(KERN_DEBUG "tdfx_vid: Can't get the page\n"); return (struct page *)0UL; } return page; } /* memory handler functions */ static struct vm_operations_struct tdfx_vid_vm_ops = { open: tdfx_vid_mopen, /* mmap-open */ close: tdfx_vid_mclose,/* mmap-close */ nopage: tdfx_vid_nopage, /* no-page fault handler */ }; static int tdfx_vid_mmap(struct file *file, struct vm_area_struct *vma) { size_t size; #ifdef MP_DEBUG printk(KERN_DEBUG "tdfx_vid: mapping agp memory into userspace\n"); #endif size = (vma->vm_end-vma->vm_start + PAGE_SIZE - 1) / PAGE_SIZE; if(map_start) { // Ok we map directly in the physcal ram if(size*PAGE_SIZE > map_max) { printk(KERN_ERR "tdfx_vid: Not enouth mem\n"); return -EAGAIN; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,3) if(remap_page_range(vma, vma->vm_start,map_start, vma->vm_end - vma->vm_start, vma->vm_page_prot)) #else if(remap_page_range(vma->vm_start, (unsigned long)map_start, 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 "Physical mem 0x%lx mapped in userspace\n",map_start); return 0; } if(agp_mem) return -EAGAIN; 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); if(tdfx_map_io) { #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; } } else { // Never swap it out vma->vm_flags |= VM_LOCKED | VM_IO; vma->vm_ops = &tdfx_vid_vm_ops; vma->vm_ops->open(vma); printk(KERN_INFO "Page fault handler ready !!!!!\n"); } return 0; } static int tdfx_vid_release(struct inode *inode, struct file *file) { #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"); }