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 /* 22 * Mode list variables 23 */ 24 static struct card_info cards[]; /* List of cards to probe for */ 25 26 /* 27 * Common variables 28 */ 29 int adapter; /* 0=CGA/MDA/HGC, 1=EGA, 2=VGA+ */ 30 u16 video_segment; 31 int force_x, force_y; /* Don't query the BIOS for cols/rows */ 32 33 int do_restore = 0; /* Screen contents changed during mode flip */ 34 int graphic_mode; /* Graphic mode with linear frame buffer */ 35 36 static void store_cursor_position(void) 37 { 38 u16 curpos; 39 u16 ax, bx; 40 41 ax = 0x0300; 42 bx = 0; 43 asm(INT10 44 : "=d" (curpos), "+a" (ax), "+b" (bx) 45 : : "ecx", "esi", "edi"); 46 47 boot_params.screen_info.orig_x = curpos; 48 boot_params.screen_info.orig_y = curpos >> 8; 49 } 50 51 static void store_video_mode(void) 52 { 53 u16 ax, page; 54 55 /* N.B.: the saving of the video page here is a bit silly, 56 since we pretty much assume page 0 everywhere. */ 57 ax = 0x0f00; 58 asm(INT10 59 : "+a" (ax), "=b" (page) 60 : : "ecx", "edx", "esi", "edi"); 61 62 /* Not all BIOSes are clean with respect to the top bit */ 63 boot_params.screen_info.orig_video_mode = ax & 0x7f; 64 boot_params.screen_info.orig_video_page = page >> 8; 65 } 66 67 /* 68 * Store the video mode parameters for later usage by the kernel. 69 * This is done by asking the BIOS except for the rows/columns 70 * parameters in the default 80x25 mode -- these are set directly, 71 * because some very obscure BIOSes supply insane values. 72 */ 73 static void store_mode_params(void) 74 { 75 u16 font_size; 76 int x, y; 77 78 /* For graphics mode, it is up to the mode-setting driver 79 (currently only video-vesa.c) to store the parameters */ 80 if (graphic_mode) 81 return; 82 83 store_cursor_position(); 84 store_video_mode(); 85 86 if (boot_params.screen_info.orig_video_mode == 0x07) { 87 /* MDA, HGC, or VGA in monochrome mode */ 88 video_segment = 0xb000; 89 } else { 90 /* CGA, EGA, VGA and so forth */ 91 video_segment = 0xb800; 92 } 93 94 set_fs(0); 95 font_size = rdfs16(0x485); /* Font size, BIOS area */ 96 boot_params.screen_info.orig_video_points = font_size; 97 98 x = rdfs16(0x44a); 99 y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1; 100 101 if (force_x) 102 x = force_x; 103 if (force_y) 104 y = force_y; 105 106 boot_params.screen_info.orig_video_cols = x; 107 boot_params.screen_info.orig_video_lines = y; 108 } 109 110 /* Probe the video drivers and have them generate their mode lists. */ 111 static void probe_cards(int unsafe) 112 { 113 struct card_info *card; 114 static u8 probed[2]; 115 116 if (probed[unsafe]) 117 return; 118 119 probed[unsafe] = 1; 120 121 for (card = video_cards; card < video_cards_end; card++) { 122 if (card->unsafe == unsafe) { 123 if (card->probe) 124 card->nmodes = card->probe(); 125 else 126 card->nmodes = 0; 127 } 128 } 129 } 130 131 /* Test if a mode is defined */ 132 int mode_defined(u16 mode) 133 { 134 struct card_info *card; 135 struct mode_info *mi; 136 int i; 137 138 for (card = video_cards; card < video_cards_end; card++) { 139 mi = card->modes; 140 for (i = 0; i < card->nmodes; i++, mi++) { 141 if (mi->mode == mode) 142 return 1; 143 } 144 } 145 146 return 0; 147 } 148 149 /* Set mode (without recalc) */ 150 static int raw_set_mode(u16 mode, u16 *real_mode) 151 { 152 int nmode, i; 153 struct card_info *card; 154 struct mode_info *mi; 155 156 /* Drop the recalc bit if set */ 157 mode &= ~VIDEO_RECALC; 158 159 /* Scan for mode based on fixed ID, position, or resolution */ 160 nmode = 0; 161 for (card = video_cards; card < video_cards_end; card++) { 162 mi = card->modes; 163 for (i = 0; i < card->nmodes; i++, mi++) { 164 int visible = mi->x || mi->y; 165 166 if ((mode == nmode && visible) || 167 mode == mi->mode || 168 mode == (mi->y << 8)+mi->x) { 169 *real_mode = mi->mode; 170 return card->set_mode(mi); 171 } 172 173 if (visible) 174 nmode++; 175 } 176 } 177 178 /* Nothing found? Is it an "exceptional" (unprobed) mode? */ 179 for (card = video_cards; card < video_cards_end; card++) { 180 if (mode >= card->xmode_first && 181 mode < card->xmode_first+card->xmode_n) { 182 struct mode_info mix; 183 *real_mode = mix.mode = mode; 184 mix.x = mix.y = 0; 185 return card->set_mode(&mix); 186 } 187 } 188 189 /* Otherwise, failure... */ 190 return -1; 191 } 192 193 /* 194 * Recalculate the vertical video cutoff (hack!) 195 */ 196 static void vga_recalc_vertical(void) 197 { 198 unsigned int font_size, rows; 199 u16 crtc; 200 u8 pt, ov; 201 202 set_fs(0); 203 font_size = rdfs8(0x485); /* BIOS: font size (pixels) */ 204 rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */ 205 206 rows *= font_size; /* Visible scan lines */ 207 rows--; /* ... minus one */ 208 209 crtc = vga_crtc(); 210 211 pt = in_idx(crtc, 0x11); 212 pt &= ~0x80; /* Unlock CR0-7 */ 213 out_idx(pt, crtc, 0x11); 214 215 out_idx((u8)rows, crtc, 0x12); /* Lower height register */ 216 217 ov = in_idx(crtc, 0x07); /* Overflow register */ 218 ov &= 0xbd; 219 ov |= (rows >> (8-1)) & 0x02; 220 ov |= (rows >> (9-6)) & 0x40; 221 out_idx(ov, crtc, 0x07); 222 } 223 224 /* Set mode (with recalc if specified) */ 225 static int set_mode(u16 mode) 226 { 227 int rv; 228 u16 real_mode; 229 230 /* Very special mode numbers... */ 231 if (mode == VIDEO_CURRENT_MODE) 232 return 0; /* Nothing to do... */ 233 else if (mode == NORMAL_VGA) 234 mode = VIDEO_80x25; 235 else if (mode == EXTENDED_VGA) 236 mode = VIDEO_8POINT; 237 238 rv = raw_set_mode(mode, &real_mode); 239 if (rv) 240 return rv; 241 242 if (mode & VIDEO_RECALC) 243 vga_recalc_vertical(); 244 245 /* Save the canonical mode number for the kernel, not 246 an alias, size specification or menu position */ 247 boot_params.hdr.vid_mode = real_mode; 248 return 0; 249 } 250 251 static unsigned int get_entry(void) 252 { 253 char entry_buf[4]; 254 int i, len = 0; 255 int key; 256 unsigned int v; 257 258 do { 259 key = getchar(); 260 261 if (key == '\b') { 262 if (len > 0) { 263 puts("\b \b"); 264 len--; 265 } 266 } else if ((key >= '0' && key <= '9') || 267 (key >= 'A' && key <= 'Z') || 268 (key >= 'a' && key <= 'z')) { 269 if (len < sizeof entry_buf) { 270 entry_buf[len++] = key; 271 putchar(key); 272 } 273 } 274 } while (key != '\r'); 275 putchar('\n'); 276 277 if (len == 0) 278 return VIDEO_CURRENT_MODE; /* Default */ 279 280 v = 0; 281 for (i = 0; i < len; i++) { 282 v <<= 4; 283 key = entry_buf[i] | 0x20; 284 v += (key > '9') ? key-'a'+10 : key-'0'; 285 } 286 287 return v; 288 } 289 290 static void display_menu(void) 291 { 292 struct card_info *card; 293 struct mode_info *mi; 294 char ch; 295 int i; 296 297 puts("Mode: COLSxROWS:\n"); 298 299 ch = '0'; 300 for (card = video_cards; card < video_cards_end; card++) { 301 mi = card->modes; 302 for (i = 0; i < card->nmodes; i++, mi++) { 303 int visible = mi->x && mi->y; 304 u16 mode_id = mi->mode ? mi->mode : 305 (mi->y << 8)+mi->x; 306 307 if (!visible) 308 continue; /* Hidden mode */ 309 310 printf("%c %04X %3dx%-3d %s\n", 311 ch, mode_id, mi->x, mi->y, card->card_name); 312 313 if (ch == '9') 314 ch = 'a'; 315 else if (ch == 'z' || ch == ' ') 316 ch = ' '; /* Out of keys... */ 317 else 318 ch++; 319 } 320 } 321 } 322 323 #define H(x) ((x)-'a'+10) 324 #define SCAN ((H('s')<<12)+(H('c')<<8)+(H('a')<<4)+H('n')) 325 326 static unsigned int mode_menu(void) 327 { 328 int key; 329 unsigned int sel; 330 331 puts("Press <ENTER> to see video modes available, " 332 "<SPACE> to continue, or wait 30 sec\n"); 333 334 kbd_flush(); 335 while (1) { 336 key = getchar_timeout(); 337 if (key == ' ' || key == 0) 338 return VIDEO_CURRENT_MODE; /* Default */ 339 if (key == '\r') 340 break; 341 putchar('\a'); /* Beep! */ 342 } 343 344 345 for (;;) { 346 display_menu(); 347 348 puts("Enter a video mode or \"scan\" to scan for " 349 "additional modes: "); 350 sel = get_entry(); 351 if (sel != SCAN) 352 return sel; 353 354 probe_cards(1); 355 } 356 } 357 358 #ifdef CONFIG_VIDEO_RETAIN 359 /* Save screen content to the heap */ 360 struct saved_screen { 361 int x, y; 362 int curx, cury; 363 u16 *data; 364 } saved; 365 366 static void save_screen(void) 367 { 368 /* Should be called after store_mode_params() */ 369 saved.x = boot_params.screen_info.orig_video_cols; 370 saved.y = boot_params.screen_info.orig_video_lines; 371 saved.curx = boot_params.screen_info.orig_x; 372 saved.cury = boot_params.screen_info.orig_y; 373 374 if (heap_free() < saved.x*saved.y*sizeof(u16)+512) 375 return; /* Not enough heap to save the screen */ 376 377 saved.data = GET_HEAP(u16, saved.x*saved.y); 378 379 set_fs(video_segment); 380 copy_from_fs(saved.data, 0, saved.x*saved.y*sizeof(u16)); 381 } 382 383 static void restore_screen(void) 384 { 385 /* Should be called after store_mode_params() */ 386 int xs = boot_params.screen_info.orig_video_cols; 387 int ys = boot_params.screen_info.orig_video_lines; 388 int y; 389 addr_t dst = 0; 390 u16 *src = saved.data; 391 u16 ax, bx, dx; 392 393 if (graphic_mode) 394 return; /* Can't restore onto a graphic mode */ 395 396 if (!src) 397 return; /* No saved screen contents */ 398 399 /* Restore screen contents */ 400 401 set_fs(video_segment); 402 for (y = 0; y < ys; y++) { 403 int npad; 404 405 if (y < saved.y) { 406 int copy = (xs < saved.x) ? xs : saved.x; 407 copy_to_fs(dst, src, copy*sizeof(u16)); 408 dst += copy*sizeof(u16); 409 src += saved.x; 410 npad = (xs < saved.x) ? 0 : xs-saved.x; 411 } else { 412 npad = xs; 413 } 414 415 /* Writes "npad" blank characters to 416 video_segment:dst and advances dst */ 417 asm volatile("pushw %%es ; " 418 "movw %2,%%es ; " 419 "shrw %%cx ; " 420 "jnc 1f ; " 421 "stosw \n\t" 422 "1: rep;stosl ; " 423 "popw %%es" 424 : "+D" (dst), "+c" (npad) 425 : "bdS" (video_segment), 426 "a" (0x07200720)); 427 } 428 429 /* Restore cursor position */ 430 ax = 0x0200; /* Set cursor position */ 431 bx = 0; /* Page number (<< 8) */ 432 dx = (saved.cury << 8)+saved.curx; 433 asm volatile(INT10 434 : "+a" (ax), "+b" (bx), "+d" (dx) 435 : : "ecx", "esi", "edi"); 436 } 437 #else 438 #define save_screen() ((void)0) 439 #define restore_screen() ((void)0) 440 #endif 441 442 void set_video(void) 443 { 444 u16 mode = boot_params.hdr.vid_mode; 445 446 RESET_HEAP(); 447 448 store_mode_params(); 449 save_screen(); 450 probe_cards(0); 451 452 for (;;) { 453 if (mode == ASK_VGA) 454 mode = mode_menu(); 455 456 if (!set_mode(mode)) 457 break; 458 459 printf("Undefined video mode number: %x\n", mode); 460 mode = ASK_VGA; 461 } 462 vesa_store_edid(); 463 store_mode_params(); 464 465 if (do_restore) 466 restore_screen(); 467 } 468