/* -*- linux-c -*- ------------------------------------------------------- * * * Copyright (C) 1991, 1992 Linus Torvalds * Copyright 2007-2008 rPath, Inc. - All Rights Reserved * * This file is part of the Linux kernel, and is made available under * the terms of the GNU General Public License version 2. * * ----------------------------------------------------------------------- */ /* * arch/i386/boot/video-mode.c * * Set the video mode. This is separated out into a different * file in order to be shared with the ACPI wakeup code. */ #include "boot.h" #include "video.h" #include "vesa.h" /* * Common variables */ int adapter; /* 0=CGA/MDA/HGC, 1=EGA, 2=VGA+ */ u16 video_segment; int force_x, force_y; /* Don't query the BIOS for cols/rows */ int do_restore; /* Screen contents changed during mode flip */ int graphic_mode; /* Graphic mode with linear frame buffer */ /* Probe the video drivers and have them generate their mode lists. */ void probe_cards(int unsafe) { struct card_info *card; static u8 probed[2]; if (probed[unsafe]) return; probed[unsafe] = 1; for (card = video_cards; card < video_cards_end; card++) { if (card->unsafe == unsafe) { if (card->probe) card->nmodes = card->probe(); else card->nmodes = 0; } } } /* Test if a mode is defined */ int mode_defined(u16 mode) { struct card_info *card; struct mode_info *mi; int i; for (card = video_cards; card < video_cards_end; card++) { mi = card->modes; for (i = 0; i < card->nmodes; i++, mi++) { if (mi->mode == mode) return 1; } } return 0; } /* Set mode (without recalc) */ static int raw_set_mode(u16 mode, u16 *real_mode) { int nmode, i; struct card_info *card; struct mode_info *mi; /* Drop the recalc bit if set */ mode &= ~VIDEO_RECALC; /* Scan for mode based on fixed ID, position, or resolution */ nmode = 0; for (card = video_cards; card < video_cards_end; card++) { mi = card->modes; for (i = 0; i < card->nmodes; i++, mi++) { int visible = mi->x || mi->y; if ((mode == nmode && visible) || mode == mi->mode || mode == (mi->y << 8)+mi->x) { *real_mode = mi->mode; return card->set_mode(mi); } if (visible) nmode++; } } /* Nothing found? Is it an "exceptional" (unprobed) mode? */ for (card = video_cards; card < video_cards_end; card++) { if (mode >= card->xmode_first && mode < card->xmode_first+card->xmode_n) { struct mode_info mix; *real_mode = mix.mode = mode; mix.x = mix.y = 0; return card->set_mode(&mix); } } /* Otherwise, failure... */ return -1; } /* * Recalculate the vertical video cutoff (hack!) */ static void vga_recalc_vertical(void) { unsigned int font_size, rows; u16 crtc; u8 pt, ov; set_fs(0); font_size = rdfs8(0x485); /* BIOS: font size (pixels) */ rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */ rows *= font_size; /* Visible scan lines */ rows--; /* ... minus one */ crtc = vga_crtc(); pt = in_idx(crtc, 0x11); pt &= ~0x80; /* Unlock CR0-7 */ out_idx(pt, crtc, 0x11); out_idx((u8)rows, crtc, 0x12); /* Lower height register */ ov = in_idx(crtc, 0x07); /* Overflow register */ ov &= 0xbd; ov |= (rows >> (8-1)) & 0x02; ov |= (rows >> (9-6)) & 0x40; out_idx(ov, crtc, 0x07); } /* Set mode (with recalc if specified) */ int set_mode(u16 mode) { int rv; u16 real_mode; /* Very special mode numbers... */ if (mode == VIDEO_CURRENT_MODE) return 0; /* Nothing to do... */ else if (mode == NORMAL_VGA) mode = VIDEO_80x25; else if (mode == EXTENDED_VGA) mode = VIDEO_8POINT; rv = raw_set_mode(mode, &real_mode); if (rv) return rv; if (mode & VIDEO_RECALC) vga_recalc_vertical(); /* Save the canonical mode number for the kernel, not an alias, size specification or menu position */ #ifndef _WAKEUP boot_params.hdr.vid_mode = real_mode; #endif return 0; }