diff options
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/cyber2000fb.c | 1057 | ||||
-rw-r--r-- | drivers/video/cyber2000fb.h | 78 | ||||
-rw-r--r-- | drivers/video/fbcon-vga-planes.c | 364 | ||||
-rw-r--r-- | drivers/video/vga16fb.c | 1064 |
4 files changed, 2563 insertions, 0 deletions
diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c new file mode 100644 index 000000000..c24288124 --- /dev/null +++ b/drivers/video/cyber2000fb.c @@ -0,0 +1,1057 @@ +/* + * linux/drivers/video/cyber2000fb.c + * + * Integraphics Cyber2000 frame buffer device + * + * Based on cyberfb.c + */ +#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/malloc.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> + +#include <video/fbcon.h> +#include <video/fbcon-cfb8.h> +#include <video/fbcon-cfb16.h> +#include <video/fbcon-cfb24.h> + +/* + * Some defaults + */ +#define DEFAULT_XRES 640 +#define DEFAULT_YRES 480 +#define DEFAULT_BPP 8 + +static volatile unsigned char *CyberRegs; + +#include "cyber2000fb.h" + +static struct display global_disp; +static struct fb_info fb_info; +static struct cyber2000fb_par current_par; +static struct display_switch *dispsw; +static struct fb_var_screeninfo __initdata init_var = {}; + +#ifdef DEBUG +static void debug_printf(char *fmt, ...) +{ + char buffer[128]; + va_list ap; + + va_start(ap, fmt); + vsprintf(buffer, fmt, ap); + va_end(ap); + + printascii(buffer); +} +#else +#define debug_printf(x...) do { } while (0) +#endif + +/* + * Predefined Video Modes + */ +static const struct res cyber2000_res[] = { + { + 640, 480, + { + 0x5f, 0x4f, 0x50, 0x80, 0x52, 0x9d, 0x0b, 0x3e, + 0x00, 0x40, + 0xe9, 0x8b, 0xdf, 0x50, 0x00, 0xe6, 0x04, 0xc3 + }, + 0x00, + { 0xd2, 0xce, 0xdb, 0x54 } + }, + + { + 800, 600, + { + 0x7f, 0x63, 0x64, 0x00, 0x66, 0x10, 0x6f, 0xf0, + 0x00, 0x60, + 0x5b, 0x8f, 0x57, 0x64, 0x00, 0x59, 0x6e, 0xe3 + }, + 0x00, + { 0x52, 0x85, 0xdb, 0x54 } + }, + + { + 1024, 768, + { + 0x9f, 0x7f, 0x80, 0x80, 0x8b, 0x94, 0x1e, 0xfd, + 0x00, 0x60, + 0x03, 0x86, 0xff, 0x80, 0x0f, 0x00, 0x1e, 0xe3 + }, + 0x00, + { 0xd0, 0x52, 0xdb, 0x54 } + }, +#if 0 + { + 1152, 886, + { + }, + { + } + }, +#endif + { + 1280, 1024, + { + 0xce, 0x9f, 0xa0, 0x8f, 0xa2, 0x1f, 0x28, 0x52, + 0x00, 0x40, + 0x08, 0x8f, 0xff, 0xa0, 0x00, 0x03, 0x27, 0xe3 + }, + 0x1d, + { 0xb4, 0x4b, 0xdb, 0x54 } + }, + + { + 1600, 1200, + { + 0xff, 0xc7, 0xc9, 0x9f, 0xcf, 0xa0, 0xfe, 0x10, + 0x00, 0x40, + 0xcf, 0x89, 0xaf, 0xc8, 0x00, 0xbc, 0xf1, 0xe3 + }, + 0x1f, + { 0xbd, 0x10, 0xdb, 0x54 } + } +}; + +#define NUM_TOTAL_MODES arraysize(cyber2000_res) + +static const char igs_regs[] = { + 0x10, 0x10, 0x12, 0x00, 0x13, 0x00, + 0x30, 0x21, 0x31, 0x00, 0x32, 0x00, 0x33, 0x01, + 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, + 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x01, + 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, + 0x70, 0x0b, 0x71, 0x10, 0x72, 0x45, 0x73, 0x30, + 0x74, 0x1b, 0x75, 0x1e, 0x76, 0x00, 0x7a, 0xc8 +}; + +static const char crtc_idx[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 +}; + +static void cyber2000_init_hw(const struct res *res) +{ + int i; + + debug_printf("init vga hw for %dx%d\n", res->xres, res->yres); + + cyber2000_outb(0xef, 0x3c2); + cyber2000_crtcw(0x0b, 0x11); + cyber2000_attrw(0x00, 0x11); + + cyber2000_seqw(0x01, 0x00); + cyber2000_seqw(0x01, 0x01); + cyber2000_seqw(0x0f, 0x02); + cyber2000_seqw(0x00, 0x03); + cyber2000_seqw(0x0e, 0x04); + cyber2000_seqw(0x03, 0x00); + + for (i = 0; i < sizeof(crtc_idx); i++) + cyber2000_crtcw(res->crtc_regs[i], crtc_idx[i]); + + for (i = 0x0a; i < 0x10; i++) + cyber2000_crtcw(0, i); + + cyber2000_crtcw(0xff, 0x18); + + cyber2000_grphw(0x00, 0x00); + cyber2000_grphw(0x00, 0x01); + cyber2000_grphw(0x00, 0x02); + cyber2000_grphw(0x00, 0x03); + cyber2000_grphw(0x00, 0x04); + cyber2000_grphw(0x60, 0x05); + cyber2000_grphw(0x05, 0x06); + cyber2000_grphw(0x0f, 0x07); + cyber2000_grphw(0xff, 0x08); + + for (i = 0; i < 16; i++) + cyber2000_attrw(i, i); + + cyber2000_attrw(0x01, 0x10); + cyber2000_attrw(0x00, 0x11); + cyber2000_attrw(0x0f, 0x12); + cyber2000_attrw(0x00, 0x13); + cyber2000_attrw(0x00, 0x14); + + for (i = 0; i < sizeof(igs_regs); i += 2) + cyber2000_grphw(igs_regs[i+1], igs_regs[i]); + + cyber2000_grphw(res->crtc_ofl, 0x11); + + for (i = 0; i < 4; i += 1) + cyber2000_grphw(res->clk_regs[i], 0xb0 + i); + + cyber2000_grphw(0x01, 0x90); + cyber2000_grphw(0x80, 0xb9); + cyber2000_grphw(0x00, 0xb9); + + cyber2000_outb(0x56, 0x3ce); + i = cyber2000_inb(0x3cf); + cyber2000_outb(i | 4, 0x3cf); + cyber2000_outb(0x04, 0x3c6); + cyber2000_outb(i, 0x3cf); + + cyber2000_outb(0x20, 0x3c0); + cyber2000_outb(0xff, 0x3c6); + + for (i = 0; i < 256; i++) { + cyber2000_outb(i, 0x3c8); + cyber2000_outb(0, 0x3c9); + cyber2000_outb(0, 0x3c9); + cyber2000_outb(0, 0x3c9); + } +} + + +static struct fb_ops cyber2000fb_ops; + +/* -------------------- Hardware specific routines ------------------------- */ + +/* + * Hardware Cyber2000 Acceleration + */ +static void cyber2000_accel_wait(void) +{ + int count = 10000; + + while (cyber2000_inb(0xbf011) & 0x80) { + if (!count--) { + debug_printf("accel_wait timed out\n"); + cyber2000_outb(0, 0xbf011); + return; + } + udelay(10); + } +} + +static void +cyber2000_accel_setup(struct display *p) +{ + dispsw->setup(p); +} + +static void +cyber2000_accel_bmove(struct display *p, int sy, int sx, int dy, int dx, + int height, int width) +{ + unsigned long src, dst, chwidth = p->var.xres_virtual * fontheight(p); + int v = 0x8000; + + if (sx < dx) { + sx += width - 1; + dx += width - 1; + v |= 4; + } + + if (sy < dy) { + sy += height - 1; + dy += height - 1; + v |= 2; + } + + sx *= fontwidth(p); + dx *= fontwidth(p); + src = sx + sy * chwidth; + dst = dx + dy * chwidth; + width = width * fontwidth(p) - 1; + height = height * fontheight(p) - 1; + + cyber2000_accel_wait(); + cyber2000_outb(0x00, 0xbf011); + cyber2000_outb(0x03, 0xbf048); + cyber2000_outw(width, 0xbf060); + + if (p->var.bits_per_pixel != 24) { + cyber2000_outl(dst, 0xbf178); + cyber2000_outl(src, 0xbf170); + } else { + cyber2000_outl(dst * 3, 0xbf178); + cyber2000_outb(dst, 0xbf078); + cyber2000_outl(src * 3, 0xbf170); + } + + cyber2000_outw(height, 0xbf062); + cyber2000_outw(v, 0xbf07c); + cyber2000_outw(0x2800, 0xbf07e); +} + +static void +cyber2000_accel_clear(struct vc_data *conp, struct display *p, int sy, int sx, + int height, int width) +{ + unsigned long dst; + u32 bgx = attr_bgcol_ec(p, conp); + + dst = sx * fontwidth(p) + sy * p->var.xres_virtual * fontheight(p); + width = width * fontwidth(p) - 1; + height = height * fontheight(p) - 1; + + cyber2000_accel_wait(); + cyber2000_outb(0x00, 0xbf011); + cyber2000_outb(0x03, 0xbf048); + cyber2000_outw(width, 0xbf060); + cyber2000_outw(height, 0xbf062); + + switch (p->var.bits_per_pixel) { + case 16: + bgx = ((u16 *)p->dispsw_data)[bgx]; + case 8: + cyber2000_outl(dst, 0xbf178); + break; + + case 24: + cyber2000_outl(dst * 3, 0xbf178); + cyber2000_outb(dst, 0xbf078); + bgx = ((u32 *)p->dispsw_data)[bgx]; + break; + } + + cyber2000_outl(bgx, 0xbf058); + cyber2000_outw(0x8000, 0xbf07c); + cyber2000_outw(0x0800, 0xbf07e); +} + +static void +cyber2000_accel_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx) +{ + cyber2000_accel_wait(); + dispsw->putc(conp, p, c, yy, xx); +} + +static void +cyber2000_accel_putcs(struct vc_data *conp, struct display *p, + const unsigned short *s, int count, int yy, int xx) +{ + cyber2000_accel_wait(); + dispsw->putcs(conp, p, s, count, yy, xx); +} + +static void +cyber2000_accel_revc(struct display *p, int xx, int yy) +{ + cyber2000_accel_wait(); + dispsw->revc(p, xx, yy); +} + +static void +cyber2000_accel_clear_margins(struct vc_data *conp, struct display *p, int bottom_only) +{ + dispsw->clear_margins(conp, p, bottom_only); +} + +static struct display_switch fbcon_cyber_accel = { + cyber2000_accel_setup, + cyber2000_accel_bmove, + cyber2000_accel_clear, + cyber2000_accel_putc, + cyber2000_accel_putcs, + cyber2000_accel_revc, + NULL, + NULL, + cyber2000_accel_clear_margins, + FONTWIDTH(8)|FONTWIDTH(16) +}; + +/* + * Palette + */ +static int +cyber2000_getcolreg(u_int regno, u_int * red, u_int * green, u_int * blue, + u_int * transp, struct fb_info *fb_info) +{ + int t; + + if (regno >= 256) + return 1; + + t = current_par.palette[regno].red; + *red = t << 10 | t << 4 | t >> 2; + + t = current_par.palette[regno].green; + *green = t << 10 | t << 4 | t >> 2; + + t = current_par.palette[regno].blue; + *blue = t << 10 | t << 4 | t >> 2; + + *transp = 0; + + return 0; +} + +/* + * Set a single color register. Return != 0 for invalid regno. + */ +static int +cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *fb_info) +{ + if (regno > 255) + return 1; + + red >>= 10; + green >>= 10; + blue >>= 10; + + current_par.palette[regno].red = red; + current_par.palette[regno].green = green; + current_par.palette[regno].blue = blue; + + switch (fb_display[current_par.currcon].var.bits_per_pixel) { + case 8: + cyber2000_outb(regno, 0x3c8); + cyber2000_outb(red, 0x3c9); + cyber2000_outb(green, 0x3c9); + cyber2000_outb(blue, 0x3c9); + break; + +#ifdef FBCON_HAS_CFB16 + case 16: + if (regno < 64) { + /* write green */ + cyber2000_outb(regno << 2, 0x3c8); + cyber2000_outb(current_par.palette[regno >> 1].red, 0x3c9); + cyber2000_outb(green, 0x3c9); + cyber2000_outb(current_par.palette[regno >> 1].blue, 0x3c9); + } + + if (regno < 32) { + /* write red,blue */ + cyber2000_outb(regno << 3, 0x3c8); + cyber2000_outb(red, 0x3c9); + cyber2000_outb(current_par.palette[regno << 1].green, 0x3c9); + cyber2000_outb(blue, 0x3c9); + } + + if (regno < 16) + current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 11; + break; +#endif + +#ifdef FBCON_HAS_CFB24 + case 24: + cyber2000_outb(regno, 0x3c8); + cyber2000_outb(red, 0x3c9); + cyber2000_outb(green, 0x3c9); + cyber2000_outb(blue, 0x3c9); + + if (regno < 16) + current_par.c_table.cfb24[regno] = regno | regno << 8 | regno << 16; + break; +#endif + + default: + return 1; + } + + return 0; +} + +static int cyber2000fb_set_timing(struct fb_var_screeninfo *var) +{ + int width = var->xres_virtual; + int scr_pitch, fetchrow; + int i; + char b, col; + + switch (var->bits_per_pixel) { + case 8: /* PSEUDOCOLOUR, 256 */ + b = 0; + col = 1; + scr_pitch = var->xres_virtual / 8; + break; + + case 16:/* DIRECTCOLOUR, 64k */ + b = 1; + col = 2; + scr_pitch = var->xres_virtual / 8 * 2; + break; + case 24:/* TRUECOLOUR, 16m */ + b = 2; + col = 4; + scr_pitch = var->xres_virtual / 8 * 3; + width *= 3; + break; + + default: + return 1; + } + + for (i = 0; i < NUM_TOTAL_MODES; i++) + if (var->xres == cyber2000_res[i].xres && + var->yres == cyber2000_res[i].yres) + break; + + if (i < NUM_TOTAL_MODES) + cyber2000_init_hw(cyber2000_res + i); + + fetchrow = scr_pitch + 1; + + debug_printf("Setting regs: pitch=%X, fetchrow=%X, col=%X, b=%X\n", + scr_pitch, fetchrow, col, b); + + cyber2000_outb(0x13, 0x3d4); + cyber2000_outb(scr_pitch, 0x3d5); + cyber2000_outb(0x14, 0x3ce); + cyber2000_outb(fetchrow, 0x3cf); + cyber2000_outb(0x15, 0x3ce); + /* FIXME: is this the right way round? */ + cyber2000_outb(((fetchrow >> 4) & 0xf0) | ((scr_pitch >> 8) & 0x0f), 0x3cf); + cyber2000_outb(0x77, 0x3ce); + cyber2000_outb(col, 0x3cf); + + + cyber2000_outb(0x33, 0x3ce); + cyber2000_outb(0x1c, 0x3cf); + + cyber2000_outw(width - 1, 0xbf018); + cyber2000_outw(width - 1, 0xbf218); + cyber2000_outb(b, 0xbf01c); + + return 0; +} + +static inline void +cyber2000fb_update_start(struct fb_var_screeninfo *var) +{ +#if 0 + unsigned int base; + + base = var->yoffset * var->xres_virtual + var->xoffset; + + cyber2000_outb(0x0c, 0x3d4); + cyber2000_outb(base, 0x3d5); + cyber2000_outb(0x0d, 0x3d4); + cyber2000_outb(base >> 8, 0x3d5); + /* FIXME: need the upper bits of the start offset */ +/* cyber2000_outb(0x??, 0x3d4); + cyber2000_outb(base >> 16, 0x3d5);*/ +#endif +} + +/* + * Open/Release the frame buffer device + */ +static int cyber2000fb_open(struct fb_info *info, int user) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static int cyber2000fb_release(struct fb_info *info, int user) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * Get the Colormap + */ +static int +cyber2000fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + int err = 0; + + if (con == current_par.currcon) /* current console? */ + err = fb_get_cmap(cmap, kspc, cyber2000_getcolreg, info); + else if (fb_display[con].cmap.len) /* non default colormap? */ + fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); + else + fb_copy_cmap(fb_default_cmap(1 << fb_display[con].var.bits_per_pixel), + cmap, kspc ? 0 : 2); + return err; +} + + +/* + * Set the Colormap + */ +static int +cyber2000fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + struct display *disp = &fb_display[con]; + int err = 0; + + if (!disp->cmap.len) { /* no colormap allocated? */ + int size; + + if (disp->var.bits_per_pixel == 16) + size = 32; + else + size = 256; + + err = fb_alloc_cmap(&disp->cmap, size, 0); + } + if (!err) { + if (con == current_par.currcon) /* current console? */ + err = fb_set_cmap(cmap, kspc, cyber2000_setcolreg, + info); + else + fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1); + } + + return err; +} + +static int +cyber2000fb_decode_var(struct fb_var_screeninfo *var, int con, int *visual) +{ + switch (var->bits_per_pixel) { +#ifdef FBCON_HAS_CFB8 + case 8: + *visual = FB_VISUAL_PSEUDOCOLOR; + break; +#endif +#ifdef FBCON_HAS_CFB16 + case 16: + *visual = FB_VISUAL_DIRECTCOLOR; + break; +#endif +#ifdef FBCON_HAS_CFB24 + case 24: + *visual = FB_VISUAL_TRUECOLOR; + break; +#endif + default: + return -EINVAL; + } + + return 0; +} + +/* + * Get the Fixed Part of the Display + */ +static int +cyber2000fb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *fb_info) +{ + struct display *display; + + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + strcpy(fix->id, "Cyber2000"); + + if (con >= 0) + display = fb_display + con; + else + display = &global_disp; + + fix->smem_start = (char *)current_par.screen_base_p; + fix->smem_len = current_par.screen_size; + fix->mmio_start = (char *)current_par.regs_base_p; + fix->mmio_len = 0x000c0000; + fix->type = display->type; + fix->type_aux = display->type_aux; + fix->xpanstep = 0; + fix->ypanstep = display->ypanstep; + fix->ywrapstep = display->ywrapstep; + fix->visual = display->visual; + fix->line_length = display->line_length; + fix->accel = 22; /*FB_ACCEL_IGS_CYBER2000*/ + + return 0; +} + + +/* + * Get the User Defined Part of the Display + */ +static int +cyber2000fb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *fb_info) +{ + if (con == -1) + *var = global_disp.var; + else + *var = fb_display[con].var; + + return 0; +} + +/* + * Set the User Defined Part of the Display + */ +static int +cyber2000fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info) +{ + struct display *display; + int err, chgvar = 0, visual; + + if (con >= 0) + display = fb_display + con; + else + display = &global_disp; + + err = cyber2000fb_decode_var(var, con, &visual); + if (err) + return err; + + switch (var->activate & FB_ACTIVATE_MASK) { + case FB_ACTIVATE_TEST: + return 0; + + case FB_ACTIVATE_NXTOPEN: + case FB_ACTIVATE_NOW: + break; + + default: + return -EINVAL; + } + + if (con >= 0) { + if (display->var.xres != var->xres) + chgvar = 1; + if (display->var.yres != var->yres) + chgvar = 1; + if (display->var.xres_virtual != var->xres_virtual) + chgvar = 1; + if (display->var.yres_virtual != var->yres_virtual) + chgvar = 1; + if (display->var.accel_flags != var->accel_flags) + chgvar = 1; + if (memcmp(&display->var.red, &var->red, sizeof(var->red))) + chgvar = 1; + if (memcmp(&display->var.green, &var->green, sizeof(var->green))) + chgvar = 1; + if (memcmp(&display->var.blue, &var->blue, sizeof(var->green))) + chgvar = 1; + } + + display->var = *var; + + display->screen_base = (char *)current_par.screen_base; + display->visual = visual; + display->type = FB_TYPE_PACKED_PIXELS; + display->type_aux = 0; + display->ypanstep = 0; + display->ywrapstep = 0; + display->line_length = + display->next_line = (var->xres_virtual * var->bits_per_pixel) / 8; + display->can_soft_blank = 1; + display->inverse = 0; + + switch (display->var.bits_per_pixel) { +#ifdef FBCON_HAS_CFB8 + case 8: + dispsw = &fbcon_cfb8; + display->dispsw_data = NULL; + break; +#endif +#ifdef FBCON_HAS_CFB16 + case 16: + dispsw = &fbcon_cfb16; + display->dispsw_data = current_par.c_table.cfb16; + break; +#endif +#ifdef FBCON_HAS_CFB24 + case 24: + dispsw = &fbcon_cfb24; + display->dispsw_data = current_par.c_table.cfb24; + break; +#endif + default: + printk(KERN_WARNING "cyber2000: no support for %dbpp\n", + display->var.bits_per_pixel); + dispsw = &fbcon_dummy; + break; + } + + if (display->var.accel_flags & FB_ACCELF_TEXT && + dispsw != &fbcon_dummy) + display->dispsw = &fbcon_cyber_accel; + else + display->dispsw = dispsw; + + if (chgvar && info && info->changevar) + info->changevar(con); + + if (con == current_par.currcon) { + struct fb_cmap *cmap; + + cyber2000fb_update_start(var); + cyber2000fb_set_timing(var); + + if (display->cmap.len) + cmap = &display->cmap; + else + cmap = fb_default_cmap(current_par.palette_size); + + fb_set_cmap(cmap, 1, cyber2000_setcolreg, info); + } + return 0; +} + + +/* + * Pan or Wrap the Display + */ +static int cyber2000fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + u_int y_bottom; + + y_bottom = var->yoffset; + + if (!(var->vmode & FB_VMODE_YWRAP)) + y_bottom += var->yres; + + if (var->xoffset > (var->xres_virtual - var->xres)) + return -EINVAL; + if (y_bottom > fb_display[con].var.yres_virtual) + return -EINVAL; +return -EINVAL; + + cyber2000fb_update_start(var); + + fb_display[con].var.xoffset = var->xoffset; + fb_display[con].var.yoffset = var->yoffset; + if (var->vmode & FB_VMODE_YWRAP) + fb_display[con].var.vmode |= FB_VMODE_YWRAP; + else + fb_display[con].var.vmode &= ~FB_VMODE_YWRAP; + + return 0; +} + + +static int cyber2000fb_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg, int con, struct fb_info *info) +{ + return -EINVAL; +} + + +/* + * Update the `var' structure (called by fbcon.c) + * + * This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'. + * Since it's called by a kernel driver, no range checking is done. + */ +static int +cyber2000fb_updatevar(int con, struct fb_info *info) +{ + if (con == current_par.currcon) + cyber2000fb_update_start(&fb_display[con].var); + return 0; +} + +static int +cyber2000fb_switch(int con, struct fb_info *info) +{ + struct fb_cmap *cmap; + + if (current_par.currcon >= 0) { + cmap = &fb_display[current_par.currcon].cmap; + + if (cmap->len) + fb_get_cmap(cmap, 1, cyber2000_getcolreg, info); + } + + current_par.currcon = con; + + fb_display[con].var.activate = FB_ACTIVATE_NOW; + + cyber2000fb_set_var(&fb_display[con].var, con, info); + + return 0; +} + +/* + * (Un)Blank the display. + */ +static void cyber2000fb_blank(int blank, struct fb_info *fb_info) +{ + int i; + + if (blank) { + for (i = 0; i < 256; i++) { + cyber2000_outb(i, 0x3c8); + cyber2000_outb(0, 0x3c9); + cyber2000_outb(0, 0x3c9); + cyber2000_outb(0, 0x3c9); + } + } else { + for (i = 0; i < 256; i++) { + cyber2000_outb(i, 0x3c8); + cyber2000_outb(current_par.palette[i].red, 0x3c9); + cyber2000_outb(current_par.palette[i].green, 0x3c9); + cyber2000_outb(current_par.palette[i].blue, 0x3c9); + } + } +} + +__initfunc(void cyber2000fb_setup(char *options, int *ints)) +{ +} + +static struct fb_ops cyber2000fb_ops = +{ + cyber2000fb_open, + cyber2000fb_release, + cyber2000fb_get_fix, + cyber2000fb_get_var, + cyber2000fb_set_var, + cyber2000fb_get_cmap, + cyber2000fb_set_cmap, + cyber2000fb_pan_display, + cyber2000fb_ioctl +}; + +__initfunc(static void +cyber2000fb_init_fbinfo(void)) +{ + static int first = 1; + + if (!first) + return; + first = 0; + + strcpy(fb_info.modename, "Cyber2000"); + strcpy(fb_info.fontname, "Acorn8x8"); + + fb_info.node = -1; + fb_info.fbops = &cyber2000fb_ops; + fb_info.disp = &global_disp; + fb_info.changevar = NULL; + fb_info.switch_con = cyber2000fb_switch; + fb_info.updatevar = cyber2000fb_updatevar; + fb_info.blank = cyber2000fb_blank; + fb_info.flags = FBINFO_FLAG_DEFAULT; + + /* + * setup initial parameters + */ + memset(&init_var, 0, sizeof(init_var)); + init_var.xres_virtual = + init_var.xres = DEFAULT_XRES; + init_var.yres_virtual = + init_var.yres = DEFAULT_YRES; + init_var.bits_per_pixel = DEFAULT_BPP; + + init_var.red.msb_right = 0; + init_var.green.msb_right = 0; + init_var.blue.msb_right = 0; + + switch(init_var.bits_per_pixel) { + case 8: + init_var.bits_per_pixel = 8; + init_var.red.offset = 0; + init_var.red.length = 8; + init_var.green.offset = 0; + init_var.green.length = 8; + init_var.blue.offset = 0; + init_var.blue.length = 8; + break; + + case 16: + init_var.bits_per_pixel = 16; + init_var.red.offset = 11; + init_var.red.length = 5; + init_var.green.offset = 5; + init_var.green.length = 6; + init_var.blue.offset = 0; + init_var.blue.length = 5; + break; + + case 24: + init_var.bits_per_pixel = 24; + init_var.red.offset = 16; + init_var.red.length = 8; + init_var.green.offset = 8; + init_var.green.length = 8; + init_var.blue.offset = 0; + init_var.blue.length = 8; + break; + } + + init_var.nonstd = 0; + init_var.activate = FB_ACTIVATE_NOW; + init_var.height = -1; + init_var.width = -1; + init_var.accel_flags = FB_ACCELF_TEXT; + init_var.sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT; + init_var.vmode = FB_VMODE_NONINTERLACED; +} + +/* + * Initialization + */ +__initfunc(void cyber2000fb_init(void)) +{ + struct pci_dev *dev; + u_int h_sync, v_sync; + + dev = pci_find_device(PCI_VENDOR_ID_INTERG, 0x2000, NULL); + if (!dev) + return; + + CyberRegs = bus_to_virt(dev->base_address[0]) + 0x00800000;/*FIXME*/ + + cyber2000_outb(0x18, 0x46e8); + cyber2000_outb(0x01, 0x102); + cyber2000_outb(0x08, 0x46e8); + + cyber2000fb_init_fbinfo(); + + current_par.currcon = -1; + current_par.screen_base_p = 0x80000000 + dev->base_address[0]; + current_par.screen_base = (u_int)bus_to_virt(dev->base_address[0]); + current_par.screen_size = 0x00200000; + current_par.regs_base_p = 0x80800000 + dev->base_address[0]; + + cyber2000fb_set_var(&init_var, -1, &fb_info); + + h_sync = 1953125000 / init_var.pixclock; + h_sync = h_sync * 512 / (init_var.xres + init_var.left_margin + + init_var.right_margin + init_var.hsync_len); + v_sync = h_sync / (init_var.yres + init_var.upper_margin + + init_var.lower_margin + init_var.vsync_len); + + printk("Cyber2000: %ldkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n", + current_par.screen_size >> 10, + init_var.xres, init_var.yres, + h_sync / 1000, h_sync % 1000, v_sync); + + if (register_framebuffer(&fb_info) < 0) + return; + + MOD_INC_USE_COUNT; /* TODO: This driver cannot be unloaded yet */ +} + + + +#ifdef MODULE +int init_module(void) +{ + cyber2000fb_init(); + return 0; +} + +void cleanup_module(void) +{ + /* Not reached because the usecount will never be + decremented to zero */ + unregister_framebuffer(&fb_info); + /* TODO: clean up ... */ +} + +#endif /* MODULE */ diff --git a/drivers/video/cyber2000fb.h b/drivers/video/cyber2000fb.h new file mode 100644 index 000000000..f1e81dfa0 --- /dev/null +++ b/drivers/video/cyber2000fb.h @@ -0,0 +1,78 @@ +/* + * linux/drivers/video/cyber2000fb.h + * + * Integraphics Cyber2000 frame buffer device + */ + +#define arraysize(x) (sizeof(x)/sizeof(*(x))) +#define cyber2000_outb(dat,reg) (CyberRegs[reg] = dat) +#define cyber2000_outw(dat,reg) (*(unsigned short *)&CyberRegs[reg] = dat) +#define cyber2000_outl(dat,reg) (*(unsigned long *)&CyberRegs[reg] = dat) + +#define cyber2000_inb(reg) (CyberRegs[reg]) +#define cyber2000_inw(reg) (*(unsigned short *)&CyberRegs[reg]) +#define cyber2000_inl(reg) (*(unsigned long *)&CyberRegs[reg]) + +static inline void cyber2000_crtcw(int val, int reg) +{ + cyber2000_outb(reg, 0x3d4); + cyber2000_outb(val, 0x3d5); +} + +static inline void cyber2000_grphw(int val, int reg) +{ + cyber2000_outb(reg, 0x3ce); + cyber2000_outb(val, 0x3cf); +} + +static inline void cyber2000_attrw(int val, int reg) +{ + cyber2000_inb(0x3da); + cyber2000_outb(reg, 0x3c0); + cyber2000_inb(0x3c1); + cyber2000_outb(val, 0x3c0); +} + +static inline void cyber2000_seqw(int val, int reg) +{ + cyber2000_outb(reg, 0x3c4); + cyber2000_outb(val, 0x3c5); +} + +struct cyber2000fb_par { + unsigned long screen_base; + unsigned long screen_base_p; + unsigned long regs_base; + unsigned long regs_base_p; + unsigned long screen_end; + unsigned long screen_size; + unsigned int palette_size; + signed int currcon; + /* + * palette + */ + struct { + u8 red; + u8 green; + u8 blue; + } palette[256]; + /* + * colour mapping table + */ + union { +#ifdef FBCON_HAS_CFB16 + u16 cfb16[16]; +#endif +#ifdef FBCON_HAS_CFB24 + u32 cfb24[16]; +#endif + } c_table; +}; + +struct res { + int xres; + int yres; + unsigned char crtc_regs[18]; + unsigned char crtc_ofl; + unsigned char clk_regs[4]; +}; diff --git a/drivers/video/fbcon-vga-planes.c b/drivers/video/fbcon-vga-planes.c new file mode 100644 index 000000000..391ceb22e --- /dev/null +++ b/drivers/video/fbcon-vga-planes.c @@ -0,0 +1,364 @@ +/* + * 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) +{ + *p |= 1; +} + +/* 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++) + *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; + 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++) + *where++ = 0; + 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); + *where = bg; + rmb(); + *(volatile char*)where; /* fill latches */ + setmode(3); + wmb(); + for (y = 0; y < fontheight(p); y++, where += p->line_length) + *where = cdat[y]; + 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) +{ + int fg = attr_fgcol(p,scr_readw(s)); + int bg = attr_bgcol(p,scr_readw(s)); + + char *where; + int n; + + setmode(2); + setop(0); + selectmask(); + + setmask(0xff); + where = p->screen_base + xx + yy * p->line_length * fontheight(p); + *where = bg; + rmb(); + *(volatile char*)where; + 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(); + *where = fg; + 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) +{ + int fg = attr_fgcol(p,*s); + int bg = attr_bgcol(p,*s); + + 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); + *where = bg; + rmb(); + *(volatile char*)where; /* fill latches with background */ + setmode(3); + wmb(); + for (n = 0; n < count; n++) { + int y; + int c = *s++ & p->charmask; + u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p); + + for (y = 0; y < fontheight(p); y++, cdat++) { + *where = *cdat; + 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 = { + fbcon_vga_planes_setup, fbcon_vga_planes_bmove, fbcon_vga_planes_clear, + fbcon_vga_planes_putc, fbcon_vga_planes_putcs, fbcon_vga_planes_revc, + NULL, NULL, NULL, FONTWIDTH(8) +}; + +struct display_switch fbcon_ega_planes = { + fbcon_vga_planes_setup, fbcon_vga_planes_bmove, fbcon_vga_planes_clear, + fbcon_ega_planes_putc, fbcon_ega_planes_putcs, fbcon_vga_planes_revc, + NULL, NULL, NULL, FONTWIDTH(8) +}; + +#ifdef MODULE +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: + */ + diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c new file mode 100644 index 000000000..7f7b1af4d --- /dev/null +++ b/drivers/video/vga16fb.c @@ -0,0 +1,1064 @@ +/* + * 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/malloc.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> + +#define dac_reg (0x3c8) +#define dac_val (0x3c9) + +/* --------------------------------------------------------------------- */ + +/* + * 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; + +/* Some of the code below is taken from SVGAlib. The original, + unmodified copyright notice for that code is below. */ +/* VGAlib version 1.2 - (c) 1993 Tommy Frandsen */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it without any restrictions. This library is distributed */ +/* in the hope that it will be useful, but without any warranty. */ + +/* Multi-chipset support Copyright 1993 Harm Hanemaayer */ +/* partially copyrighted (C) 1993 by Hartmut Schirmer */ + +/* VGA data register ports */ +#define CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */ +#define CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */ +#define ATT_R 0x3C1 /* Attribute Controller Data Read Register */ +#define GRA_D 0x3CF /* Graphics Controller Data Register */ +#define SEQ_D 0x3C5 /* Sequencer Data Register */ +#define MIS_R 0x3CC /* Misc Output Read Register */ +#define MIS_W 0x3C2 /* Misc Output Write Register */ +#define IS1_RC 0x3DA /* Input Status Register 1 - color emulation */ +#define IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */ +#define PEL_D 0x3C9 /* PEL Data Register */ +#define PEL_MSK 0x3C6 /* PEL mask register */ + +/* EGA-specific registers */ +#define GRA_E0 0x3CC /* Graphics enable processor 0 */ +#define GRA_E1 0x3CA /* Graphics enable processor 1 */ + + +/* VGA index register ports */ +#define CRT_IC 0x3D4 /* CRT Controller Index - color emulation */ +#define CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */ +#define ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */ +#define GRA_I 0x3CE /* Graphics Controller Index */ +#define SEQ_I 0x3C4 /* Sequencer Index */ +#define PEL_IW 0x3C8 /* PEL Write Index */ +#define PEL_IR 0x3C7 /* PEL Read Index */ + +/* standard VGA indexes max counts */ +#define CRT_C 24 /* 24 CRT Controller Registers */ +#define ATT_C 21 /* 21 Attribute Controller Registers */ +#define GRA_C 9 /* 9 Graphics Controller Registers */ +#define SEQ_C 5 /* 5 Sequencer Registers */ +#define MIS_C 1 /* 1 Misc Output Register */ + +#define CRTC_H_TOTAL 0 +#define CRTC_H_DISP 1 +#define CRTC_H_BLANK_START 2 +#define CRTC_H_BLANK_END 3 +#define CRTC_H_SYNC_START 4 +#define CRTC_H_SYNC_END 5 +#define CRTC_V_TOTAL 6 +#define CRTC_OVERFLOW 7 +#define CRTC_PRESET_ROW 8 +#define CRTC_MAX_SCAN 9 +#define CRTC_CURSOR_START 0x0A +#define CRTC_CURSOR_END 0x0B +#define CRTC_START_HI 0x0C +#define CRTC_START_LO 0x0D +#define CRTC_CURSOR_HI 0x0E +#define CRTC_CURSOR_LO 0x0F +#define CRTC_V_SYNC_START 0x10 +#define CRTC_V_SYNC_END 0x11 +#define CRTC_V_DISP_END 0x12 +#define CRTC_OFFSET 0x13 +#define CRTC_UNDERLINE 0x14 +#define CRTC_V_BLANK_START 0x15 +#define CRTC_V_BLANK_END 0x16 +#define CRTC_MODE 0x17 +#define CRTC_LINE_COMPARE 0x18 +#define CRTC_REGS 0x19 + +#define ATC_MODE 0x10 +#define ATC_OVERSCAN 0x11 +#define ATC_PLANE_ENABLE 0x12 +#define ATC_PEL 0x13 +#define ATC_COLOR_PAGE 0x14 + +#define SEQ_CLOCK_MODE 0x01 +#define SEQ_PLANE_WRITE 0x02 +#define SEQ_CHARACTER_MAP 0x03 +#define SEQ_MEMORY_MODE 0x04 + +#define GDC_SR_VALUE 0x00 +#define GDC_SR_ENABLE 0x01 +#define GDC_COMPARE_VALUE 0x02 +#define GDC_DATA_ROTATE 0x03 +#define GDC_PLANE_READ 0x04 +#define GDC_MODE 0x05 +#define GDC_MISC 0x06 +#define GDC_COMPARE_MASK 0x07 +#define GDC_BIT_MASK 0x08 + +struct vga16fb_par { + u8 crtc[CRTC_REGS]; + u8 atc[ATT_C]; + u8 gdc[GRA_C]; + u8 seq[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; + +/* --------------------------------------------------------------------- */ + + /* + * Open/Release the frame buffer device + */ + +static int vga16fb_open(struct fb_info *info, int user) +{ + /* + * Nothing, only a usage count for the moment + */ + MOD_INC_USE_COUNT; + return(0); +} + +static int vga16fb_release(struct fb_info *info, int user) +{ + MOD_DEC_USE_COUNT; + return(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(CRTC_START_HI, CRT_IC); + outb(pos >> 8, CRT_DC); + outb(CRTC_START_LO, CRT_IC); + outb(pos & 0xFF, CRT_DC); +#if 0 + /* if someone supports xoffset in bit resolution */ + inb(IS1_RC); /* reset flip-flop */ + outb(ATC_PEL, ATT_IW); + outb(xoffset & 7, ATT_IW); + inb(IS1_RC); + outb(0x20, 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 = (char *) 0xa0000; + fix->smem_len = 65536; + 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[SEQ_CLOCK_MODE] |= best->seq_clock_mode; + par->var.pixclock = best->pixclock; +} + +#define FAIL(X) return -EINVAL + +static 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[CRTC_H_TOTAL] = xtotal - 5; + par->crtc[CRTC_H_BLANK_START] = xres - 1; + par->crtc[CRTC_H_DISP] = xres - 1; + pos = xres + right; + par->crtc[CRTC_H_SYNC_START] = pos; + pos += hslen; + par->crtc[CRTC_H_SYNC_END] = pos & 0x1F; + pos += left - 2; /* blank_end + 2 <= total + 5 */ + par->crtc[CRTC_H_BLANK_END] = (pos & 0x1F) | 0x80; + if (pos & 0x20) + par->crtc[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[CRTC_V_TOTAL] = ytotal - 2; + r7 = 0x10; /* disable linecompare */ + if (ytotal & 0x100) r7 |= 0x01; + if (ytotal & 0x200) r7 |= 0x20; + par->crtc[CRTC_PRESET_ROW] = 0; + par->crtc[CRTC_MAX_SCAN] = 0x40; /* 1 scanline, no linecmp */ + par->var.vmode = var->vmode; + if (var->vmode & FB_VMODE_DOUBLE) + par->crtc[CRTC_MAX_SCAN] |= 0x80; + par->crtc[CRTC_CURSOR_START] = 0x20; + par->crtc[CRTC_CURSOR_END] = 0x00; + pos = yoffset * vxres + (xoffset >> 3); + par->crtc[CRTC_START_HI] = pos >> 8; + par->crtc[CRTC_START_LO] = pos & 0xFF; + par->crtc[CRTC_CURSOR_HI] = 0x00; + par->crtc[CRTC_CURSOR_LO] = 0x00; + pos = yres - 1; + par->crtc[CRTC_V_DISP_END] = pos & 0xFF; + par->crtc[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[CRTC_MAX_SCAN] |= 0x20; /* BLANK_START */ + } + pos += lower; + par->crtc[CRTC_V_SYNC_START] = pos & 0xFF; + if (pos & 0x100) + r7 |= 0x04; + if (pos & 0x200) + r7 |= 0x80; + pos += vslen; + par->crtc[CRTC_V_SYNC_END] = (pos & 0x0F) | 0x10; /* disabled IRQ */ + pos += upper - 1; /* blank_end + 1 <= ytotal + 2 */ + par->crtc[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[CRTC_OFFSET] = vxres >> 1; + par->crtc[CRTC_UNDERLINE] = 0x1F; + par->crtc[CRTC_MODE] = rMode | 0xE3; + par->crtc[CRTC_LINE_COMPARE] = 0xFF; + par->crtc[CRTC_OVERFLOW] = r7; + + par->vss = 0x00; /* 3DA */ + + for (i = 0x00; i < 0x10; i++) + par->atc[i] = i; + par->atc[ATC_MODE] = 0x81; + par->atc[ATC_OVERSCAN] = 0x00; /* 0 for EGA, 0xFF for VGA */ + par->atc[ATC_PLANE_ENABLE] = 0x0F; + par->atc[ATC_PEL] = xoffset & 7; + par->atc[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[SEQ_CLOCK_MODE] = 0x01; + par->seq[SEQ_PLANE_WRITE] = 0x0F; + par->seq[SEQ_CHARACTER_MAP] = 0x00; + par->seq[SEQ_MEMORY_MODE] = 0x06; + + par->gdc[GDC_SR_VALUE] = 0x00; + par->gdc[GDC_SR_ENABLE] = 0x0F; + par->gdc[GDC_COMPARE_VALUE] = 0x00; + par->gdc[GDC_DATA_ROTATE] = 0x20; + par->gdc[GDC_PLANE_READ] = 0; + par->gdc[GDC_MODE] = 0x00; + par->gdc[GDC_MISC] = 0x05; + par->gdc[GDC_COMPARE_MASK] = 0x0F; + par->gdc[GDC_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 FAIL + +static int vga16fb_set_par(const struct vga16fb_par *par, + struct vga16fb_info *info) +{ + int i; + + outb(inb(MIS_R) | 0x01, MIS_W); + + /* Enable graphics register modification */ + if (!info->isVGA) { + outb(0x00, GRA_E0); + outb(0x01, GRA_E1); + } + + /* update misc output register */ + outb(par->misc, MIS_W); + + /* synchronous reset on */ + outb(0x00, SEQ_I); + outb(0x01, SEQ_D); + + /* write sequencer registers */ + outb(1, SEQ_I); + outb(par->seq[1] | 0x20, SEQ_D); + for (i = 2; i < SEQ_C; i++) { + outb(i, SEQ_I); + outb(par->seq[i], SEQ_D); + } + + /* synchronous reset off */ + outb(0x00, SEQ_I); + outb(0x03, SEQ_D); + + /* deprotect CRT registers 0-7 */ + outb(0x11, CRT_IC); + outb(par->crtc[0x11], CRT_DC); + + /* write CRT registers */ + for (i = 0; i < CRTC_REGS; i++) { + outb(i, CRT_IC); + outb(par->crtc[i], CRT_DC); + } + + /* write graphics controller registers */ + for (i = 0; i < GRA_C; i++) { + outb(i, GRA_I); + outb(par->gdc[i], GRA_D); + } + + /* write attribute controller registers */ + for (i = 0; i < ATT_C; i++) { + inb_p(IS1_RC); /* reset flip-flop */ + outb_p(i, ATT_IW); + outb_p(par->atc[i], ATT_IW); + } + + /* Wait for screen to stabilize. */ + mdelay(50); + + outb(0x01, SEQ_I); + outb(par->seq[1], SEQ_D); + + inb(IS1_RC); + outb(0x20, ATT_IW); + + return 0; +} + +static int vga16fb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *fb) +{ + struct vga16fb_info *info = (struct vga16fb_info*)fb; + struct vga16fb_par par; + struct display *display; + int err; + + if (con < 0) + display = fb->disp; + else + display = fb_display + con; + if ((err = vga16fb_decode_var(var, &par, info)) != 0) + return err; + vga16fb_encode_var(var, &par, info); + + if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST) + return 0; + + if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { + u32 oldxres, oldyres, oldvxres, oldvyres, oldbpp; + + oldxres = display->var.xres; + oldyres = display->var.yres; + oldvxres = display->var.xres_virtual; + oldvyres = display->var.yres_virtual; + oldbpp = display->var.bits_per_pixel; + + display->var = *var; + + if (oldxres != var->xres || oldyres != var->yres || + oldvxres != var->xres_virtual || oldvyres != var->yres_virtual || + oldbpp != var->bits_per_pixel) { + vga16fb_set_disp(con, info); + if (info->fb_info.changevar) + info->fb_info.changevar(con); + } + if (con == currcon) + vga16fb_set_par(&par, info); + } + + return 0; +} + +static void ega16_setpalette(int regno, unsigned red, unsigned green, unsigned blue) +{ + static unsigned char map[] = { 000, 001, 010, 011 }; + int val; + + val = map[red>>14] | ((map[green>>14]) << 1) | ((map[blue>>14]) << 2); + inb_p(0x3DA); /* ! 0x3BA */ + outb_p(regno, 0x3C0); + outb_p(val, 0x3C0); + inb_p(0x3DA); /* some clones need it */ + outb_p(0x20, 0x3C0); /* unblank screen */ +} + +static int vga16_getcolreg(unsigned regno, unsigned *red, unsigned *green, + unsigned *blue, unsigned *transp, + struct fb_info *fb_info) +{ + /* + * Read a single color register and split it into colors/transparent. + * Return != 0 for invalid regno. + */ + + if (regno >= 16) + return 1; + + *red = palette[regno].red; + *green = palette[regno].green; + *blue = palette[regno].blue; + *transp = 0; + return 0; +} + +static void vga16_setpalette(int regno, unsigned red, unsigned green, unsigned blue) +{ + outb(regno, dac_reg); + outb(red >> 10, dac_val); + outb(green >> 10, dac_val); + outb(blue >> 10, dac_val); +} + +static int vga16_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *fb_info) +{ + int gray; + + /* + * Set a single color register. The values supplied are + * already rounded down to the hardware's capabilities + * (according to the entries in the `var' structure). Return + * != 0 for invalid regno. + */ + + if (regno >= 16) + return 1; + + palette[regno].red = red; + palette[regno].green = green; + palette[regno].blue = blue; + + if (currcon < 0) + gray = disp.var.grayscale; + else + gray = fb_display[currcon].var.grayscale; + if (gray) { + /* gray = 0.30*R + 0.59*G + 0.11*B */ + red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; + } + if (((struct vga16fb_info *) fb_info)->isVGA) + vga16_setpalette(regno,red,green,blue); + else + ega16_setpalette(regno,red,green,blue); + + return 0; +} + +static void do_install_cmap(int con, struct fb_info *info) +{ + if (con != currcon) + return; + if (fb_display[con].cmap.len) + fb_set_cmap(&fb_display[con].cmap, 1, vga16_setcolreg, info); + else + fb_set_cmap(fb_default_cmap(16), 1, vga16_setcolreg, + info); +} + +static int vga16fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + if (con == currcon) /* current console? */ + return fb_get_cmap(cmap, kspc, vga16_getcolreg, info); + else if (fb_display[con].cmap.len) /* non default colormap? */ + fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); + else + fb_copy_cmap(fb_default_cmap(16), + cmap, kspc ? 0 : 2); + return 0; +} + +static int vga16fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + int err; + + if (!fb_display[con].cmap.len) { /* no colormap allocated? */ + err = fb_alloc_cmap(&fb_display[con].cmap,16,0); + if (err) + return err; + } + if (con == currcon) /* current console? */ + return fb_set_cmap(cmap, kspc, vga16_setcolreg, info); + else + fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); + return 0; +} + +static int vga16fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + if (var->xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual || + var->yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual) + return -EINVAL; + if (con == currcon) + vga16fb_pan_var(info, var); + fb_display[con].var.xoffset = var->xoffset; + fb_display[con].var.yoffset = var->yoffset; + fb_display[con].var.vmode &= ~FB_VMODE_YWRAP; + return 0; +} + +static int vga16fb_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, int con, + struct fb_info *info) +{ + return -EINVAL; +} + +static struct fb_ops vga16fb_ops = { + vga16fb_open, + vga16fb_release, + vga16fb_get_fix, + vga16fb_get_var, + vga16fb_set_var, + vga16fb_get_cmap, + vga16fb_set_cmap, + vga16fb_pan_display, + vga16fb_ioctl +}; + +void vga16fb_setup(char *options, int *ints) +{ + char *this_opt; + + vga16fb.fb_info.fontname[0] = '\0'; + + if (!options || !*options) + return; + + for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) { + if (!*this_opt) continue; + + if (!strncmp(this_opt, "font:", 5)) + strcpy(vga16fb.fb_info.fontname, this_opt+5); + } +} + +static int vga16fb_switch(int con, struct fb_info *fb) +{ + struct vga16fb_par par; + struct vga16fb_info *info = (struct vga16fb_info*)fb; + + /* Do we have to save the colormap? */ + if (fb_display[currcon].cmap.len) + fb_get_cmap(&fb_display[currcon].cmap, 1, vga16_getcolreg, + fb); + + currcon = con; + vga16fb_decode_var(&fb_display[con].var, &par, info); + vga16fb_set_par(&par, info); + vga16fb_set_disp(con, info); + + /* Install new colormap */ + do_install_cmap(con, fb); +/* vga16fb_update_var(con, fb); */ + return 1; +} + +/* The following VESA blanking code is taken from vgacon.c. The VGA + blanking code was originally by Huang shi chao, and modified by + Christoph Rimek (chrimek@toppoint.de) and todd j. derr + (tjd@barefoot.org) for Linux. */ +#define attrib_port 0x3c0 +#define seq_port_reg 0x3c4 +#define seq_port_val 0x3c5 +#define gr_port_reg 0x3ce +#define gr_port_val 0x3cf +#define video_misc_rd 0x3cc +#define video_misc_wr 0x3c2 +#define vga_video_port_reg 0x3d4 +#define vga_video_port_val 0x3d5 + +static void vga_vesa_blank(struct vga16fb_info *info, int mode) +{ + unsigned char SeqCtrlIndex; + unsigned char CrtCtrlIndex; + + cli(); + SeqCtrlIndex = inb_p(seq_port_reg); + CrtCtrlIndex = inb_p(vga_video_port_reg); + + /* save original values of VGA controller registers */ + if(!info->vesa_blanked) { + info->vga_state.CrtMiscIO = inb_p(video_misc_rd); + sti(); + + outb_p(0x00,vga_video_port_reg); /* HorizontalTotal */ + info->vga_state.HorizontalTotal = inb_p(vga_video_port_val); + outb_p(0x01,vga_video_port_reg); /* HorizDisplayEnd */ + info->vga_state.HorizDisplayEnd = inb_p(vga_video_port_val); + outb_p(0x04,vga_video_port_reg); /* StartHorizRetrace */ + info->vga_state.StartHorizRetrace = inb_p(vga_video_port_val); + outb_p(0x05,vga_video_port_reg); /* EndHorizRetrace */ + info->vga_state.EndHorizRetrace = inb_p(vga_video_port_val); + outb_p(0x07,vga_video_port_reg); /* Overflow */ + info->vga_state.Overflow = inb_p(vga_video_port_val); + outb_p(0x10,vga_video_port_reg); /* StartVertRetrace */ + info->vga_state.StartVertRetrace = inb_p(vga_video_port_val); + outb_p(0x11,vga_video_port_reg); /* EndVertRetrace */ + info->vga_state.EndVertRetrace = inb_p(vga_video_port_val); + outb_p(0x17,vga_video_port_reg); /* ModeControl */ + info->vga_state.ModeControl = inb_p(vga_video_port_val); + outb_p(0x01,seq_port_reg); /* ClockingMode */ + info->vga_state.ClockingMode = inb_p(seq_port_val); + } + + /* assure that video is enabled */ + /* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */ + cli(); + outb_p(0x01,seq_port_reg); + outb_p(info->vga_state.ClockingMode | 0x20,seq_port_val); + + /* test for vertical retrace in process.... */ + if ((info->vga_state.CrtMiscIO & 0x80) == 0x80) + outb_p(info->vga_state.CrtMiscIO & 0xef,video_misc_wr); + + /* + * Set <End of vertical retrace> to minimum (0) and + * <Start of vertical Retrace> to maximum (incl. overflow) + * Result: turn off vertical sync (VSync) pulse. + */ + if (mode & VESA_VSYNC_SUSPEND) { + outb_p(0x10,vga_video_port_reg); /* StartVertRetrace */ + outb_p(0xff,vga_video_port_val); /* maximum value */ + outb_p(0x11,vga_video_port_reg); /* EndVertRetrace */ + outb_p(0x40,vga_video_port_val); /* minimum (bits 0..3) */ + outb_p(0x07,vga_video_port_reg); /* Overflow */ + outb_p(info->vga_state.Overflow | 0x84,vga_video_port_val); /* bits 9,10 of vert. retrace */ + } + + if (mode & VESA_HSYNC_SUSPEND) { + /* + * Set <End of horizontal retrace> to minimum (0) and + * <Start of horizontal Retrace> to maximum + * Result: turn off horizontal sync (HSync) pulse. + */ + outb_p(0x04,vga_video_port_reg); /* StartHorizRetrace */ + outb_p(0xff,vga_video_port_val); /* maximum */ + outb_p(0x05,vga_video_port_reg); /* EndHorizRetrace */ + outb_p(0x00,vga_video_port_val); /* minimum (0) */ + } + + /* restore both index registers */ + outb_p(SeqCtrlIndex,seq_port_reg); + outb_p(CrtCtrlIndex,vga_video_port_reg); + sti(); +} + +static void vga_vesa_unblank(struct vga16fb_info *info) +{ + unsigned char SeqCtrlIndex; + unsigned char CrtCtrlIndex; + + cli(); + SeqCtrlIndex = inb_p(seq_port_reg); + CrtCtrlIndex = inb_p(vga_video_port_reg); + + /* restore original values of VGA controller registers */ + outb_p(info->vga_state.CrtMiscIO,video_misc_wr); + + outb_p(0x00,vga_video_port_reg); /* HorizontalTotal */ + outb_p(info->vga_state.HorizontalTotal,vga_video_port_val); + outb_p(0x01,vga_video_port_reg); /* HorizDisplayEnd */ + outb_p(info->vga_state.HorizDisplayEnd,vga_video_port_val); + outb_p(0x04,vga_video_port_reg); /* StartHorizRetrace */ + outb_p(info->vga_state.StartHorizRetrace,vga_video_port_val); + outb_p(0x05,vga_video_port_reg); /* EndHorizRetrace */ + outb_p(info->vga_state.EndHorizRetrace,vga_video_port_val); + outb_p(0x07,vga_video_port_reg); /* Overflow */ + outb_p(info->vga_state.Overflow,vga_video_port_val); + outb_p(0x10,vga_video_port_reg); /* StartVertRetrace */ + outb_p(info->vga_state.StartVertRetrace,vga_video_port_val); + outb_p(0x11,vga_video_port_reg); /* EndVertRetrace */ + outb_p(info->vga_state.EndVertRetrace,vga_video_port_val); + outb_p(0x17,vga_video_port_reg); /* ModeControl */ + outb_p(info->vga_state.ModeControl,vga_video_port_val); + outb_p(0x01,seq_port_reg); /* ClockingMode */ + outb_p(info->vga_state.ClockingMode,seq_port_val); + + /* restore index/control registers */ + outb_p(SeqCtrlIndex,seq_port_reg); + outb_p(CrtCtrlIndex,vga_video_port_reg); + sti(); +} + +static void vga_pal_blank(void) +{ + int i; + + for (i=0; i<16; i++) { + outb_p (i, dac_reg) ; + outb_p (0, dac_val) ; + outb_p (0, dac_val) ; + outb_p (0, dac_val) ; + } +} + +/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ +static void vga16fb_blank(int blank, struct fb_info *fb_info) +{ + struct vga16fb_info *info = (struct vga16fb_info*)fb_info; + + switch (blank) { + case 0: /* Unblank */ + if (info->vesa_blanked) { + vga_vesa_unblank(info); + info->vesa_blanked = 0; + } + if (info->palette_blanked) { + do_install_cmap(currcon, fb_info); + info->palette_blanked = 0; + } + break; + case 1: /* blank */ + vga_pal_blank(); + info->palette_blanked = 1; + break; + default: /* VESA blanking */ + vga_vesa_blank(info, blank-1); + info->vesa_blanked = 1; + break; + } +} + +__initfunc(void vga16fb_init(void)) +{ + int i,j; + + printk("vga16fb: initializing\n"); + + vga16fb.video_vbase = ioremap((unsigned long)0xa0000, 65536); + printk("vga16fb: mapped to 0x%p\n", vga16fb.video_vbase); + + vga16fb.isVGA = ORIG_VIDEO_ISVGA; + vga16fb.palette_blanked = 0; + vga16fb.vesa_blanked = 0; + + i = vga16fb.isVGA? 6 : 2; + + vga16fb_defined.red.length = i; + vga16fb_defined.green.length = i; + vga16fb_defined.blue.length = i; + for(i = 0; i < 16; i++) { + j = color_table[i]; + palette[i].red = default_red[j]; + palette[i].green = default_grn[j]; + palette[i].blue = default_blu[j]; + } + if (vga16fb.isVGA) + request_region(0x3c0, 32, "vga+"); + else + request_region(0x3C0, 32, "ega"); + + disp.var = vga16fb_defined; + + /* name should not depend on EGA/VGA */ + strcpy(vga16fb.fb_info.modename, "VGA16 VGA"); + vga16fb.fb_info.changevar = NULL; + vga16fb.fb_info.node = -1; + vga16fb.fb_info.fbops = &vga16fb_ops; + vga16fb.fb_info.disp=&disp; + vga16fb.fb_info.switch_con=&vga16fb_switch; + vga16fb.fb_info.updatevar=&vga16fb_update_var; + vga16fb.fb_info.blank=&vga16fb_blank; + vga16fb.fb_info.flags=FBINFO_FLAG_DEFAULT; + vga16fb_set_disp(-1, &vga16fb); + + if (register_framebuffer(&vga16fb.fb_info)<0) + return; + + printk("fb%d: %s frame buffer device\n", + GET_FB_IDX(vga16fb.fb_info.node), vga16fb.fb_info.modename); +} + + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ + |