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