2007-07-11 23:18:52 +04:00
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
* Copyright 2007 rPath , Inc . - All Rights Reserved
2009-04-02 05:20:11 +04:00
* Copyright 2009 Intel Corporation ; author H . Peter Anvin
2007-07-11 23:18:52 +04:00
*
* This file is part of the Linux kernel , and is made available under
* the terms of the GNU General Public License version 2.
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
* Select video mode
*/
2015-11-30 13:10:33 +03:00
# include <uapi/asm/boot.h>
2007-07-11 23:18:52 +04:00
# include "boot.h"
# include "video.h"
# include "vesa.h"
2015-01-24 21:11:32 +03:00
static u16 video_segment ;
2007-07-11 23:18:52 +04:00
static void store_cursor_position ( void )
{
2009-04-02 05:20:11 +04:00
struct biosregs ireg , oreg ;
2007-07-11 23:18:52 +04:00
2009-04-02 05:20:11 +04:00
initregs ( & ireg ) ;
ireg . ah = 0x03 ;
intcall ( 0x10 , & ireg , & oreg ) ;
2007-07-11 23:18:52 +04:00
2009-04-02 05:20:11 +04:00
boot_params . screen_info . orig_x = oreg . dl ;
boot_params . screen_info . orig_y = oreg . dh ;
2009-11-13 22:57:00 +03:00
if ( oreg . ch & 0x20 )
boot_params . screen_info . flags | = VIDEO_FLAGS_NOCURSOR ;
if ( ( oreg . ch & 0x1f ) > ( oreg . cl & 0x1f ) )
boot_params . screen_info . flags | = VIDEO_FLAGS_NOCURSOR ;
2007-07-11 23:18:52 +04:00
}
static void store_video_mode ( void )
{
2009-04-02 05:20:11 +04:00
struct biosregs ireg , oreg ;
2007-07-11 23:18:52 +04:00
/* N.B.: the saving of the video page here is a bit silly,
since we pretty much assume page 0 everywhere . */
2009-04-02 05:20:11 +04:00
initregs ( & ireg ) ;
ireg . ah = 0x0f ;
intcall ( 0x10 , & ireg , & oreg ) ;
2007-07-11 23:18:52 +04:00
/* Not all BIOSes are clean with respect to the top bit */
2009-04-02 05:20:11 +04:00
boot_params . screen_info . orig_video_mode = oreg . al & 0x7f ;
boot_params . screen_info . orig_video_page = oreg . bh ;
2007-07-11 23:18:52 +04:00
}
/*
* Store the video mode parameters for later usage by the kernel .
* This is done by asking the BIOS except for the rows / columns
* parameters in the default 80 x25 mode - - these are set directly ,
* because some very obscure BIOSes supply insane values .
*/
static void store_mode_params ( void )
{
u16 font_size ;
int x , y ;
/* For graphics mode, it is up to the mode-setting driver
( currently only video - vesa . c ) to store the parameters */
if ( graphic_mode )
return ;
store_cursor_position ( ) ;
store_video_mode ( ) ;
if ( boot_params . screen_info . orig_video_mode = = 0x07 ) {
/* MDA, HGC, or VGA in monochrome mode */
video_segment = 0xb000 ;
} else {
/* CGA, EGA, VGA and so forth */
video_segment = 0xb800 ;
}
set_fs ( 0 ) ;
font_size = rdfs16 ( 0x485 ) ; /* Font size, BIOS area */
boot_params . screen_info . orig_video_points = font_size ;
x = rdfs16 ( 0x44a ) ;
y = ( adapter = = ADAPTER_CGA ) ? 25 : rdfs8 ( 0x484 ) + 1 ;
if ( force_x )
x = force_x ;
if ( force_y )
y = force_y ;
boot_params . screen_info . orig_video_cols = x ;
boot_params . screen_info . orig_video_lines = y ;
}
static unsigned int get_entry ( void )
{
char entry_buf [ 4 ] ;
int i , len = 0 ;
int key ;
unsigned int v ;
do {
key = getchar ( ) ;
if ( key = = ' \b ' ) {
if ( len > 0 ) {
puts ( " \b \b " ) ;
len - - ;
}
} else if ( ( key > = ' 0 ' & & key < = ' 9 ' ) | |
( key > = ' A ' & & key < = ' Z ' ) | |
( key > = ' a ' & & key < = ' z ' ) ) {
if ( len < sizeof entry_buf ) {
entry_buf [ len + + ] = key ;
putchar ( key ) ;
}
}
} while ( key ! = ' \r ' ) ;
putchar ( ' \n ' ) ;
if ( len = = 0 )
return VIDEO_CURRENT_MODE ; /* Default */
v = 0 ;
for ( i = 0 ; i < len ; i + + ) {
v < < = 4 ;
key = entry_buf [ i ] | 0x20 ;
v + = ( key > ' 9 ' ) ? key - ' a ' + 10 : key - ' 0 ' ;
}
return v ;
}
static void display_menu ( void )
{
struct card_info * card ;
struct mode_info * mi ;
char ch ;
int i ;
2008-01-30 15:33:02 +03:00
int nmodes ;
int modes_per_line ;
int col ;
2007-07-11 23:18:52 +04:00
2008-01-30 15:33:02 +03:00
nmodes = 0 ;
for ( card = video_cards ; card < video_cards_end ; card + + )
nmodes + = card - > nmodes ;
2007-07-11 23:18:52 +04:00
2008-01-30 15:33:02 +03:00
modes_per_line = 1 ;
if ( nmodes > = 20 )
modes_per_line = 3 ;
for ( col = 0 ; col < modes_per_line ; col + + )
puts ( " Mode: Resolution: Type: " ) ;
putchar ( ' \n ' ) ;
col = 0 ;
2007-07-11 23:18:52 +04:00
ch = ' 0 ' ;
for ( card = video_cards ; card < video_cards_end ; card + + ) {
mi = card - > modes ;
for ( i = 0 ; i < card - > nmodes ; i + + , mi + + ) {
2008-01-30 15:33:02 +03:00
char resbuf [ 32 ] ;
2007-07-11 23:18:52 +04:00
int visible = mi - > x & & mi - > y ;
u16 mode_id = mi - > mode ? mi - > mode :
( mi - > y < < 8 ) + mi - > x ;
if ( ! visible )
continue ; /* Hidden mode */
2008-01-30 15:33:02 +03:00
if ( mi - > depth )
sprintf ( resbuf , " %dx%d " , mi - > y , mi - > depth ) ;
else
sprintf ( resbuf , " %d " , mi - > y ) ;
printf ( " %c %03X %4dx%-7s %-6s " ,
ch , mode_id , mi - > x , resbuf , card - > card_name ) ;
col + + ;
if ( col > = modes_per_line ) {
putchar ( ' \n ' ) ;
col = 0 ;
}
2007-07-11 23:18:52 +04:00
if ( ch = = ' 9 ' )
ch = ' a ' ;
else if ( ch = = ' z ' | | ch = = ' ' )
ch = ' ' ; /* Out of keys... */
else
ch + + ;
}
}
2008-01-30 15:33:02 +03:00
if ( col )
putchar ( ' \n ' ) ;
2007-07-11 23:18:52 +04:00
}
# define H(x) ((x)-'a'+10)
# define SCAN ((H('s')<<12)+(H('c')<<8)+(H('a')<<4)+H('n'))
static unsigned int mode_menu ( void )
{
int key ;
unsigned int sel ;
puts ( " Press <ENTER> to see video modes available, "
" <SPACE> to continue, or wait 30 sec \n " ) ;
kbd_flush ( ) ;
while ( 1 ) {
key = getchar_timeout ( ) ;
if ( key = = ' ' | | key = = 0 )
return VIDEO_CURRENT_MODE ; /* Default */
if ( key = = ' \r ' )
break ;
putchar ( ' \a ' ) ; /* Beep! */
}
for ( ; ; ) {
display_menu ( ) ;
puts ( " Enter a video mode or \" scan \" to scan for "
" additional modes: " ) ;
sel = get_entry ( ) ;
if ( sel ! = SCAN )
return sel ;
probe_cards ( 1 ) ;
}
}
/* Save screen content to the heap */
2008-11-23 21:37:09 +03:00
static struct saved_screen {
2007-07-11 23:18:52 +04:00
int x , y ;
int curx , cury ;
u16 * data ;
} saved ;
static void save_screen ( void )
{
/* Should be called after store_mode_params() */
saved . x = boot_params . screen_info . orig_video_cols ;
saved . y = boot_params . screen_info . orig_video_lines ;
saved . curx = boot_params . screen_info . orig_x ;
saved . cury = boot_params . screen_info . orig_y ;
2007-10-26 03:09:38 +04:00
if ( ! heap_free ( saved . x * saved . y * sizeof ( u16 ) + 512 ) )
2007-07-11 23:18:52 +04:00
return ; /* Not enough heap to save the screen */
saved . data = GET_HEAP ( u16 , saved . x * saved . y ) ;
set_fs ( video_segment ) ;
copy_from_fs ( saved . data , 0 , saved . x * saved . y * sizeof ( u16 ) ) ;
}
static void restore_screen ( void )
{
/* Should be called after store_mode_params() */
int xs = boot_params . screen_info . orig_video_cols ;
int ys = boot_params . screen_info . orig_video_lines ;
int y ;
addr_t dst = 0 ;
u16 * src = saved . data ;
2009-04-02 05:20:11 +04:00
struct biosregs ireg ;
2007-07-11 23:18:52 +04:00
if ( graphic_mode )
return ; /* Can't restore onto a graphic mode */
if ( ! src )
return ; /* No saved screen contents */
/* Restore screen contents */
set_fs ( video_segment ) ;
for ( y = 0 ; y < ys ; y + + ) {
int npad ;
if ( y < saved . y ) {
int copy = ( xs < saved . x ) ? xs : saved . x ;
copy_to_fs ( dst , src , copy * sizeof ( u16 ) ) ;
dst + = copy * sizeof ( u16 ) ;
src + = saved . x ;
npad = ( xs < saved . x ) ? 0 : xs - saved . x ;
} else {
npad = xs ;
}
/* Writes "npad" blank characters to
video_segment : dst and advances dst */
asm volatile ( " pushw %%es ; "
" movw %2,%%es ; "
" shrw %%cx ; "
" jnc 1f ; "
" stosw \n \t "
" 1: rep;stosl ; "
" popw %%es "
: " +D " ( dst ) , " +c " ( npad )
2007-07-15 03:47:13 +04:00
: " bdS " ( video_segment ) ,
2007-07-11 23:18:52 +04:00
" a " ( 0x07200720 ) ) ;
}
/* Restore cursor position */
2010-02-18 05:32:06 +03:00
if ( saved . curx > = xs )
saved . curx = xs - 1 ;
if ( saved . cury > = ys )
saved . cury = ys - 1 ;
2009-04-02 05:20:11 +04:00
initregs ( & ireg ) ;
ireg . ah = 0x02 ; /* Set cursor position */
ireg . dh = saved . cury ;
ireg . dl = saved . curx ;
intcall ( 0x10 , & ireg , NULL ) ;
2010-02-18 05:32:06 +03:00
store_cursor_position ( ) ;
2007-07-11 23:18:52 +04:00
}
void set_video ( void )
{
u16 mode = boot_params . hdr . vid_mode ;
RESET_HEAP ( ) ;
store_mode_params ( ) ;
save_screen ( ) ;
probe_cards ( 0 ) ;
for ( ; ; ) {
if ( mode = = ASK_VGA )
mode = mode_menu ( ) ;
if ( ! set_mode ( mode ) )
break ;
printf ( " Undefined video mode number: %x \n " , mode ) ;
mode = ASK_VGA ;
}
2008-04-11 01:28:10 +04:00
boot_params . hdr . vid_mode = mode ;
2007-07-11 23:18:52 +04:00
vesa_store_edid ( ) ;
store_mode_params ( ) ;
if ( do_restore )
restore_screen ( ) ;
}