aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/boot
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2007-07-11 15:18:52 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-12 13:55:55 -0400
commit5e8ddcbe8692ca9854991c6875d302fa7e424e3c (patch)
tree31264c71e36c34ce24db9f26ac2f6384dbd75790 /arch/i386/boot
parent337496eb73ec970fe008095fdb2b2af60a2a7fa3 (diff)
Video mode probing support for the new x86 setup code
Video mode probing for the new x86 setup code. This code breaks down different drivers into modules. This code deliberately drops support for a lot of the vendor-specific mode probing present in the assembly version, since a lot of those probes have been found to be stale in current versions of those chips -- frequently, support for those modes have been dropped from recent video BIOSes due to space constraints, but the video BIOS signatures are still the same. However, additional drivers should be extremely straightforward to plug in, if desirable. Signed-off-by: H. Peter Anvin <hpa@zytor.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/i386/boot')
-rw-r--r--arch/i386/boot/vesa.h79
-rw-r--r--arch/i386/boot/video-bios.c125
-rw-r--r--arch/i386/boot/video-vesa.c284
-rw-r--r--arch/i386/boot/video-vga.c260
-rw-r--r--arch/i386/boot/video.c456
-rw-r--r--arch/i386/boot/video.h145
6 files changed, 1349 insertions, 0 deletions
diff --git a/arch/i386/boot/vesa.h b/arch/i386/boot/vesa.h
new file mode 100644
index 000000000000..ff5b73cd406f
--- /dev/null
+++ b/arch/i386/boot/vesa.h
@@ -0,0 +1,79 @@
1/* ----------------------------------------------------------------------- *
2 *
3 * Copyright 1999-2007 H. Peter Anvin - All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 * Boston MA 02111-1307, USA; either version 2 of the License, or
9 * (at your option) any later version; incorporated herein by reference.
10 *
11 * ----------------------------------------------------------------------- */
12
13#ifndef BOOT_VESA_H
14#define BOOT_VESA_H
15
16typedef struct {
17 u16 off, seg;
18} far_ptr;
19
20/* VESA General Information table */
21struct vesa_general_info {
22 u32 signature; /* 0 Magic number = "VESA" */
23 u16 version; /* 4 */
24 far_ptr vendor_string; /* 6 */
25 u32 capabilities; /* 10 */
26 far_ptr video_mode_ptr; /* 14 */
27 u16 total_memory; /* 18 */
28
29 u16 oem_software_rev; /* 20 */
30 far_ptr oem_vendor_name_ptr; /* 22 */
31 far_ptr oem_product_name_ptr; /* 26 */
32 far_ptr oem_product_rev_ptr; /* 30 */
33
34 u8 reserved[222]; /* 34 */
35 u8 oem_data[256]; /* 256 */
36} __attribute__ ((packed));
37
38#define VESA_MAGIC ('V' + ('E' << 8) + ('S' << 16) + ('A' << 24))
39#define VBE2_MAGIC ('V' + ('B' << 8) + ('E' << 16) + ('2' << 24))
40
41struct vesa_mode_info {
42 u16 mode_attr; /* 0 */
43 u8 win_attr[2]; /* 2 */
44 u16 win_grain; /* 4 */
45 u16 win_size; /* 6 */
46 u16 win_seg[2]; /* 8 */
47 far_ptr win_scheme; /* 12 */
48 u16 logical_scan; /* 16 */
49
50 u16 h_res; /* 18 */
51 u16 v_res; /* 20 */
52 u8 char_width; /* 22 */
53 u8 char_height; /* 23 */
54 u8 memory_planes; /* 24 */
55 u8 bpp; /* 25 */
56 u8 banks; /* 26 */
57 u8 memory_layout; /* 27 */
58 u8 bank_size; /* 28 */
59 u8 image_planes; /* 29 */
60 u8 page_function; /* 30 */
61
62 u8 rmask; /* 31 */
63 u8 rpos; /* 32 */
64 u8 gmask; /* 33 */
65 u8 gpos; /* 34 */
66 u8 bmask; /* 35 */
67 u8 bpos; /* 36 */
68 u8 resv_mask; /* 37 */
69 u8 resv_pos; /* 38 */
70 u8 dcm_info; /* 39 */
71
72 u32 lfb_ptr; /* 40 Linear frame buffer address */
73 u32 offscreen_ptr; /* 44 Offscreen memory address */
74 u16 offscreen_size; /* 48 */
75
76 u8 reserved[206]; /* 50 */
77} __attribute__ ((packed));
78
79#endif /* LIB_SYS_VESA_H */
diff --git a/arch/i386/boot/video-bios.c b/arch/i386/boot/video-bios.c
new file mode 100644
index 000000000000..afea46c500cc
--- /dev/null
+++ b/arch/i386/boot/video-bios.c
@@ -0,0 +1,125 @@
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-bios.c
13 *
14 * Standard video BIOS modes
15 *
16 * We have two options for this; silent and scanned.
17 */
18
19#include "boot.h"
20#include "video.h"
21
22__videocard video_bios;
23
24/* Set a conventional BIOS mode */
25static int set_bios_mode(u8 mode);
26
27static int bios_set_mode(struct mode_info *mi)
28{
29 return set_bios_mode(mi->mode - VIDEO_FIRST_BIOS);
30}
31
32static int set_bios_mode(u8 mode)
33{
34 u16 ax;
35 u8 new_mode;
36
37 ax = mode; /* AH=0x00 Set Video Mode */
38 asm volatile(INT10
39 : "+a" (ax)
40 : : "ebx", "ecx", "edx", "esi", "edi");
41
42 ax = 0x0f00; /* Get Current Video Mode */
43 asm volatile(INT10
44 : "+a" (ax)
45 : : "ebx", "ecx", "edx", "esi", "edi");
46
47 do_restore = 1; /* Assume video contents was lost */
48 new_mode = ax & 0x7f; /* Not all BIOSes are clean with the top bit */
49
50 if (new_mode == mode)
51 return 0; /* Mode change OK */
52
53 if (new_mode != boot_params.screen_info.orig_video_mode) {
54 /* Mode setting failed, but we didn't end up where we
55 started. That's bad. Try to revert to the original
56 video mode. */
57 ax = boot_params.screen_info.orig_video_mode;
58 asm volatile(INT10
59 : "+a" (ax)
60 : : "ebx", "ecx", "edx", "esi", "edi");
61 }
62 return -1;
63}
64
65static int bios_probe(void)
66{
67 u8 mode;
68 u8 saved_mode = boot_params.screen_info.orig_video_mode;
69 u16 crtc;
70 struct mode_info *mi;
71 int nmodes = 0;
72
73 if (adapter != ADAPTER_EGA && adapter != ADAPTER_VGA)
74 return 0;
75
76 set_fs(0);
77 crtc = vga_crtc();
78
79 video_bios.modes = GET_HEAP(struct mode_info, 0);
80
81 for (mode = 0x14; mode <= 0x7f; mode++) {
82 if (heap_free() < sizeof(struct mode_info))
83 break;
84
85 if (mode_defined(VIDEO_FIRST_BIOS+mode))
86 continue;
87
88 if (set_bios_mode(mode))
89 continue;
90
91 /* Try to verify that it's a text mode. */
92
93 /* Attribute Controller: make graphics controller disabled */
94 if (in_idx(0x3c0, 0x10) & 0x01)
95 continue;
96
97 /* Graphics Controller: verify Alpha addressing enabled */
98 if (in_idx(0x3ce, 0x06) & 0x01)
99 continue;
100
101 /* CRTC cursor location low should be zero(?) */
102 if (in_idx(crtc, 0x0f))
103 continue;
104
105 mi = GET_HEAP(struct mode_info, 1);
106 mi->mode = VIDEO_FIRST_BIOS+mode;
107 mi->x = rdfs16(0x44a);
108 mi->y = rdfs8(0x484)+1;
109 nmodes++;
110 }
111
112 set_bios_mode(saved_mode);
113
114 return nmodes;
115}
116
117__videocard video_bios =
118{
119 .card_name = "BIOS (scanned)",
120 .probe = bios_probe,
121 .set_mode = bios_set_mode,
122 .unsafe = 1,
123 .xmode_first = VIDEO_FIRST_BIOS,
124 .xmode_n = 0x80,
125};
diff --git a/arch/i386/boot/video-vesa.c b/arch/i386/boot/video-vesa.c
new file mode 100644
index 000000000000..e6aa9eb8d93a
--- /dev/null
+++ b/arch/i386/boot/video-vesa.c
@@ -0,0 +1,284 @@
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-vesa.c
13 *
14 * VESA text modes
15 */
16
17#include "boot.h"
18#include "video.h"
19#include "vesa.h"
20
21/* VESA information */
22static struct vesa_general_info vginfo;
23static struct vesa_mode_info vminfo;
24
25__videocard video_vesa;
26
27static void vesa_store_mode_params_graphics(void);
28
29static int vesa_probe(void)
30{
31#if defined(CONFIG_VIDEO_VESA) || defined(CONFIG_FIRMWARE_EDID)
32 u16 ax;
33 u16 mode;
34 addr_t mode_ptr;
35 struct mode_info *mi;
36 int nmodes = 0;
37
38 video_vesa.modes = GET_HEAP(struct mode_info, 0);
39
40 vginfo.signature = VBE2_MAGIC;
41
42 /* Optimistically assume a VESA BIOS is register-clean... */
43 ax = 0x4f00;
44 asm("int $0x10" : "+a" (ax), "=m" (vginfo) : "D" (&vginfo));
45
46 if (ax != 0x004f ||
47 vginfo.signature != VESA_MAGIC ||
48 vginfo.version < 0x0102)
49 return 0; /* Not present */
50#endif /* CONFIG_VIDEO_VESA || CONFIG_FIRMWARE_EDID */
51#ifdef CONFIG_VIDEO_VESA
52 set_fs(vginfo.video_mode_ptr.seg);
53 mode_ptr = vginfo.video_mode_ptr.off;
54
55 while ((mode = rdfs16(mode_ptr)) != 0xffff) {
56 mode_ptr += 2;
57
58 if (heap_free() < sizeof(struct mode_info))
59 break; /* Heap full, can't save mode info */
60
61 if (mode & ~0x1ff)
62 continue;
63
64 memset(&vminfo, 0, sizeof vminfo); /* Just in case... */
65
66 ax = 0x4f01;
67 asm("int $0x10"
68 : "+a" (ax), "=m" (vminfo)
69 : "c" (mode), "D" (&vminfo));
70
71 if (ax != 0x004f)
72 continue;
73
74 if ((vminfo.mode_attr & 0x15) == 0x05) {
75 /* Text Mode, TTY BIOS supported,
76 supported by hardware */
77 mi = GET_HEAP(struct mode_info, 1);
78 mi->mode = mode + VIDEO_FIRST_VESA;
79 mi->x = vminfo.h_res;
80 mi->y = vminfo.v_res;
81 nmodes++;
82 } else if ((vminfo.mode_attr & 0x99) == 0x99) {
83#ifdef CONFIG_FB
84 /* Graphics mode, color, linear frame buffer
85 supported -- register the mode but hide from
86 the menu. Only do this if framebuffer is
87 configured, however, otherwise the user will
88 be left without a screen. */
89 mi = GET_HEAP(struct mode_info, 1);
90 mi->mode = mode + VIDEO_FIRST_VESA;
91 mi->x = mi->y = 0;
92 nmodes++;
93#endif
94 }
95 }
96
97 return nmodes;
98#else
99 return 0;
100#endif /* CONFIG_VIDEO_VESA */
101}
102
103static int vesa_set_mode(struct mode_info *mode)
104{
105 u16 ax;
106 int is_graphic;
107 u16 vesa_mode = mode->mode - VIDEO_FIRST_VESA;
108
109 memset(&vminfo, 0, sizeof vminfo); /* Just in case... */
110
111 ax = 0x4f01;
112 asm("int $0x10"
113 : "+a" (ax), "=m" (vminfo)
114 : "c" (vesa_mode), "D" (&vminfo));
115
116 if (ax != 0x004f)
117 return -1;
118
119 if ((vminfo.mode_attr & 0x15) == 0x05) {
120 /* It's a supported text mode */
121 is_graphic = 0;
122 } else if ((vminfo.mode_attr & 0x99) == 0x99) {
123 /* It's a graphics mode with linear frame buffer */
124 is_graphic = 1;
125 vesa_mode |= 0x4000; /* Request linear frame buffer */
126 } else {
127 return -1; /* Invalid mode */
128 }
129
130
131 ax = 0x4f02;
132 asm volatile("int $0x10"
133 : "+a" (ax)
134 : "b" (vesa_mode), "D" (0));
135
136 if (ax != 0x004f)
137 return -1;
138
139 graphic_mode = is_graphic;
140 if (!is_graphic) {
141 /* Text mode */
142 force_x = mode->x;
143 force_y = mode->y;
144 do_restore = 1;
145 } else {
146 /* Graphics mode */
147 vesa_store_mode_params_graphics();
148 }
149
150 return 0;
151}
152
153
154/* Switch DAC to 8-bit mode */
155static void vesa_dac_set_8bits(void)
156{
157 u8 dac_size = 6;
158
159 /* If possible, switch the DAC to 8-bit mode */
160 if (vginfo.capabilities & 1) {
161 u16 ax, bx;
162
163 ax = 0x4f08;
164 bx = 0x0800;
165 asm volatile(INT10
166 : "+a" (ax), "+b" (bx)
167 : : "ecx", "edx", "esi", "edi");
168
169 if (ax == 0x004f)
170 dac_size = bx >> 8;
171 }
172
173 /* Set the color sizes to the DAC size, and offsets to 0 */
174 boot_params.screen_info.red_size = dac_size;
175 boot_params.screen_info.green_size = dac_size;
176 boot_params.screen_info.blue_size = dac_size;
177 boot_params.screen_info.rsvd_size = dac_size;
178
179 boot_params.screen_info.red_pos = 0;
180 boot_params.screen_info.green_pos = 0;
181 boot_params.screen_info.blue_pos = 0;
182 boot_params.screen_info.rsvd_pos = 0;
183}
184
185/* Save the VESA protected mode info */
186static void vesa_store_pm_info(void)
187{
188 u16 ax, bx, di, es;
189
190 ax = 0x4f0a;
191 bx = di = 0;
192 asm("pushw %%es; "INT10"; movw %%es,%0; popw %%es"
193 : "=d" (es), "+a" (ax), "+b" (bx), "+D" (di)
194 : : "ecx", "esi");
195
196 if (ax != 0x004f)
197 return;
198
199 boot_params.screen_info.vesapm_seg = es;
200 boot_params.screen_info.vesapm_off = di;
201}
202
203/*
204 * Save video mode parameters for graphics mode
205 */
206static void vesa_store_mode_params_graphics(void)
207{
208 /* Tell the kernel we're in VESA graphics mode */
209 boot_params.screen_info.orig_video_isVGA = 0x23;
210
211 /* Mode parameters */
212 boot_params.screen_info.vesa_attributes = vminfo.mode_attr;
213 boot_params.screen_info.lfb_linelength = vminfo.logical_scan;
214 boot_params.screen_info.lfb_width = vminfo.h_res;
215 boot_params.screen_info.lfb_height = vminfo.v_res;
216 boot_params.screen_info.lfb_depth = vminfo.bpp;
217 boot_params.screen_info.pages = vminfo.image_planes;
218 boot_params.screen_info.lfb_base = vminfo.lfb_ptr;
219 memcpy(&boot_params.screen_info.red_size,
220 &vminfo.rmask, 8);
221
222 /* General parameters */
223 boot_params.screen_info.lfb_size = vginfo.total_memory;
224
225 if (vminfo.bpp <= 8)
226 vesa_dac_set_8bits();
227
228 vesa_store_pm_info();
229}
230
231/*
232 * Save EDID information for the kernel; this is invoked, separately,
233 * after mode-setting.
234 */
235void vesa_store_edid(void)
236{
237#ifdef CONFIG_FIRMWARE_EDID
238 u16 ax, bx, cx, dx, di;
239
240 /* Apparently used as a nonsense token... */
241 memset(&boot_params.edid_info, 0x13, sizeof boot_params.edid_info);
242
243 if (vginfo.version < 0x0200)
244 return; /* EDID requires VBE 2.0+ */
245
246 ax = 0x4f15; /* VBE DDC */
247 bx = 0x0000; /* Report DDC capabilities */
248 cx = 0; /* Controller 0 */
249 di = 0; /* ES:DI must be 0 by spec */
250
251 /* Note: The VBE DDC spec is different from the main VESA spec;
252 we genuinely have to assume all registers are destroyed here. */
253
254 asm("pushw %%es; movw %2,%%es; "INT10"; popw %%es"
255 : "+a" (ax), "+b" (bx)
256 : "c" (cx), "D" (di)
257 : "esi");
258
259 if (ax != 0x004f)
260 return; /* No EDID */
261
262 /* BH = time in seconds to transfer EDD information */
263 /* BL = DDC level supported */
264
265 ax = 0x4f15; /* VBE DDC */
266 bx = 0x0001; /* Read EDID */
267 cx = 0; /* Controller 0 */
268 dx = 0; /* EDID block number */
269 di =(size_t) &boot_params.edid_info; /* (ES:)Pointer to block */
270 asm(INT10
271 : "+a" (ax), "+b" (bx), "+d" (dx)
272 : "c" (cx), "D" (di)
273 : "esi");
274#endif /* CONFIG_FIRMWARE_EDID */
275}
276
277__videocard video_vesa =
278{
279 .card_name = "VESA",
280 .probe = vesa_probe,
281 .set_mode = vesa_set_mode,
282 .xmode_first = VIDEO_FIRST_VESA,
283 .xmode_n = 0x200,
284};
diff --git a/arch/i386/boot/video-vga.c b/arch/i386/boot/video-vga.c
new file mode 100644
index 000000000000..700d09a9c9b3
--- /dev/null
+++ b/arch/i386/boot/video-vga.c
@@ -0,0 +1,260 @@
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-vga.c
13 *
14 * Common all-VGA modes
15 */
16
17#include "boot.h"
18#include "video.h"
19
20static struct mode_info vga_modes[] = {
21 { VIDEO_80x25, 80, 25 },
22 { VIDEO_8POINT, 80, 50 },
23 { VIDEO_80x43, 80, 43 },
24 { VIDEO_80x28, 80, 28 },
25 { VIDEO_80x30, 80, 30 },
26 { VIDEO_80x34, 80, 34 },
27 { VIDEO_80x60, 80, 60 },
28};
29
30static struct mode_info ega_modes[] = {
31 { VIDEO_80x25, 80, 25 },
32 { VIDEO_8POINT, 80, 43 },
33};
34
35static struct mode_info cga_modes[] = {
36 { VIDEO_80x25, 80, 25 },
37};
38
39__videocard video_vga;
40
41/* Set basic 80x25 mode */
42static u8 vga_set_basic_mode(void)
43{
44 u16 ax;
45 u8 rows;
46 u8 mode;
47
48#ifdef CONFIG_VIDEO_400_HACK
49 if (adapter >= ADAPTER_VGA) {
50 asm(INT10
51 : : "a" (0x1202), "b" (0x0030)
52 : "ecx", "edx", "esi", "edi");
53 }
54#endif
55
56 ax = 0x0f00;
57 asm(INT10
58 : "+a" (ax)
59 : : "ebx", "ecx", "edx", "esi", "edi");
60
61 mode = (u8)ax;
62
63 set_fs(0);
64 rows = rdfs8(0x484); /* rows minus one */
65
66#ifndef CONFIG_VIDEO_400_HACK
67 if ((ax == 0x5003 || ax == 0x5007) &&
68 (rows == 0 || rows == 24))
69 return mode;
70#endif
71
72 if (mode != 3 && mode != 7)
73 mode = 3;
74
75 /* Set the mode */
76 asm volatile(INT10
77 : : "a" (mode)
78 : "ebx", "ecx", "edx", "esi", "edi");
79 do_restore = 1;
80 return mode;
81}
82
83static void vga_set_8font(void)
84{
85 /* Set 8x8 font - 80x43 on EGA, 80x50 on VGA */
86
87 /* Set 8x8 font */
88 asm volatile(INT10 : : "a" (0x1112), "b" (0));
89
90 /* Use alternate print screen */
91 asm volatile(INT10 : : "a" (0x1200), "b" (0x20));
92
93 /* Turn off cursor emulation */
94 asm volatile(INT10 : : "a" (0x1201), "b" (0x34));
95
96 /* Cursor is scan lines 6-7 */
97 asm volatile(INT10 : : "a" (0x0100), "c" (0x0607));
98}
99
100static void vga_set_14font(void)
101{
102 /* Set 9x14 font - 80x28 on VGA */
103
104 /* Set 9x14 font */
105 asm volatile(INT10 : : "a" (0x1111), "b" (0));
106
107 /* Turn off cursor emulation */
108 asm volatile(INT10 : : "a" (0x1201), "b" (0x34));
109
110 /* Cursor is scan lines 11-12 */
111 asm volatile(INT10 : : "a" (0x0100), "c" (0x0b0c));
112}
113
114static void vga_set_80x43(void)
115{
116 /* Set 80x43 mode on VGA (not EGA) */
117
118 /* Set 350 scans */
119 asm volatile(INT10 : : "a" (0x1201), "b" (0x30));
120
121 /* Reset video mode */
122 asm volatile(INT10 : : "a" (0x0003));
123
124 vga_set_8font();
125}
126
127/* I/O address of the VGA CRTC */
128u16 vga_crtc(void)
129{
130 return (inb(0x3cc) & 1) ? 0x3d4 : 0x3b4;
131}
132
133static void vga_set_480_scanlines(int end)
134{
135 u16 crtc;
136 u8 csel;
137
138 crtc = vga_crtc();
139
140 out_idx(0x0c, crtc, 0x11); /* Vertical sync end, unlock CR0-7 */
141 out_idx(0x0b, crtc, 0x06); /* Vertical total */
142 out_idx(0x3e, crtc, 0x07); /* Vertical overflow */
143 out_idx(0xea, crtc, 0x10); /* Vertical sync start */
144 out_idx(end, crtc, 0x12); /* Vertical display end */
145 out_idx(0xe7, crtc, 0x15); /* Vertical blank start */
146 out_idx(0x04, crtc, 0x16); /* Vertical blank end */
147 csel = inb(0x3cc);
148 csel &= 0x0d;
149 csel |= 0xe2;
150 outb(csel, 0x3cc);
151}
152
153static void vga_set_80x30(void)
154{
155 vga_set_480_scanlines(0xdf);
156}
157
158static void vga_set_80x34(void)
159{
160 vga_set_14font();
161 vga_set_480_scanlines(0xdb);
162}
163
164static void vga_set_80x60(void)
165{
166 vga_set_8font();
167 vga_set_480_scanlines(0xdf);
168}
169
170static int vga_set_mode(struct mode_info *mode)
171{
172 /* Set the basic mode */
173 vga_set_basic_mode();
174
175 /* Override a possibly broken BIOS */
176 force_x = mode->x;
177 force_y = mode->y;
178
179 switch (mode->mode) {
180 case VIDEO_80x25:
181 break;
182 case VIDEO_8POINT:
183 vga_set_8font();
184 break;
185 case VIDEO_80x43:
186 vga_set_80x43();
187 break;
188 case VIDEO_80x28:
189 vga_set_14font();
190 break;
191 case VIDEO_80x30:
192 vga_set_80x30();
193 break;
194 case VIDEO_80x34:
195 vga_set_80x34();
196 break;
197 case VIDEO_80x60:
198 vga_set_80x60();
199 break;
200 }
201
202 return 0;
203}
204
205/*
206 * Note: this probe includes basic information required by all
207 * systems. It should be executed first, by making sure
208 * video-vga.c is listed first in the Makefile.
209 */
210static int vga_probe(void)
211{
212 static const char *card_name[] = {
213 "CGA/MDA/HGC", "EGA", "VGA"
214 };
215 static struct mode_info *mode_lists[] = {
216 cga_modes,
217 ega_modes,
218 vga_modes,
219 };
220 static int mode_count[] = {
221 sizeof(cga_modes)/sizeof(struct mode_info),
222 sizeof(ega_modes)/sizeof(struct mode_info),
223 sizeof(vga_modes)/sizeof(struct mode_info),
224 };
225 u8 vga_flag;
226
227 asm(INT10
228 : "=b" (boot_params.screen_info.orig_video_ega_bx)
229 : "a" (0x1200), "b" (0x10) /* Check EGA/VGA */
230 : "ecx", "edx", "esi", "edi");
231
232 /* If we have MDA/CGA/HGC then BL will be unchanged at 0x10 */
233 if ((u8)boot_params.screen_info.orig_video_ega_bx != 0x10) {
234 /* EGA/VGA */
235 asm(INT10
236 : "=a" (vga_flag)
237 : "a" (0x1a00)
238 : "ebx", "ecx", "edx", "esi", "edi");
239
240 if (vga_flag == 0x1a) {
241 adapter = ADAPTER_VGA;
242 boot_params.screen_info.orig_video_isVGA = 1;
243 } else {
244 adapter = ADAPTER_EGA;
245 }
246 } else {
247 adapter = ADAPTER_CGA;
248 }
249
250 video_vga.modes = mode_lists[adapter];
251 video_vga.card_name = card_name[adapter];
252 return mode_count[adapter];
253}
254
255__videocard video_vga =
256{
257 .card_name = "VGA",
258 .probe = vga_probe,
259 .set_mode = vga_set_mode,
260};
diff --git a/arch/i386/boot/video.c b/arch/i386/boot/video.c
new file mode 100644
index 000000000000..3bb3573cd6a1
--- /dev/null
+++ b/arch/i386/boot/video.c
@@ -0,0 +1,456 @@
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 */
24static struct card_info cards[]; /* List of cards to probe for */
25
26/*
27 * Common variables
28 */
29int adapter; /* 0=CGA/MDA/HGC, 1=EGA, 2=VGA+ */
30u16 video_segment;
31int force_x, force_y; /* Don't query the BIOS for cols/rows */
32
33int do_restore = 0; /* Screen contents changed during mode flip */
34int graphic_mode; /* Graphic mode with linear frame buffer */
35
36static 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
51static 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;
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 */
73static 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. */
111static 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 */
132int 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) */
150static int raw_set_mode(u16 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 return card->set_mode(mi);
170
171 if (visible)
172 nmode++;
173 }
174 }
175
176 /* Nothing found? Is it an "exceptional" (unprobed) mode? */
177 for (card = video_cards; card < video_cards_end; card++) {
178 if (mode >= card->xmode_first &&
179 mode < card->xmode_first+card->xmode_n) {
180 struct mode_info mix;
181 mix.mode = mode;
182 mix.x = mix.y = 0;
183 return card->set_mode(&mix);
184 }
185 }
186
187 /* Otherwise, failure... */
188 return -1;
189}
190
191/*
192 * Recalculate the vertical video cutoff (hack!)
193 */
194static void vga_recalc_vertical(void)
195{
196 unsigned int font_size, rows;
197 u16 crtc;
198 u8 ov;
199
200 set_fs(0);
201 font_size = rdfs8(0x485); /* BIOS: font size (pixels) */
202 rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */
203
204 rows *= font_size; /* Visible scan lines */
205 rows--; /* ... minus one */
206
207 crtc = vga_crtc();
208
209 out_idx((u8)rows, crtc, 0x12); /* Lower height register */
210 ov = in_idx(crtc, 0x07); /* Overflow register */
211 ov &= 0xbd;
212 ov |= (rows >> (8-1)) & 0x02;
213 ov |= (rows >> (9-6)) & 0x40;
214 out_idx(ov, crtc, 0x07);
215}
216
217/* Set mode (with recalc if specified) */
218static int set_mode(u16 mode)
219{
220 int rv;
221
222 /* Very special mode numbers... */
223 if (mode == VIDEO_CURRENT_MODE)
224 return 0; /* Nothing to do... */
225 else if (mode == NORMAL_VGA)
226 mode = VIDEO_80x25;
227 else if (mode == EXTENDED_VGA)
228 mode = VIDEO_8POINT;
229
230 rv = raw_set_mode(mode);
231 if (rv)
232 return rv;
233
234 if (mode & VIDEO_RECALC)
235 vga_recalc_vertical();
236
237 return 0;
238}
239
240static unsigned int get_entry(void)
241{
242 char entry_buf[4];
243 int i, len = 0;
244 int key;
245 unsigned int v;
246
247 do {
248 key = getchar();
249
250 if (key == '\b') {
251 if (len > 0) {
252 puts("\b \b");
253 len--;
254 }
255 } else if ((key >= '0' && key <= '9') ||
256 (key >= 'A' && key <= 'Z') ||
257 (key >= 'a' && key <= 'z')) {
258 if (len < sizeof entry_buf) {
259 entry_buf[len++] = key;
260 putchar(key);
261 }
262 }
263 } while (key != '\r');
264 putchar('\n');
265
266 if (len == 0)
267 return VIDEO_CURRENT_MODE; /* Default */
268
269 v = 0;
270 for (i = 0; i < len; i++) {
271 v <<= 4;
272 key = entry_buf[i] | 0x20;
273 v += (key > '9') ? key-'a'+10 : key-'0';
274 }
275
276 return v;
277}
278
279static void display_menu(void)
280{
281 struct card_info *card;
282 struct mode_info *mi;
283 char ch;
284 int i;
285
286 puts("Mode: COLSxROWS:\n");
287
288 ch = '0';
289 for (card = video_cards; card < video_cards_end; card++) {
290 mi = card->modes;
291 for (i = 0; i < card->nmodes; i++, mi++) {
292 int visible = mi->x && mi->y;
293 u16 mode_id = mi->mode ? mi->mode :
294 (mi->y << 8)+mi->x;
295
296 if (!visible)
297 continue; /* Hidden mode */
298
299 printf("%c %04X %3dx%-3d %s\n",
300 ch, mode_id, mi->x, mi->y, card->card_name);
301
302 if (ch == '9')
303 ch = 'a';
304 else if (ch == 'z' || ch == ' ')
305 ch = ' '; /* Out of keys... */
306 else
307 ch++;
308 }
309 }
310}
311
312#define H(x) ((x)-'a'+10)
313#define SCAN ((H('s')<<12)+(H('c')<<8)+(H('a')<<4)+H('n'))
314
315static unsigned int mode_menu(void)
316{
317 int key;
318 unsigned int sel;
319
320 puts("Press <ENTER> to see video modes available, "
321 "<SPACE> to continue, or wait 30 sec\n");
322
323 kbd_flush();
324 while (1) {
325 key = getchar_timeout();
326 if (key == ' ' || key == 0)
327 return VIDEO_CURRENT_MODE; /* Default */
328 if (key == '\r')
329 break;
330 putchar('\a'); /* Beep! */
331 }
332
333
334 for (;;) {
335 display_menu();
336
337 puts("Enter a video mode or \"scan\" to scan for "
338 "additional modes: ");
339 sel = get_entry();
340 if (sel != SCAN)
341 return sel;
342
343 probe_cards(1);
344 }
345}
346
347#ifdef CONFIG_VIDEO_RETAIN
348/* Save screen content to the heap */
349struct saved_screen {
350 int x, y;
351 int curx, cury;
352 u16 *data;
353} saved;
354
355static void save_screen(void)
356{
357 /* Should be called after store_mode_params() */
358 saved.x = boot_params.screen_info.orig_video_cols;
359 saved.y = boot_params.screen_info.orig_video_lines;
360 saved.curx = boot_params.screen_info.orig_x;
361 saved.cury = boot_params.screen_info.orig_y;
362
363 if (heap_free() < saved.x*saved.y*sizeof(u16)+512)
364 return; /* Not enough heap to save the screen */
365
366 saved.data = GET_HEAP(u16, saved.x*saved.y);
367
368 set_fs(video_segment);
369 copy_from_fs(saved.data, 0, saved.x*saved.y*sizeof(u16));
370}
371
372static void restore_screen(void)
373{
374 /* Should be called after store_mode_params() */
375 int xs = boot_params.screen_info.orig_video_cols;
376 int ys = boot_params.screen_info.orig_video_lines;
377 int y;
378 addr_t dst = 0;
379 u16 *src = saved.data;
380 u16 ax, bx, dx;
381
382 if (graphic_mode)
383 return; /* Can't restore onto a graphic mode */
384
385 if (!src)
386 return; /* No saved screen contents */
387
388 /* Restore screen contents */
389
390 set_fs(video_segment);
391 for (y = 0; y < ys; y++) {
392 int npad;
393
394 if (y < saved.y) {
395 int copy = (xs < saved.x) ? xs : saved.x;
396 copy_to_fs(dst, src, copy*sizeof(u16));
397 dst += copy*sizeof(u16);
398 src += saved.x;
399 npad = (xs < saved.x) ? 0 : xs-saved.x;
400 } else {
401 npad = xs;
402 }
403
404 /* Writes "npad" blank characters to
405 video_segment:dst and advances dst */
406 asm volatile("pushw %%es ; "
407 "movw %2,%%es ; "
408 "shrw %%cx ; "
409 "jnc 1f ; "
410 "stosw \n\t"
411 "1: rep;stosl ; "
412 "popw %%es"
413 : "+D" (dst), "+c" (npad)
414 : "bdSm" (video_segment),
415 "a" (0x07200720));
416 }
417
418 /* Restore cursor position */
419 ax = 0x0200; /* Set cursor position */
420 bx = 0; /* Page number (<< 8) */
421 dx = (saved.cury << 8)+saved.curx;
422 asm volatile(INT10
423 : "+a" (ax), "+b" (bx), "+d" (dx)
424 : : "ecx", "esi", "edi");
425}
426#else
427#define save_screen() ((void)0)
428#define restore_screen() ((void)0)
429#endif
430
431void set_video(void)
432{
433 u16 mode = boot_params.hdr.vid_mode;
434
435 RESET_HEAP();
436
437 store_mode_params();
438 save_screen();
439 probe_cards(0);
440
441 for (;;) {
442 if (mode == ASK_VGA)
443 mode = mode_menu();
444
445 if (!set_mode(mode))
446 break;
447
448 printf("Undefined video mode number: %x\n", mode);
449 mode = ASK_VGA;
450 }
451 vesa_store_edid();
452 store_mode_params();
453
454 if (do_restore)
455 restore_screen();
456}
diff --git a/arch/i386/boot/video.h b/arch/i386/boot/video.h
new file mode 100644
index 000000000000..29eca1710b2c
--- /dev/null
+++ b/arch/i386/boot/video.h
@@ -0,0 +1,145 @@
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.h
13 *
14 * Header file for the real-mode video probing code
15 */
16
17#ifndef BOOT_VIDEO_H
18#define BOOT_VIDEO_H
19
20#include <linux/types.h>
21
22/* Enable autodetection of SVGA adapters and modes. */
23#undef CONFIG_VIDEO_SVGA
24
25/* Enable autodetection of VESA modes */
26#define CONFIG_VIDEO_VESA
27
28/* Retain screen contents when switching modes */
29#define CONFIG_VIDEO_RETAIN
30
31/* Force 400 scan lines for standard modes (hack to fix bad BIOS behaviour */
32#undef CONFIG_VIDEO_400_HACK
33
34/* This code uses an extended set of video mode numbers. These include:
35 * Aliases for standard modes
36 * NORMAL_VGA (-1)
37 * EXTENDED_VGA (-2)
38 * ASK_VGA (-3)
39 * Video modes numbered by menu position -- NOT RECOMMENDED because of lack
40 * of compatibility when extending the table. These are between 0x00 and 0xff.
41 */
42#define VIDEO_FIRST_MENU 0x0000
43
44/* Standard BIOS video modes (BIOS number + 0x0100) */
45#define VIDEO_FIRST_BIOS 0x0100
46
47/* VESA BIOS video modes (VESA number + 0x0200) */
48#define VIDEO_FIRST_VESA 0x0200
49
50/* Video7 special modes (BIOS number + 0x0900) */
51#define VIDEO_FIRST_V7 0x0900
52
53/* Special video modes */
54#define VIDEO_FIRST_SPECIAL 0x0f00
55#define VIDEO_80x25 0x0f00
56#define VIDEO_8POINT 0x0f01
57#define VIDEO_80x43 0x0f02
58#define VIDEO_80x28 0x0f03
59#define VIDEO_CURRENT_MODE 0x0f04
60#define VIDEO_80x30 0x0f05
61#define VIDEO_80x34 0x0f06
62#define VIDEO_80x60 0x0f07
63#define VIDEO_GFX_HACK 0x0f08
64#define VIDEO_LAST_SPECIAL 0x0f09
65
66/* Video modes given by resolution */
67#define VIDEO_FIRST_RESOLUTION 0x1000
68
69/* The "recalculate timings" flag */
70#define VIDEO_RECALC 0x8000
71
72/* Define DO_STORE according to CONFIG_VIDEO_RETAIN */
73#ifdef CONFIG_VIDEO_RETAIN
74void store_screen(void);
75#define DO_STORE() store_screen()
76#else
77#define DO_STORE() ((void)0)
78#endif /* CONFIG_VIDEO_RETAIN */
79
80/*
81 * Mode table structures
82 */
83
84struct mode_info {
85 u16 mode; /* Mode number (vga= style) */
86 u8 x, y; /* Width, height */
87};
88
89struct card_info {
90 const char *card_name;
91 int (*set_mode)(struct mode_info *mode);
92 int (*probe)(void);
93 struct mode_info *modes;
94 int nmodes; /* Number of probed modes so far */
95 int unsafe; /* Probing is unsafe, only do after "scan" */
96 u16 xmode_first; /* Unprobed modes to try to call anyway */
97 u16 xmode_n; /* Size of unprobed mode range */
98};
99
100#define __videocard struct card_info __attribute__((section(".videocards")))
101extern struct card_info video_cards[], video_cards_end[];
102
103int mode_defined(u16 mode); /* video.c */
104
105/* Basic video information */
106#define ADAPTER_CGA 0 /* CGA/MDA/HGC */
107#define ADAPTER_EGA 1
108#define ADAPTER_VGA 2
109
110extern int adapter;
111extern u16 video_segment;
112extern int force_x, force_y; /* Don't query the BIOS for cols/rows */
113extern int do_restore; /* Restore screen contents */
114extern int graphic_mode; /* Graphics mode with linear frame buffer */
115
116/*
117 * int $0x10 is notorious for touching registers it shouldn't.
118 * gcc doesn't like %ebp being clobbered, so define it as a push/pop
119 * sequence here.
120 */
121#define INT10 "pushl %%ebp; int $0x10; popl %%ebp"
122
123/* Accessing VGA indexed registers */
124static inline u8 in_idx(u16 port, u8 index)
125{
126 outb(index, port);
127 return inb(port+1);
128}
129
130static inline void out_idx(u8 v, u16 port, u8 index)
131{
132 outw(index+(v << 8), port);
133}
134
135/* Writes a value to an indexed port and then reads the port again */
136static inline u8 tst_idx(u8 v, u16 port, u8 index)
137{
138 out_idx(port, index, v);
139 return in_idx(port, index);
140}
141
142/* Get the I/O port of the VGA CRTC */
143u16 vga_crtc(void); /* video-vga.c */
144
145#endif /* BOOT_VIDEO_H */