xref: /openbmc/linux/arch/x86/boot/video.c (revision 96de0e252cedffad61b3cb5e05662c591898e69a)
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