1/* -*- linux-c -*- ------------------------------------------------------- *
2 *
3 *   Copyright (C) 1991, 1992 Linus Torvalds
4 *   Copyright 2007-2008 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-mode.c
13 *
14 * Set the video mode.  This is separated out into a different
15 * file in order to be shared with the ACPI wakeup code.
16 */
17
18#include "boot.h"
19#include "video.h"
20#include "vesa.h"
21
22#include <uapi/asm/boot.h>
23
24/*
25 * Common variables
26 */
27int adapter;		/* 0=CGA/MDA/HGC, 1=EGA, 2=VGA+ */
28int force_x, force_y;	/* Don't query the BIOS for cols/rows */
29int do_restore;		/* Screen contents changed during mode flip */
30int graphic_mode;	/* Graphic mode with linear frame buffer */
31
32/* Probe the video drivers and have them generate their mode lists. */
33void probe_cards(int unsafe)
34{
35	struct card_info *card;
36	static u8 probed[2];
37
38	if (probed[unsafe])
39		return;
40
41	probed[unsafe] = 1;
42
43	for (card = video_cards; card < video_cards_end; card++) {
44		if (card->unsafe == unsafe) {
45			if (card->probe)
46				card->nmodes = card->probe();
47			else
48				card->nmodes = 0;
49		}
50	}
51}
52
53/* Test if a mode is defined */
54int mode_defined(u16 mode)
55{
56	struct card_info *card;
57	struct mode_info *mi;
58	int i;
59
60	for (card = video_cards; card < video_cards_end; card++) {
61		mi = card->modes;
62		for (i = 0; i < card->nmodes; i++, mi++) {
63			if (mi->mode == mode)
64				return 1;
65		}
66	}
67
68	return 0;
69}
70
71/* Set mode (without recalc) */
72static int raw_set_mode(u16 mode, u16 *real_mode)
73{
74	int nmode, i;
75	struct card_info *card;
76	struct mode_info *mi;
77
78	/* Drop the recalc bit if set */
79	mode &= ~VIDEO_RECALC;
80
81	/* Scan for mode based on fixed ID, position, or resolution */
82	nmode = 0;
83	for (card = video_cards; card < video_cards_end; card++) {
84		mi = card->modes;
85		for (i = 0; i < card->nmodes; i++, mi++) {
86			int visible = mi->x || mi->y;
87
88			if ((mode == nmode && visible) ||
89			    mode == mi->mode ||
90			    mode == (mi->y << 8)+mi->x) {
91				*real_mode = mi->mode;
92				return card->set_mode(mi);
93			}
94
95			if (visible)
96				nmode++;
97		}
98	}
99
100	/* Nothing found?  Is it an "exceptional" (unprobed) mode? */
101	for (card = video_cards; card < video_cards_end; card++) {
102		if (mode >= card->xmode_first &&
103		    mode < card->xmode_first+card->xmode_n) {
104			struct mode_info mix;
105			*real_mode = mix.mode = mode;
106			mix.x = mix.y = 0;
107			return card->set_mode(&mix);
108		}
109	}
110
111	/* Otherwise, failure... */
112	return -1;
113}
114
115/*
116 * Recalculate the vertical video cutoff (hack!)
117 */
118static void vga_recalc_vertical(void)
119{
120	unsigned int font_size, rows;
121	u16 crtc;
122	u8 pt, ov;
123
124	set_fs(0);
125	font_size = rdfs8(0x485); /* BIOS: font size (pixels) */
126	rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */
127
128	rows *= font_size;	/* Visible scan lines */
129	rows--;			/* ... minus one */
130
131	crtc = vga_crtc();
132
133	pt = in_idx(crtc, 0x11);
134	pt &= ~0x80;		/* Unlock CR0-7 */
135	out_idx(pt, crtc, 0x11);
136
137	out_idx((u8)rows, crtc, 0x12); /* Lower height register */
138
139	ov = in_idx(crtc, 0x07); /* Overflow register */
140	ov &= 0xbd;
141	ov |= (rows >> (8-1)) & 0x02;
142	ov |= (rows >> (9-6)) & 0x40;
143	out_idx(ov, crtc, 0x07);
144}
145
146/* Set mode (with recalc if specified) */
147int set_mode(u16 mode)
148{
149	int rv;
150	u16 real_mode;
151
152	/* Very special mode numbers... */
153	if (mode == VIDEO_CURRENT_MODE)
154		return 0;	/* Nothing to do... */
155	else if (mode == NORMAL_VGA)
156		mode = VIDEO_80x25;
157	else if (mode == EXTENDED_VGA)
158		mode = VIDEO_8POINT;
159
160	rv = raw_set_mode(mode, &real_mode);
161	if (rv)
162		return rv;
163
164	if (mode & VIDEO_RECALC)
165		vga_recalc_vertical();
166
167	/* Save the canonical mode number for the kernel, not
168	   an alias, size specification or menu position */
169#ifndef _WAKEUP
170	boot_params.hdr.vid_mode = real_mode;
171#endif
172	return 0;
173}
174