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. */
probe_cards(int unsafe)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 */
mode_defined(u16 mode)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) */
raw_set_mode(u16 mode,u16 * real_mode)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   */
vga_recalc_vertical(void)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(0x485); /* BIOS: font size (pixels) */
124  	rows = force_y ? force_y : rdfs8(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(crtc, 0x11);
132  	pt &= ~0x80;		/* Unlock CR0-7 */
133  	out_idx(pt, crtc, 0x11);
134  
135  	out_idx((u8)rows, crtc, 0x12); /* Lower height register */
136  
137  	ov = in_idx(crtc, 0x07); /* Overflow register */
138  	ov &= 0xbd;
139  	ov |= (rows >> (8-1)) & 0x02;
140  	ov |= (rows >> (9-6)) & 0x40;
141  	out_idx(ov, crtc, 0x07);
142  }
143  
144  /* Set mode (with recalc if specified) */
set_mode(u16 mode)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);
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