1 /* -*- linux-c -*- ------------------------------------------------------- * 2 * 3 * Copyright (C) 1991, 1992 Linus Torvalds 4 * Copyright 2007 rPath, Inc. - All Rights Reserved 5 * Copyright 2009 Intel Corporation; author H. Peter Anvin 6 * 7 * This file is part of the Linux kernel, and is made available under 8 * the terms of the GNU General Public License version 2. 9 * 10 * ----------------------------------------------------------------------- */ 11 12 /* 13 * Select video mode 14 */ 15 16 #include "boot.h" 17 #include "video.h" 18 #include "vesa.h" 19 20 static void store_cursor_position(void) 21 { 22 struct biosregs ireg, oreg; 23 24 initregs(&ireg); 25 ireg.ah = 0x03; 26 intcall(0x10, &ireg, &oreg); 27 28 boot_params.screen_info.orig_x = oreg.dl; 29 boot_params.screen_info.orig_y = oreg.dh; 30 31 if (oreg.ch & 0x20) 32 boot_params.screen_info.flags |= VIDEO_FLAGS_NOCURSOR; 33 34 if ((oreg.ch & 0x1f) > (oreg.cl & 0x1f)) 35 boot_params.screen_info.flags |= VIDEO_FLAGS_NOCURSOR; 36 } 37 38 static void store_video_mode(void) 39 { 40 struct biosregs ireg, oreg; 41 42 /* N.B.: the saving of the video page here is a bit silly, 43 since we pretty much assume page 0 everywhere. */ 44 initregs(&ireg); 45 ireg.ah = 0x0f; 46 intcall(0x10, &ireg, &oreg); 47 48 /* Not all BIOSes are clean with respect to the top bit */ 49 boot_params.screen_info.orig_video_mode = oreg.al & 0x7f; 50 boot_params.screen_info.orig_video_page = oreg.bh; 51 } 52 53 /* 54 * Store the video mode parameters for later usage by the kernel. 55 * This is done by asking the BIOS except for the rows/columns 56 * parameters in the default 80x25 mode -- these are set directly, 57 * because some very obscure BIOSes supply insane values. 58 */ 59 static void store_mode_params(void) 60 { 61 u16 font_size; 62 int x, y; 63 64 /* For graphics mode, it is up to the mode-setting driver 65 (currently only video-vesa.c) to store the parameters */ 66 if (graphic_mode) 67 return; 68 69 store_cursor_position(); 70 store_video_mode(); 71 72 if (boot_params.screen_info.orig_video_mode == 0x07) { 73 /* MDA, HGC, or VGA in monochrome mode */ 74 video_segment = 0xb000; 75 } else { 76 /* CGA, EGA, VGA and so forth */ 77 video_segment = 0xb800; 78 } 79 80 set_fs(0); 81 font_size = rdfs16(0x485); /* Font size, BIOS area */ 82 boot_params.screen_info.orig_video_points = font_size; 83 84 x = rdfs16(0x44a); 85 y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1; 86 87 if (force_x) 88 x = force_x; 89 if (force_y) 90 y = force_y; 91 92 boot_params.screen_info.orig_video_cols = x; 93 boot_params.screen_info.orig_video_lines = y; 94 } 95 96 static unsigned int get_entry(void) 97 { 98 char entry_buf[4]; 99 int i, len = 0; 100 int key; 101 unsigned int v; 102 103 do { 104 key = getchar(); 105 106 if (key == '\b') { 107 if (len > 0) { 108 puts("\b \b"); 109 len--; 110 } 111 } else if ((key >= '0' && key <= '9') || 112 (key >= 'A' && key <= 'Z') || 113 (key >= 'a' && key <= 'z')) { 114 if (len < sizeof entry_buf) { 115 entry_buf[len++] = key; 116 putchar(key); 117 } 118 } 119 } while (key != '\r'); 120 putchar('\n'); 121 122 if (len == 0) 123 return VIDEO_CURRENT_MODE; /* Default */ 124 125 v = 0; 126 for (i = 0; i < len; i++) { 127 v <<= 4; 128 key = entry_buf[i] | 0x20; 129 v += (key > '9') ? key-'a'+10 : key-'0'; 130 } 131 132 return v; 133 } 134 135 static void display_menu(void) 136 { 137 struct card_info *card; 138 struct mode_info *mi; 139 char ch; 140 int i; 141 int nmodes; 142 int modes_per_line; 143 int col; 144 145 nmodes = 0; 146 for (card = video_cards; card < video_cards_end; card++) 147 nmodes += card->nmodes; 148 149 modes_per_line = 1; 150 if (nmodes >= 20) 151 modes_per_line = 3; 152 153 for (col = 0; col < modes_per_line; col++) 154 puts("Mode: Resolution: Type: "); 155 putchar('\n'); 156 157 col = 0; 158 ch = '0'; 159 for (card = video_cards; card < video_cards_end; card++) { 160 mi = card->modes; 161 for (i = 0; i < card->nmodes; i++, mi++) { 162 char resbuf[32]; 163 int visible = mi->x && mi->y; 164 u16 mode_id = mi->mode ? mi->mode : 165 (mi->y << 8)+mi->x; 166 167 if (!visible) 168 continue; /* Hidden mode */ 169 170 if (mi->depth) 171 sprintf(resbuf, "%dx%d", mi->y, mi->depth); 172 else 173 sprintf(resbuf, "%d", mi->y); 174 175 printf("%c %03X %4dx%-7s %-6s", 176 ch, mode_id, mi->x, resbuf, card->card_name); 177 col++; 178 if (col >= modes_per_line) { 179 putchar('\n'); 180 col = 0; 181 } 182 183 if (ch == '9') 184 ch = 'a'; 185 else if (ch == 'z' || ch == ' ') 186 ch = ' '; /* Out of keys... */ 187 else 188 ch++; 189 } 190 } 191 if (col) 192 putchar('\n'); 193 } 194 195 #define H(x) ((x)-'a'+10) 196 #define SCAN ((H('s')<<12)+(H('c')<<8)+(H('a')<<4)+H('n')) 197 198 static unsigned int mode_menu(void) 199 { 200 int key; 201 unsigned int sel; 202 203 puts("Press <ENTER> to see video modes available, " 204 "<SPACE> to continue, or wait 30 sec\n"); 205 206 kbd_flush(); 207 while (1) { 208 key = getchar_timeout(); 209 if (key == ' ' || key == 0) 210 return VIDEO_CURRENT_MODE; /* Default */ 211 if (key == '\r') 212 break; 213 putchar('\a'); /* Beep! */ 214 } 215 216 217 for (;;) { 218 display_menu(); 219 220 puts("Enter a video mode or \"scan\" to scan for " 221 "additional modes: "); 222 sel = get_entry(); 223 if (sel != SCAN) 224 return sel; 225 226 probe_cards(1); 227 } 228 } 229 230 /* Save screen content to the heap */ 231 static 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 struct biosregs ireg; 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 if (saved.curx >= xs) 302 saved.curx = xs-1; 303 if (saved.cury >= ys) 304 saved.cury = ys-1; 305 306 initregs(&ireg); 307 ireg.ah = 0x02; /* Set cursor position */ 308 ireg.dh = saved.cury; 309 ireg.dl = saved.curx; 310 intcall(0x10, &ireg, NULL); 311 312 store_cursor_position(); 313 } 314 315 void set_video(void) 316 { 317 u16 mode = boot_params.hdr.vid_mode; 318 319 RESET_HEAP(); 320 321 store_mode_params(); 322 save_screen(); 323 probe_cards(0); 324 325 for (;;) { 326 if (mode == ASK_VGA) 327 mode = mode_menu(); 328 329 if (!set_mode(mode)) 330 break; 331 332 printf("Undefined video mode number: %x\n", mode); 333 mode = ASK_VGA; 334 } 335 boot_params.hdr.vid_mode = mode; 336 vesa_store_edid(); 337 store_mode_params(); 338 339 if (do_restore) 340 restore_screen(); 341 } 342