Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5e8ddcbe authored by H. Peter Anvin's avatar H. Peter Anvin Committed by Linus Torvalds
Browse files

Video mode probing support for the new x86 setup code



Video mode probing for the new x86 setup code.  This code breaks down
different drivers into modules.  This code deliberately drops support
for a lot of the vendor-specific mode probing present in the assembly
version, since a lot of those probes have been found to be stale in
current versions of those chips -- frequently, support for those modes
have been dropped from recent video BIOSes due to space constraints,
but the video BIOS signatures are still the same.

However, additional drivers should be extremely straightforward to plug
in, if desirable.

Signed-off-by: default avatarH. Peter Anvin <hpa@zytor.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 337496eb
Loading
Loading
Loading
Loading

arch/i386/boot/vesa.h

0 → 100644
+79 −0
Original line number Diff line number Diff line
/* ----------------------------------------------------------------------- *
 *
 *   Copyright 1999-2007 H. Peter Anvin - All Rights Reserved
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
 *   Boston MA 02111-1307, USA; either version 2 of the License, or
 *   (at your option) any later version; incorporated herein by reference.
 *
 * ----------------------------------------------------------------------- */

#ifndef BOOT_VESA_H
#define BOOT_VESA_H

typedef struct {
	u16 off, seg;
} far_ptr;

/* VESA General Information table */
struct vesa_general_info {
	u32 signature;		/* 0 Magic number = "VESA" */
	u16 version;		/* 4 */
	far_ptr vendor_string;	/* 6 */
	u32 capabilities;	/* 10 */
	far_ptr video_mode_ptr;	/* 14 */
	u16 total_memory;	/* 18 */

	u16 oem_software_rev;	/* 20 */
	far_ptr oem_vendor_name_ptr;	/* 22 */
	far_ptr oem_product_name_ptr;	/* 26 */
	far_ptr oem_product_rev_ptr;	/* 30 */

	u8 reserved[222];	/* 34 */
	u8 oem_data[256];	/* 256 */
} __attribute__ ((packed));

#define VESA_MAGIC ('V' + ('E' << 8) + ('S' << 16) + ('A' << 24))
#define VBE2_MAGIC ('V' + ('B' << 8) + ('E' << 16) + ('2' << 24))

struct vesa_mode_info {
	u16 mode_attr;		/* 0 */
	u8 win_attr[2];		/* 2 */
	u16 win_grain;		/* 4 */
	u16 win_size;		/* 6 */
	u16 win_seg[2];		/* 8 */
	far_ptr win_scheme;	/* 12 */
	u16 logical_scan;	/* 16 */

	u16 h_res;		/* 18 */
	u16 v_res;		/* 20 */
	u8 char_width;		/* 22 */
	u8 char_height;		/* 23 */
	u8 memory_planes;	/* 24 */
	u8 bpp;			/* 25 */
	u8 banks;		/* 26 */
	u8 memory_layout;	/* 27 */
	u8 bank_size;		/* 28 */
	u8 image_planes;	/* 29 */
	u8 page_function;	/* 30 */

	u8 rmask;		/* 31 */
	u8 rpos;		/* 32 */
	u8 gmask;		/* 33 */
	u8 gpos;		/* 34 */
	u8 bmask;		/* 35 */
	u8 bpos;		/* 36 */
	u8 resv_mask;		/* 37 */
	u8 resv_pos;		/* 38 */
	u8 dcm_info;		/* 39 */

	u32 lfb_ptr;		/* 40 Linear frame buffer address */
	u32 offscreen_ptr;	/* 44 Offscreen memory address */
	u16 offscreen_size;	/* 48 */

	u8 reserved[206];	/* 50 */
} __attribute__ ((packed));

#endif				/* LIB_SYS_VESA_H */
+125 −0
Original line number Diff line number Diff line
/* -*- linux-c -*- ------------------------------------------------------- *
 *
 *   Copyright (C) 1991, 1992 Linus Torvalds
 *   Copyright 2007 rPath, Inc. - All Rights Reserved
 *
 *   This file is part of the Linux kernel, and is made available under
 *   the terms of the GNU General Public License version 2.
 *
 * ----------------------------------------------------------------------- */

/*
 * arch/i386/boot/video-bios.c
 *
 * Standard video BIOS modes
 *
 * We have two options for this; silent and scanned.
 */

#include "boot.h"
#include "video.h"

__videocard video_bios;

/* Set a conventional BIOS mode */
static int set_bios_mode(u8 mode);

static int bios_set_mode(struct mode_info *mi)
{
	return set_bios_mode(mi->mode - VIDEO_FIRST_BIOS);
}

static int set_bios_mode(u8 mode)
{
	u16 ax;
	u8 new_mode;

	ax = mode;		/* AH=0x00 Set Video Mode */
	asm volatile(INT10
		     : "+a" (ax)
		     : : "ebx", "ecx", "edx", "esi", "edi");

	ax = 0x0f00;		/* Get Current Video Mode */
	asm volatile(INT10
		     : "+a" (ax)
		     : : "ebx", "ecx", "edx", "esi", "edi");

	do_restore = 1;		/* Assume video contents was lost */
	new_mode = ax & 0x7f;	/* Not all BIOSes are clean with the top bit */

	if (new_mode == mode)
		return 0;	/* Mode change OK */

	if (new_mode != boot_params.screen_info.orig_video_mode) {
		/* Mode setting failed, but we didn't end up where we
		   started.  That's bad.  Try to revert to the original
		   video mode. */
		ax = boot_params.screen_info.orig_video_mode;
		asm volatile(INT10
			     : "+a" (ax)
			     : : "ebx", "ecx", "edx", "esi", "edi");
	}
	return -1;
}

static int bios_probe(void)
{
	u8 mode;
	u8 saved_mode = boot_params.screen_info.orig_video_mode;
	u16 crtc;
	struct mode_info *mi;
	int nmodes = 0;

	if (adapter != ADAPTER_EGA && adapter != ADAPTER_VGA)
		return 0;

	set_fs(0);
	crtc = vga_crtc();

	video_bios.modes = GET_HEAP(struct mode_info, 0);

	for (mode = 0x14; mode <= 0x7f; mode++) {
		if (heap_free() < sizeof(struct mode_info))
			break;

		if (mode_defined(VIDEO_FIRST_BIOS+mode))
			continue;

		if (set_bios_mode(mode))
			continue;

		/* Try to verify that it's a text mode. */

		/* Attribute Controller: make graphics controller disabled */
		if (in_idx(0x3c0, 0x10) & 0x01)
			continue;

		/* Graphics Controller: verify Alpha addressing enabled */
		if (in_idx(0x3ce, 0x06) & 0x01)
			continue;

		/* CRTC cursor location low should be zero(?) */
		if (in_idx(crtc, 0x0f))
			continue;

		mi = GET_HEAP(struct mode_info, 1);
		mi->mode = VIDEO_FIRST_BIOS+mode;
		mi->x = rdfs16(0x44a);
		mi->y = rdfs8(0x484)+1;
		nmodes++;
	}

	set_bios_mode(saved_mode);

	return nmodes;
}

__videocard video_bios =
{
	.card_name	= "BIOS (scanned)",
	.probe		= bios_probe,
	.set_mode	= bios_set_mode,
	.unsafe		= 1,
	.xmode_first	= VIDEO_FIRST_BIOS,
	.xmode_n	= 0x80,
};
+284 −0
Original line number Diff line number Diff line
/* -*- linux-c -*- ------------------------------------------------------- *
 *
 *   Copyright (C) 1991, 1992 Linus Torvalds
 *   Copyright 2007 rPath, Inc. - All Rights Reserved
 *
 *   This file is part of the Linux kernel, and is made available under
 *   the terms of the GNU General Public License version 2.
 *
 * ----------------------------------------------------------------------- */

/*
 * arch/i386/boot/video-vesa.c
 *
 * VESA text modes
 */

#include "boot.h"
#include "video.h"
#include "vesa.h"

/* VESA information */
static struct vesa_general_info vginfo;
static struct vesa_mode_info vminfo;

