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