1*97873a3dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
296ae6ea0SThomas Gleixner /* -*- linux-c -*- ------------------------------------------------------- *
396ae6ea0SThomas Gleixner *
496ae6ea0SThomas Gleixner * Copyright (C) 1991, 1992 Linus Torvalds
596ae6ea0SThomas Gleixner * Copyright 2007 rPath, Inc. - All Rights Reserved
6cf06de7bSH. Peter Anvin * Copyright 2009 Intel Corporation; author H. Peter Anvin
796ae6ea0SThomas Gleixner *
896ae6ea0SThomas Gleixner * ----------------------------------------------------------------------- */
996ae6ea0SThomas Gleixner
1096ae6ea0SThomas Gleixner /*
1196ae6ea0SThomas Gleixner * Common all-VGA modes
1296ae6ea0SThomas Gleixner */
1396ae6ea0SThomas Gleixner
1496ae6ea0SThomas Gleixner #include "boot.h"
1596ae6ea0SThomas Gleixner #include "video.h"
1696ae6ea0SThomas Gleixner
1796ae6ea0SThomas Gleixner static struct mode_info vga_modes[] = {
181cac5004SH. Peter Anvin { VIDEO_80x25, 80, 25, 0 },
191cac5004SH. Peter Anvin { VIDEO_8POINT, 80, 50, 0 },
201cac5004SH. Peter Anvin { VIDEO_80x43, 80, 43, 0 },
211cac5004SH. Peter Anvin { VIDEO_80x28, 80, 28, 0 },
221cac5004SH. Peter Anvin { VIDEO_80x30, 80, 30, 0 },
231cac5004SH. Peter Anvin { VIDEO_80x34, 80, 34, 0 },
241cac5004SH. Peter Anvin { VIDEO_80x60, 80, 60, 0 },
2596ae6ea0SThomas Gleixner };
2696ae6ea0SThomas Gleixner
2796ae6ea0SThomas Gleixner static struct mode_info ega_modes[] = {
281cac5004SH. Peter Anvin { VIDEO_80x25, 80, 25, 0 },
291cac5004SH. Peter Anvin { VIDEO_8POINT, 80, 43, 0 },
3096ae6ea0SThomas Gleixner };
3196ae6ea0SThomas Gleixner
3296ae6ea0SThomas Gleixner static struct mode_info cga_modes[] = {
331cac5004SH. Peter Anvin { VIDEO_80x25, 80, 25, 0 },
3496ae6ea0SThomas Gleixner };
3596ae6ea0SThomas Gleixner
36a1a00b58SHannes Eder static __videocard video_vga;
3796ae6ea0SThomas Gleixner
3896ae6ea0SThomas Gleixner /* Set basic 80x25 mode */
vga_set_basic_mode(void)3996ae6ea0SThomas Gleixner static u8 vga_set_basic_mode(void)
4096ae6ea0SThomas Gleixner {
41cf06de7bSH. Peter Anvin struct biosregs ireg, oreg;
4296ae6ea0SThomas Gleixner u8 mode;
4396ae6ea0SThomas Gleixner
44cf06de7bSH. Peter Anvin initregs(&ireg);
45cf06de7bSH. Peter Anvin
468e92dc76SH. Peter Anvin /* Query current mode */
47cf3bdc29SAndi Kleen ireg.ax = 0x0f00;
48cf06de7bSH. Peter Anvin intcall(0x10, &ireg, &oreg);
49cf06de7bSH. Peter Anvin mode = oreg.al;
5096ae6ea0SThomas Gleixner
5196ae6ea0SThomas Gleixner if (mode != 3 && mode != 7)
5296ae6ea0SThomas Gleixner mode = 3;
5396ae6ea0SThomas Gleixner
5496ae6ea0SThomas Gleixner /* Set the mode */
55cf06de7bSH. Peter Anvin ireg.ax = mode; /* AH=0: set mode */
56cf06de7bSH. Peter Anvin intcall(0x10, &ireg, NULL);
5796ae6ea0SThomas Gleixner do_restore = 1;
5896ae6ea0SThomas Gleixner return mode;
5996ae6ea0SThomas Gleixner }
6096ae6ea0SThomas Gleixner
vga_set_8font(void)6196ae6ea0SThomas Gleixner static void vga_set_8font(void)
6296ae6ea0SThomas Gleixner {
6396ae6ea0SThomas Gleixner /* Set 8x8 font - 80x43 on EGA, 80x50 on VGA */
64cf06de7bSH. Peter Anvin struct biosregs ireg;
65cf06de7bSH. Peter Anvin
66cf06de7bSH. Peter Anvin initregs(&ireg);
6796ae6ea0SThomas Gleixner
6896ae6ea0SThomas Gleixner /* Set 8x8 font */
69cf06de7bSH. Peter Anvin ireg.ax = 0x1112;
70cf06de7bSH. Peter Anvin /* ireg.bl = 0; */
71cf06de7bSH. Peter Anvin intcall(0x10, &ireg, NULL);
7296ae6ea0SThomas Gleixner
7396ae6ea0SThomas Gleixner /* Use alternate print screen */
74cf06de7bSH. Peter Anvin ireg.ax = 0x1200;
75cf06de7bSH. Peter Anvin ireg.bl = 0x20;
76cf06de7bSH. Peter Anvin intcall(0x10, &ireg, NULL);
7796ae6ea0SThomas Gleixner
7896ae6ea0SThomas Gleixner /* Turn off cursor emulation */
79cf06de7bSH. Peter Anvin ireg.ax = 0x1201;
80cf06de7bSH. Peter Anvin ireg.bl = 0x34;
81cf06de7bSH. Peter Anvin intcall(0x10, &ireg, NULL);
8296ae6ea0SThomas Gleixner
8396ae6ea0SThomas Gleixner /* Cursor is scan lines 6-7 */
84cf06de7bSH. Peter Anvin ireg.ax = 0x0100;
85cf06de7bSH. Peter Anvin ireg.cx = 0x0607;
86cf06de7bSH. Peter Anvin intcall(0x10, &ireg, NULL);
8796ae6ea0SThomas Gleixner }
8896ae6ea0SThomas Gleixner
vga_set_14font(void)8996ae6ea0SThomas Gleixner static void vga_set_14font(void)
9096ae6ea0SThomas Gleixner {
9196ae6ea0SThomas Gleixner /* Set 9x14 font - 80x28 on VGA */
92cf06de7bSH. Peter Anvin struct biosregs ireg;
93cf06de7bSH. Peter Anvin
94cf06de7bSH. Peter Anvin initregs(&ireg);
9596ae6ea0SThomas Gleixner
9696ae6ea0SThomas Gleixner /* Set 9x14 font */
97cf06de7bSH. Peter Anvin ireg.ax = 0x1111;
98cf06de7bSH. Peter Anvin /* ireg.bl = 0; */
99cf06de7bSH. Peter Anvin intcall(0x10, &ireg, NULL);
10096ae6ea0SThomas Gleixner
10196ae6ea0SThomas Gleixner /* Turn off cursor emulation */
102cf06de7bSH. Peter Anvin ireg.ax = 0x1201;
103cf06de7bSH. Peter Anvin ireg.bl = 0x34;
104cf06de7bSH. Peter Anvin intcall(0x10, &ireg, NULL);
10596ae6ea0SThomas Gleixner
10696ae6ea0SThomas Gleixner /* Cursor is scan lines 11-12 */
107cf06de7bSH. Peter Anvin ireg.ax = 0x0100;
108cf06de7bSH. Peter Anvin ireg.cx = 0x0b0c;
109cf06de7bSH. Peter Anvin intcall(0x10, &ireg, NULL);
11096ae6ea0SThomas Gleixner }
11196ae6ea0SThomas Gleixner
vga_set_80x43(void)11296ae6ea0SThomas Gleixner static void vga_set_80x43(void)
11396ae6ea0SThomas Gleixner {
11496ae6ea0SThomas Gleixner /* Set 80x43 mode on VGA (not EGA) */
115cf06de7bSH. Peter Anvin struct biosregs ireg;
116cf06de7bSH. Peter Anvin
117cf06de7bSH. Peter Anvin initregs(&ireg);
11896ae6ea0SThomas Gleixner
11996ae6ea0SThomas Gleixner /* Set 350 scans */
120cf06de7bSH. Peter Anvin ireg.ax = 0x1201;
121cf06de7bSH. Peter Anvin ireg.bl = 0x30;
122cf06de7bSH. Peter Anvin intcall(0x10, &ireg, NULL);
12396ae6ea0SThomas Gleixner
12496ae6ea0SThomas Gleixner /* Reset video mode */
125cf06de7bSH. Peter Anvin ireg.ax = 0x0003;
126cf06de7bSH. Peter Anvin intcall(0x10, &ireg, NULL);
12796ae6ea0SThomas Gleixner
12896ae6ea0SThomas Gleixner vga_set_8font();
12996ae6ea0SThomas Gleixner }
13096ae6ea0SThomas Gleixner
13196ae6ea0SThomas Gleixner /* I/O address of the VGA CRTC */
vga_crtc(void)13296ae6ea0SThomas Gleixner u16 vga_crtc(void)
13396ae6ea0SThomas Gleixner {
13496ae6ea0SThomas Gleixner return (inb(0x3cc) & 1) ? 0x3d4 : 0x3b4;
13596ae6ea0SThomas Gleixner }
13696ae6ea0SThomas Gleixner
vga_set_480_scanlines(void)1371e274a58SH. Peter Anvin static void vga_set_480_scanlines(void)
13896ae6ea0SThomas Gleixner {
1395f641356SH. Peter Anvin u16 crtc; /* CRTC base address */
1405f641356SH. Peter Anvin u8 csel; /* CRTC miscellaneous output register */
14196ae6ea0SThomas Gleixner
14296ae6ea0SThomas Gleixner crtc = vga_crtc();
14396ae6ea0SThomas Gleixner
14496ae6ea0SThomas Gleixner out_idx(0x0c, crtc, 0x11); /* Vertical sync end, unlock CR0-7 */
14596ae6ea0SThomas Gleixner out_idx(0x0b, crtc, 0x06); /* Vertical total */
1461e274a58SH. Peter Anvin out_idx(0x3e, crtc, 0x07); /* Vertical overflow */
14796ae6ea0SThomas Gleixner out_idx(0xea, crtc, 0x10); /* Vertical sync start */
1481e274a58SH. Peter Anvin out_idx(0xdf, crtc, 0x12); /* Vertical display end */
14996ae6ea0SThomas Gleixner out_idx(0xe7, crtc, 0x15); /* Vertical blank start */
15096ae6ea0SThomas Gleixner out_idx(0x04, crtc, 0x16); /* Vertical blank end */
15196ae6ea0SThomas Gleixner csel = inb(0x3cc);
15296ae6ea0SThomas Gleixner csel &= 0x0d;
15396ae6ea0SThomas Gleixner csel |= 0xe2;
1545f641356SH. Peter Anvin outb(csel, 0x3c2);
15596ae6ea0SThomas Gleixner }
15696ae6ea0SThomas Gleixner
vga_set_vertical_end(int lines)1571e274a58SH. Peter Anvin static void vga_set_vertical_end(int lines)
1581e274a58SH. Peter Anvin {
1591e274a58SH. Peter Anvin u16 crtc; /* CRTC base address */
1601e274a58SH. Peter Anvin u8 ovfw; /* CRTC overflow register */
1611e274a58SH. Peter Anvin int end = lines-1;
1621e274a58SH. Peter Anvin
1631e274a58SH. Peter Anvin crtc = vga_crtc();
1641e274a58SH. Peter Anvin
1651e274a58SH. Peter Anvin ovfw = 0x3c | ((end >> (8-1)) & 0x02) | ((end >> (9-6)) & 0x40);
1661e274a58SH. Peter Anvin
1671e274a58SH. Peter Anvin out_idx(ovfw, crtc, 0x07); /* Vertical overflow */
1681e274a58SH. Peter Anvin out_idx(end, crtc, 0x12); /* Vertical display end */
1691e274a58SH. Peter Anvin }
1701e274a58SH. Peter Anvin
vga_set_80x30(void)17196ae6ea0SThomas Gleixner static void vga_set_80x30(void)
17296ae6ea0SThomas Gleixner {
1731e274a58SH. Peter Anvin vga_set_480_scanlines();
1741e274a58SH. Peter Anvin vga_set_vertical_end(30*16);
17596ae6ea0SThomas Gleixner }
17696ae6ea0SThomas Gleixner
vga_set_80x34(void)17796ae6ea0SThomas Gleixner static void vga_set_80x34(void)
17896ae6ea0SThomas Gleixner {
1791e274a58SH. Peter Anvin vga_set_480_scanlines();
18096ae6ea0SThomas Gleixner vga_set_14font();
1811e274a58SH. Peter Anvin vga_set_vertical_end(34*14);
18296ae6ea0SThomas Gleixner }
18396ae6ea0SThomas Gleixner
vga_set_80x60(void)18496ae6ea0SThomas Gleixner static void vga_set_80x60(void)
18596ae6ea0SThomas Gleixner {
1861e274a58SH. Peter Anvin vga_set_480_scanlines();
18796ae6ea0SThomas Gleixner vga_set_8font();
1881e274a58SH. Peter Anvin vga_set_vertical_end(60*8);
18996ae6ea0SThomas Gleixner }
19096ae6ea0SThomas Gleixner
vga_set_mode(struct mode_info * mode)19196ae6ea0SThomas Gleixner static int vga_set_mode(struct mode_info *mode)
19296ae6ea0SThomas Gleixner {
19396ae6ea0SThomas Gleixner /* Set the basic mode */
19496ae6ea0SThomas Gleixner vga_set_basic_mode();
19596ae6ea0SThomas Gleixner
19696ae6ea0SThomas Gleixner /* Override a possibly broken BIOS */
19796ae6ea0SThomas Gleixner force_x = mode->x;
19896ae6ea0SThomas Gleixner force_y = mode->y;
19996ae6ea0SThomas Gleixner
20096ae6ea0SThomas Gleixner switch (mode->mode) {
20196ae6ea0SThomas Gleixner case VIDEO_80x25:
20296ae6ea0SThomas Gleixner break;
20396ae6ea0SThomas Gleixner case VIDEO_8POINT:
20496ae6ea0SThomas Gleixner vga_set_8font();
20596ae6ea0SThomas Gleixner break;
20696ae6ea0SThomas Gleixner case VIDEO_80x43:
20796ae6ea0SThomas Gleixner vga_set_80x43();
20896ae6ea0SThomas Gleixner break;
20996ae6ea0SThomas Gleixner case VIDEO_80x28:
21096ae6ea0SThomas Gleixner vga_set_14font();
21196ae6ea0SThomas Gleixner break;
21296ae6ea0SThomas Gleixner case VIDEO_80x30:
21396ae6ea0SThomas Gleixner vga_set_80x30();
21496ae6ea0SThomas Gleixner break;
21596ae6ea0SThomas Gleixner case VIDEO_80x34:
21696ae6ea0SThomas Gleixner vga_set_80x34();
21796ae6ea0SThomas Gleixner break;
21896ae6ea0SThomas Gleixner case VIDEO_80x60:
21996ae6ea0SThomas Gleixner vga_set_80x60();
22096ae6ea0SThomas Gleixner break;
22196ae6ea0SThomas Gleixner }
22296ae6ea0SThomas Gleixner
22396ae6ea0SThomas Gleixner return 0;
22496ae6ea0SThomas Gleixner }
22596ae6ea0SThomas Gleixner
22696ae6ea0SThomas Gleixner /*
22796ae6ea0SThomas Gleixner * Note: this probe includes basic information required by all
22896ae6ea0SThomas Gleixner * systems. It should be executed first, by making sure
22996ae6ea0SThomas Gleixner * video-vga.c is listed first in the Makefile.
23096ae6ea0SThomas Gleixner */
vga_probe(void)23196ae6ea0SThomas Gleixner static int vga_probe(void)
23296ae6ea0SThomas Gleixner {
23396ae6ea0SThomas Gleixner static const char *card_name[] = {
23496ae6ea0SThomas Gleixner "CGA/MDA/HGC", "EGA", "VGA"
23596ae6ea0SThomas Gleixner };
23696ae6ea0SThomas Gleixner static struct mode_info *mode_lists[] = {
23796ae6ea0SThomas Gleixner cga_modes,
23896ae6ea0SThomas Gleixner ega_modes,
23996ae6ea0SThomas Gleixner vga_modes,
24096ae6ea0SThomas Gleixner };
24196ae6ea0SThomas Gleixner static int mode_count[] = {
2420cfe5b5fSJérémy Lefaure ARRAY_SIZE(cga_modes),
2430cfe5b5fSJérémy Lefaure ARRAY_SIZE(ega_modes),
2440cfe5b5fSJérémy Lefaure ARRAY_SIZE(vga_modes),
24596ae6ea0SThomas Gleixner };
24696ae6ea0SThomas Gleixner
247cf06de7bSH. Peter Anvin struct biosregs ireg, oreg;
248cf06de7bSH. Peter Anvin
249cf06de7bSH. Peter Anvin initregs(&ireg);
250cf06de7bSH. Peter Anvin
251cf06de7bSH. Peter Anvin ireg.ax = 0x1200;
252cf06de7bSH. Peter Anvin ireg.bl = 0x10; /* Check EGA/VGA */
253cf06de7bSH. Peter Anvin intcall(0x10, &ireg, &oreg);
25496ae6ea0SThomas Gleixner
255e44b7b75SPavel Machek #ifndef _WAKEUP
256cf06de7bSH. Peter Anvin boot_params.screen_info.orig_video_ega_bx = oreg.bx;
257e44b7b75SPavel Machek #endif
258e44b7b75SPavel Machek
25996ae6ea0SThomas Gleixner /* If we have MDA/CGA/HGC then BL will be unchanged at 0x10 */
260cf06de7bSH. Peter Anvin if (oreg.bl != 0x10) {
26196ae6ea0SThomas Gleixner /* EGA/VGA */
262cf06de7bSH. Peter Anvin ireg.ax = 0x1a00;
263cf06de7bSH. Peter Anvin intcall(0x10, &ireg, &oreg);
26496ae6ea0SThomas Gleixner
265cf06de7bSH. Peter Anvin if (oreg.al == 0x1a) {
26696ae6ea0SThomas Gleixner adapter = ADAPTER_VGA;
267e44b7b75SPavel Machek #ifndef _WAKEUP
26896ae6ea0SThomas Gleixner boot_params.screen_info.orig_video_isVGA = 1;
269e44b7b75SPavel Machek #endif
27096ae6ea0SThomas Gleixner } else {
27196ae6ea0SThomas Gleixner adapter = ADAPTER_EGA;
27296ae6ea0SThomas Gleixner }
27396ae6ea0SThomas Gleixner } else {
27496ae6ea0SThomas Gleixner adapter = ADAPTER_CGA;
27596ae6ea0SThomas Gleixner }
27696ae6ea0SThomas Gleixner
27796ae6ea0SThomas Gleixner video_vga.modes = mode_lists[adapter];
27896ae6ea0SThomas Gleixner video_vga.card_name = card_name[adapter];
27996ae6ea0SThomas Gleixner return mode_count[adapter];
28096ae6ea0SThomas Gleixner }
28196ae6ea0SThomas Gleixner
282a1a00b58SHannes Eder static __videocard video_vga = {
28396ae6ea0SThomas Gleixner .card_name = "VGA",
28496ae6ea0SThomas Gleixner .probe = vga_probe,
28596ae6ea0SThomas Gleixner .set_mode = vga_set_mode,
28696ae6ea0SThomas Gleixner };
287