xref: /openbmc/linux/arch/x86/boot/video-vga.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
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