| /* |
| * linux/drivers/video/fbcon-vga-planes.c -- Low level frame buffer operations |
| * for VGA 4-plane modes |
| * |
| * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz> |
| * Based on code by Michael Schmitz |
| * Based on the old macfb.c 4bpp code by Alan Cox |
| * |
| * 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/tty.h> |
| #include <linux/console.h> |
| #include <linux/string.h> |
| #include <linux/fb.h> |
| #include <linux/vt_buffer.h> |
| |
| #include <asm/io.h> |
| |
| #include <video/fbcon.h> |
| #include <video/fbcon-vga-planes.h> |
| |
| #define GRAPHICS_ADDR_REG 0x3ce /* Graphics address register. */ |
| #define GRAPHICS_DATA_REG 0x3cf /* Graphics data register. */ |
| |
| #define SET_RESET_INDEX 0 /* Set/Reset Register index. */ |
| #define ENABLE_SET_RESET_INDEX 1 /* Enable Set/Reset Register index. */ |
| #define DATA_ROTATE_INDEX 3 /* Data Rotate Register index. */ |
| #define GRAPHICS_MODE_INDEX 5 /* Graphics Mode Register index. */ |
| #define BIT_MASK_INDEX 8 /* Bit Mask Register index. */ |
| |
| /* 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 *p) |
| { |
| readb(p); |
| writeb(1, p); |
| } |
| |
| /* Set the Graphics Mode Register. Bits 0-1 are write mode, bit 3 is |
| read mode. */ |
| static inline void setmode(int mode) |
| { |
| outb(GRAPHICS_MODE_INDEX, GRAPHICS_ADDR_REG); |
| outb(mode, GRAPHICS_DATA_REG); |
| } |
| |
| /* Select the Bit Mask Register. */ |
| static inline void selectmask(void) |
| { |
| outb(BIT_MASK_INDEX, GRAPHICS_ADDR_REG); |
| } |
| |
| /* Set the value of the Bit Mask Register. It must already have been |
| selected with selectmask(). */ |
| static inline void setmask(int mask) |
| { |
| outb(mask, GRAPHICS_DATA_REG); |
| } |
| |
| /* Set the Data Rotate Register. Bits 0-2 are rotate count, bits 3-4 |
| are logical operation (0=NOP, 1=AND, 2=OR, 3=XOR). */ |
| static inline void setop(int op) |
| { |
| outb(DATA_ROTATE_INDEX, GRAPHICS_ADDR_REG); |
| outb(op, GRAPHICS_DATA_REG); |
| } |
| |
| /* Set the Enable Set/Reset Register. The code here always uses value |
| 0xf for this register. */ |
| static inline void setsr(int sr) |
| { |
| outb(ENABLE_SET_RESET_INDEX, GRAPHICS_ADDR_REG); |
| outb(sr, GRAPHICS_DATA_REG); |
| } |
| |
| /* Set the Set/Reset Register. */ |
| static inline void setcolor(int color) |
| { |
| outb(SET_RESET_INDEX, GRAPHICS_ADDR_REG); |
| outb(color, GRAPHICS_DATA_REG); |
| } |
| |
| /* Set the value in the Graphics Address Register. */ |
| static inline void setindex(int index) |
| { |
| outb(index, GRAPHICS_ADDR_REG); |
| } |
| |
| void fbcon_vga_planes_setup(struct display *p) |
| { |
| } |
| |
| void fbcon_vga_planes_bmove(struct display *p, int sy, int sx, int dy, int dx, |
| int height, int width) |
| { |
| char *src; |
| char *dest; |
| int line_ofs; |
| int x; |
| |
| setmode(1); |
| setop(0); |
| setsr(0xf); |
| |
| sy *= fontheight(p); |
| dy *= fontheight(p); |
| height *= fontheight(p); |
| |
| if (dy < sy || (dy == sy && dx < sx)) { |
| line_ofs = p->line_length - width; |
| dest = p->screen_base + dx + dy * p->line_length; |
| src = p->screen_base + sx + sy * p->line_length; |
| while (height--) { |
| for (x = 0; x < width; x++) { |
| readb(src); |
| writeb(0, dest); |
| dest++; |
| src++; |
| } |
| src += line_ofs; |
| dest += line_ofs; |
| } |
| } else { |
| line_ofs = p->line_length - width; |
| dest = p->screen_base + dx + width + (dy + height - 1) * p->line_length; |
| src = p->screen_base + sx + width + (sy + height - 1) * p->line_length; |
| while (height--) { |
| for (x = 0; x < width; x++) { |
| dest--; |
| src--; |
| readb(src); |
| writeb(0, dest); |
| } |
| src -= line_ofs; |
| dest -= line_ofs; |
| } |
| } |
| } |
| |
| void fbcon_vga_planes_clear(struct vc_data *conp, struct display *p, int sy, int sx, |
| int height, int width) |
| { |
| int line_ofs = p->line_length - width; |
| char *where; |
| int x; |
| |
| setmode(0); |
| setop(0); |
| setsr(0xf); |
| setcolor(attr_bgcol_ec(p, conp)); |
| selectmask(); |
| |
| setmask(0xff); |
| |
| sy *= fontheight(p); |
| height *= fontheight(p); |
| |
| where = p->screen_base + sx + sy * p->line_length; |
| while (height--) { |
| for (x = 0; x < width; x++) { |
| writeb(0, where); |
| where++; |
| } |
| where += line_ofs; |
| } |
| } |
| |
| void fbcon_ega_planes_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx) |
| { |
| int fg = attr_fgcol(p,c); |
| int bg = attr_bgcol(p,c); |
| |
| int y; |
| u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p); |
| char *where = p->screen_base + xx + yy * p->line_length * fontheight(p); |
| |
| setmode(0); |
| setop(0); |
| setsr(0xf); |
| setcolor(bg); |
| selectmask(); |
| |
| setmask(0xff); |
| for (y = 0; y < fontheight(p); y++, where += p->line_length) |
| rmw(where); |
| |
| where -= p->line_length * y; |
| setcolor(fg); |
| selectmask(); |
| for (y = 0; y < fontheight(p); y++, where += p->line_length) |
| if (cdat[y]) { |
| setmask(cdat[y]); |
| rmw(where); |
| } |
| } |
| |
| void fbcon_vga_planes_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx) |
| { |
| int fg = attr_fgcol(p,c); |
| int bg = attr_bgcol(p,c); |
| |
| int y; |
| u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p); |
| char *where = p->screen_base + xx + yy * p->line_length * fontheight(p); |
| |
| setmode(2); |
| setop(0); |
| setsr(0xf); |
| setcolor(fg); |
| selectmask(); |
| |
| setmask(0xff); |
| writeb(bg, where); |
| rmb(); |
| readb(where); /* fill latches */ |
| setmode(3); |
| wmb(); |
| for (y = 0; y < fontheight(p); y++, where += p->line_length) |
| writeb(cdat[y], where); |
| wmb(); |
| } |
| |
| /* 28.50 in my test */ |
| void fbcon_ega_planes_putcs(struct vc_data *conp, struct display *p, const unsigned short *s, |
| int count, int yy, int xx) |
| { |
| u16 c = scr_readw(s); |
| int fg = attr_fgcol(p, c); |
| int bg = attr_bgcol(p, c); |
| |
| char *where; |
| int n; |
| |
| setmode(2); |
| setop(0); |
| selectmask(); |
| |
| setmask(0xff); |
| where = p->screen_base + xx + yy * p->line_length * fontheight(p); |
| writeb(bg, where); |
| rmb(); |
| readb(where); /* fill latches */ |
| wmb(); |
| selectmask(); |
| for (n = 0; n < count; n++) { |
| int c = scr_readw(s++) & p->charmask; |
| u8 *cdat = p->fontdata + c * fontheight(p); |
| u8 *end = cdat + fontheight(p); |
| |
| while (cdat < end) { |
| outb(*cdat++, GRAPHICS_DATA_REG); |
| wmb(); |
| writeb(fg, where); |
| where += p->line_length; |
| } |
| where += 1 - p->line_length * fontheight(p); |
| } |
| |
| wmb(); |
| } |
| |
| /* 6.96 in my test */ |
| void fbcon_vga_planes_putcs(struct vc_data *conp, struct display *p, const unsigned short *s, |
| int count, int yy, int xx) |
| { |
| u16 c = scr_readw(s); |
| int fg = attr_fgcol(p, c); |
| int bg = attr_bgcol(p, c); |
| |
| char *where; |
| int n; |
| |
| setmode(2); |
| setop(0); |
| setsr(0xf); |
| setcolor(fg); |
| selectmask(); |
| |
| setmask(0xff); |
| where = p->screen_base + xx + yy * p->line_length * fontheight(p); |
| writeb(bg, where); |
| rmb(); |
| readb(where); /* fill latches */ |
| setmode(3); |
| wmb(); |
| for (n = 0; n < count; n++) { |
| int y; |
| int c = scr_readw(s++) & p->charmask; |
| u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p); |
| |
| for (y = 0; y < fontheight(p); y++, cdat++) { |
| writeb (*cdat, where); |
| where += p->line_length; |
| } |
| where += 1 - p->line_length * fontheight(p); |
| } |
| |
| wmb(); |
| } |
| |
| void fbcon_vga_planes_revc(struct display *p, int xx, int yy) |
| { |
| char *where = p->screen_base + xx + yy * p->line_length * fontheight(p); |
| int y; |
| |
| setmode(0); |
| setop(0x18); |
| setsr(0xf); |
| setcolor(0xf); |
| selectmask(); |
| |
| setmask(0xff); |
| for (y = 0; y < fontheight(p); y++) { |
| rmw(where); |
| where += p->line_length; |
| } |
| } |
| |
| struct display_switch fbcon_vga_planes = { |
| setup: fbcon_vga_planes_setup, |
| bmove: fbcon_vga_planes_bmove, |
| clear: fbcon_vga_planes_clear, |
| putc: fbcon_vga_planes_putc, |
| putcs: fbcon_vga_planes_putcs, |
| revc: fbcon_vga_planes_revc, |
| fontwidthmask: FONTWIDTH(8) |
| }; |
| |
| struct display_switch fbcon_ega_planes = { |
| setup: fbcon_vga_planes_setup, |
| bmove: fbcon_vga_planes_bmove, |
| clear: fbcon_vga_planes_clear, |
| putc: fbcon_ega_planes_putc, |
| putcs: fbcon_ega_planes_putcs, |
| revc: fbcon_vga_planes_revc, |
| fontwidthmask: FONTWIDTH(8) |
| }; |
| |
| #ifdef MODULE |
| MODULE_LICENSE("GPL"); |
| |
| int init_module(void) |
| { |
| return 0; |
| } |
| |
| void cleanup_module(void) |
| {} |
| #endif /* MODULE */ |
| |
| |
| /* |
| * Visible symbols for modules |
| */ |
| |
| EXPORT_SYMBOL(fbcon_vga_planes); |
| EXPORT_SYMBOL(fbcon_vga_planes_setup); |
| EXPORT_SYMBOL(fbcon_vga_planes_bmove); |
| EXPORT_SYMBOL(fbcon_vga_planes_clear); |
| EXPORT_SYMBOL(fbcon_vga_planes_putc); |
| EXPORT_SYMBOL(fbcon_vga_planes_putcs); |
| EXPORT_SYMBOL(fbcon_vga_planes_revc); |
| |
| EXPORT_SYMBOL(fbcon_ega_planes); |
| EXPORT_SYMBOL(fbcon_ega_planes_putc); |
| EXPORT_SYMBOL(fbcon_ega_planes_putcs); |
| |
| /* |
| * Overrides for Emacs so that we follow Linus's tabbing style. |
| * --------------------------------------------------------------------------- |
| * Local variables: |
| * c-basic-offset: 8 |
| * End: |
| */ |
| |