Mercurial > mplayer.hg
view libvo/vo_3dfx.c @ 31769:c6e58ec31959
MPlayer is licensed as GPL version 2, not any later version.
author | diego |
---|---|
date | Wed, 28 Jul 2010 05:19:36 +0000 |
parents | 0f1b5b68af32 |
children | ddb45e9443ec |
line wrap: on
line source
/* * video_out_3dfx.c * Heavily based on video_out_mga.c of Aaron Holtzman's mpeg2dec. * * Copyright (C) Colin Cross Apr 2000 * * 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 <stdio.h> #include <stdlib.h> #include <string.h> #include "config.h" #include "mp_msg.h" #include "help_mp.h" #include "video_out.h" #include "video_out_internal.h" #include "x11_common.h" #include <sys/ioctl.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <errno.h> #include <wchar.h> #include <signal.h> #include <X11/Xlib.h> #include <X11/extensions/xf86dga.h> #include <X11/Xutil.h> //#define LOG(x) syslog(LOG_USER | LOG_DEBUG,x) #define LOG(x) #include "drivers/3dfx.h" #include "fastmemcpy.h" static const vo_info_t info = { "3dfx (/dev/3dfx)", "3dfx", "Colin Cross <colin@MIT.EDU>", "" }; const LIBVO_EXTERN(3dfx) static uint32_t is_fullscreen = 1; static uint32_t vidwidth; static uint32_t vidheight; static uint32_t screenwidth; static uint32_t screenheight; static uint32_t screendepth = 2; //Only 16bpp supported right now static uint32_t dispwidth = 1280; // You can change these to whatever you want static uint32_t dispheight = 720; // 16:9 screen ratio?? static uint32_t dispx; static uint32_t dispy; static uint32_t *vidpage0; static uint32_t *vidpage1; static uint32_t *vidpage2; static uint32_t vidpage0offset; static uint32_t vidpage1offset; static uint32_t vidpage2offset; // Current pointer into framebuffer where display is located static uint32_t targetoffset; static uint32_t page_space; static voodoo_io_reg *reg_IO; static voodoo_2d_reg *reg_2d; static voodoo_yuv_reg *reg_YUV; static voodoo_yuv_fb *fb_YUV; static uint32_t *memBase0, *memBase1; static uint32_t baseAddr0, baseAddr1; /* X11 related variables */ static Display *display; static Window mywindow; static int bpp; static XWindowAttributes attribs; static int fd=-1; static void restore(void) { //reg_IO->vidDesktopStartAddr = vidpage0offset; XF86DGADirectVideo(display,0,0); } static void sighup(int foo) { //reg_IO->vidDesktopStartAddr = vidpage0offset; XF86DGADirectVideo(display,0,0); exit(0); } static void restore_regs(voodoo_2d_reg *regs) { reg_2d->commandExtra = regs->commandExtra; reg_2d->clip0Min = regs->clip0Min; reg_2d->clip0Max = regs->clip0Max; reg_2d->srcBaseAddr = regs->srcBaseAddr; reg_2d->srcXY = regs->srcXY; reg_2d->srcFormat = regs->srcFormat; reg_2d->srcSize = regs->srcSize; reg_2d->dstBaseAddr = regs->dstBaseAddr; reg_2d->dstXY = regs->dstXY; reg_2d->dstFormat = regs->dstFormat; reg_2d->dstSize = regs->dstSize; reg_2d->command = 0; } static uint32_t create_window(Display *display, char *title) { int screen; unsigned int fg, bg; XSizeHints hint; XVisualInfo vinfo; XEvent xev; Colormap theCmap; XSetWindowAttributes xswa; unsigned long xswamask; screen = DefaultScreen(display); hint.x = 0; hint.y = 10; hint.width = dispwidth; hint.height = dispheight; hint.flags = PPosition | PSize; bg = WhitePixel(display, screen); fg = BlackPixel(display, screen); XGetWindowAttributes(display, DefaultRootWindow(display), &attribs); bpp = attribs.depth; if (bpp != 16) { mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_3DFX_Only16BppSupported); exit(-1); } XMatchVisualInfo(display,screen,bpp,TrueColor,&vinfo); mp_msg(MSGT_VO,MSGL_INFO, MSGTR_LIBVO_3DFX_VisualIdIs,vinfo.visualid); theCmap = XCreateColormap(display, RootWindow(display,screen), vinfo.visual, AllocNone); xswa.background_pixel = 0; xswa.border_pixel = 1; xswa.colormap = theCmap; xswamask = CWBackPixel | CWBorderPixel |CWColormap; mywindow = XCreateWindow(display, RootWindow(display,screen), hint.x, hint.y, hint.width, hint.height, 4, bpp,CopyFromParent,vinfo.visual,xswamask,&xswa); vo_x11_classhint( display,mywindow,"3dfx" ); XSelectInput(display, mywindow, StructureNotifyMask); /* Tell other applications about this window */ XSetStandardProperties(display, mywindow, title, title, None, NULL, 0, &hint); /* Map window. */ XMapWindow(display, mywindow); /* Wait for map. */ do { XNextEvent(display, &xev); } while (xev.type != MapNotify || xev.xmap.event != mywindow); XSelectInput(display, mywindow, NoEventMask); XSync(display, False); return 0; } static void dump_yuv_planar(uint32_t *y, uint32_t *u, uint32_t *v, uint32_t to, uint32_t width, uint32_t height) { // YUV conversion works like this: // // We write the Y, U, and V planes separately into 3dfx YUV Planar memory // region. The nice chip then takes these and packs them into the YUYV // format in the regular frame buffer, starting at yuvBaseAddr, page 2 here. // Then we tell the 3dfx to do a Screen to Screen Stretch BLT to copy all // of the data on page 2 onto page 1, converting it to 16 bpp RGB as it goes. // The result is a nice image on page 1 ready for display. uint32_t j; uint32_t y_imax, uv_imax, jmax; reg_YUV->yuvBaseAddr = to; reg_YUV->yuvStride = screenwidth*2; LOG("video_out_3dfx: starting planar dump\n"); jmax = height>>1; // vidheight/2, height of U and V planes y_imax = width; // Y plane is twice as wide as U and V planes uv_imax = width>>1; // vidwidth/2/4, width of U and V planes in 32-bit words for (j=0;j<jmax;j++) { //XXX this should be hand-rolled 32 bit memcpy for safeness. fast_memcpy(fb_YUV->U + (uint32_t) VOODOO_YUV_STRIDE* j ,((uint8_t*) u) + uv_imax* j , uv_imax); fast_memcpy(fb_YUV->V + (uint32_t) VOODOO_YUV_STRIDE* j ,((uint8_t*) v) + uv_imax* j , uv_imax); fast_memcpy(fb_YUV->Y + (uint32_t) VOODOO_YUV_STRIDE* (j<<1) ,((uint8_t*) y) + y_imax * (j<<1) , y_imax); fast_memcpy(fb_YUV->Y + (uint32_t) VOODOO_YUV_STRIDE*((j<<1)+1),((uint8_t*) y) + y_imax *((j<<1)+1), y_imax); } LOG("video_out_3dfx: done planar dump\n"); } static void screen_to_screen_stretch_blt(uint32_t to, uint32_t from, uint32_t width, uint32_t height) { //FIXME - this function should be called by a show_frame function that // uses a series of blts to show only those areas not covered // by another window voodoo_2d_reg saved_regs; LOG("video_out_3dfx: saving registers\n"); // Save VGA regs (so X kinda works when we're done) saved_regs = *reg_2d; /* The following lines set up the screen to screen stretch blt from page2 to page 1 */ LOG("video_out_3dfx: setting blt registers\n"); reg_2d->commandExtra = 4; //disable colorkeying, enable wait for v-refresh (0100b) reg_2d->clip0Min = 0; reg_2d->clip0Max = 0xFFFFFFFF; //no clipping reg_2d->srcBaseAddr = from; reg_2d->srcXY = 0; reg_2d->srcFormat = screenwidth*2 | VOODOO_BLT_FORMAT_YUYV; // | 1<<21; reg_2d->srcSize = vidwidth | (vidheight << 16); reg_2d->dstBaseAddr = to; reg_2d->dstXY = 0; reg_2d->dstFormat = screenwidth*2 | VOODOO_BLT_FORMAT_16; reg_2d->dstSize = width | (height << 16); LOG("video_out_3dfx: starting blt\n"); // Executes screen to screen stretch blt reg_2d->command = 2 | 1<<8 | 0xCC<<24; LOG("video_out_3dfx: restoring regs\n"); restore_regs(&saved_regs); LOG("video_out_3dfx: done blt\n"); } static void update_target(void) { uint32_t xp, yp, w, h, b, d; Window root; XGetGeometry(display,mywindow,&root,&xp,&yp,&w,&h,&b,&d); XTranslateCoordinates(display,mywindow,root,0,0,&xp,&yp,&root); dispx = (uint32_t) xp; dispy = (uint32_t) yp; dispwidth = (uint32_t) w; dispheight = (uint32_t) h; if (is_fullscreen) targetoffset = vidpage0offset + (screenheight - dispheight)/2*screenwidth*screendepth + (screenwidth-dispwidth)/2*screendepth; else targetoffset = vidpage0offset + (dispy*screenwidth + dispx)*screendepth; } static int config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t fullscreen, char *title, uint32_t format) { char *name = ":0.0"; pioData data; uint32_t retval; //TODO use x11_common for X and window handling if(getenv("DISPLAY")) name = getenv("DISPLAY"); display = XOpenDisplay(name); screenwidth = XDisplayWidth(display,0); screenheight = XDisplayHeight(display,0); page_space = screenwidth*screenheight*screendepth; vidpage0offset = 0; vidpage1offset = page_space; // Use third and fourth pages vidpage2offset = page_space*2; signal(SIGALRM,sighup); //alarm(120); // Open driver device if ( fd == -1 ) { mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_3DFX_UnableToOpenDevice); return -1; } // Store sizes for later vidwidth = width; vidheight = height; is_fullscreen = fullscreen = 0; if (!is_fullscreen) create_window(display, title); // Ask 3dfx driver for base memory address 0 data.port = 0x10; // PCI_BASE_ADDRESS_0_LINUX; data.size = 4; data.value = &baseAddr0; data.device = 0; if ((retval = ioctl(fd,_IOC(_IOC_READ,'3',3,0),&data)) < 0) { mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_3DFX_Error,retval); return -1; } // Ask 3dfx driver for base memory address 1 data.port = 0x14; // PCI_BASE_ADDRESS_1_LINUX; data.size = 4; data.value = &baseAddr1; data.device = 0; if ((retval = ioctl(fd,_IOC(_IOC_READ,'3',3,0),&data)) < 0) { mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_3DFX_Error,retval); return -1; } // Map all 3dfx memory areas memBase0 = mmap(0,0x1000000,PROT_READ | PROT_WRITE,MAP_SHARED,fd,baseAddr0); memBase1 = mmap(0,3*page_space,PROT_READ | PROT_WRITE,MAP_SHARED,fd,baseAddr1); if (memBase0 == (uint32_t *) 0xFFFFFFFF || memBase1 == (uint32_t *) 0xFFFFFFFF) { mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_3DFX_CouldntMapMemoryArea, memBase0,memBase1,errno); } // Set up global pointers reg_IO = (void *)memBase0 + VOODOO_IO_REG_OFFSET; reg_2d = (void *)memBase0 + VOODOO_2D_REG_OFFSET; reg_YUV = (void *)memBase0 + VOODOO_YUV_REG_OFFSET; fb_YUV = (void *)memBase0 + VOODOO_YUV_PLANE_OFFSET; vidpage0 = (void *)memBase1 + (unsigned long int)vidpage0offset; vidpage1 = (void *)memBase1 + (unsigned long int)vidpage1offset; vidpage2 = (void *)memBase1 + (unsigned long int)vidpage2offset; // Clear pages 1,2,3 // leave page 0, that belongs to X. // So does part of 1. Oops. memset(vidpage1,0x00,page_space); memset(vidpage2,0x00,page_space); if (is_fullscreen) memset(vidpage0,0x00,page_space); #ifndef VOODOO_DEBUG // Show page 0 (unblanked) reg_IO->vidDesktopStartAddr = vidpage0offset; /* Stop X from messing with my video registers! Find a better way to do this? Currently I use DGA to tell XF86 to not screw with registers, but I can't really use it to do FB stuff because I need to know the absolute FB position and offset FB position to feed to BLT command */ //XF86DGADirectVideo(display,0,XF86DGADirectGraphics); //| XF86DGADirectMouse | XF86DGADirectKeyb); #endif atexit(restore); mp_msg(MSGT_VO,MSGL_INFO, MSGTR_LIBVO_3DFX_DisplayInitialized,memBase1); return 0; } static int draw_frame(uint8_t *src[]) { LOG("video_out_3dfx: starting display_frame\n"); // Put packed data onto page 2 dump_yuv_planar((uint32_t *)src[0],(uint32_t *)src[1],(uint32_t *)src[2], vidpage2offset,vidwidth,vidheight); LOG("video_out_3dfx: done display_frame\n"); return 0; } static int //draw_slice(uint8_t *src[], uint32_t slice_num) draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y) { uint32_t target; target = vidpage2offset + (screenwidth*2 * y); dump_yuv_planar((uint32_t *)src[0],(uint32_t *)src[1],(uint32_t *)src[2],target,vidwidth,h); return 0; } static void draw_osd(void) { } static void flip_page(void) { //FIXME - update_target() should be called by event handler when window // is resized or moved update_target(); LOG("video_out_3dfx: calling blt function\n"); screen_to_screen_stretch_blt(targetoffset, vidpage2offset, dispwidth, dispheight); } static int query_format(uint32_t format) { /* does this supports scaling? up & down? */ switch(format){ case IMGFMT_YV12: // case IMGFMT_YUY2: // case IMGFMT_RGB|24: // case IMGFMT_BGR|24: return VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW; } return 0; } static void uninit(void) { if( fd != -1 ) close(fd); } static void check_events(void) { } static int preinit(const char *arg) { if ( (fd = open("/dev/3dfx",O_RDWR) ) == -1) { mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_3DFX_UnableToOpenDevice); return -1; } if(arg) { mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_3DFX_UnknownSubdevice,arg); return ENOSYS; } return 0; } static int control(uint32_t request, void *data, ...) { switch (request) { case VOCTRL_QUERY_FORMAT: return query_format(*((uint32_t*)data)); } return VO_NOTIMPL; }