xref: /openbmc/linux/arch/x86/boot/video.c (revision 643d1f7f)
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 	int nmodes;
297 	int modes_per_line;
298 	int col;
299 
300 	nmodes = 0;
301 	for (card = video_cards; card < video_cards_end; card++)
302 		nmodes += card->nmodes;
303 
304 	modes_per_line = 1;
305 	if (nmodes >= 20)
306 		modes_per_line = 3;
307 
308 	for (col = 0; col < modes_per_line; col++)
309 		puts("Mode: Resolution:  Type: ");
310 	putchar('\n');
311 
312 	col = 0;
313 	ch = '0';
314 	for (card = video_cards; card < video_cards_end; card++) {
315 		mi = card->modes;
316 		for (i = 0; i < card->nmodes; i++, mi++) {
317 			char resbuf[32];
318 			int visible = mi->x && mi->y;
319 			u16 mode_id = mi->mode ? mi->mode :
320 				(mi->y << 8)+mi->x;
321 
322 			if (!visible)
323 				continue; /* Hidden mode */
324 
325 			if (mi->depth)
326 				sprintf(resbuf, "%dx%d", mi->y, mi->depth);
327 			else
328 				sprintf(resbuf, "%d", mi->y);
329 
330 			printf("%c %03X %4dx%-7s %-6s",
331 			       ch, mode_id, mi->x, resbuf, card->card_name);
332 			col++;
333 			if (col >= modes_per_line) {
334 				putchar('\n');
335 				col = 0;
336 			}
337 
338 			if (ch == '9')
339 				ch = 'a';
340 			else if (ch == 'z' || ch == ' ')
341 				ch = ' '; /* Out of keys... */
342 			else
343 				ch++;
344 		}
345 	}
346 	if (col)
347 		putchar('\n');
348 }
349 
350 #define H(x)	((x)-'a'+10)
351 #define SCAN	((H('s')<<12)+(H('c')<<8)+(H('a')<<4)+H('n'))
352 
353 static unsigned int mode_menu(void)
354 {
355 	int key;
356 	unsigned int sel;
357 
358 	puts("Press <ENTER> to see video modes available, "
359 	     "<SPACE> to continue, or wait 30 sec\n");
360 
361 	kbd_flush();
362 	while (1) {
363 		key = getchar_timeout();
364 		if (key == ' ' || key == 0)
365 			return VIDEO_CURRENT_MODE; /* Default */
366 		if (key == '\r')
367 			break;
368 		putchar('\a');	/* Beep! */
369 	}
370 
371 
372 	for (;;) {
373 		display_menu();
374 
375 		puts("Enter a video mode or \"scan\" to scan for "
376 		     "additional modes: ");
377 		sel = get_entry();
378 		if (sel != SCAN)
379 			return sel;
380 
381 		probe_cards(1);
382 	}
383 }
384 
385 #ifdef CONFIG_VIDEO_RETAIN
386 /* Save screen content to the heap */
387 struct saved_screen {
388 	int x, y;
389 	int curx, cury;
390 	u16 *data;
391 } saved;
392 
393 static void save_screen(void)
394 {
395 	/* Should be called after store_mode_params() */
396 	saved.x = boot_params.screen_info.orig_video_cols;
397 	saved.y = boot_params.screen_info.orig_video_lines;
398 	saved.curx = boot_params.screen_info.orig_x;
399 	saved.cury = boot_params.screen_info.orig_y;
400 
401 	if (!heap_free(saved.x*saved.y*sizeof(u16)+512))
402 		return;		/* Not enough heap to save the screen */
403 
404 	saved.data = GET_HEAP(u16, saved.x*saved.y);
405 
406 	set_fs(video_segment);
407 	copy_from_fs(saved.data, 0, saved.x*saved.y*sizeof(u16));
408 }
409 
410 static void restore_screen(void)
411 {
412 	/* Should be called after store_mode_params() */
413 	int xs = boot_params.screen_info.orig_video_cols;
414 	int ys = boot_params.screen_info.orig_video_lines;
415 	int y;
416 	addr_t dst = 0;
417 	u16 *src = saved.data;
418 	u16 ax, bx, dx;
419 
420 	if (graphic_mode)
421 		return;		/* Can't restore onto a graphic mode */
422 
423 	if (!src)
424 		return;		/* No saved screen contents */
425 
426 	/* Restore screen contents */
427 
428 	set_fs(video_segment);
429 	for (y = 0; y < ys; y++) {
430 		int npad;
431 
432 		if (y < saved.y) {
433 			int copy = (xs < saved.x) ? xs : saved.x;
434 			copy_to_fs(dst, src, copy*sizeof(u16));
435 			dst += copy*sizeof(u16);
436 			src += saved.x;
437 			npad = (xs < saved.x) ? 0 : xs-saved.x;
438 		} else {
439 			npad = xs;
440 		}
441 
442 		/* Writes "npad" blank characters to
443 		   video_segment:dst and advances dst */
444 		asm volatile("pushw %%es ; "
445 			     "movw %2,%%es ; "
446 			     "shrw %%cx ; "
447 			     "jnc 1f ; "
448 			     "stosw \n\t"
449 			     "1: rep;stosl ; "
450 			     "popw %%es"
451 			     : "+D" (dst), "+c" (npad)
452 			     : "bdS" (video_segment),
453 			       "a" (0x07200720));
454 	}
455 
456 	/* Restore cursor position */
457 	ax = 0x0200;		/* Set cursor position */
458 	bx = 0;			/* Page number (<< 8) */
459 	dx = (saved.cury << 8)+saved.curx;
460 	asm volatile(INT10
461 		     : "+a" (ax), "+b" (bx), "+d" (dx)
462 		     : : "ecx", "esi", "edi");
463 }
464 #else
465 #define save_screen()		((void)0)
466 #define restore_screen()	((void)0)
467 #endif
468 
469 void set_video(void)
470 {
471 	u16 mode = boot_params.hdr.vid_mode;
472 
473 	RESET_HEAP();
474 
475 	store_mode_params();
476 	save_screen();
477 	probe_cards(0);
478 
479 	for (;;) {
480 		if (mode == ASK_VGA)
481 			mode = mode_menu();
482 
483 		if (!set_mode(mode))
484 			break;
485 
486 		printf("Undefined video mode number: %x\n", mode);
487 		mode = ASK_VGA;
488 	}
489 	vesa_store_edid();
490 	store_mode_params();
491 
492 	if (do_restore)
493 		restore_screen();
494 }
495