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