xref: /illumos-gate/usr/src/boot/i386/libi386/vbe.c (revision 22028508)
19890ff83SToomas Soome /*
29890ff83SToomas Soome  * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
39890ff83SToomas Soome  * All rights reserved.
49890ff83SToomas Soome  *
59890ff83SToomas Soome  * Redistribution and use in source and binary forms, with or without
69890ff83SToomas Soome  * modification, are permitted provided that the following conditions
79890ff83SToomas Soome  * are met:
89890ff83SToomas Soome  * 1. Redistributions of source code must retain the above copyright
99890ff83SToomas Soome  *    notice, this list of conditions and the following disclaimer.
109890ff83SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
119890ff83SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
129890ff83SToomas Soome  *    documentation and/or other materials provided with the distribution.
139890ff83SToomas Soome  *
149890ff83SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
159890ff83SToomas Soome  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
169890ff83SToomas Soome  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
179890ff83SToomas Soome  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
189890ff83SToomas Soome  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
199890ff83SToomas Soome  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
209890ff83SToomas Soome  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
219890ff83SToomas Soome  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
229890ff83SToomas Soome  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
239890ff83SToomas Soome  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
249890ff83SToomas Soome  * POSSIBILITY OF SUCH DAMAGE.
259890ff83SToomas Soome  */
269890ff83SToomas Soome 
279890ff83SToomas Soome /*
289890ff83SToomas Soome  * VESA BIOS Extensions routines
299890ff83SToomas Soome  */
309890ff83SToomas Soome 
319890ff83SToomas Soome #include <stand.h>
329890ff83SToomas Soome #include <stdbool.h>
339890ff83SToomas Soome #include <bootstrap.h>
349890ff83SToomas Soome #include <machine/bootinfo.h>
359890ff83SToomas Soome #include <machine/metadata.h>
369890ff83SToomas Soome #include <sys/multiboot2.h>
379890ff83SToomas Soome #include <btxv86.h>
389890ff83SToomas Soome #include "libi386.h"
399890ff83SToomas Soome #include "gfx_fb.h"	/* for EDID */
409890ff83SToomas Soome #include "vbe.h"
41337411fcSToomas Soome #include <sys/font.h>
42fa9eb222SToomas Soome #include <sys/rgb.h>
439890ff83SToomas Soome #include <sys/vgareg.h>
449890ff83SToomas Soome #include <sys/vgasubr.h>
459890ff83SToomas Soome 
469890ff83SToomas Soome multiboot_tag_vbe_t vbestate;
479890ff83SToomas Soome static struct vbeinfoblock *vbe =
488d2ce326SToomas Soome 	(struct vbeinfoblock *)&vbestate.vbe_control_info;
499890ff83SToomas Soome static struct modeinfoblock *vbe_mode =
508d2ce326SToomas Soome 	(struct modeinfoblock *)&vbestate.vbe_mode_info;
517449d372SToomas Soome static uint16_t *vbe_mode_list;
527449d372SToomas Soome static size_t vbe_mode_list_size;
531b1c4b08SToomas Soome struct vesa_edid_info *edid_info = NULL;
5491061836SToomas Soome multiboot_color_t *cmap;
5591061836SToomas Soome /* The default VGA color palette format is 6 bits per primary color. */
5691061836SToomas Soome int palette_format = 6;
579890ff83SToomas Soome 
587449d372SToomas Soome #define	VESA_MODE_BASE		0x100
597449d372SToomas Soome #define	VESA_END_OF_MODE_LIST	0xffff
60d20422bdSToomas Soome 
619890ff83SToomas Soome /* Actually assuming mode 3. */
629890ff83SToomas Soome void
bios_set_text_mode(int mode)639890ff83SToomas Soome bios_set_text_mode(int mode)
649890ff83SToomas Soome {
659890ff83SToomas Soome 	int atr;
669890ff83SToomas Soome 
6791061836SToomas Soome 	if (vbe->Capabilities & VBE_CAP_DAC8) {
6891061836SToomas Soome 		int m;
6991061836SToomas Soome 
7091061836SToomas Soome 		/*
7191061836SToomas Soome 		 * The mode change should reset the palette format to
7291061836SToomas Soome 		 * 6 bits, but apparently some systems do fail with 8-bit
7391061836SToomas Soome 		 * palette, so we switch to 6-bit here.
7491061836SToomas Soome 		 */
7591061836SToomas Soome 		m = 0x0600;
7691061836SToomas Soome 		(void) biosvbe_palette_format(&m);
7791061836SToomas Soome 		palette_format = m;
7891061836SToomas Soome 	}
799890ff83SToomas Soome 	v86.ctl = V86_FLAGS;
809890ff83SToomas Soome 	v86.addr = 0x10;
819890ff83SToomas Soome 	v86.eax = mode;				/* set VGA text mode */
829890ff83SToomas Soome 	v86int();
839890ff83SToomas Soome 	atr = vga_get_atr(VGA_REG_ADDR, VGA_ATR_MODE);
849890ff83SToomas Soome 	atr &= ~VGA_ATR_MODE_BLINK;
859890ff83SToomas Soome 	atr &= ~VGA_ATR_MODE_9WIDE;
869890ff83SToomas Soome 	vga_set_atr(VGA_REG_ADDR, VGA_ATR_MODE, atr);
879890ff83SToomas Soome 
889890ff83SToomas Soome 	vbestate.vbe_mode = 0;			/* vbe is disabled */
899890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_type =
909890ff83SToomas Soome 	    MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT;
919890ff83SToomas Soome 	/* 16 bits per character */
929890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_bpp = 16;
939890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_addr =
949890ff83SToomas Soome 	    VGA_MEM_ADDR + VGA_COLOR_BASE;
959890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_width = TEXT_COLS;
969890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_height = TEXT_ROWS;
979890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_pitch = TEXT_COLS * 2;
989890ff83SToomas Soome }
999890ff83SToomas Soome 
1009890ff83SToomas Soome /* Function 00h - Return VBE Controller Information */
1019890ff83SToomas Soome static int
biosvbe_info(struct vbeinfoblock * vbe)1029890ff83SToomas Soome biosvbe_info(struct vbeinfoblock *vbe)
1039890ff83SToomas Soome {
1049890ff83SToomas Soome 	v86.ctl = V86_FLAGS;
1059890ff83SToomas Soome 	v86.addr = 0x10;
1069890ff83SToomas Soome 	v86.eax = 0x4f00;
1079890ff83SToomas Soome 	v86.es = VTOPSEG(vbe);
1089890ff83SToomas Soome 	v86.edi = VTOPOFF(vbe);
1099890ff83SToomas Soome 	v86int();
1109890ff83SToomas Soome 	return (v86.eax & 0xffff);
1119890ff83SToomas Soome }
1129890ff83SToomas Soome 
1139890ff83SToomas Soome /* Function 01h - Return VBE Mode Information */
1149890ff83SToomas Soome static int
biosvbe_get_mode_info(int mode,struct modeinfoblock * mi)1159890ff83SToomas Soome biosvbe_get_mode_info(int mode, struct modeinfoblock *mi)
1169890ff83SToomas Soome {
1179890ff83SToomas Soome 	v86.ctl = V86_FLAGS;
1189890ff83SToomas Soome 	v86.addr = 0x10;
1199890ff83SToomas Soome 	v86.eax = 0x4f01;
1209890ff83SToomas Soome 	v86.ecx = mode;
1219890ff83SToomas Soome 	v86.es = VTOPSEG(mi);
1229890ff83SToomas Soome 	v86.edi = VTOPOFF(mi);
1239890ff83SToomas Soome 	v86int();
1249890ff83SToomas Soome 	return (v86.eax & 0xffff);
1259890ff83SToomas Soome }
1269890ff83SToomas Soome 
1279890ff83SToomas Soome /* Function 02h - Set VBE Mode */
1289890ff83SToomas Soome static int
biosvbe_set_mode(int mode,struct crtciinfoblock * ci)1299890ff83SToomas Soome biosvbe_set_mode(int mode, struct crtciinfoblock *ci)
1309890ff83SToomas Soome {
13191061836SToomas Soome 	int rv;
13291061836SToomas Soome 
13391061836SToomas Soome 	if (vbe->Capabilities & VBE_CAP_DAC8) {
13491061836SToomas Soome 		int m;
13591061836SToomas Soome 
13691061836SToomas Soome 		/*
13791061836SToomas Soome 		 * The mode change should reset the palette format to
13891061836SToomas Soome 		 * 6 bits, but apparently some systems do fail with 8-bit
13991061836SToomas Soome 		 * palette, so we switch to 6-bit here.
14091061836SToomas Soome 		 */
14191061836SToomas Soome 		m = 0x0600;
14291061836SToomas Soome 		if (biosvbe_palette_format(&m) == VBE_SUCCESS)
14391061836SToomas Soome 			palette_format = m;
14491061836SToomas Soome 	}
1459890ff83SToomas Soome 	v86.ctl = V86_FLAGS;
1469890ff83SToomas Soome 	v86.addr = 0x10;
1479890ff83SToomas Soome 	v86.eax = 0x4f02;
1489890ff83SToomas Soome 	v86.ebx = mode | 0x4000;	/* set linear FB bit */
1499890ff83SToomas Soome 	v86.es = VTOPSEG(ci);
1509890ff83SToomas Soome 	v86.edi = VTOPOFF(ci);
1519890ff83SToomas Soome 	v86int();
15291061836SToomas Soome 	rv = v86.eax & 0xffff;
15391061836SToomas Soome 	if (vbe->Capabilities & VBE_CAP_DAC8) {
15491061836SToomas Soome 		int m;
15591061836SToomas Soome 
15691061836SToomas Soome 		/* Switch to 8-bits per primary color. */
15791061836SToomas Soome 		m = 0x0800;
15891061836SToomas Soome 		if (biosvbe_palette_format(&m) == VBE_SUCCESS)
15991061836SToomas Soome 			palette_format = m;
16091061836SToomas Soome 	}
16191061836SToomas Soome 	return (rv);
1629890ff83SToomas Soome }
1639890ff83SToomas Soome 
1649890ff83SToomas Soome /* Function 03h - Get VBE Mode */
1659890ff83SToomas Soome static int
biosvbe_get_mode(int * mode)1669890ff83SToomas Soome biosvbe_get_mode(int *mode)
1679890ff83SToomas Soome {
1689890ff83SToomas Soome 	v86.ctl = V86_FLAGS;
1699890ff83SToomas Soome 	v86.addr = 0x10;
1709890ff83SToomas Soome 	v86.eax = 0x4f03;
1719890ff83SToomas Soome 	v86int();
1722d106d6bSToomas Soome 	*mode = v86.ebx & 0x3fff;	/* Bits 0-13 */
1739890ff83SToomas Soome 	return (v86.eax & 0xffff);
1749890ff83SToomas Soome }
1759890ff83SToomas Soome 
1769890ff83SToomas Soome /* Function 08h - Set/Get DAC Palette Format */
1779890ff83SToomas Soome int
biosvbe_palette_format(int * format)1789890ff83SToomas Soome biosvbe_palette_format(int *format)
1799890ff83SToomas Soome {
1809890ff83SToomas Soome 	v86.ctl = V86_FLAGS;
1819890ff83SToomas Soome 	v86.addr = 0x10;
1829890ff83SToomas Soome 	v86.eax = 0x4f08;
1839890ff83SToomas Soome 	v86.ebx = *format;
1849890ff83SToomas Soome 	v86int();
18591061836SToomas Soome 	*format = (v86.ebx >> 8) & 0xff;
1869890ff83SToomas Soome 	return (v86.eax & 0xffff);
1879890ff83SToomas Soome }
1889890ff83SToomas Soome 
1899890ff83SToomas Soome /* Function 09h - Set/Get Palette Data */
1909890ff83SToomas Soome static int
biosvbe_palette_data(int mode,int reg,struct paletteentry * pe)1919890ff83SToomas Soome biosvbe_palette_data(int mode, int reg, struct paletteentry *pe)
1929890ff83SToomas Soome {
1939890ff83SToomas Soome 	v86.ctl = V86_FLAGS;
1949890ff83SToomas Soome 	v86.addr = 0x10;
1959890ff83SToomas Soome 	v86.eax = 0x4f09;
1969890ff83SToomas Soome 	v86.ebx = mode;
1979890ff83SToomas Soome 	v86.edx = reg;
1989890ff83SToomas Soome 	v86.ecx = 1;
1999890ff83SToomas Soome 	v86.es = VTOPSEG(pe);
2009890ff83SToomas Soome 	v86.edi = VTOPOFF(pe);
2019890ff83SToomas Soome 	v86int();
2029890ff83SToomas Soome 	return (v86.eax & 0xffff);
2039890ff83SToomas Soome }
2049890ff83SToomas Soome 
2059890ff83SToomas Soome /*
2069890ff83SToomas Soome  * Function 15h BL=00h - Report VBE/DDC Capabilities
2079890ff83SToomas Soome  *
2089890ff83SToomas Soome  * int biosvbe_ddc_caps(void)
2099890ff83SToomas Soome  * return: VBE/DDC capabilities
2109890ff83SToomas Soome  */
2119890ff83SToomas Soome static int
biosvbe_ddc_caps(void)2129890ff83SToomas Soome biosvbe_ddc_caps(void)
2139890ff83SToomas Soome {
2149890ff83SToomas Soome 	v86.ctl = V86_FLAGS;
2159890ff83SToomas Soome 	v86.addr = 0x10;
2169890ff83SToomas Soome 	v86.eax = 0x4f15;	/* display identification extensions */
2179890ff83SToomas Soome 	v86.ebx = 0;		/* report DDC capabilities */
2189890ff83SToomas Soome 	v86.ecx = 0;		/* controller unit number (00h = primary) */
2199890ff83SToomas Soome 	v86.es = 0;
2209890ff83SToomas Soome 	v86.edi = 0;
2219890ff83SToomas Soome 	v86int();
2229890ff83SToomas Soome 	if (VBE_ERROR(v86.eax & 0xffff))
2239890ff83SToomas Soome 		return (0);
2249890ff83SToomas Soome 	return (v86.ebx & 0xffff);
2259890ff83SToomas Soome }
2269890ff83SToomas Soome 
2274d503977SToomas Soome /* Function 11h BL=01h - Flat Panel status */
2284d503977SToomas Soome static int
biosvbe_ddc_read_flat_panel_info(void * buf)2294d503977SToomas Soome biosvbe_ddc_read_flat_panel_info(void *buf)
2304d503977SToomas Soome {
2314d503977SToomas Soome 	v86.ctl = V86_FLAGS;
2324d503977SToomas Soome 	v86.addr = 0x10;
2334d503977SToomas Soome 	v86.eax = 0x4f11;	/* Flat Panel Interface extensions */
2344d503977SToomas Soome 	v86.ebx = 1;		/* Return Flat Panel Information */
2354d503977SToomas Soome 	v86.es = VTOPSEG(buf);
2364d503977SToomas Soome 	v86.edi = VTOPOFF(buf);
2374d503977SToomas Soome 	v86int();
2384d503977SToomas Soome 	return (v86.eax & 0xffff);
2394d503977SToomas Soome }
2404d503977SToomas Soome 
2419890ff83SToomas Soome /* Function 15h BL=01h - Read EDID */
2429890ff83SToomas Soome static int
biosvbe_ddc_read_edid(int blockno,void * buf)2439890ff83SToomas Soome biosvbe_ddc_read_edid(int blockno, void *buf)
2449890ff83SToomas Soome {
2459890ff83SToomas Soome 	v86.ctl = V86_FLAGS;
2469890ff83SToomas Soome 	v86.addr = 0x10;
2479890ff83SToomas Soome 	v86.eax = 0x4f15;	/* display identification extensions */
2489890ff83SToomas Soome 	v86.ebx = 1;		/* read EDID */
2499890ff83SToomas Soome 	v86.ecx = 0;		/* controller unit number (00h = primary) */
2509890ff83SToomas Soome 	v86.edx = blockno;
2519890ff83SToomas Soome 	v86.es = VTOPSEG(buf);
2529890ff83SToomas Soome 	v86.edi = VTOPOFF(buf);
2539890ff83SToomas Soome 	v86int();
2549890ff83SToomas Soome 	return (v86.eax & 0xffff);
2559890ff83SToomas Soome }
2569890ff83SToomas Soome 
2579890ff83SToomas Soome static int
vbe_mode_is_supported(struct modeinfoblock * mi)2589890ff83SToomas Soome vbe_mode_is_supported(struct modeinfoblock *mi)
2599890ff83SToomas Soome {
2609890ff83SToomas Soome 	if ((mi->ModeAttributes & 0x01) == 0)
2618d2ce326SToomas Soome 		return (0);	/* mode not supported by hardware */
2629890ff83SToomas Soome 	if ((mi->ModeAttributes & 0x08) == 0)
2638d2ce326SToomas Soome 		return (0);	/* linear fb not available */
2649890ff83SToomas Soome 	if ((mi->ModeAttributes & 0x10) == 0)
2658d2ce326SToomas Soome 		return (0);	/* text mode */
2669890ff83SToomas Soome 	if (mi->NumberOfPlanes != 1)
2678d2ce326SToomas Soome 		return (0);	/* planar mode not supported */
2689890ff83SToomas Soome 	if (mi->MemoryModel != 0x04 /* Packed pixel */ &&
2699890ff83SToomas Soome 	    mi->MemoryModel != 0x06 /* Direct Color */)
2708d2ce326SToomas Soome 		return (0);	/* unsupported pixel format */
2718d2ce326SToomas Soome 	return (1);
2729890ff83SToomas Soome }
2739890ff83SToomas Soome 
2749890ff83SToomas Soome static int
vbe_check(void)2759890ff83SToomas Soome vbe_check(void)
2769890ff83SToomas Soome {
2779890ff83SToomas Soome 	if (vbestate.mb_type != MULTIBOOT_TAG_TYPE_VBE) {
2789890ff83SToomas Soome 		printf("VBE not available\n");
2799890ff83SToomas Soome 		return (0);
2809890ff83SToomas Soome 	}
2819890ff83SToomas Soome 	return (1);
2829890ff83SToomas Soome }
2839890ff83SToomas Soome 
2847449d372SToomas Soome /*
2857449d372SToomas Soome  * Translate selector:offset style address to linear adress.
2867449d372SToomas Soome  * selector = farptr >> 16;
2877449d372SToomas Soome  * offset = farptr & 0xffff;
2887449d372SToomas Soome  * linear = (selector * 4) + offset.
2897449d372SToomas Soome  * By using mask 0xffff0000, we wil get the optimised line below.
2907449d372SToomas Soome  * As a final step, translate physical address to loader virtual address.
2917449d372SToomas Soome  */
2927449d372SToomas Soome static void *
vbe_farptr(uint32_t farptr)2937449d372SToomas Soome vbe_farptr(uint32_t farptr)
2947449d372SToomas Soome {
2957449d372SToomas Soome 	return (PTOV((((farptr & 0xffff0000) >> 12) + (farptr & 0xffff))));
2967449d372SToomas Soome }
2977449d372SToomas Soome 
2989890ff83SToomas Soome void
vbe_init(void)2999890ff83SToomas Soome vbe_init(void)
3009890ff83SToomas Soome {
3017449d372SToomas Soome 	uint16_t *p, *ml;
3027449d372SToomas Soome 
3039890ff83SToomas Soome 	/* First set FB for text mode. */
3049890ff83SToomas Soome 	gfx_fb.framebuffer_common.mb_type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER;
3059890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_type =
3069890ff83SToomas Soome 	    MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT;
3079890ff83SToomas Soome 	/* 16 bits per character */
3089890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_bpp = 16;
3099890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_addr =
3109890ff83SToomas Soome 	    VGA_MEM_ADDR + VGA_COLOR_BASE;
3119890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_width = TEXT_COLS;
3129890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_height = TEXT_ROWS;
3139890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_pitch = TEXT_COLS * 2;
3149890ff83SToomas Soome 
3159890ff83SToomas Soome 	/* Now check if we have vesa. */
3168d2ce326SToomas Soome 	memset(vbe, 0, sizeof (*vbe));
3179890ff83SToomas Soome 	memcpy(vbe->VbeSignature, "VBE2", 4);
3189890ff83SToomas Soome 	if (biosvbe_info(vbe) != VBE_SUCCESS)
3199890ff83SToomas Soome 		return;
3209890ff83SToomas Soome 	if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
3219890ff83SToomas Soome 		return;
3229890ff83SToomas Soome 
3237449d372SToomas Soome 	/*
3247449d372SToomas Soome 	 * Copy mode list array. We must do this because some systems do
3257449d372SToomas Soome 	 * place this array to scratch memory, which will be reused by
3267449d372SToomas Soome 	 * subsequent VBE calls. (vbox 6.1 is one example).
3277449d372SToomas Soome 	 */
3287449d372SToomas Soome 	p = ml = vbe_farptr(vbe->VideoModePtr);
3297449d372SToomas Soome 	while (*p++ != VESA_END_OF_MODE_LIST)
3307449d372SToomas Soome 		;
3317449d372SToomas Soome 
3327449d372SToomas Soome 	vbe_mode_list_size = (uintptr_t)p - (uintptr_t)ml;
3337449d372SToomas Soome 
3347449d372SToomas Soome 	/*
3357449d372SToomas Soome 	 * Since vbe_init() is used only once at very start of the loader,
3367449d372SToomas Soome 	 * we assume malloc will not fail there. But in case it does,
3377449d372SToomas Soome 	 * we point vbe_mode_list to memory pointed by VideoModePtr.
3387449d372SToomas Soome 	 * If the VideoModePtr memory area is not valid, we will fail to
3397449d372SToomas Soome 	 * pick usable VBE mode and fall back to use text mode.
3407449d372SToomas Soome 	 */
3417449d372SToomas Soome 	vbe_mode_list = malloc(vbe_mode_list_size);
3427449d372SToomas Soome 	if (vbe_mode_list == NULL)
3437449d372SToomas Soome 		vbe_mode_list = ml;
3447449d372SToomas Soome 	else
3457449d372SToomas Soome 		bcopy(ml, vbe_mode_list, vbe_mode_list_size);
3467449d372SToomas Soome 
3477449d372SToomas Soome 	/* reset VideoModePtr, to make sure, we only do use vbe_mode_list. */
3487449d372SToomas Soome 	vbe->VideoModePtr = 0;
3497449d372SToomas Soome 
3509890ff83SToomas Soome 	vbestate.mb_type = MULTIBOOT_TAG_TYPE_VBE;
3519890ff83SToomas Soome 	vbestate.mb_size = sizeof (vbestate);
3529890ff83SToomas Soome 	vbestate.vbe_mode = 0;
3537449d372SToomas Soome 
3549890ff83SToomas Soome 	/* vbe_set_mode() will set up the rest. */
3559890ff83SToomas Soome }
3569890ff83SToomas Soome 
3579890ff83SToomas Soome int
vbe_available(void)3589890ff83SToomas Soome vbe_available(void)
3599890ff83SToomas Soome {
3608d2ce326SToomas Soome 	return (vbestate.mb_type);
3619890ff83SToomas Soome }
3629890ff83SToomas Soome 
3639890ff83SToomas Soome int
vbe_set_palette(const struct paletteentry * entry,size_t slot)3649890ff83SToomas Soome vbe_set_palette(const struct paletteentry *entry, size_t slot)
3659890ff83SToomas Soome {
3669890ff83SToomas Soome 	struct paletteentry pe;
36791061836SToomas Soome 	int mode, ret;
3689890ff83SToomas Soome 
36991061836SToomas Soome 	if (!vbe_check() || (vbe->Capabilities & VBE_CAP_DAC8) == 0)
3709890ff83SToomas Soome 		return (1);
3719890ff83SToomas Soome 
3729890ff83SToomas Soome 	if (gfx_fb.framebuffer_common.framebuffer_type !=
3739890ff83SToomas Soome 	    MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED) {
3749890ff83SToomas Soome 		return (1);
3759890ff83SToomas Soome 	}
3769890ff83SToomas Soome 
37791061836SToomas Soome 	if (cmap == NULL)
37891061836SToomas Soome 		cmap = calloc(CMAP_SIZE, sizeof (*cmap));
37991061836SToomas Soome 
3809890ff83SToomas Soome 	pe.Blue = entry->Blue;
3819890ff83SToomas Soome 	pe.Green = entry->Green;
3829890ff83SToomas Soome 	pe.Red = entry->Red;
3832d84dc94SToomas Soome 	pe.Reserved = entry->Reserved;
3849890ff83SToomas Soome 
38591061836SToomas Soome 	if (vbe->Capabilities & VBE_CAP_SNOW)
38691061836SToomas Soome 		mode = 0x80;
38791061836SToomas Soome 	else
38891061836SToomas Soome 		mode = 0;
38991061836SToomas Soome 
39091061836SToomas Soome 	ret = biosvbe_palette_data(mode, slot, &pe);
39191061836SToomas Soome 	if (cmap != NULL && slot < CMAP_SIZE) {
3929890ff83SToomas Soome 		cmap[slot].mb_red = entry->Red;
3939890ff83SToomas Soome 		cmap[slot].mb_green = entry->Green;
3949890ff83SToomas Soome 		cmap[slot].mb_blue = entry->Blue;
3959890ff83SToomas Soome 	}
3969890ff83SToomas Soome 
3979890ff83SToomas Soome 	return (ret == VBE_SUCCESS ? 0 : 1);
3989890ff83SToomas Soome }
3999890ff83SToomas Soome 
4009890ff83SToomas Soome int
vbe_get_mode(void)4019890ff83SToomas Soome vbe_get_mode(void)
4029890ff83SToomas Soome {
4038d2ce326SToomas Soome 	return (vbestate.vbe_mode);
4049890ff83SToomas Soome }
4059890ff83SToomas Soome 
4069890ff83SToomas Soome int
vbe_set_mode(int modenum)4079890ff83SToomas Soome vbe_set_mode(int modenum)
4089890ff83SToomas Soome {
409554e720aSToomas Soome 	extern struct paletteentry *shadow_fb;
4109890ff83SToomas Soome 	struct modeinfoblock mi;
4119890ff83SToomas Soome 	int ret;
4129890ff83SToomas Soome 
4139890ff83SToomas Soome 	if (!vbe_check())
4149890ff83SToomas Soome 		return (1);
4159890ff83SToomas Soome 
4169890ff83SToomas Soome 	ret = biosvbe_get_mode_info(modenum, &mi);
4179890ff83SToomas Soome 	if (VBE_ERROR(ret)) {
4189890ff83SToomas Soome 		printf("mode 0x%x invalid\n", modenum);
4199890ff83SToomas Soome 		return (1);
4209890ff83SToomas Soome 	}
4219890ff83SToomas Soome 
4229890ff83SToomas Soome 	if (!vbe_mode_is_supported(&mi)) {
4239890ff83SToomas Soome 		printf("mode 0x%x not supported\n", modenum);
4249890ff83SToomas Soome 		return (1);
4259890ff83SToomas Soome 	}
4269890ff83SToomas Soome 
4279890ff83SToomas Soome 	/* calculate bytes per pixel */
4289890ff83SToomas Soome 	switch (mi.BitsPerPixel) {
4299890ff83SToomas Soome 	case 32:
4309890ff83SToomas Soome 	case 24:
4319890ff83SToomas Soome 	case 16:
4329890ff83SToomas Soome 	case 15:
4339890ff83SToomas Soome 	case 8:
4349890ff83SToomas Soome 		break;
4359890ff83SToomas Soome 	default:
4369890ff83SToomas Soome 		printf("BitsPerPixel %d is not supported\n", mi.BitsPerPixel);
4379890ff83SToomas Soome 		return (1);
4389890ff83SToomas Soome 	}
4399890ff83SToomas Soome 
4409890ff83SToomas Soome 	ret = biosvbe_set_mode(modenum, NULL);
4419890ff83SToomas Soome 	if (VBE_ERROR(ret)) {
4429890ff83SToomas Soome 		printf("mode 0x%x could not be set\n", modenum);
4439890ff83SToomas Soome 		return (1);
4449890ff83SToomas Soome 	}
4459890ff83SToomas Soome 
4469890ff83SToomas Soome 	/* make sure we have current MI in vbestate */
4479890ff83SToomas Soome 	memcpy(vbe_mode, &mi, sizeof (*vbe_mode));
4489890ff83SToomas Soome 	vbestate.vbe_mode = modenum;
4499890ff83SToomas Soome 
450554e720aSToomas Soome 	if (shadow_fb != NULL)
451554e720aSToomas Soome 		free(shadow_fb);
452554e720aSToomas Soome 	shadow_fb = malloc(mi.XResolution * mi.YResolution *
453554e720aSToomas Soome 	    sizeof (*shadow_fb));
454554e720aSToomas Soome 
4559890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_addr =
4569890ff83SToomas Soome 	    (uint64_t)mi.PhysBasePtr & 0xffffffff;
4579890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_width = mi.XResolution;
4589890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_height = mi.YResolution;
4599890ff83SToomas Soome 	gfx_fb.framebuffer_common.framebuffer_bpp = mi.BitsPerPixel;
4609890ff83SToomas Soome 
4619890ff83SToomas Soome 	/* vbe_mode_is_supported() excludes the rest */
4629890ff83SToomas Soome 	switch (mi.MemoryModel) {
4639890ff83SToomas Soome 	case 0x4:
4649890ff83SToomas Soome 		gfx_fb.framebuffer_common.framebuffer_type =
4659890ff83SToomas Soome 		    MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED;
46691061836SToomas Soome 		break;
4679890ff83SToomas Soome 	case 0x6:
4689890ff83SToomas Soome 		gfx_fb.framebuffer_common.framebuffer_type =
4699890ff83SToomas Soome 		    MULTIBOOT_FRAMEBUFFER_TYPE_RGB;
4709890ff83SToomas Soome 		break;
4719890ff83SToomas Soome 	}
4729890ff83SToomas Soome 
4739890ff83SToomas Soome 	if (vbe->VbeVersion >= 0x300) {
4749890ff83SToomas Soome 		gfx_fb.framebuffer_common.framebuffer_pitch =
4759890ff83SToomas Soome 		    mi.LinBytesPerScanLine;
4769890ff83SToomas Soome 		gfx_fb.u.fb2.framebuffer_red_field_position =
4779890ff83SToomas Soome 		    mi.LinRedFieldPosition;
4789890ff83SToomas Soome 		gfx_fb.u.fb2.framebuffer_red_mask_size = mi.LinRedMaskSize;
4799890ff83SToomas Soome 		gfx_fb.u.fb2.framebuffer_green_field_position =
4809890ff83SToomas Soome 		    mi.LinGreenFieldPosition;
4819890ff83SToomas Soome 		gfx_fb.u.fb2.framebuffer_green_mask_size = mi.LinGreenMaskSize;
4829890ff83SToomas Soome 		gfx_fb.u.fb2.framebuffer_blue_field_position =
4839890ff83SToomas Soome 		    mi.LinBlueFieldPosition;
4849890ff83SToomas Soome 		gfx_fb.u.fb2.framebuffer_blue_mask_size = mi.LinBlueMaskSize;
4859890ff83SToomas Soome 	} else {
4869890ff83SToomas Soome 		gfx_fb.framebuffer_common.framebuffer_pitch =
4879890ff83SToomas Soome 		    mi.BytesPerScanLine;
4889890ff83SToomas Soome 		gfx_fb.u.fb2.framebuffer_red_field_position =
4899890ff83SToomas Soome 		    mi.RedFieldPosition;
4909890ff83SToomas Soome 		gfx_fb.u.fb2.framebuffer_red_mask_size = mi.RedMaskSize;
4919890ff83SToomas Soome 		gfx_fb.u.fb2.framebuffer_green_field_position =
4929890ff83SToomas Soome 		    mi.GreenFieldPosition;
4939890ff83SToomas Soome 		gfx_fb.u.fb2.framebuffer_green_mask_size = mi.GreenMaskSize;
4949890ff83SToomas Soome 		gfx_fb.u.fb2.framebuffer_blue_field_position =
4959890ff83SToomas Soome 		    mi.BlueFieldPosition;
4969890ff83SToomas Soome 		gfx_fb.u.fb2.framebuffer_blue_mask_size = mi.BlueMaskSize;
4979890ff83SToomas Soome 	}
4989890ff83SToomas Soome 
499fa9eb222SToomas Soome 	/*
500fa9eb222SToomas Soome 	 * Support for color mapping.
501fa9eb222SToomas Soome 	 * For 8, 24 and 32 bit depth, use mask size 8.
502fa9eb222SToomas Soome 	 * 15/16 bit depth needs to use mask size from mode, or we will
503fa9eb222SToomas Soome 	 * lose color information from 32-bit to 15/16 bit translation.
504fa9eb222SToomas Soome 	 */
505fa9eb222SToomas Soome 	if (mi.BitsPerPixel == 15 || mi.BitsPerPixel == 16) {
506fa9eb222SToomas Soome 		rgb_info.red.size = gfx_fb.u.fb2.framebuffer_red_mask_size;
507fa9eb222SToomas Soome 		rgb_info.green.size = gfx_fb.u.fb2.framebuffer_green_mask_size;
508fa9eb222SToomas Soome 		rgb_info.blue.size = gfx_fb.u.fb2.framebuffer_blue_mask_size;
509fa9eb222SToomas Soome 	} else {
510fa9eb222SToomas Soome 		rgb_info.red.size = 8;
511fa9eb222SToomas Soome 		rgb_info.green.size = 8;
512fa9eb222SToomas Soome 		rgb_info.blue.size = 8;
513fa9eb222SToomas Soome 	}
514fa9eb222SToomas Soome 	rgb_info.red.pos = 16;
515fa9eb222SToomas Soome 	rgb_info.green.pos = 8;
516fa9eb222SToomas Soome 	rgb_info.blue.pos = 0;
517fa9eb222SToomas Soome 
5189890ff83SToomas Soome 	return (0);
5199890ff83SToomas Soome }
5209890ff83SToomas Soome 
5219890ff83SToomas Soome /*
5229890ff83SToomas Soome  * Verify existance of mode number or find mode by
5239890ff83SToomas Soome  * dimensions. If depth is not given, walk values 32, 24, 16, 8.
5249890ff83SToomas Soome  */
5259890ff83SToomas Soome static int
vbe_find_mode_xydm(int x,int y,int depth,int m)5269890ff83SToomas Soome vbe_find_mode_xydm(int x, int y, int depth, int m)
5279890ff83SToomas Soome {
5289890ff83SToomas Soome 	struct modeinfoblock mi;
5299890ff83SToomas Soome 	uint16_t mode;
5307449d372SToomas Soome 	size_t idx, nentries;
5317449d372SToomas Soome 	int i;
5329890ff83SToomas Soome 
5338d2ce326SToomas Soome 	memset(vbe, 0, sizeof (vbe));
5349890ff83SToomas Soome 	memcpy(vbe->VbeSignature, "VBE2", 4);
5359890ff83SToomas Soome 	if (biosvbe_info(vbe) != VBE_SUCCESS)
5369890ff83SToomas Soome 		return (0);
5379890ff83SToomas Soome 	if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
5389890ff83SToomas Soome 		return (0);
5399890ff83SToomas Soome 
5409890ff83SToomas Soome 	if (m != -1)
5419890ff83SToomas Soome 		i = 8;
5429890ff83SToomas Soome 	else if (depth == -1)
5439890ff83SToomas Soome 		i = 32;
5449890ff83SToomas Soome 	else
5459890ff83SToomas Soome 		i = depth;
5469890ff83SToomas Soome 
5477449d372SToomas Soome 	nentries = vbe_mode_list_size / sizeof (*vbe_mode_list);
5489890ff83SToomas Soome 	while (i > 0) {
5497449d372SToomas Soome 		for (idx = 0; idx < nentries; idx++) {
5507449d372SToomas Soome 			mode = vbe_mode_list[idx];
5517449d372SToomas Soome 			if (mode == VESA_END_OF_MODE_LIST)
5527449d372SToomas Soome 				break;
5537449d372SToomas Soome 
5547449d372SToomas Soome 			if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS)
5559890ff83SToomas Soome 				continue;
5567449d372SToomas Soome 
5579890ff83SToomas Soome 			/* we only care about linear modes here */
5589890ff83SToomas Soome 			if (vbe_mode_is_supported(&mi) == 0)
5599890ff83SToomas Soome 				continue;
5609890ff83SToomas Soome 
5619890ff83SToomas Soome 			if (m != -1) {
5629890ff83SToomas Soome 				if (m == mode)
5639890ff83SToomas Soome 					return (mode);
5649890ff83SToomas Soome 				else
5659890ff83SToomas Soome 					continue;
5669890ff83SToomas Soome 			}
5679890ff83SToomas Soome 
5689890ff83SToomas Soome 			if (mi.XResolution == x &&
5699890ff83SToomas Soome 			    mi.YResolution == y &&
5709890ff83SToomas Soome 			    mi.BitsPerPixel == i)
5718d2ce326SToomas Soome 				return (mode);
5729890ff83SToomas Soome 		}
5739890ff83SToomas Soome 		if (depth != -1)
5749890ff83SToomas Soome 			break;
5759890ff83SToomas Soome 
5769890ff83SToomas Soome 		i -= 8;
5779890ff83SToomas Soome 	}
5789890ff83SToomas Soome 
5799890ff83SToomas Soome 	return (0);
5809890ff83SToomas Soome }
5819890ff83SToomas Soome 
5829890ff83SToomas Soome static int
vbe_find_mode(char * str)5839890ff83SToomas Soome vbe_find_mode(char *str)
5849890ff83SToomas Soome {
5859890ff83SToomas Soome 	int x, y, depth;
5869890ff83SToomas Soome 
5879890ff83SToomas Soome 	if (!gfx_parse_mode_str(str, &x, &y, &depth))
5889890ff83SToomas Soome 		return (0);
5899890ff83SToomas Soome 
5909890ff83SToomas Soome 	return (vbe_find_mode_xydm(x, y, depth, -1));
5919890ff83SToomas Soome }
5929890ff83SToomas Soome 
5939890ff83SToomas Soome static void
vbe_dump_mode(int modenum,struct modeinfoblock * mi)5949890ff83SToomas Soome vbe_dump_mode(int modenum, struct modeinfoblock *mi)
5959890ff83SToomas Soome {
5969890ff83SToomas Soome 	printf("0x%x=%dx%dx%d", modenum,
5979890ff83SToomas Soome 	    mi->XResolution, mi->YResolution, mi->BitsPerPixel);
5989890ff83SToomas Soome }
5999890ff83SToomas Soome 
6009890ff83SToomas Soome static bool
vbe_get_edid(edid_res_list_t * res)601d06ea0b9SToomas Soome vbe_get_edid(edid_res_list_t *res)
6029890ff83SToomas Soome {
6031b1c4b08SToomas Soome 	struct vesa_edid_info *edidp;
6049890ff83SToomas Soome 	const uint8_t magic[] = EDID_MAGIC;
605b18a8f64SToomas Soome 	int ddc_caps;
606b18a8f64SToomas Soome 	bool ret = false;
6079890ff83SToomas Soome 
6081b1c4b08SToomas Soome 	if (edid_info != NULL)
6091b1c4b08SToomas Soome 		return (gfx_get_edid_resolution(edid_info, res));
6101b1c4b08SToomas Soome 
6119890ff83SToomas Soome 	ddc_caps = biosvbe_ddc_caps();
6129890ff83SToomas Soome 	if (ddc_caps == 0) {
613b18a8f64SToomas Soome 		return (ret);
6149890ff83SToomas Soome 	}
6159890ff83SToomas Soome 
6161b1c4b08SToomas Soome 	edidp = bio_alloc(sizeof (*edidp));
6171b1c4b08SToomas Soome 	if (edidp == NULL)
618b18a8f64SToomas Soome 		return (ret);
6191b1c4b08SToomas Soome 	memset(edidp, 0, sizeof (*edidp));
6209890ff83SToomas Soome 
6211b1c4b08SToomas Soome 	if (VBE_ERROR(biosvbe_ddc_read_edid(0, edidp)))
622b18a8f64SToomas Soome 		goto done;
6239890ff83SToomas Soome 
6241b1c4b08SToomas Soome 	if (memcmp(edidp, magic, sizeof (magic)) != 0)
625b18a8f64SToomas Soome 		goto done;
626b18a8f64SToomas Soome 
627d06ea0b9SToomas Soome 	/* Unknown EDID version. */
6281b1c4b08SToomas Soome 	if (edidp->header.version != 1)
629b18a8f64SToomas Soome 		goto done;
6309890ff83SToomas Soome 
6311b1c4b08SToomas Soome 	ret = gfx_get_edid_resolution(edidp, res);
6321b1c4b08SToomas Soome 	edid_info = malloc(sizeof (*edid_info));
6331b1c4b08SToomas Soome 	if (edid_info != NULL)
6341b1c4b08SToomas Soome 		memcpy(edid_info, edidp, sizeof (*edid_info));
635b18a8f64SToomas Soome done:
6361b1c4b08SToomas Soome 	bio_free(edidp, sizeof (*edidp));
637b18a8f64SToomas Soome 	return (ret);
6389890ff83SToomas Soome }
6399890ff83SToomas Soome 
6404d503977SToomas Soome static bool
vbe_get_flatpanel(uint_t * pwidth,uint_t * pheight)6414d503977SToomas Soome vbe_get_flatpanel(uint_t *pwidth, uint_t *pheight)
6424d503977SToomas Soome {
6434d503977SToomas Soome 	struct flatpanelinfo *fp_info;
6444d503977SToomas Soome 	bool ret = false;
6454d503977SToomas Soome 
6464d503977SToomas Soome 	fp_info = bio_alloc(sizeof (*fp_info));
6474d503977SToomas Soome 	if (fp_info == NULL)
6484d503977SToomas Soome 		return (ret);
6494d503977SToomas Soome 	memset(fp_info, 0, sizeof (*fp_info));
6504d503977SToomas Soome 
6514d503977SToomas Soome 	if (VBE_ERROR(biosvbe_ddc_read_flat_panel_info(fp_info)))
6524d503977SToomas Soome 		goto done;
6534d503977SToomas Soome 
6544d503977SToomas Soome 	*pwidth = fp_info->HorizontalSize;
6554d503977SToomas Soome 	*pheight = fp_info->VerticalSize;
6564d503977SToomas Soome 	ret = true;
6574d503977SToomas Soome 
6584d503977SToomas Soome done:
6594d503977SToomas Soome 	bio_free(fp_info, sizeof (*fp_info));
6604d503977SToomas Soome 	return (ret);
6614d503977SToomas Soome }
6624d503977SToomas Soome 
6639890ff83SToomas Soome static void
vbe_print_vbe_info(struct vbeinfoblock * vbep)6649890ff83SToomas Soome vbe_print_vbe_info(struct vbeinfoblock *vbep)
6659890ff83SToomas Soome {
6669890ff83SToomas Soome 	char *oemstring = "";
6679890ff83SToomas Soome 	char *oemvendor = "", *oemproductname = "", *oemproductrev = "";
6689890ff83SToomas Soome 
6699890ff83SToomas Soome 	if (vbep->OemStringPtr != 0)
6709890ff83SToomas Soome 		oemstring = vbe_farptr(vbep->OemStringPtr);
6719890ff83SToomas Soome 
6729890ff83SToomas Soome 	if (vbep->OemVendorNamePtr != 0)
6739890ff83SToomas Soome 		oemvendor = vbe_farptr(vbep->OemVendorNamePtr);
6749890ff83SToomas Soome 
6759890ff83SToomas Soome 	if (vbep->OemProductNamePtr != 0)
6769890ff83SToomas Soome 		oemproductname = vbe_farptr(vbep->OemProductNamePtr);
6779890ff83SToomas Soome 
6789890ff83SToomas Soome 	if (vbep->OemProductRevPtr != 0)
6799890ff83SToomas Soome 		oemproductrev = vbe_farptr(vbep->OemProductRevPtr);
6809890ff83SToomas Soome 
6819890ff83SToomas Soome 	printf("VESA VBE Version %d.%d\n%s\n", vbep->VbeVersion >> 8,
6829890ff83SToomas Soome 	    vbep->VbeVersion & 0xF, oemstring);
6839890ff83SToomas Soome 
6849890ff83SToomas Soome 	if (vbep->OemSoftwareRev != 0) {
6859890ff83SToomas Soome 		printf("OEM Version %d.%d, %s (%s, %s)\n",
6869890ff83SToomas Soome 		    vbep->OemSoftwareRev >> 8, vbep->OemSoftwareRev & 0xF,
6878d2ce326SToomas Soome 		    oemvendor, oemproductname, oemproductrev);
6889890ff83SToomas Soome 	}
6899890ff83SToomas Soome }
6909890ff83SToomas Soome 
6919890ff83SToomas Soome /* List available modes, filter by depth. If depth is -1, list all. */
6929890ff83SToomas Soome void
vbe_modelist(int depth)6939890ff83SToomas Soome vbe_modelist(int depth)
6949890ff83SToomas Soome {
6959890ff83SToomas Soome 	struct modeinfoblock mi;
6969890ff83SToomas Soome 	uint16_t mode;
6977449d372SToomas Soome 	int nmodes, idx, nentries;
6989890ff83SToomas Soome 	int ddc_caps;
6994d503977SToomas Soome 	uint_t width, height;
7004d503977SToomas Soome 	bool edid = false;
701d06ea0b9SToomas Soome 	edid_res_list_t res;
702d06ea0b9SToomas Soome 	struct resolution *rp;
7039890ff83SToomas Soome 
7049890ff83SToomas Soome 	if (!vbe_check())
7059890ff83SToomas Soome 		return;
7069890ff83SToomas Soome 
7079890ff83SToomas Soome 	ddc_caps = biosvbe_ddc_caps();
7089890ff83SToomas Soome 	if (ddc_caps & 3) {
7099890ff83SToomas Soome 		printf("DDC");
7109890ff83SToomas Soome 		if (ddc_caps & 1)
7119890ff83SToomas Soome 			printf(" [DDC1]");
7129890ff83SToomas Soome 		if (ddc_caps & 2)
7139890ff83SToomas Soome 			printf(" [DDC2]");
7149890ff83SToomas Soome 
715d06ea0b9SToomas Soome 		TAILQ_INIT(&res);
716d06ea0b9SToomas Soome 		edid = vbe_get_edid(&res);
717d06ea0b9SToomas Soome 		if (edid) {
718d06ea0b9SToomas Soome 			printf(": EDID");
719d06ea0b9SToomas Soome 			while ((rp = TAILQ_FIRST(&res)) != NULL) {
720d06ea0b9SToomas Soome 				printf(" %dx%d", rp->width, rp->height);
721d06ea0b9SToomas Soome 				TAILQ_REMOVE(&res, rp, next);
722d06ea0b9SToomas Soome 				free(rp);
723d06ea0b9SToomas Soome 			}
724d06ea0b9SToomas Soome 			printf("\n");
725d06ea0b9SToomas Soome 		} else {
7269890ff83SToomas Soome 			printf(": no EDID information\n");
727d06ea0b9SToomas Soome 		}
7289890ff83SToomas Soome 	}
7294d503977SToomas Soome 	if (!edid)
7304d503977SToomas Soome 		if (vbe_get_flatpanel(&width, &height))
7314d503977SToomas Soome 			printf(": Panel %dx%d\n", width, height);
7329890ff83SToomas Soome 
7337449d372SToomas Soome 	nmodes = 0;
7348d2ce326SToomas Soome 	memset(vbe, 0, sizeof (vbe));
7359890ff83SToomas Soome 	memcpy(vbe->VbeSignature, "VBE2", 4);
7369890ff83SToomas Soome 	if (biosvbe_info(vbe) != VBE_SUCCESS)
7379890ff83SToomas Soome 		goto done;
7389890ff83SToomas Soome 	if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
7399890ff83SToomas Soome 		goto done;
7409890ff83SToomas Soome 
7419890ff83SToomas Soome 	vbe_print_vbe_info(vbe);
7429890ff83SToomas Soome 	printf("Modes: ");
7439890ff83SToomas Soome 
7447449d372SToomas Soome 	nentries = vbe_mode_list_size / sizeof (*vbe_mode_list);
7457449d372SToomas Soome 	for (idx = 0; idx < nentries; idx++) {
7467449d372SToomas Soome 		mode = vbe_mode_list[idx];
7477449d372SToomas Soome 		if (mode == VESA_END_OF_MODE_LIST)
7489890ff83SToomas Soome 			break;
7497449d372SToomas Soome 
7509890ff83SToomas Soome 		if (biosvbe_get_mode_info(mode, &mi) != VBE_SUCCESS)
7519890ff83SToomas Soome 			continue;
7527449d372SToomas Soome 
7539890ff83SToomas Soome 		/* we only care about linear modes here */
7549890ff83SToomas Soome 		if (vbe_mode_is_supported(&mi) == 0)
7559890ff83SToomas Soome 			continue;
7569890ff83SToomas Soome 
7579890ff83SToomas Soome 		/* apply requested filter */
7589890ff83SToomas Soome 		if (depth != -1 && mi.BitsPerPixel != depth)
7599890ff83SToomas Soome 			continue;
7609890ff83SToomas Soome 
7619890ff83SToomas Soome 		if (nmodes % 4 == 0)
7629890ff83SToomas Soome 			printf("\n");
7639890ff83SToomas Soome 		else
7649890ff83SToomas Soome 			printf("  ");
7659890ff83SToomas Soome 
7669890ff83SToomas Soome 		vbe_dump_mode(mode, &mi);
7679890ff83SToomas Soome 		nmodes++;
7689890ff83SToomas Soome 	}
7699890ff83SToomas Soome 
7709890ff83SToomas Soome done:
7719890ff83SToomas Soome 	if (nmodes == 0)
7729890ff83SToomas Soome 		printf("none found");
7739890ff83SToomas Soome 	printf("\n");
7749890ff83SToomas Soome }
7759890ff83SToomas Soome 
7769890ff83SToomas Soome static void
vbe_print_mode(bool verbose)77791061836SToomas Soome vbe_print_mode(bool verbose)
7789890ff83SToomas Soome {
77991061836SToomas Soome 	int nc, mode, i, rc;
78091061836SToomas Soome 
78191061836SToomas Soome 	if (verbose)
78291061836SToomas Soome 		nc = 256;
78391061836SToomas Soome 	else
78491061836SToomas Soome 		nc = 16;
7859890ff83SToomas Soome 
7868d2ce326SToomas Soome 	memset(vbe, 0, sizeof (vbe));
7879890ff83SToomas Soome 	memcpy(vbe->VbeSignature, "VBE2", 4);
7889890ff83SToomas Soome 	if (biosvbe_info(vbe) != VBE_SUCCESS)
7899890ff83SToomas Soome 		return;
7909890ff83SToomas Soome 
7919890ff83SToomas Soome 	if (memcmp(vbe->VbeSignature, "VESA", 4) != 0)
7929890ff83SToomas Soome 		return;
7939890ff83SToomas Soome 
7949890ff83SToomas Soome 	vbe_print_vbe_info(vbe);
7959890ff83SToomas Soome 
7969890ff83SToomas Soome 	if (biosvbe_get_mode(&mode) != VBE_SUCCESS) {
7979890ff83SToomas Soome 		printf("Error getting current VBE mode\n");
7989890ff83SToomas Soome 		return;
7999890ff83SToomas Soome 	}
8009890ff83SToomas Soome 
8019890ff83SToomas Soome 	if (biosvbe_get_mode_info(mode, vbe_mode) != VBE_SUCCESS ||
8029890ff83SToomas Soome 	    vbe_mode_is_supported(vbe_mode) == 0) {
8039890ff83SToomas Soome 		printf("VBE mode (0x%x) is not framebuffer mode\n", mode);
8049890ff83SToomas Soome 		return;
8059890ff83SToomas Soome 	}
8069890ff83SToomas Soome 
8079890ff83SToomas Soome 	printf("\nCurrent VBE mode: ");
8089890ff83SToomas Soome 	vbe_dump_mode(mode, vbe_mode);
8099890ff83SToomas Soome 	printf("\n");
8109890ff83SToomas Soome 
8112f572942SToomas Soome 	printf("%ux%ux%u, stride=%u\n",
8129890ff83SToomas Soome 	    gfx_fb.framebuffer_common.framebuffer_width,
8139890ff83SToomas Soome 	    gfx_fb.framebuffer_common.framebuffer_height,
8149890ff83SToomas Soome 	    gfx_fb.framebuffer_common.framebuffer_bpp,
8158d2ce326SToomas Soome 	    (gfx_fb.framebuffer_common.framebuffer_pitch << 3) /
8169890ff83SToomas Soome 	    gfx_fb.framebuffer_common.framebuffer_bpp);
8172f572942SToomas Soome 	printf("    frame buffer: address=%jx, size=%jx\n",
8188d2ce326SToomas Soome 	    (uintmax_t)gfx_fb.framebuffer_common.framebuffer_addr,
8198d2ce326SToomas Soome 	    (uintmax_t)gfx_fb.framebuffer_common.framebuffer_height *
8209890ff83SToomas Soome 	    gfx_fb.framebuffer_common.framebuffer_pitch);
8219890ff83SToomas Soome 
8229890ff83SToomas Soome 	if (vbe_mode->MemoryModel == 0x6) {
8232f572942SToomas Soome 		printf("    color mask: R=%08x, G=%08x, B=%08x\n",
8249890ff83SToomas Soome 		    ((1 << gfx_fb.u.fb2.framebuffer_red_mask_size) - 1) <<
8259890ff83SToomas Soome 		    gfx_fb.u.fb2.framebuffer_red_field_position,
8269890ff83SToomas Soome 		    ((1 << gfx_fb.u.fb2.framebuffer_green_mask_size) - 1) <<
8279890ff83SToomas Soome 		    gfx_fb.u.fb2.framebuffer_green_field_position,
8289890ff83SToomas Soome 		    ((1 << gfx_fb.u.fb2.framebuffer_blue_mask_size) - 1) <<
8299890ff83SToomas Soome 		    gfx_fb.u.fb2.framebuffer_blue_field_position);
8309890ff83SToomas Soome 		return;
8319890ff83SToomas Soome 	}
8329890ff83SToomas Soome 
8339890ff83SToomas Soome 	mode = 1;	/* get DAC palette width */
8349890ff83SToomas Soome 	rc = biosvbe_palette_format(&mode);
8359890ff83SToomas Soome 	if (rc != VBE_SUCCESS)
8369890ff83SToomas Soome 		return;
8379890ff83SToomas Soome 
83891061836SToomas Soome 	printf("    palette format: %x bits per primary\n", mode);
83991061836SToomas Soome 	if (cmap == NULL)
84091061836SToomas Soome 		return;
8419890ff83SToomas Soome 
84291061836SToomas Soome 	pager_open();
84391061836SToomas Soome 	for (i = 0; i < nc; i++) {
84491061836SToomas Soome 		printf("%d: R=%02x, G=%02x, B=%02x", i,
84591061836SToomas Soome 		    cmap[i].mb_red, cmap[i].mb_green, cmap[i].mb_blue);
84691061836SToomas Soome 		if (pager_output("\n") != 0)
84791061836SToomas Soome 			break;
8489890ff83SToomas Soome 	}
84991061836SToomas Soome 	pager_close();
8509890ff83SToomas Soome }
8519890ff83SToomas Soome 
8524d503977SToomas Soome /*
8534d503977SToomas Soome  * Try EDID preferred mode, if EDID or the suggested mode is not available,
8544d503977SToomas Soome  * then try flat panel information.
8554d503977SToomas Soome  * Fall back to VBE_DEFAULT_MODE.
8564d503977SToomas Soome  */
8579890ff83SToomas Soome int
vbe_default_mode(void)8589890ff83SToomas Soome vbe_default_mode(void)
8599890ff83SToomas Soome {
860d06ea0b9SToomas Soome 	edid_res_list_t res;
861d06ea0b9SToomas Soome 	struct resolution *rp;
8629890ff83SToomas Soome 	int modenum;
8634d503977SToomas Soome 	uint_t width, height;
8649890ff83SToomas Soome 
8654d503977SToomas Soome 	modenum = 0;
866d06ea0b9SToomas Soome 	TAILQ_INIT(&res);
867d06ea0b9SToomas Soome 	if (vbe_get_edid(&res)) {
868d06ea0b9SToomas Soome 		while ((rp = TAILQ_FIRST(&res)) != NULL) {
869d06ea0b9SToomas Soome 			if (modenum == 0) {
870d06ea0b9SToomas Soome 				modenum = vbe_find_mode_xydm(
871d06ea0b9SToomas Soome 				    rp->width, rp->height, -1, -1);
872d06ea0b9SToomas Soome 			}
873d06ea0b9SToomas Soome 			TAILQ_REMOVE(&res, rp, next);
874d06ea0b9SToomas Soome 			free(rp);
875d06ea0b9SToomas Soome 		}
876d06ea0b9SToomas Soome 	}
8774d503977SToomas Soome 
8784d503977SToomas Soome 	if (modenum == 0 &&
8794d503977SToomas Soome 	    vbe_get_flatpanel(&width, &height)) {
8804d503977SToomas Soome 		modenum = vbe_find_mode_xydm(width, height, -1, -1);
8819890ff83SToomas Soome 	}
8824d503977SToomas Soome 
8834d503977SToomas Soome 	/* Still no mode? Fall back to default. */
8844d503977SToomas Soome 	if (modenum == 0)
8854d503977SToomas Soome 		modenum = vbe_find_mode(VBE_DEFAULT_MODE);
8869890ff83SToomas Soome 	return (modenum);
8879890ff83SToomas Soome }
8889890ff83SToomas Soome 
8899890ff83SToomas Soome COMMAND_SET(framebuffer, "framebuffer", "framebuffer mode management",
8909890ff83SToomas Soome     command_vesa);
8919890ff83SToomas Soome 
8929890ff83SToomas Soome int
command_vesa(int argc,char * argv[])8939890ff83SToomas Soome command_vesa(int argc, char *argv[])
8949890ff83SToomas Soome {
8959890ff83SToomas Soome 	char *arg, *cp;
8969890ff83SToomas Soome 	int modenum = -1, n;
8979890ff83SToomas Soome 
8989890ff83SToomas Soome 	if (!vbe_check())
8999890ff83SToomas Soome 		return (CMD_OK);
9009890ff83SToomas Soome 
9019890ff83SToomas Soome 	if (argc < 2)
9029890ff83SToomas Soome 		goto usage;
9039890ff83SToomas Soome 
9049890ff83SToomas Soome 	if (strcmp(argv[1], "list") == 0) {
9059890ff83SToomas Soome 		n = -1;
9069890ff83SToomas Soome 		if (argc != 2 && argc != 3)
9079890ff83SToomas Soome 			goto usage;
9089890ff83SToomas Soome 
9099890ff83SToomas Soome 		if (argc == 3) {
9109890ff83SToomas Soome 			arg = argv[2];
9119890ff83SToomas Soome 			errno = 0;
9129890ff83SToomas Soome 			n = strtoul(arg, &cp, 0);
9139890ff83SToomas Soome 			if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
9149890ff83SToomas Soome 				snprintf(command_errbuf,
9159890ff83SToomas Soome 				    sizeof (command_errbuf),
9169890ff83SToomas Soome 				    "depth should be an integer");
9179890ff83SToomas Soome 				return (CMD_ERROR);
9189890ff83SToomas Soome 			}
9199890ff83SToomas Soome 		}
9209890ff83SToomas Soome 		vbe_modelist(n);
9219890ff83SToomas Soome 		return (CMD_OK);
9229890ff83SToomas Soome 	}
9239890ff83SToomas Soome 
9249890ff83SToomas Soome 	if (strcmp(argv[1], "get") == 0) {
92591061836SToomas Soome 		bool verbose = false;
92691061836SToomas Soome 
92791061836SToomas Soome 		if (argc > 2) {
92891061836SToomas Soome 			if (argc > 3 || strcmp(argv[2], "-v") != 0)
92991061836SToomas Soome 				goto usage;
93091061836SToomas Soome 			verbose = true;
93191061836SToomas Soome 		}
9329890ff83SToomas Soome 
93391061836SToomas Soome 		vbe_print_mode(verbose);
9349890ff83SToomas Soome 		return (CMD_OK);
9359890ff83SToomas Soome 	}
9369890ff83SToomas Soome 
9379890ff83SToomas Soome 	if (strcmp(argv[1], "off") == 0) {
9389890ff83SToomas Soome 		if (argc != 2)
9399890ff83SToomas Soome 			goto usage;
9409890ff83SToomas Soome 
9419890ff83SToomas Soome 		if (vbestate.vbe_mode == 0)
9429890ff83SToomas Soome 			return (CMD_OK);
9439890ff83SToomas Soome 
944337411fcSToomas Soome 		reset_font_flags();
945e0721d5aSToomas Soome 		bios_text_font(true);
9469890ff83SToomas Soome 		bios_set_text_mode(VGA_TEXT_MODE);
9479890ff83SToomas Soome 		plat_cons_update_mode(0);
9489890ff83SToomas Soome 		return (CMD_OK);
9499890ff83SToomas Soome 	}
9509890ff83SToomas Soome 
9519890ff83SToomas Soome 	if (strcmp(argv[1], "on") == 0) {
9529890ff83SToomas Soome 		if (argc != 2)
9539890ff83SToomas Soome 			goto usage;
9549890ff83SToomas Soome 
9559890ff83SToomas Soome 		modenum = vbe_default_mode();
9569890ff83SToomas Soome 		if (modenum == 0) {
9579890ff83SToomas Soome 			snprintf(command_errbuf, sizeof (command_errbuf),
9589890ff83SToomas Soome 			    "%s: no suitable VBE mode number found", argv[0]);
9599890ff83SToomas Soome 			return (CMD_ERROR);
9609890ff83SToomas Soome 		}
9619890ff83SToomas Soome 	} else if (strcmp(argv[1], "set") == 0) {
9629890ff83SToomas Soome 		if (argc != 3)
9639890ff83SToomas Soome 			goto usage;
9649890ff83SToomas Soome 
9659890ff83SToomas Soome 		if (strncmp(argv[2], "0x", 2) == 0) {
9669890ff83SToomas Soome 			arg = argv[2];
9679890ff83SToomas Soome 			errno = 0;
9689890ff83SToomas Soome 			n = strtoul(arg, &cp, 0);
9699890ff83SToomas Soome 			if (errno != 0 || *arg == '\0' || cp[0] != '\0') {
9709890ff83SToomas Soome 				snprintf(command_errbuf,
9719890ff83SToomas Soome 				    sizeof (command_errbuf),
9729890ff83SToomas Soome 				    "mode should be an integer");
9739890ff83SToomas Soome 				return (CMD_ERROR);
9749890ff83SToomas Soome 			}
9759890ff83SToomas Soome 			modenum = vbe_find_mode_xydm(0, 0, 0, n);
9769890ff83SToomas Soome 		} else if (strchr(argv[2], 'x') != NULL) {
9779890ff83SToomas Soome 			modenum = vbe_find_mode(argv[2]);
9789890ff83SToomas Soome 		}
9798d2ce326SToomas Soome 	} else {
9808d2ce326SToomas Soome 		goto usage;
9819890ff83SToomas Soome 	}
9829890ff83SToomas Soome 
9839890ff83SToomas Soome 	if (modenum == 0) {
9849890ff83SToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
9859890ff83SToomas Soome 		    "%s: mode %s not supported by firmware\n",
9869890ff83SToomas Soome 		    argv[0], argv[2]);
9879890ff83SToomas Soome 		return (CMD_ERROR);
9889890ff83SToomas Soome 	}
9899890ff83SToomas Soome 
990d20422bdSToomas Soome 	if (modenum >= VESA_MODE_BASE) {
9919890ff83SToomas Soome 		if (vbestate.vbe_mode != modenum) {
992337411fcSToomas Soome 			reset_font_flags();
993e0721d5aSToomas Soome 			bios_text_font(false);
9949890ff83SToomas Soome 			vbe_set_mode(modenum);
9959890ff83SToomas Soome 			plat_cons_update_mode(1);
9969890ff83SToomas Soome 		}
9979890ff83SToomas Soome 		return (CMD_OK);
9989890ff83SToomas Soome 	} else {
9999890ff83SToomas Soome 		snprintf(command_errbuf, sizeof (command_errbuf),
10009890ff83SToomas Soome 		    "%s: mode %s is not framebuffer mode\n", argv[0], argv[2]);
10019890ff83SToomas Soome 		return (CMD_ERROR);
10029890ff83SToomas Soome 	}
10039890ff83SToomas Soome 
10049890ff83SToomas Soome usage:
10059890ff83SToomas Soome 	snprintf(command_errbuf, sizeof (command_errbuf),
100691061836SToomas Soome 	    "usage: %s on | off | get [-v] | list [depth] | "
10079890ff83SToomas Soome 	    "set <display or VBE mode number>", argv[0]);
10089890ff83SToomas Soome 	return (CMD_ERROR);
10099890ff83SToomas Soome }
1010