📄 neofb.c
字号:
/* * linux/drivers/video/neofb.c -- NeoMagic Framebuffer Driver * * Copyright (c) 2001 Denis Oliver Kropp <dok@convergence.de> * * * Card specific code is based on XFree86's neomagic driver. * Framebuffer framework code is based on code of cyber2000fb. * * This file is subject to the terms and conditions of the GNU General * Public License. See the file COPYING in the main directory of this * archive for more details. * * * 0.3.2 * - got rid of all floating point (dok) * * 0.3.1 * - added module license (dok) * * 0.3 * - hardware accelerated clear and move for 2200 and above (dok) * - maximum allowed dotclock is handled now (dok) * * 0.2.1 * - correct panning after X usage (dok) * - added module and kernel parameters (dok) * - no stretching if external display is enabled (dok) * * 0.2 * - initial version (dok) * * * TODO * - ioctl for internal/external switching * - blanking * - 32bit depth support, maybe impossible * - disable pan-on-sync, need specs * * BUGS * - white margin on bootup like with tdfxfb (colormap problem?) * */#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/tty.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/pci.h>#include <linux/init.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/pgtable.h>#include <asm/system.h>#include <asm/uaccess.h>#ifdef CONFIG_MTRR#include <asm/mtrr.h>#endif#include <video/fbcon.h>#include <video/fbcon-cfb8.h>#include <video/fbcon-cfb16.h>#include <video/fbcon-cfb24.h>#include <video/fbcon-cfb32.h>#include "neofb.h"#define NEOFB_VERSION "0.3.2"/* --------------------------------------------------------------------- */static int disabled = 0;static int internal = 0;static int external = 0;static int nostretch = 0;static int nopciburst = 0;#ifdef MODULEMODULE_AUTHOR("(c) 2001-2002 Denis Oliver Kropp <dok@convergence.de>");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("FBDev driver for NeoMagic PCI Chips");MODULE_PARM(disabled, "i");MODULE_PARM_DESC(disabled, "Disable this driver's initialization.");MODULE_PARM(internal, "i");MODULE_PARM_DESC(internal, "Enable output on internal LCD Display.");MODULE_PARM(external, "i");MODULE_PARM_DESC(external, "Enable output on external CRT.");MODULE_PARM(nostretch, "i");MODULE_PARM_DESC(nostretch, "Disable stretching of modes smaller than LCD.");MODULE_PARM(nopciburst, "i");MODULE_PARM_DESC(nopciburst, "Disable PCI burst mode.");#endif/* --------------------------------------------------------------------- */static biosMode bios8[] = { { 320, 240, 0x40 }, { 300, 400, 0x42 }, { 640, 400, 0x20 }, { 640, 480, 0x21 }, { 800, 600, 0x23 }, { 1024, 768, 0x25 },};static biosMode bios16[] = { { 320, 200, 0x2e }, { 320, 240, 0x41 }, { 300, 400, 0x43 }, { 640, 480, 0x31 }, { 800, 600, 0x34 }, { 1024, 768, 0x37 },};static biosMode bios24[] = { { 640, 480, 0x32 }, { 800, 600, 0x35 }, { 1024, 768, 0x38 }};#ifdef NO_32BIT_SUPPORT_YET/* FIXME: guessed values, wrong */static biosMode bios32[] = { { 640, 480, 0x33 }, { 800, 600, 0x36 }, { 1024, 768, 0x39 } };#endifstatic int neoFindMode (int xres, int yres, int depth){ int xres_s; int i, size; biosMode *mode; switch (depth) { case 8: size = sizeof(bios8) / sizeof(biosMode); mode = bios8; break; case 16: size = sizeof(bios16) / sizeof(biosMode); mode = bios16; break; case 24: size = sizeof(bios24) / sizeof(biosMode); mode = bios24; break;#ifdef NO_32BIT_SUPPORT_YET case 32: size = sizeof(bios32) / sizeof(biosMode); mode = bios32; break;#endif default: return 0; } for (i = 0; i < size; i++) { if (xres <= mode[i].x_res) { xres_s = mode[i].x_res; for (; i < size; i++) { if (mode[i].x_res != xres_s) return mode[i-1].mode; if (yres <= mode[i].y_res) return mode[i].mode; } } } return mode[size - 1].mode;}/* -------------------- Hardware specific routines ------------------------- *//* * Hardware Acceleration for Neo2200+ */static inline void neo2200_wait_idle (struct neofb_info *fb){ int waitcycles; while (fb->neo2200->bltStat & 1) waitcycles++;}static inline void neo2200_wait_fifo (struct neofb_info *fb, int requested_fifo_space){ // ndev->neo.waitfifo_calls++; // ndev->neo.waitfifo_sum += requested_fifo_space; /* FIXME: does not work if (neo_fifo_space < requested_fifo_space) { neo_fifo_waitcycles++; while (1) { neo_fifo_space = (neo2200->bltStat >> 8); if (neo_fifo_space >= requested_fifo_space) break; } } else { neo_fifo_cache_hits++; } neo_fifo_space -= requested_fifo_space; */ neo2200_wait_idle (fb);}static inline void neo2200_accel_init (struct neofb_info *fb, struct fb_var_screeninfo *var){ Neo2200 *neo2200 = fb->neo2200; u32 bltMod, pitch; neo2200_wait_idle (fb); switch (var->bits_per_pixel) { case 8: bltMod = NEO_MODE1_DEPTH8; pitch = var->xres_virtual; break; case 15: case 16: bltMod = NEO_MODE1_DEPTH16; pitch = var->xres_virtual * 2; break; default: printk( KERN_ERR "neofb: neo2200_accel_init: unexpected bits per pixel!\n" ); return; } neo2200->bltStat = bltMod << 16; neo2200->pitch = (pitch << 16) | pitch;}static void neo2200_accel_setup (struct display *p){ struct neofb_info *fb = (struct neofb_info *)p->fb_info; struct fb_var_screeninfo *var = &p->fb_info->var; fb->dispsw->setup(p); neo2200_accel_init (fb, var);}static voidneo2200_accel_bmove (struct display *p, int sy, int sx, int dy, int dx, int height, int width){ struct neofb_info *fb = (struct neofb_info *)p->fb_info; struct fb_var_screeninfo *var = &p->fb_info->var; Neo2200 *neo2200 = fb->neo2200; u_long src, dst; int bpp, pitch, inc_y; u_int fh, fw; if (sx != dx) { neo2200_wait_idle (fb); fb->dispsw->bmove(p, sy, sx, dy, dx, height, width); return; } bpp = (var->bits_per_pixel+7) / 8; pitch = var->xres_virtual * bpp; fw = fontwidth(p); sx *= fw * bpp; dx *= fw * bpp; width *= fw; fh = fontheight(p); sy *= fh; dy *= fh; if (sy > dy) inc_y = fh; else { inc_y = -fh; sy += (height - 1) * fh; dy += (height - 1) * fh; } neo2200_wait_fifo (fb, 1); /* set blt control */ neo2200->bltCntl = NEO_BC3_FIFO_EN | NEO_BC3_SKIP_MAPPING | 0x0c0000; while (height--) { src = sx + sy * pitch; dst = dx + dy * pitch; neo2200_wait_fifo (fb, 3); neo2200->srcStart = src; neo2200->dstStart = dst; neo2200->xyExt = (fh << 16) | (width & 0xffff); sy += inc_y; dy += inc_y; }}static voidneo2200_accel_clear (struct vc_data *conp, struct display *p, int sy, int sx, int height, int width){ struct neofb_info *fb = (struct neofb_info *)p->fb_info; struct fb_var_screeninfo *var = &p->fb_info->var; Neo2200 *neo2200 = fb->neo2200; u_long dst; u_int fw, fh; u32 bgx = attr_bgcol_ec(p, conp); fw = fontwidth(p); fh = fontheight(p); dst = sx * fw + sy * var->xres_virtual * fh; width = width * fw; height = height * fh; neo2200_wait_fifo (fb, 4); /* set blt control */ neo2200->bltCntl = NEO_BC3_FIFO_EN | NEO_BC0_SRC_IS_FG | NEO_BC3_SKIP_MAPPING | 0x0c0000; switch (var->bits_per_pixel) { case 8: neo2200->fgColor = bgx; break; case 16: neo2200->fgColor = ((u16 *)(p->fb_info)->pseudo_palette)[bgx]; break; } neo2200->dstStart = dst * ((var->bits_per_pixel+7) / 8); neo2200->xyExt = (height << 16) | (width & 0xffff);}static voidneo2200_accel_putc (struct vc_data *conp, struct display *p, int c, int yy, int xx){ struct neofb_info *fb = (struct neofb_info *)p->fb_info; neo2200_wait_idle (fb); fb->dispsw->putc(conp, p, c, yy, xx);}static voidneo2200_accel_putcs (struct vc_data *conp, struct display *p, const unsigned short *s, int count, int yy, int xx){ struct neofb_info *fb = (struct neofb_info *)p->fb_info; neo2200_wait_idle (fb); fb->dispsw->putcs(conp, p, s, count, yy, xx);}static void neo2200_accel_revc (struct display *p, int xx, int yy){ struct neofb_info *fb = (struct neofb_info *)p->fb_info; neo2200_wait_idle (fb); fb->dispsw->revc (p, xx, yy);}static voidneo2200_accel_clear_margins (struct vc_data *conp, struct display *p, int bottom_only){ struct neofb_info *fb = (struct neofb_info *)p->fb_info; fb->dispsw->clear_margins (conp, p, bottom_only);}static struct display_switch fbcon_neo2200_accel = { setup: neo2200_accel_setup, bmove: neo2200_accel_bmove, clear: neo2200_accel_clear, putc: neo2200_accel_putc, putcs: neo2200_accel_putcs, revc: neo2200_accel_revc, clear_margins: neo2200_accel_clear_margins, fontwidthmask: FONTWIDTH(8)|FONTWIDTH(16)};/* --------------------------------------------------------------------- *//* * Set a single color register. Return != 0 for invalid regno. */static int neo_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *fb){ struct neofb_info *info = (struct neofb_info *)fb; if (regno >= NR_PALETTE) return -EINVAL; info->palette[regno].red = red; info->palette[regno].green = green; info->palette[regno].blue = blue; info->palette[regno].transp = transp; switch (fb->var.bits_per_pixel) {#ifdef FBCON_HAS_CFB8 case 8: outb(regno, 0x3c8); outb(red >> 10, 0x3c9); outb(green >> 10, 0x3c9); outb(blue >> 10, 0x3c9); break;#endif#ifdef FBCON_HAS_CFB16 case 16: if (regno < 16) ((u16 *)fb->pseudo_palette)[regno] = ((red & 0xf800) ) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11); break;#endif#ifdef FBCON_HAS_CFB24 case 24: if (regno < 16) ((u32 *)fb->pseudo_palette)[regno] = ((red & 0xff00) << 8) | ((green & 0xff00) ) | ((blue & 0xff00) >> 8); break;#endif#ifdef NO_32BIT_SUPPORT_YET#ifdef FBCON_HAS_CFB32 case 32: if (regno < 16) ((u32 *)fb->pseudo_palette)[regno] = ((transp & 0xff00) << 16) | ((red & 0xff00) << 8) | ((green & 0xff00) ) | ((blue & 0xff00) >> 8); break;#endif#endif default: return 1; } return 0;}static void vgaHWLock (void){ /* Protect CRTC[0-7] */ VGAwCR (0x11, VGArCR (0x11) | 0x80);}static void vgaHWUnlock (void){ /* Unprotect CRTC[0-7] */ VGAwCR (0x11, VGArCR (0x11) & ~0x80);}static void neoLock (void){ VGAwGR (0x09, 0x00); vgaHWLock();}static void neoUnlock (void){ vgaHWUnlock(); VGAwGR (0x09, 0x26);}/* * vgaHWSeqReset * perform a sequencer reset. */voidvgaHWSeqReset(int start){ if (start) VGAwSEQ (0x00, 0x01); /* Synchronous Reset */ else VGAwSEQ (0x00, 0x03); /* End Reset */}voidvgaHWProtect(int on){ unsigned char tmp; if (on) { /* * Turn off screen and disable sequencer. */ tmp = VGArSEQ (0x01); vgaHWSeqReset (1); /* start synchronous reset */ VGAwSEQ (0x01, tmp | 0x20); /* disable the display */ VGAenablePalette(); } else { /* * Reenable sequencer, then turn on screen. */ tmp = VGArSEQ (0x01); VGAwSEQ (0x01, tmp & ~0x20); /* reenable display */ vgaHWSeqReset (0); /* clear synchronousreset */ VGAdisablePalette(); }}static void vgaHWRestore (const struct neofb_info *info, const struct neofb_par *par){ int i; VGAwMISC (par->MiscOutReg); for (i = 1; i < 5; i++) VGAwSEQ (i, par->Sequencer[i]); /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 or CRTC[17] */ VGAwCR (17, par->CRTC[17] & ~0x80); for (i = 0; i < 25; i++) VGAwCR (i, par->CRTC[i]); for (i = 0; i < 9; i++) VGAwGR (i, par->Graphics[i]); VGAenablePalette(); for (i = 0; i < 21; i++) VGAwATTR (i, par->Attribute[i]); VGAdisablePalette();}static void neofb_set_par (struct neofb_info *info, const struct neofb_par *par){ unsigned char temp; int i; int clock_hi = 0; DBG("neofb_set_par"); neoUnlock(); vgaHWProtect (1); /* Blank the screen */ /* linear colormap for non palettized modes */ switch (par->depth) { case 8: break; case 16: for (i=0; i<64; i++) { outb(i, 0x3c8); outb(i << 1, 0x3c9); outb(i, 0x3c9);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -