📄 vga16fb.c
字号:
/* * linux/drivers/video/vga16.c -- VGA 16-color framebuffer driver * * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz> * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de> * * 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. */#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/console.h>#include <linux/selection.h>#include <linux/ioport.h>#include <linux/init.h>#include <asm/io.h>#include <video/fbcon.h>#include <video/fbcon-vga-planes.h>#include "vga.h"#define dac_reg (0x3c8)#define dac_val (0x3c9)#define VGA_FB_PHYS 0xA0000#define VGA_FB_PHYS_LEN 65536/* --------------------------------------------------------------------- *//* * card parameters */static struct vga16fb_info { struct fb_info fb_info; char *video_vbase; /* 0xa0000 map address */ int isVGA; /* structure holding original VGA register settings when the screen is blanked */ struct { unsigned char SeqCtrlIndex; /* Sequencer Index reg. */ unsigned char CrtCtrlIndex; /* CRT-Contr. Index reg. */ unsigned char CrtMiscIO; /* Miscellaneous register */ unsigned char HorizontalTotal; /* CRT-Controller:00h */ unsigned char HorizDisplayEnd; /* CRT-Controller:01h */ unsigned char StartHorizRetrace; /* CRT-Controller:04h */ unsigned char EndHorizRetrace; /* CRT-Controller:05h */ unsigned char Overflow; /* CRT-Controller:07h */ unsigned char StartVertRetrace; /* CRT-Controller:10h */ unsigned char EndVertRetrace; /* CRT-Controller:11h */ unsigned char ModeControl; /* CRT-Controller:17h */ unsigned char ClockingMode; /* Seq-Controller:01h */ } vga_state; int palette_blanked; int vesa_blanked;} vga16fb;struct vga16fb_par { u8 crtc[VGA_CRT_C]; u8 atc[VGA_ATT_C]; u8 gdc[VGA_GFX_C]; u8 seq[VGA_SEQ_C]; u8 misc; u8 vss; struct fb_var_screeninfo var;};/* --------------------------------------------------------------------- */static struct fb_var_screeninfo vga16fb_defined = { 640,480,640,480,/* W,H, W, H (virtual) load xres,xres_virtual*/ 0,0, /* virtual -> visible no offset */ 4, /* depth -> load bits_per_pixel */ 0, /* greyscale ? */ {0,0,0}, /* R */ {0,0,0}, /* G */ {0,0,0}, /* B */ {0,0,0}, /* transparency */ 0, /* standard pixel format */ FB_ACTIVATE_NOW, -1,-1, 0, 39721, 48, 16, 39, 8, 96, 2, 0, /* No sync info */ FB_VMODE_NONINTERLACED, {0,0,0,0,0,0}};static struct display disp;static struct { u_short blue, green, red, pad; } palette[256];static int currcon = 0;/* --------------------------------------------------------------------- */static void vga16fb_pan_var(struct fb_info *info, struct fb_var_screeninfo *var){ u32 pos = (var->xres_virtual * var->yoffset + var->xoffset) >> 3; outb(VGA_CRTC_START_HI, VGA_CRT_IC); outb(pos >> 8, VGA_CRT_DC); outb(VGA_CRTC_START_LO, VGA_CRT_IC); outb(pos & 0xFF, VGA_CRT_DC);#if 0 /* if someone supports xoffset in bit resolution */ inb(VGA_IS1_RC); /* reset flip-flop */ outb(VGA_ATC_PEL, VGA_ATT_IW); outb(xoffset & 7, VGA_ATT_IW); inb(VGA_IS1_RC); outb(0x20, VGA_ATT_IW);#endif}static int vga16fb_update_var(int con, struct fb_info *info){ vga16fb_pan_var(info, &fb_display[con].var); return 0;}static int vga16fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info){ struct display *p; if (con < 0) p = &disp; else p = fb_display + con; memset(fix, 0, sizeof(struct fb_fix_screeninfo)); strcpy(fix->id,"VGA16 VGA"); fix->smem_start = VGA_FB_PHYS; fix->smem_len = VGA_FB_PHYS_LEN; fix->type = FB_TYPE_VGA_PLANES; fix->visual = FB_VISUAL_PSEUDOCOLOR; fix->xpanstep = 8; fix->ypanstep = 1; fix->ywrapstep = 0; fix->line_length = p->var.xres_virtual / 8; return 0;}static int vga16fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info){ if(con==-1) memcpy(var, &vga16fb_defined, sizeof(struct fb_var_screeninfo)); else *var=fb_display[con].var; return 0;}static void vga16fb_set_disp(int con, struct vga16fb_info *info){ struct fb_fix_screeninfo fix; struct display *display; if (con < 0) display = &disp; else display = fb_display + con; vga16fb_get_fix(&fix, con, &info->fb_info); display->screen_base = info->video_vbase; display->visual = fix.visual; display->type = fix.type; display->type_aux = fix.type_aux; display->ypanstep = fix.ypanstep; display->ywrapstep = fix.ywrapstep; display->line_length = fix.line_length; display->next_line = fix.line_length; display->can_soft_blank = 1; display->inverse = 0; if (info->isVGA) display->dispsw = &fbcon_vga_planes; else display->dispsw = &fbcon_ega_planes; display->scrollmode = SCROLL_YREDRAW;}static void vga16fb_encode_var(struct fb_var_screeninfo *var, const struct vga16fb_par *par, const struct vga16fb_info *info){ *var = par->var;}static void vga16fb_clock_chip(struct vga16fb_par *par, unsigned int pixclock, const struct vga16fb_info *info){ static struct { u32 pixclock; u8 misc; u8 seq_clock_mode; } *ptr, *best, vgaclocks[] = { { 79442 /* 12.587 */, 0x00, 0x08}, { 70616 /* 14.161 */, 0x04, 0x08}, { 39721 /* 25.175 */, 0x00, 0x00}, { 35308 /* 28.322 */, 0x04, 0x00}, { 0 /* bad */, 0x00, 0x00}}; int err; best = vgaclocks; err = pixclock - best->pixclock; if (err < 0) err = -err; for (ptr = vgaclocks + 1; ptr->pixclock; ptr++) { int tmp; tmp = pixclock - ptr->pixclock; if (tmp < 0) tmp = -tmp; if (tmp < err) { err = tmp; best = ptr; } } par->misc |= best->misc; par->seq[VGA_SEQ_CLOCK_MODE] |= best->seq_clock_mode; par->var.pixclock = best->pixclock; } #define FAIL(X) return -EINVALstatic int vga16fb_decode_var(const struct fb_var_screeninfo *var, struct vga16fb_par *par, const struct vga16fb_info *info){ u32 xres, right, hslen, left, xtotal; u32 yres, lower, vslen, upper, ytotal; u32 vxres, xoffset, vyres, yoffset; u32 pos; u8 r7, rMode; int i; if (var->bits_per_pixel != 4) return -EINVAL; xres = (var->xres + 7) & ~7; vxres = (var->xres_virtual + 0xF) & ~0xF; xoffset = (var->xoffset + 7) & ~7; left = (var->left_margin + 7) & ~7; right = (var->right_margin + 7) & ~7; hslen = (var->hsync_len + 7) & ~7; if (vxres < xres) vxres = xres; if (xres + xoffset > vxres) xoffset = vxres - xres; par->var.xres = xres; par->var.right_margin = right; par->var.hsync_len = hslen; par->var.left_margin = left; par->var.xres_virtual = vxres; par->var.xoffset = xoffset; xres >>= 3; right >>= 3; hslen >>= 3; left >>= 3; vxres >>= 3; xtotal = xres + right + hslen + left; if (xtotal >= 256) FAIL("xtotal too big"); if (hslen > 32) FAIL("hslen too big"); if (right + hslen + left > 64) FAIL("hblank too big"); par->crtc[VGA_CRTC_H_TOTAL] = xtotal - 5; par->crtc[VGA_CRTC_H_BLANK_START] = xres - 1; par->crtc[VGA_CRTC_H_DISP] = xres - 1; pos = xres + right; par->crtc[VGA_CRTC_H_SYNC_START] = pos; pos += hslen; par->crtc[VGA_CRTC_H_SYNC_END] = pos & 0x1F; pos += left - 2; /* blank_end + 2 <= total + 5 */ par->crtc[VGA_CRTC_H_BLANK_END] = (pos & 0x1F) | 0x80; if (pos & 0x20) par->crtc[VGA_CRTC_H_SYNC_END] |= 0x80; yres = var->yres; lower = var->lower_margin; vslen = var->vsync_len; upper = var->upper_margin; vyres = var->yres_virtual; yoffset = var->yoffset; if (yres > vyres) vyres = yres; if (vxres * vyres > 65536) { vyres = 65536 / vxres; if (vyres < yres) return -ENOMEM; } if (yoffset + yres > vyres) yoffset = vyres - yres; par->var.yres = yres; par->var.lower_margin = lower; par->var.vsync_len = vslen; par->var.upper_margin = upper; par->var.yres_virtual = vyres; par->var.yoffset = yoffset; if (var->vmode & FB_VMODE_DOUBLE) { yres <<= 1; lower <<= 1; vslen <<= 1; upper <<= 1; } ytotal = yres + lower + vslen + upper; if (ytotal > 1024) { ytotal >>= 1; yres >>= 1; lower >>= 1; vslen >>= 1; upper >>= 1; rMode = 0x04; } else rMode = 0x00; if (ytotal > 1024) FAIL("ytotal too big"); if (vslen > 16) FAIL("vslen too big"); par->crtc[VGA_CRTC_V_TOTAL] = ytotal - 2; r7 = 0x10; /* disable linecompare */ if (ytotal & 0x100) r7 |= 0x01; if (ytotal & 0x200) r7 |= 0x20; par->crtc[VGA_CRTC_PRESET_ROW] = 0; par->crtc[VGA_CRTC_MAX_SCAN] = 0x40; /* 1 scanline, no linecmp */ par->var.vmode = var->vmode; if (var->vmode & FB_VMODE_DOUBLE) par->crtc[VGA_CRTC_MAX_SCAN] |= 0x80; par->crtc[VGA_CRTC_CURSOR_START] = 0x20; par->crtc[VGA_CRTC_CURSOR_END] = 0x00; pos = yoffset * vxres + (xoffset >> 3); par->crtc[VGA_CRTC_START_HI] = pos >> 8; par->crtc[VGA_CRTC_START_LO] = pos & 0xFF; par->crtc[VGA_CRTC_CURSOR_HI] = 0x00; par->crtc[VGA_CRTC_CURSOR_LO] = 0x00; pos = yres - 1; par->crtc[VGA_CRTC_V_DISP_END] = pos & 0xFF; par->crtc[VGA_CRTC_V_BLANK_START] = pos & 0xFF; if (pos & 0x100) r7 |= 0x0A; /* 0x02 -> DISP_END, 0x08 -> BLANK_START */ if (pos & 0x200) { r7 |= 0x40; /* 0x40 -> DISP_END */ par->crtc[VGA_CRTC_MAX_SCAN] |= 0x20; /* BLANK_START */ } pos += lower; par->crtc[VGA_CRTC_V_SYNC_START] = pos & 0xFF; if (pos & 0x100) r7 |= 0x04; if (pos & 0x200) r7 |= 0x80; pos += vslen; par->crtc[VGA_CRTC_V_SYNC_END] = (pos & 0x0F) & ~0x10; /* disabled IRQ */ pos += upper - 1; /* blank_end + 1 <= ytotal + 2 */ par->crtc[VGA_CRTC_V_BLANK_END] = pos & 0xFF; /* 0x7F for original VGA, but some SVGA chips requires all 8 bits to set */ if (vxres >= 512) FAIL("vxres too long"); par->crtc[VGA_CRTC_OFFSET] = vxres >> 1; par->crtc[VGA_CRTC_UNDERLINE] = 0x1F; par->crtc[VGA_CRTC_MODE] = rMode | 0xE3; par->crtc[VGA_CRTC_LINE_COMPARE] = 0xFF; par->crtc[VGA_CRTC_OVERFLOW] = r7; par->vss = 0x00; /* 3DA */ for (i = 0x00; i < 0x10; i++) par->atc[i] = i; par->atc[VGA_ATC_MODE] = 0x81; par->atc[VGA_ATC_OVERSCAN] = 0x00; /* 0 for EGA, 0xFF for VGA */ par->atc[VGA_ATC_PLANE_ENABLE] = 0x0F; par->atc[VGA_ATC_PEL] = xoffset & 7; par->atc[VGA_ATC_COLOR_PAGE] = 0x00; par->misc = 0xC3; /* enable CPU, ports 0x3Dx, positive sync */ par->var.sync = var->sync; if (var->sync & FB_SYNC_HOR_HIGH_ACT) par->misc &= ~0x40; if (var->sync & FB_SYNC_VERT_HIGH_ACT) par->misc &= ~0x80; par->seq[VGA_SEQ_CLOCK_MODE] = 0x01; par->seq[VGA_SEQ_PLANE_WRITE] = 0x0F; par->seq[VGA_SEQ_CHARACTER_MAP] = 0x00; par->seq[VGA_SEQ_MEMORY_MODE] = 0x06; par->gdc[VGA_GFX_SR_VALUE] = 0x00; par->gdc[VGA_GFX_SR_ENABLE] = 0x0F; par->gdc[VGA_GFX_COMPARE_VALUE] = 0x00; par->gdc[VGA_GFX_DATA_ROTATE] = 0x20; par->gdc[VGA_GFX_PLANE_READ] = 0; par->gdc[VGA_GFX_MODE] = 0x00; par->gdc[VGA_GFX_MISC] = 0x05; par->gdc[VGA_GFX_COMPARE_MASK] = 0x0F; par->gdc[VGA_GFX_BIT_MASK] = 0xFF; vga16fb_clock_chip(par, var->pixclock, info); par->var.bits_per_pixel = 4; par->var.grayscale = var->grayscale; par->var.red.offset = par->var.green.offset = par->var.blue.offset = par->var.transp.offset = 0; par->var.red.length = par->var.green.length = par->var.blue.length = (info->isVGA) ? 6 : 2; par->var.transp.length = 0; par->var.nonstd = 0; par->var.activate = FB_ACTIVATE_NOW; par->var.height = -1; par->var.width = -1; par->var.accel_flags = 0; return 0;}#undef FAILstatic int vga16fb_set_par(const struct vga16fb_par *par, struct vga16fb_info *info){ int i; outb(inb(VGA_MIS_R) | 0x01, VGA_MIS_W); /* Enable graphics register modification */ if (!info->isVGA) { outb(0x00, EGA_GFX_E0); outb(0x01, EGA_GFX_E1); } /* update misc output register */ outb(par->misc, VGA_MIS_W); /* synchronous reset on */ outb(0x00, VGA_SEQ_I); outb(0x01, VGA_SEQ_D); /* write sequencer registers */ outb(1, VGA_SEQ_I); outb(par->seq[1] | 0x20, VGA_SEQ_D); for (i = 2; i < VGA_SEQ_C; i++) { outb(i, VGA_SEQ_I); outb(par->seq[i], VGA_SEQ_D); } /* synchronous reset off */ outb(0x00, VGA_SEQ_I); outb(0x03, VGA_SEQ_D); /* deprotect CRT registers 0-7 */ outb(0x11, VGA_CRT_IC); outb(par->crtc[0x11], VGA_CRT_DC); /* write CRT registers */ for (i = 0; i < VGA_CRT_C; i++) { outb(i, VGA_CRT_IC); outb(par->crtc[i], VGA_CRT_DC); } /* write graphics controller registers */ for (i = 0; i < VGA_GFX_C; i++) { outb(i, VGA_GFX_I); outb(par->gdc[i], VGA_GFX_D); } /* write attribute controller registers */ for (i = 0; i < VGA_ATT_C; i++) { inb_p(VGA_IS1_RC); /* reset flip-flop */ outb_p(i, VGA_ATT_IW); outb_p(par->atc[i], VGA_ATT_IW);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -