Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / arch / x86 / boot / video-mode.c
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 /*
23  * Common variables
24  */
25 int adapter;                    /* 0=CGA/MDA/HGC, 1=EGA, 2=VGA+ */
26 u16 video_segment;
27 int force_x, force_y;   /* Don't query the BIOS for cols/rows */
28
29 int do_restore;         /* Screen contents changed during mode flip */
30 int graphic_mode;       /* Graphic mode with linear frame buffer */
31
32 /* Probe the video drivers and have them generate their mode lists. */
33 void 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 */
54 int 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) */
72 static 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  */
118 static 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) */
147 int 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 }