📄 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/slab.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/platform_device.h>#include <linux/screen_info.h>#include <asm/io.h>#include <video/vga.h>#define VGA_FB_PHYS 0xA0000#define VGA_FB_PHYS_LEN 65536#define MODE_SKIP4 1#define MODE_8BPP 2#define MODE_CFB 4#define MODE_TEXT 8/* --------------------------------------------------------------------- *//* * card parameters */struct vga16fb_par { /* 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; struct vgastate state; unsigned int ref_count; int palette_blanked, vesa_blanked, mode, isVGA; u8 misc, pel_msk, vss, clkdiv; u8 crtc[VGA_CRT_C];};/* --------------------------------------------------------------------- */static struct fb_var_screeninfo vga16fb_defined __initdata = { .xres = 640, .yres = 480, .xres_virtual = 640, .yres_virtual = 480, .bits_per_pixel = 4, .activate = FB_ACTIVATE_TEST, .height = -1, .width = -1, .pixclock = 39721, .left_margin = 48, .right_margin = 16, .upper_margin = 33, .lower_margin = 10, .hsync_len = 96, .vsync_len = 2, .vmode = FB_VMODE_NONINTERLACED,};/* name should not depend on EGA/VGA */static struct fb_fix_screeninfo vga16fb_fix __initdata = { .id = "VGA16 VGA", .smem_start = VGA_FB_PHYS, .smem_len = VGA_FB_PHYS_LEN, .type = FB_TYPE_VGA_PLANES, .type_aux = FB_AUX_VGA_PLANES_VGA4, .visual = FB_VISUAL_PSEUDOCOLOR, .xpanstep = 8, .ypanstep = 1, .line_length = 640 / 8, .accel = FB_ACCEL_NONE};/* The VGA's weird architecture often requires that we read a byte and write a byte to the same location. It doesn't matter *what* byte we write, however. This is because all the action goes on behind the scenes in the VGA's 32-bit latch register, and reading and writing video memory just invokes latch behavior. To avoid race conditions (is this necessary?), reading and writing the memory byte should be done with a single instruction. One suitable instruction is the x86 bitwise OR. The following read-modify-write routine should optimize to one such bitwise OR. */static inline void rmw(volatile char __iomem *p){ readb(p); writeb(1, p);}/* Set the Graphics Mode Register, and return its previous value. Bits 0-1 are write mode, bit 3 is read mode. */static inline int setmode(int mode){ int oldmode; oldmode = vga_io_rgfx(VGA_GFX_MODE); vga_io_w(VGA_GFX_D, mode); return oldmode;}/* Select the Bit Mask Register and return its value. */static inline int selectmask(void){ return vga_io_rgfx(VGA_GFX_BIT_MASK);}/* Set the value of the Bit Mask Register. It must already have been selected with selectmask(). */static inline void setmask(int mask){ vga_io_w(VGA_GFX_D, mask);}/* Set the Data Rotate Register and return its old value. Bits 0-2 are rotate count, bits 3-4 are logical operation (0=NOP, 1=AND, 2=OR, 3=XOR). */static inline int setop(int op){ int oldop; oldop = vga_io_rgfx(VGA_GFX_DATA_ROTATE); vga_io_w(VGA_GFX_D, op); return oldop;}/* Set the Enable Set/Reset Register and return its old value. The code here always uses value 0xf for thsi register. */static inline int setsr(int sr){ int oldsr; oldsr = vga_io_rgfx(VGA_GFX_SR_ENABLE); vga_io_w(VGA_GFX_D, sr); return oldsr;}/* Set the Set/Reset Register and return its old value. */static inline int setcolor(int color){ int oldcolor; oldcolor = vga_io_rgfx(VGA_GFX_SR_VALUE); vga_io_w(VGA_GFX_D, color); return oldcolor;}/* Return the value in the Graphics Address Register. */static inline int getindex(void){ return vga_io_r(VGA_GFX_I);}/* Set the value in the Graphics Address Register. */static inline void setindex(int index){ vga_io_w(VGA_GFX_I, index);}static void vga16fb_pan_var(struct fb_info *info, struct fb_var_screeninfo *var){ struct vga16fb_par *par = info->par; u32 xoffset, pos; xoffset = var->xoffset; if (info->var.bits_per_pixel == 8) { pos = (info->var.xres_virtual * var->yoffset + xoffset) >> 2; } else if (par->mode & MODE_TEXT) { int fh = 16; // FIXME !!! font height. Fugde for now. pos = (info->var.xres_virtual * (var->yoffset / fh) + xoffset) >> 3; } else { if (info->var.nonstd) xoffset--; pos = (info->var.xres_virtual * var->yoffset + xoffset) >> 3; } vga_io_wcrt(VGA_CRTC_START_HI, pos >> 8); vga_io_wcrt(VGA_CRTC_START_LO, pos & 0xFF); /* if we support CFB4, then we must! support xoffset with pixel * granularity if someone supports xoffset in bit resolution */ vga_io_r(VGA_IS1_RC); /* reset flip-flop */ vga_io_w(VGA_ATT_IW, VGA_ATC_PEL); if (var->bits_per_pixel == 8) vga_io_w(VGA_ATT_IW, (xoffset & 3) << 1); else vga_io_w(VGA_ATT_IW, xoffset & 7); vga_io_r(VGA_IS1_RC); vga_io_w(VGA_ATT_IW, 0x20);}static void vga16fb_update_fix(struct fb_info *info){ if (info->var.bits_per_pixel == 4) { if (info->var.nonstd) { info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.line_length = info->var.xres_virtual / 2; } else { info->fix.type = FB_TYPE_VGA_PLANES; info->fix.type_aux = FB_AUX_VGA_PLANES_VGA4; info->fix.line_length = info->var.xres_virtual / 8; } } else if (info->var.bits_per_pixel == 0) { info->fix.type = FB_TYPE_TEXT; info->fix.type_aux = FB_AUX_TEXT_CGA; info->fix.line_length = info->var.xres_virtual / 4; } else { /* 8bpp */ if (info->var.nonstd) { info->fix.type = FB_TYPE_VGA_PLANES; info->fix.type_aux = FB_AUX_VGA_PLANES_CFB8; info->fix.line_length = info->var.xres_virtual / 4; } else { info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.line_length = info->var.xres_virtual; } }}static void vga16fb_clock_chip(struct vga16fb_par *par, unsigned int pixclock, const struct fb_info *info, int mul, int div){ static const 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; pixclock = (pixclock * mul) / div; 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->clkdiv = best->seq_clock_mode; pixclock = (best->pixclock * div) / mul; } #define FAIL(X) return -EINVALstatic int vga16fb_open(struct fb_info *info, int user){ struct vga16fb_par *par = info->par; if (!par->ref_count) { memset(&par->state, 0, sizeof(struct vgastate)); par->state.flags = VGA_SAVE_FONTS | VGA_SAVE_MODE | VGA_SAVE_CMAP; save_vga(&par->state); } par->ref_count++; return 0;}static int vga16fb_release(struct fb_info *info, int user){ struct vga16fb_par *par = info->par; if (!par->ref_count) return -EINVAL; if (par->ref_count == 1) restore_vga(&par->state); par->ref_count--; return 0;}static int vga16fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info){ struct vga16fb_par *par = info->par; u32 xres, right, hslen, left, xtotal; u32 yres, lower, vslen, upper, ytotal; u32 vxres, xoffset, vyres, yoffset; u32 pos; u8 r7, rMode; int shift; int mode; u32 maxmem; par->pel_msk = 0xFF; if (var->bits_per_pixel == 4) { if (var->nonstd) { if (!par->isVGA) return -EINVAL; shift = 3; mode = MODE_SKIP4 | MODE_CFB; maxmem = 16384; par->pel_msk = 0x0F; } else { shift = 3; mode = 0; maxmem = 65536; } } else if (var->bits_per_pixel == 8) { if (!par->isVGA) return -EINVAL; /* no support on EGA */ shift = 2; if (var->nonstd) { mode = MODE_8BPP | MODE_CFB; maxmem = 65536; } else { mode = MODE_SKIP4 | MODE_8BPP | MODE_CFB; maxmem = 16384; } } else 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; var->xres = xres; var->right_margin = right; var->hsync_len = hslen; var->left_margin = left; var->xres_virtual = vxres; var->xoffset = xoffset; xres >>= shift; right >>= shift; hslen >>= shift; left >>= shift; vxres >>= shift; 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 > maxmem) { vyres = maxmem / vxres; if (vyres < yres) return -ENOMEM; } if (yoffset + yres > vyres) yoffset = vyres - yres; var->yres = yres; var->lower_margin = lower; var->vsync_len = vslen; var->upper_margin = upper; var->yres_virtual = vyres; 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 */ 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; if ((mode & (MODE_CFB | MODE_8BPP)) == MODE_CFB) xoffset--; pos = yoffset * vxres + (xoffset >> shift); 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; if (mode & MODE_SKIP4) par->crtc[VGA_CRTC_UNDERLINE] = 0x5F; /* 256, cfb8 */ else par->crtc[VGA_CRTC_UNDERLINE] = 0x1F; /* 16, vgap */ par->crtc[VGA_CRTC_MODE] = rMode | ((mode & MODE_TEXT) ? 0xA3 : 0xE3);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -