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