162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* -*- linux-c -*- ------------------------------------------------------- * 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 562306a36Sopenharmony_ci * Copyright 2007-2008 rPath, Inc. - All Rights Reserved 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * ----------------------------------------------------------------------- */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * arch/i386/boot/video-mode.c 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Set the video mode. This is separated out into a different 1362306a36Sopenharmony_ci * file in order to be shared with the ACPI wakeup code. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "boot.h" 1762306a36Sopenharmony_ci#include "video.h" 1862306a36Sopenharmony_ci#include "vesa.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <uapi/asm/boot.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * Common variables 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ciint adapter; /* 0=CGA/MDA/HGC, 1=EGA, 2=VGA+ */ 2662306a36Sopenharmony_ciint force_x, force_y; /* Don't query the BIOS for cols/rows */ 2762306a36Sopenharmony_ciint do_restore; /* Screen contents changed during mode flip */ 2862306a36Sopenharmony_ciint graphic_mode; /* Graphic mode with linear frame buffer */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Probe the video drivers and have them generate their mode lists. */ 3162306a36Sopenharmony_civoid probe_cards(int unsafe) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct card_info *card; 3462306a36Sopenharmony_ci static u8 probed[2]; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (probed[unsafe]) 3762306a36Sopenharmony_ci return; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci probed[unsafe] = 1; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci for (card = video_cards; card < video_cards_end; card++) { 4262306a36Sopenharmony_ci if (card->unsafe == unsafe) { 4362306a36Sopenharmony_ci if (card->probe) 4462306a36Sopenharmony_ci card->nmodes = card->probe(); 4562306a36Sopenharmony_ci else 4662306a36Sopenharmony_ci card->nmodes = 0; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Test if a mode is defined */ 5262306a36Sopenharmony_ciint mode_defined(u16 mode) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct card_info *card; 5562306a36Sopenharmony_ci struct mode_info *mi; 5662306a36Sopenharmony_ci int i; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci for (card = video_cards; card < video_cards_end; card++) { 5962306a36Sopenharmony_ci mi = card->modes; 6062306a36Sopenharmony_ci for (i = 0; i < card->nmodes; i++, mi++) { 6162306a36Sopenharmony_ci if (mi->mode == mode) 6262306a36Sopenharmony_ci return 1; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* Set mode (without recalc) */ 7062306a36Sopenharmony_cistatic int raw_set_mode(u16 mode, u16 *real_mode) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci int nmode, i; 7362306a36Sopenharmony_ci struct card_info *card; 7462306a36Sopenharmony_ci struct mode_info *mi; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Drop the recalc bit if set */ 7762306a36Sopenharmony_ci mode &= ~VIDEO_RECALC; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Scan for mode based on fixed ID, position, or resolution */ 8062306a36Sopenharmony_ci nmode = 0; 8162306a36Sopenharmony_ci for (card = video_cards; card < video_cards_end; card++) { 8262306a36Sopenharmony_ci mi = card->modes; 8362306a36Sopenharmony_ci for (i = 0; i < card->nmodes; i++, mi++) { 8462306a36Sopenharmony_ci int visible = mi->x || mi->y; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if ((mode == nmode && visible) || 8762306a36Sopenharmony_ci mode == mi->mode || 8862306a36Sopenharmony_ci mode == (mi->y << 8)+mi->x) { 8962306a36Sopenharmony_ci *real_mode = mi->mode; 9062306a36Sopenharmony_ci return card->set_mode(mi); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (visible) 9462306a36Sopenharmony_ci nmode++; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Nothing found? Is it an "exceptional" (unprobed) mode? */ 9962306a36Sopenharmony_ci for (card = video_cards; card < video_cards_end; card++) { 10062306a36Sopenharmony_ci if (mode >= card->xmode_first && 10162306a36Sopenharmony_ci mode < card->xmode_first+card->xmode_n) { 10262306a36Sopenharmony_ci struct mode_info mix; 10362306a36Sopenharmony_ci *real_mode = mix.mode = mode; 10462306a36Sopenharmony_ci mix.x = mix.y = 0; 10562306a36Sopenharmony_ci return card->set_mode(&mix); 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Otherwise, failure... */ 11062306a36Sopenharmony_ci return -1; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* 11462306a36Sopenharmony_ci * Recalculate the vertical video cutoff (hack!) 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistatic void vga_recalc_vertical(void) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci unsigned int font_size, rows; 11962306a36Sopenharmony_ci u16 crtc; 12062306a36Sopenharmony_ci u8 pt, ov; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci set_fs(0); 12362306a36Sopenharmony_ci font_size = rdfs8(0x485); /* BIOS: font size (pixels) */ 12462306a36Sopenharmony_ci rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci rows *= font_size; /* Visible scan lines */ 12762306a36Sopenharmony_ci rows--; /* ... minus one */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci crtc = vga_crtc(); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci pt = in_idx(crtc, 0x11); 13262306a36Sopenharmony_ci pt &= ~0x80; /* Unlock CR0-7 */ 13362306a36Sopenharmony_ci out_idx(pt, crtc, 0x11); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci out_idx((u8)rows, crtc, 0x12); /* Lower height register */ 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ov = in_idx(crtc, 0x07); /* Overflow register */ 13862306a36Sopenharmony_ci ov &= 0xbd; 13962306a36Sopenharmony_ci ov |= (rows >> (8-1)) & 0x02; 14062306a36Sopenharmony_ci ov |= (rows >> (9-6)) & 0x40; 14162306a36Sopenharmony_ci out_idx(ov, crtc, 0x07); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* Set mode (with recalc if specified) */ 14562306a36Sopenharmony_ciint set_mode(u16 mode) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci int rv; 14862306a36Sopenharmony_ci u16 real_mode; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Very special mode numbers... */ 15162306a36Sopenharmony_ci if (mode == VIDEO_CURRENT_MODE) 15262306a36Sopenharmony_ci return 0; /* Nothing to do... */ 15362306a36Sopenharmony_ci else if (mode == NORMAL_VGA) 15462306a36Sopenharmony_ci mode = VIDEO_80x25; 15562306a36Sopenharmony_ci else if (mode == EXTENDED_VGA) 15662306a36Sopenharmony_ci mode = VIDEO_8POINT; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci rv = raw_set_mode(mode, &real_mode); 15962306a36Sopenharmony_ci if (rv) 16062306a36Sopenharmony_ci return rv; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (mode & VIDEO_RECALC) 16362306a36Sopenharmony_ci vga_recalc_vertical(); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* Save the canonical mode number for the kernel, not 16662306a36Sopenharmony_ci an alias, size specification or menu position */ 16762306a36Sopenharmony_ci#ifndef _WAKEUP 16862306a36Sopenharmony_ci boot_params.hdr.vid_mode = real_mode; 16962306a36Sopenharmony_ci#endif 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 172