__videocard video_vesa;

static void vesa_store_mode_params_graphics(void);

static int vesa_probe(void)
{
#if defined(CONFIG_VIDEO_VESA) || defined(CONFIG_FIRMWARE_EDID)
	u16 ax;
	u16 mode;
	addr_t mode_ptr;
	struct mode_info *mi;
	int nmodes = 0;

	video_vesa.modes = GET_HEAP(struct mode_info, 0);

	vginfo.signature = VBE2_MAGIC;

	/* Optimistically assume a VESA BIOS is register-clean... */
	ax = 0x4f00;
	asm("int $0x10" : "+a" (ax), "=m" (vginfo) : "D" (&vginfo));

	if (ax != 0x004f ||
	    vginfo.signature != VESA_MAGIC ||
	    vginfo.version < 0x0102)
		return 0;	/* Not present */
#endif /* CONFIG_VIDEO_VESA || CONFIG_FIRMWARE_EDID */
#ifdef CONFIG_VIDEO_VESA
	set_fs(vginfo.video_mode_ptr.seg);
	mode_ptr = vginfo.video_mode_ptr.off;

	while ((mode = rdfs16(mode_ptr)) != 0xffff) {
		mode_ptr += 2;

		if (heap_free() < sizeof(struct mode_info))
			break;	/* Heap full, can't save mode info */

		if (mode & ~0x1ff)
			continue;

		memset(&vminfo, 0, sizeof vminfo); /* Just in case... */

		ax = 0x4f01;
		asm("int $0x10"
		    : "+a" (ax), "=m" (vminfo)
		    : "c" (mode), "D" (&vminfo));

		if (ax != 0x004f)
			continue;

		if ((vminfo.mode_attr & 0x15) == 0x05) {
			/* Text Mode, TTY BIOS supported,
			   supported by hardware */
			mi = GET_HEAP(struct mode_info, 1);
			mi->mode = mode + VIDEO_FIRST_VESA;
			mi->x    = vminfo.h_res;
			mi->y    = vminfo.v_res;
			nmodes++;
		} else if ((vminfo.mode_attr & 0x99) == 0x99) {
#ifdef CONFIG_FB
			/* Graphics mode, color, linear frame buffer
			   supported -- register the mode but hide from
			   the menu.  Only do this if framebuffer is
			   configured, however, otherwise the user will
			   be left without a screen. */
			mi = GET_HEAP(struct mode_info, 1);
			mi->mode = mode + VIDEO_FIRST_VESA;
			mi->x = mi->y = 0;
			nmodes++;
#endif
		}
	}

	return nmodes;
#else
	return 0;
#endif /* CONFIG_VIDEO_VESA */
}

static int vesa_set_mode(struct mode_info *mode)
{
	u16 ax;
	int is_graphic;
	u16 vesa_mode = mode->mode - VIDEO_FIRST_VESA;

	memset(&vminfo, 0, sizeof vminfo); /* Just in case... */

	ax = 0x4f01;
	asm("int $0x10"
	    : "+a" (ax), "=m" (vminfo)
	    : "c" (vesa_mode), "D" (&vminfo));

	if (ax != 0x004f)
		return -1;

	if ((vminfo.mode_attr & 0x15) == 0x05) {
		/* It's a supported text mode */
		is_graphic = 0;
	} else if ((vminfo.mode_attr & 0x99) == 0x99) {
		/* It's a graphics mode with linear frame buffer */
		is_graphic = 1;
		vesa_mode |= 0x4000; /* Request linear frame buffer */
	} else {
		return -1;	/* Invalid mode */
	}


	ax = 0x4f02;
	asm volatile("int $0x10"
		     : "+a" (ax)
		     : "b" (vesa_mode), "D" (0));

	if (ax != 0x004f)
		return -1;

	graphic_mode = is_graphic;
	if (!is_graphic) {
		/* Text mode */
		force_x = mode->x;
		force_y = mode->y;
		do_restore = 1;
	} else {
		/* Graphics mode */
		vesa_store_mode_params_graphics();
	}

	return 0;
}


/* Switch DAC to 8-bit mode */
static void vesa_dac_set_8bits(void)
{
	u8 dac_size = 6;

	/* If possible, switch the DAC to 8-bit mode */
	if (vginfo.capabilities & 1) {
		u16 ax, bx;

		ax = 0x4f08;
		bx = 0x0800;
		asm volatile(INT10
			     : "+a" (ax), "+b" (bx)
			     : : "ecx", "edx", "esi", "edi");

		if (ax == 0x004f)
			dac_size = bx >> 8;
	}

	/* Set the color sizes to the DAC size, and offsets to 0 */
	boot_params.screen_info.red_size = dac_size;
	boot_params.screen_info.green_size = dac_size;
	boot_params.screen_info.blue_size = dac_size;
	boot_params.screen_info.rsvd_size = dac_size;

	boot_params.screen_info.red_pos = 0;
	boot_params.screen_info.green_pos = 0;
	boot_params.screen_info.blue_pos = 0;
	boot_params.screen_info.rsvd_pos = 0;
}

/* Save the VESA protected mode info */
static void vesa_store_pm_info(void)
{
	u16 ax, bx, di, es;

	ax = 0x4f0a;
	bx = di = 0;
	asm("pushw %%es; "INT10"; movw %%es,%0; popw %%es"
	    : "=d" (es), "+a" (ax), "+b" (bx), "+D" (di)
	    : : "ecx", "esi");

	if (ax != 0x004f)
		return;

	boot_params.screen_info.vesapm_seg = es;
	boot_params.screen_info.vesapm_off = di;
}

/*
 * Save video mode parameters for graphics mode
 */
static void vesa_store_mode_params_graphics(void)
{
	/* Tell the kernel we're in VESA graphics mode */
	boot_params.screen_info.orig_video_isVGA = 0x23;

	/* Mode parameters */
	boot_params.screen_info.vesa_attributes = vminfo.mode_attr;
	boot_params.screen_info.lfb_linelength = vminfo.logical_scan;
	boot_params.screen_info.lfb_width = vminfo.h_res;
	boot_params.screen_info.lfb_height = vminfo.v_res;
	boot_params.screen_info.lfb_depth = vminfo.bpp;
	boot_params.screen_info.pages = vminfo.image_planes;
	boot_params.screen_info.lfb_base = vminfo.lfb_ptr;
	memcpy(&boot_params.screen_info.red_size,
	       &vminfo.rmask, 8);

	/* General parameters */
	boot_params.screen_info.lfb_size = vginfo.total_memory;

	if (vminfo.bpp <= 8)
		vesa_dac_set_8bits();

	vesa_store_pm_info();
}

/*
 * Save EDID information for the kernel; this is invoked, separately,
 * after mode-setting.
 */
void vesa_store_edid(void)
{
#ifdef CONFIG_FIRMWARE_EDID
	u16 ax, bx, cx, dx, di;

	/* Apparently used as a nonsense token... */
	memset(&boot_params.edid_info, 0x13, sizeof boot_params.edid_info);

	if (vginfo.version < 0x0200)
		return;		/* EDID requires VBE 2.0+ */

	ax = 0x4f15;		/* VBE DDC */
	bx = 0x0000;		/* Report DDC capabilities */
	cx = 0;			/* Controller 0 */
	di = 0;			/* ES:DI must be 0 by spec */

	/* Note: The VBE DDC spec is different from the main VESA spec;
	   we genuinely have to assume all registers are destroyed here. */

	asm("pushw %%es; movw %2,%%es; "INT10"; popw %%es"
	    : "+a" (ax), "+b" (bx)
	    :  "c" (cx), "D" (di)
	    : "esi");

	if (ax != 0x004f)
		return;		/* No EDID */

	/* BH = time in seconds to transfer EDD information */
	/* BL = DDC level supported */

	ax = 0x4f15;		/* VBE DDC */
	bx = 0x0001;		/* Read EDID */
	cx = 0;			/* Controller 0 */
	dx = 0;			/* EDID block number */
	di =(size_t) &boot_params.edid_info; /* (ES:)Pointer to block */
	asm(INT10
	    : "+a" (ax), "+b" (bx), "+d" (dx)
	    : "c" (cx), "D" (di)
	    : "esi");
#endif /* CONFIG_FIRMWARE_EDID */
}

