📄 tdfxfb.c
字号:
struct tdfx_par *par = info->par; int size = image->height * ((image->width * image->depth + 7) >> 3); int fifo_free; int i, stride = info->fix.line_length; u32 bpp = info->var.bits_per_pixel; u32 dstfmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13); u8 *chardata = (u8 *) image->data; u32 srcfmt; u32 dx = image->dx; u32 dy = image->dy; u32 dstbase = 0; if (image->depth != 1) {#ifdef BROKEN_CODE banshee_make_room(par, 6 + ((size + 3) >> 2)); srcfmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13) | 0x400000;#else cfb_imageblit(info, image);#endif return; } banshee_make_room(par, 9); switch (info->fix.visual) { case FB_VISUAL_PSEUDOCOLOR: tdfx_outl(par, COLORFORE, image->fg_color); tdfx_outl(par, COLORBACK, image->bg_color); break; case FB_VISUAL_TRUECOLOR: default: tdfx_outl(par, COLORFORE, par->palette[image->fg_color]); tdfx_outl(par, COLORBACK, par->palette[image->bg_color]); }#ifdef __BIG_ENDIAN srcfmt = 0x400000 | BIT(20);#else srcfmt = 0x400000;#endif /* asume always image->height < 4096 */ if (dy + image->height > 4095) { dstbase = stride * dy; dy = 0; } /* asume always image->width < 4096 */ if (dx + image->width > 4095) { dstbase += dx * bpp >> 3; dx = 0; } tdfx_outl(par, DSTBASE, dstbase); tdfx_outl(par, SRCXY, 0); tdfx_outl(par, DSTXY, dx | (dy << 16)); tdfx_outl(par, COMMAND_2D, COMMAND_2D_H2S_BITBLT | (TDFX_ROP_COPY << 24)); tdfx_outl(par, SRCFORMAT, srcfmt); tdfx_outl(par, DSTFORMAT, dstfmt); tdfx_outl(par, DSTSIZE, image->width | (image->height << 16)); /* A count of how many free FIFO entries we've requested. * When this goes negative, we need to request more. */ fifo_free = 0; /* Send four bytes at a time of data */ for (i = (size >> 2); i > 0; i--) { if (--fifo_free < 0) { fifo_free = 31; banshee_make_room(par, fifo_free); } tdfx_outl(par, LAUNCH_2D, *(u32 *)chardata); chardata += 4; } /* Send the leftovers now */ banshee_make_room(par, 3); switch (size % 4) { case 0: break; case 1: tdfx_outl(par, LAUNCH_2D, *chardata); break; case 2: tdfx_outl(par, LAUNCH_2D, *(u16 *)chardata); break; case 3: tdfx_outl(par, LAUNCH_2D, *(u16 *)chardata | (chardata[3] << 24)); break; }}#endif /* CONFIG_FB_3DFX_ACCEL */static int tdfxfb_cursor(struct fb_info *info, struct fb_cursor *cursor){ struct tdfx_par *par = info->par; u32 vidcfg; if (!hwcursor) return -EINVAL; /* just to force soft_cursor() call */ /* Too large of a cursor or wrong bpp :-( */ if (cursor->image.width > 64 || cursor->image.height > 64 || cursor->image.depth > 1) return -EINVAL; vidcfg = tdfx_inl(par, VIDPROCCFG); if (cursor->enable) tdfx_outl(par, VIDPROCCFG, vidcfg | VIDCFG_HWCURSOR_ENABLE); else tdfx_outl(par, VIDPROCCFG, vidcfg & ~VIDCFG_HWCURSOR_ENABLE); /* * If the cursor is not be changed this means either we want the * current cursor state (if enable is set) or we want to query what * we can do with the cursor (if enable is not set) */ if (!cursor->set) return 0; /* fix cursor color - XFree86 forgets to restore it properly */ if (cursor->set & FB_CUR_SETCMAP) { struct fb_cmap cmap = info->cmap; u32 bg_idx = cursor->image.bg_color; u32 fg_idx = cursor->image.fg_color; unsigned long bg_color, fg_color; fg_color = (((u32)cmap.red[fg_idx] & 0xff00) << 8) | (((u32)cmap.green[fg_idx] & 0xff00) << 0) | (((u32)cmap.blue[fg_idx] & 0xff00) >> 8); bg_color = (((u32)cmap.red[bg_idx] & 0xff00) << 8) | (((u32)cmap.green[bg_idx] & 0xff00) << 0) | (((u32)cmap.blue[bg_idx] & 0xff00) >> 8); banshee_make_room(par, 2); tdfx_outl(par, HWCURC0, bg_color); tdfx_outl(par, HWCURC1, fg_color); } if (cursor->set & FB_CUR_SETPOS) { int x = cursor->image.dx; int y = cursor->image.dy - info->var.yoffset; x += 63; y += 63; banshee_make_room(par, 1); tdfx_outl(par, HWCURLOC, (y << 16) + x); } if (cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE)) { /* * Voodoo 3 and above cards use 2 monochrome cursor patterns. * The reason is so the card can fetch 8 words at a time * and are stored on chip for use for the next 8 scanlines. * This reduces the number of times for access to draw the * cursor for each screen refresh. * Each pattern is a bitmap of 64 bit wide and 64 bit high * (total of 8192 bits or 1024 bytes). The two patterns are * stored in such a way that pattern 0 always resides in the * lower half (least significant 64 bits) of a 128 bit word * and pattern 1 the upper half. If you examine the data of * the cursor image the graphics card uses then from the * begining you see line one of pattern 0, line one of * pattern 1, line two of pattern 0, line two of pattern 1, * etc etc. The linear stride for the cursor is always 16 bytes * (128 bits) which is the maximum cursor width times two for * the two monochrome patterns. */ u8 __iomem *cursorbase = info->screen_base + info->fix.smem_len; u8 *bitmap = (u8 *)cursor->image.data; u8 *mask = (u8 *)cursor->mask; int i; fb_memset(cursorbase, 0, 1024); for (i = 0; i < cursor->image.height; i++) { int h = 0; int j = (cursor->image.width + 7) >> 3; for (; j > 0; j--) { u8 data = *mask ^ *bitmap; if (cursor->rop == ROP_COPY) data = *mask & *bitmap; /* Pattern 0. Copy the cursor mask to it */ fb_writeb(*mask, cursorbase + h); mask++; /* Pattern 1. Copy the cursor bitmap to it */ fb_writeb(data, cursorbase + h + 8); bitmap++; h++; } cursorbase += 16; } } return 0;}static struct fb_ops tdfxfb_ops = { .owner = THIS_MODULE, .fb_check_var = tdfxfb_check_var, .fb_set_par = tdfxfb_set_par, .fb_setcolreg = tdfxfb_setcolreg, .fb_blank = tdfxfb_blank, .fb_pan_display = tdfxfb_pan_display, .fb_sync = banshee_wait_idle, .fb_cursor = tdfxfb_cursor,#ifdef CONFIG_FB_3DFX_ACCEL .fb_fillrect = tdfxfb_fillrect, .fb_copyarea = tdfxfb_copyarea, .fb_imageblit = tdfxfb_imageblit,#else .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit,#endif};/** * tdfxfb_probe - Device Initializiation * * @pdev: PCI Device to initialize * @id: PCI Device ID * * Initializes and allocates resources for PCI device @pdev. * */static int __devinit tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id){ struct tdfx_par *default_par; struct fb_info *info; int err, lpitch; err = pci_enable_device(pdev); if (err) { printk(KERN_ERR "tdfxfb: Can't enable pdev: %d\n", err); return err; } info = framebuffer_alloc(sizeof(struct tdfx_par), &pdev->dev); if (!info) return -ENOMEM; default_par = info->par; /* Configure the default fb_fix_screeninfo first */ switch (pdev->device) { case PCI_DEVICE_ID_3DFX_BANSHEE: strcat(tdfx_fix.id, " Banshee"); default_par->max_pixclock = BANSHEE_MAX_PIXCLOCK; break; case PCI_DEVICE_ID_3DFX_VOODOO3: strcat(tdfx_fix.id, " Voodoo3"); default_par->max_pixclock = VOODOO3_MAX_PIXCLOCK; break; case PCI_DEVICE_ID_3DFX_VOODOO5: strcat(tdfx_fix.id, " Voodoo5"); default_par->max_pixclock = VOODOO5_MAX_PIXCLOCK; break; } tdfx_fix.mmio_start = pci_resource_start(pdev, 0); tdfx_fix.mmio_len = pci_resource_len(pdev, 0); if (!request_mem_region(tdfx_fix.mmio_start, tdfx_fix.mmio_len, "tdfx regbase")) { printk(KERN_ERR "tdfxfb: Can't reserve regbase\n"); goto out_err; } default_par->regbase_virt = ioremap_nocache(tdfx_fix.mmio_start, tdfx_fix.mmio_len); if (!default_par->regbase_virt) { printk(KERN_ERR "fb: Can't remap %s register area.\n", tdfx_fix.id); goto out_err_regbase; } tdfx_fix.smem_start = pci_resource_start(pdev, 1); tdfx_fix.smem_len = do_lfb_size(default_par, pdev->device); if (!tdfx_fix.smem_len) { printk(KERN_ERR "fb: Can't count %s memory.\n", tdfx_fix.id); goto out_err_regbase; } if (!request_mem_region(tdfx_fix.smem_start, pci_resource_len(pdev, 1), "tdfx smem")) { printk(KERN_ERR "tdfxfb: Can't reserve smem\n"); goto out_err_regbase; } info->screen_base = ioremap_nocache(tdfx_fix.smem_start, tdfx_fix.smem_len); if (!info->screen_base) { printk(KERN_ERR "fb: Can't remap %s framebuffer.\n", tdfx_fix.id); goto out_err_screenbase; } default_par->iobase = pci_resource_start(pdev, 2); if (!request_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2), "tdfx iobase")) { printk(KERN_ERR "tdfxfb: Can't reserve iobase\n"); goto out_err_screenbase; } printk(KERN_INFO "fb: %s memory = %dK\n", tdfx_fix.id, tdfx_fix.smem_len >> 10); default_par->mtrr_handle = -1; if (!nomtrr) default_par->mtrr_handle = mtrr_add(tdfx_fix.smem_start, tdfx_fix.smem_len, MTRR_TYPE_WRCOMB, 1); tdfx_fix.ypanstep = nopan ? 0 : 1; tdfx_fix.ywrapstep = nowrap ? 0 : 1; info->fbops = &tdfxfb_ops; info->fix = tdfx_fix; info->pseudo_palette = default_par->palette; info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;#ifdef CONFIG_FB_3DFX_ACCEL info->flags |= FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_IMAGEBLIT | FBINFO_READS_FAST;#endif /* reserve 8192 bits for cursor */ /* the 2.4 driver says PAGE_MASK boundary is not enough for Voodoo4 */ if (hwcursor) info->fix.smem_len = (info->fix.smem_len - 1024) & (PAGE_MASK << 1); if (!mode_option) mode_option = "640x480@60"; err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8); if (!err || err == 4) info->var = tdfx_var; /* maximize virtual vertical length */ lpitch = info->var.xres_virtual * ((info->var.bits_per_pixel + 7) >> 3); info->var.yres_virtual = info->fix.smem_len / lpitch; if (info->var.yres_virtual < info->var.yres) goto out_err_iobase; if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { printk(KERN_ERR "tdfxfb: Can't allocate color map\n"); goto out_err_iobase; } if (register_framebuffer(info) < 0) { printk(KERN_ERR "tdfxfb: can't register framebuffer\n"); fb_dealloc_cmap(&info->cmap); goto out_err_iobase; } /* * Our driver data */ pci_set_drvdata(pdev, info); return 0;out_err_iobase: if (default_par->mtrr_handle >= 0) mtrr_del(default_par->mtrr_handle, info->fix.smem_start, info->fix.smem_len); release_mem_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2));out_err_screenbase: if (info->screen_base) iounmap(info->screen_base); release_mem_region(tdfx_fix.smem_start, pci_resource_len(pdev, 1));out_err_regbase: /* * Cleanup after anything that was remapped/allocated. */ if (default_par->regbase_virt) iounmap(default_par->regbase_virt); release_mem_region(tdfx_fix.mmio_start, tdfx_fix.mmio_len);out_err: framebuffer_release(info); return -ENXIO;}#ifndef MODULEstatic void tdfxfb_setup(char *options){ char *this_opt; if (!options || !*options) return; while ((this_opt = strsep(&options, ",")) != NULL) { if (!*this_opt) continue; if (!strcmp(this_opt, "nopan")) { nopan = 1; } else if (!strcmp(this_opt, "nowrap")) { nowrap = 1; } else if (!strncmp(this_opt, "hwcursor=", 9)) { hwcursor = simple_strtoul(this_opt + 9, NULL, 0);#ifdef CONFIG_MTRR } else if (!strncmp(this_opt, "nomtrr", 6)) { nomtrr = 1;#endif } else { mode_option = this_opt; } }}#endif/** * tdfxfb_remove - Device removal * * @pdev: PCI Device to cleanup * * Releases all resources allocated during the course of the driver's * lifetime for the PCI device @pdev. * */static void __devexit tdfxfb_remove(struct pci_dev *pdev){ struct fb_info *info = pci_get_drvdata(pdev); struct tdfx_par *par = info->par; unregister_framebuffer(info); if (par->mtrr_handle >= 0) mtrr_del(par->mtrr_handle, info->fix.smem_start, info->fix.smem_len); iounmap(par->regbase_virt); iounmap(info->screen_base); /* Clean up after reserved regions */ release_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2)); release_mem_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1)); release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); pci_set_drvdata(pdev, NULL); framebuffer_release(info);}static int __init tdfxfb_init(void){#ifndef MODULE char *option = NULL; if (fb_get_options("tdfxfb", &option)) return -ENODEV; tdfxfb_setup(option);#endif return pci_register_driver(&tdfxfb_driver);}static void __exit tdfxfb_exit(void){ pci_unregister_driver(&tdfxfb_driver);}MODULE_AUTHOR("Hannu Mallat <hmallat@cc.hut.fi>");MODULE_DESCRIPTION("3Dfx framebuffer device driver");MODULE_LICENSE("GPL");module_param(hwcursor, int, 0644);MODULE_PARM_DESC(hwcursor, "Enable hardware cursor " "(1=enable, 0=disable, default=1)");#ifdef CONFIG_MTRRmodule_param(nomtrr, bool, 0);MODULE_PARM_DESC(nomtrr, "Disable MTRR support (default: enabled)");#endifmodule_init(tdfxfb_init);module_exit(tdfxfb_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -