1 /* -*- linux-c -*- ------------------------------------------------------- * 2 * 3 * Copyright (C) 1991, 1992 Linus Torvalds 4 * Copyright 2007 rPath, Inc. - All Rights Reserved 5 * 6 * This file is part of the Linux kernel, and is made available under 7 * the terms of the GNU General Public License version 2. 8 * 9 * ----------------------------------------------------------------------- */ 10 11 /* 12 * arch/i386/boot/video.c 13 * 14 * Select video mode 15 */ 16 17 #include "boot.h" 18 #include "video.h" 19 #include "vesa.h" 20 21 static void store_cursor_position(void) 22 { 23 u16 curpos; 24 u16 ax, bx; 25 26 ax = 0x0300; 27 bx = 0; 28 asm(INT10 29 : "=d" (curpos), "+a" (ax), "+b" (bx) 30 : : "ecx", "esi", "edi"); 31 32 boot_params.screen_info.orig_x = curpos; 33 boot_params.screen_info.orig_y = curpos >> 8; 34 } 35 36 static void store_video_mode(void) 37 { 38 u16 ax, page; 39 40 /* N.B.: the saving of the video page here is a bit silly, 41 since we pretty much assume page 0 everywhere. */ 42 ax = 0x0f00; 43 asm(INT10 44 : "+a" (ax), "=b" (page) 45 : : "ecx", "edx", "esi", "edi"); 46 47 /* Not all BIOSes are clean with respect to the top bit */ 48 boot_params.screen_info.orig_video_mode = ax & 0x7f; 49 boot_params.screen_info.orig_video_page = page >> 8; 50 } 51 52 /* 53 * Store the video mode parameters for later usage by the kernel. 54 * This is done by asking the BIOS except for the rows/columns 55 * parameters in the default 80x25 mode -- these are set directly, 56 * because some very obscure BIOSes supply insane values. 57 */ 58 static void store_mode_params(void) 59 { 60 u16 font_size; 61 int x, y; 62 63 /* For graphics mode, it is up to the mode-setting driver 64 (currently only video-vesa.c) to store the parameters */ 65 if (graphic_mode) 66 return; 67 68 store_cursor_position(); 69 store_video_mode(); 70 71 if (boot_params.screen_info.orig_video_mode == 0x07) { 72 /* MDA, HGC, or VGA in monochrome mode */ 73 video_segment = 0xb000; 74 } else { 75 /* CGA, EGA, VGA and so forth */ 76 video_segment = 0xb800; 77 } 78 79 set_fs(0); 80 font_size = rdfs16(0x485); /* Font size, BIOS area */ 81 boot_params.screen_info.orig_video_points = font_size; 82 83 x = rdfs16(0x44a); 84 y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1; 85 86 if (force_x) 87 x = force_x; 88 if (force_y) 89 y = force_y; 90 91 boot_params.screen_info.orig_video_cols = x; 92 boot_params.screen_info.orig_video_lines = y; 93 } 94 95 static unsigned int get_entry(void) 96 { 97 char entry_buf[4]; 98 int i, len = 0; 99 int key; 100 unsigned int v; 101 102 do { 103 key = getchar(); 104 105 if (key == '\b') { 106 if (len > 0) { 107 puts("\b \b"); 108 len--; 109 } 110 } else if ((key >= '0' && key <= '9') || 111 (key >= 'A' && key <= 'Z') || 112 (key >= 'a' && key <= 'z')) { 113 if (len < sizeof entry_buf) { 114 entry_buf[len++] = key; 115 putchar(key); 116 } 117 } 118 } while (key != '\r'); 119 putchar('\n'); 120 121 if (len == 0) 122 return VIDEO_CURRENT_MODE; /* Default */ 123 124 v = 0; 125 for (i = 0; i < len; i++) { 126 v <<= 4; 127 key = entry_buf[i] | 0x20; 128 v += (key > '9') ? key-'a'+10 : key-'0'; 129 } 130 131 return v; 132 } 133 134 static void display_menu(void) 135 { 136 struct card_info *card; 137 struct mode_info *mi; 138 char ch; 139 int i; 140 int nmodes; 141 int modes_per_line; 142 int col; 143 144 nmodes = 0; 145 for (card = video_cards; card < video_cards_end; card++) 146 nmodes += card->nmodes; 147 148 modes_per_line = 1; 149 if (nmodes >= 20) 150 modes_per_line = 3; 151 152 for (col = 0; col < modes_per_line; col++) 153 puts("Mode: Resolution: Type: "); 154 putchar('\n'); 155 156 col = 0; 157 ch = '0'; 158 for (card = video_cards; card < video_cards_end; card++) { 159 mi = card->modes; 160 for (i = 0; i < card->nmodes; i++, mi++) { 161 char resbuf[32]; 162 int visible = mi->x && mi->y; 163 u16 mode_id = mi->mode ? mi->mode : 164 (mi->y << 8)+mi->x; 165 166 if (!visible) 167 continue; /* Hidden mode */ 168 169 if (mi->depth) 170 sprintf(resbuf, "%dx%d", mi->y, mi->depth); 171 else 172 sprintf(resbuf, "%d", mi->y); 173 174 printf("%c %03X %4dx%-7s %-6s", 175 ch, mode_id, mi->x, resbuf, card->card_name); 176 col++; 177 if (col >= modes_per_line) { 178 putchar('\n'); 179 col = 0; 180 } 181 182 if (ch == '9') 183 ch = 'a'; 184 else if (ch == 'z' || ch == ' ') 185 ch = ' '; /* Out of keys... */ 186 else 187 ch++; 188 } 189 } 190 if (col) 191 putchar('\n'); 192 } 193 194 #define H(x) ((x)-'a'+10) 195 #define SCAN ((H('s')<<12)+(H('c')<<8)+(H('a')<<4)+H('n')) 196 197 static unsigned int mode_menu(void) 198 { 199 int key; 200 unsigned int sel; 201 202 puts("Press <ENTER> to see video modes available, " 203 "<SPACE> to continue, or wait 30 sec\n"); 204 205 kbd_flush(); 206 while (1) { 207 key = getchar_timeout(); 208 if (key == ' ' || key == 0) 209 return VIDEO_CURRENT_MODE; /* Default */ 210 if (key == '\r') 211 break; 212 putchar('\a'); /* Beep! */ 213 } 214 215 216 for (;;) { 217 display_menu(); 218 219 puts("Enter a video mode or \"scan\" to scan for " 220 "additional modes: "); 221 sel = get_entry(); 222 if (sel != SCAN) 223 return sel; 224 225 probe_cards(1); 226 } 227 } 228 229 #ifdef CONFIG_VIDEO_RETAIN 230 /* Save screen content to the heap */ 231 struct saved_screen { 232 int x, y; 233 int curx, cury; 234 u16 *data; 235 } saved; 236 237 static void save_screen(void) 238 { 239 /* Should be called after store_mode_params() */ 240 saved.x = boot_params.screen_info.orig_video_cols; 241 saved.y = boot_params.screen_info.orig_video_lines; 242 saved.curx = boot_params.screen_info.orig_x; 243 saved.cury = boot_params.screen_info.orig_y; 244 245 if (!heap_free(saved.x*saved.y*sizeof(u16)+512)) 246 return; /* Not enough heap to save the screen */ 247 248 saved.data = GET_HEAP(u16, saved.x*saved.y); 249 250 set_fs(video_segment); 251 copy_from_fs(saved.data, 0, saved.x*saved.y*sizeof(u16)); 252 } 253 254 static void restore_screen(void) 255 { 256 /* Should be called after store_mode_params() */ 257 int xs = boot_params.screen_info.orig_video_cols; 258 int ys = boot_params.screen_info.orig_video_lines; 259 int y; 260 addr_t dst = 0; 261 u16 *src = saved.data; 262 u16 ax, bx, dx; 263 264 if (graphic_mode) 265 return; /* Can't restore onto a graphic mode */ 266 267 if (!src) 268 return; /* No saved screen contents */ 269 270 /* Restore screen contents */ 271 272 set_fs(video_segment); 273 for (y = 0; y < ys; y++) { 274 int npad; 275 276 if (y < saved.y) { 277 int copy = (xs < saved.x) ? xs : saved.x; 278 copy_to_fs(dst, src, copy*sizeof(u16)); 279 dst += copy*sizeof(u16); 280 src += saved.x; 281 npad = (xs < saved.x) ? 0 : xs-saved.x; 282 } else { 283 npad = xs; 284 } 285 286 /* Writes "npad" blank characters to 287 video_segment:dst and advances dst */ 288 asm volatile("pushw %%es ; " 289 "movw %2,%%es ; " 290 "shrw %%cx ; " 291 "jnc 1f ; " 292 "stosw \n\t" 293 "1: rep;stosl ; " 294 "popw %%es" 295 : "+D" (dst), "+c" (npad) 296 : "bdS" (video_segment), 297 "a" (0x07200720)); 298 } 299 300 /* Restore cursor position */ 301 ax = 0x0200; /* Set cursor position */ 302 bx = 0; /* Page number (<< 8) */ 303 dx = (saved.cury << 8)+saved.curx; 304 asm volatile(INT10 305 : "+a" (ax), "+b" (bx), "+d" (dx) 306 : : "ecx", "esi", "edi"); 307 } 308 #else 309 #define save_screen() ((void)0) 310 #define restore_screen() ((void)0) 311 #endif 312 313 void set_video(void) 314 { 315 u16 mode = boot_params.hdr.vid_mode; 316 317 RESET_HEAP(); 318 319 store_mode_params(); 320 save_screen(); 321 probe_cards(0); 322 323 for (;;) { 324 if (mode == ASK_VGA) 325 mode = mode_menu(); 326 327 if (!set_mode(mode)) 328 break; 329 330 printf("Undefined video mode number: %x\n", mode); 331 mode = ASK_VGA; 332 } 333 boot_params.hdr.vid_mode = mode; 334 vesa_store_edid(); 335 store_mode_params(); 336 337 if (do_restore) 338 restore_screen(); 339 } 340