__videocard video_vesa =
{
	.card_name	= "VESA",
	.probe		= vesa_probe,
	.set_mode	= vesa_set_mode,
	.xmode_first	= VIDEO_FIRST_VESA,
	.xmode_n	= 0x200,
};
+260 −0
Original line number Diff line number Diff line
/* -*- linux-c -*- ------------------------------------------------------- *
 *
 *   Copyright (C) 1991, 1992 Linus Torvalds
 *   Copyright 2007 rPath, Inc. - All Rights Reserved
 *
 *   This file is part of the Linux kernel, and is made available under
 *   the terms of the GNU General Public License version 2.
 *
 * ----------------------------------------------------------------------- */

/*
 * arch/i386/boot/video-vga.c
 *
 * Common all-VGA modes
 */

#include "boot.h"
#include "video.h"

static struct mode_info vga_modes[] = {
	{ VIDEO_80x25,  80, 25 },
	{ VIDEO_8POINT, 80, 50 },
	{ VIDEO_80x43,  80, 43 },
	{ VIDEO_80x28,  80, 28 },
	{ VIDEO_80x30,  80, 30 },
	{ VIDEO_80x34,  80, 34 },
	{ VIDEO_80x60,  80, 60 },
};

static struct mode_info ega_modes[] = {
	{ VIDEO_80x25,  80, 25 },
	{ VIDEO_8POINT, 80, 43 },
};

static struct mode_info cga_modes[] = {
	{ VIDEO_80x25,  80, 25 },
};

__videocard video_vga;

/* Set basic 80x25 mode */
static u8 vga_set_basic_mode(void)
{
	u16 ax;
	u8 rows;
	u8 mode;

#ifdef CONFIG_VIDEO_400_HACK
	if (adapter >= ADAPTER_VGA) {
		asm(INT10
		    : : "a" (0x1202), "b" (0x0030)
		    : "ecx", "edx", "esi", "edi");
	}
#endif

	ax = 0x0f00;
	asm(INT10
	    : "+a" (ax)
	    : : "ebx", "ecx", "edx", "esi", "edi");

	mode = (u8)ax;

	set_fs(0);
	rows = rdfs8(0x484);	/* rows minus one */

#ifndef CONFIG_VIDEO_400_HACK
	if ((ax == 0x5003 || ax == 0x5007) &&
	    (rows == 0 || rows == 24))
		return mode;
#endif

	if (mode != 3 && mode != 7)
		mode = 3;

	/* Set the mode */
	asm volatile(INT10
		     : : "a" (mode)
		     : "ebx", "ecx", "edx", "esi", "edi");
	do_restore = 1;
	return mode;
}

static void vga_set_8font(void)
{
	/* Set 8x8 font - 80x43 on EGA, 80x50 on VGA */

	/* Set 8x8 font */
	asm volatile(INT10 : : "a" (0x1112), "b" (0));

	/* Use alternate print screen */
	asm volatile(INT10 : : "a" (0x1200), "b" (0x20));

	/* Turn off cursor emulation */
	asm volatile(INT10 : : "a" (0x1201), "b" (0x34));

	/* Cursor is scan lines 6-7 */
	asm volatile(INT10 : : "a" (0x0100), "c" (0x0607));
}

static void vga_set_14font(void)
{
	/* Set 9x14 font - 80x28 on VGA */

	/* Set 9x14 font */
	asm volatile(INT10 : : "a" (0x1111), "b" (0));

	/* Turn off cursor emulation */
	asm volatile(INT10 : : "a" (0x1201), "b" (0x34));

	/* Cursor is scan lines 11-12 */
	asm volatile(INT10 : : "a" (0x0100), "c" (0x0b0c));
}

static void vga_set_80x43(void)
{
	/* Set 80x43 mode on VGA (not EGA) */

	/* Set 350 scans */
	asm volatile(INT10 : : "a" (0x1201), "b" (0x30));

	/* Reset video mode */
	asm volatile(INT10 : : "a" (0x0003));

	vga_set_8font();
}

