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