/* I/O address of the VGA CRTC */
u16 vga_crtc(void)
{
	return (inb(0x3cc) & 1) ? 0x3d4 : 0x3b4;
}

static void vga_set_480_scanlines(int end)
{
	u16 crtc;
	u8  csel;

	crtc = vga_crtc();

	out_idx(0x0c, crtc, 0x11); /* Vertical sync end, unlock CR0-7 */
	out_idx(0x0b, crtc, 0x06); /* Vertical total */
	out_idx(0x3e, crtc, 0x07); /* Vertical overflow */
	out_idx(0xea, crtc, 0x10); /* Vertical sync start */
	out_idx(end, crtc, 0x12); /* Vertical display end */
	out_idx(0xe7, crtc, 0x15); /* Vertical blank start */
	out_idx(0x04, crtc, 0x16); /* Vertical blank end */
	csel = inb(0x3cc);
	csel &= 0x0d;
	csel |= 0xe2;
	outb(csel, 0x3cc);
}

static void vga_set_80x30(void)
{
	vga_set_480_scanlines(0xdf);
}

static void vga_set_80x34(void)
{
	vga_set_14font();
	vga_set_480_scanlines(0xdb);
}

static void vga_set_80x60(void)
{
	vga_set_8font();
	vga_set_480_scanlines(0xdf);
}

static int vga_set_mode(struct mode_info *mode)
{
	/* Set the basic mode */
	vga_set_basic_mode();

	/* Override a possibly broken BIOS */
	force_x = mode->x;
	force_y = mode->y;

	switch (mode->mode) {
	case VIDEO_80x25:
		break;
	case VIDEO_8POINT:
		vga_set_8font();
		break;
	case VIDEO_80x43:
		vga_set_80x43();
		break;
	case VIDEO_80x28:
		vga_set_14font();
		break;
	case VIDEO_80x30:
		vga_set_80x30();
		break;
	case VIDEO_80x34:
		vga_set_80x34();
		break;
	case VIDEO_80x60:
		vga_set_80x60();
		break;
	}

	return 0;
}

/*
 * Note: this probe includes basic information required by all
 * systems.  It should be executed first, by making sure
 * video-vga.c is listed first in the Makefile.
 */
static int vga_probe(void)
{
	static const char *card_name[] = {
		"CGA/MDA/HGC", "EGA", "VGA"
	};
	static struct mode_info *mode_lists[] = {
		cga_modes,
		ega_modes,
		vga_modes,
	};
	static int mode_count[] = {
		sizeof(cga_modes)/sizeof(struct mode_info),
		sizeof(ega_modes)/sizeof(struct mode_info),
		sizeof(vga_modes)/sizeof(struct mode_info),
	};
	u8 vga_flag;

	asm(INT10
	    : "=b" (boot_params.screen_info.orig_video_ega_bx)
	    : "a" (0x1200), "b" (0x10) /* Check EGA/VGA */
	    : "ecx", "edx", "esi", "edi");

	/* If we have MDA/CGA/HGC then BL will be unchanged at 0x10 */
	if ((u8)boot_params.screen_info.orig_video_ega_bx != 0x10) {
		/* EGA/VGA */
		asm(INT10
		    : "=a" (vga_flag)
		    : "a" (0x1a00)
		    : "ebx", "ecx", "edx", "esi", "edi");

		if (vga_flag == 0x1a) {
			adapter = ADAPTER_VGA;
			boot_params.screen_info.orig_video_isVGA = 1;
		} else {
			adapter = ADAPTER_EGA;
		}
	} else {
		adapter = ADAPTER_CGA;
	}

	video_vga.modes = mode_lists[adapter];
	video_vga.card_name = card_name[adapter];
	return mode_count[adapter];
}

__videocard video_vga =
{
	.card_name	= "VGA",
	.probe		= vga_probe,
	.set_mode	= vga_set_mode,
};

arch/i386/boot/video.c

0 → 100644
+456 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading