| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 1 | /* -*- linux-c -*- ------------------------------------------------------- * | 
 | 2 |  * | 
 | 3 |  *   Copyright (C) 1991, 1992 Linus Torvalds | 
 | 4 |  *   Copyright 2007 rPath, Inc. - All Rights Reserved | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 5 |  *   Copyright 2009 Intel Corporation; author H. Peter Anvin | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 6 |  * | 
 | 7 |  *   This file is part of the Linux kernel, and is made available under | 
 | 8 |  *   the terms of the GNU General Public License version 2. | 
 | 9 |  * | 
 | 10 |  * ----------------------------------------------------------------------- */ | 
 | 11 |  | 
 | 12 | /* | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 13 |  * VESA text modes | 
 | 14 |  */ | 
 | 15 |  | 
 | 16 | #include "boot.h" | 
 | 17 | #include "video.h" | 
 | 18 | #include "vesa.h" | 
 | 19 |  | 
 | 20 | /* VESA information */ | 
 | 21 | static struct vesa_general_info vginfo; | 
 | 22 | static struct vesa_mode_info vminfo; | 
 | 23 |  | 
| roel kluin | 8bcad30 | 2008-10-21 19:49:09 -0400 | [diff] [blame] | 24 | static __videocard video_vesa; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 25 |  | 
| Pavel Machek | e44b7b7 | 2008-04-10 23:28:10 +0200 | [diff] [blame] | 26 | #ifndef _WAKEUP | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 27 | static void vesa_store_mode_params_graphics(void); | 
| Pavel Machek | e44b7b7 | 2008-04-10 23:28:10 +0200 | [diff] [blame] | 28 | #else /* _WAKEUP */ | 
 | 29 | static inline void vesa_store_mode_params_graphics(void) {} | 
 | 30 | #endif /* _WAKEUP */ | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 31 |  | 
 | 32 | static int vesa_probe(void) | 
 | 33 | { | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 34 | 	struct biosregs ireg, oreg; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 35 | 	u16 mode; | 
 | 36 | 	addr_t mode_ptr; | 
 | 37 | 	struct mode_info *mi; | 
 | 38 | 	int nmodes = 0; | 
 | 39 |  | 
 | 40 | 	video_vesa.modes = GET_HEAP(struct mode_info, 0); | 
 | 41 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 42 | 	initregs(&ireg); | 
 | 43 | 	ireg.ax = 0x4f00; | 
 | 44 | 	ireg.di = (size_t)&vginfo; | 
 | 45 | 	intcall(0x10, &ireg, &oreg); | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 46 |  | 
| Akinobu Mita | febe04d | 2009-07-01 11:13:07 +0900 | [diff] [blame] | 47 | 	if (oreg.ax != 0x004f || | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 48 | 	    vginfo.signature != VESA_MAGIC || | 
 | 49 | 	    vginfo.version < 0x0102) | 
 | 50 | 		return 0;	/* Not present */ | 
| H. Peter Anvin | 2495fbf | 2009-06-26 10:53:57 -0700 | [diff] [blame] | 51 |  | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 52 | 	set_fs(vginfo.video_mode_ptr.seg); | 
 | 53 | 	mode_ptr = vginfo.video_mode_ptr.off; | 
 | 54 |  | 
 | 55 | 	while ((mode = rdfs16(mode_ptr)) != 0xffff) { | 
 | 56 | 		mode_ptr += 2; | 
 | 57 |  | 
| H. Peter Anvin | e6e1ace | 2007-10-25 16:09:38 -0700 | [diff] [blame] | 58 | 		if (!heap_free(sizeof(struct mode_info))) | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 59 | 			break;	/* Heap full, can't save mode info */ | 
 | 60 |  | 
 | 61 | 		if (mode & ~0x1ff) | 
 | 62 | 			continue; | 
 | 63 |  | 
 | 64 | 		memset(&vminfo, 0, sizeof vminfo); /* Just in case... */ | 
 | 65 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 66 | 		ireg.ax = 0x4f01; | 
 | 67 | 		ireg.cx = mode; | 
 | 68 | 		ireg.di = (size_t)&vminfo; | 
 | 69 | 		intcall(0x10, &ireg, &oreg); | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 70 |  | 
| Akinobu Mita | febe04d | 2009-07-01 11:13:07 +0900 | [diff] [blame] | 71 | 		if (oreg.ax != 0x004f) | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 72 | 			continue; | 
 | 73 |  | 
 | 74 | 		if ((vminfo.mode_attr & 0x15) == 0x05) { | 
 | 75 | 			/* Text Mode, TTY BIOS supported, | 
 | 76 | 			   supported by hardware */ | 
 | 77 | 			mi = GET_HEAP(struct mode_info, 1); | 
| H. Peter Anvin | 1cac500 | 2008-01-30 13:33:02 +0100 | [diff] [blame] | 78 | 			mi->mode  = mode + VIDEO_FIRST_VESA; | 
 | 79 | 			mi->depth = 0; /* text */ | 
 | 80 | 			mi->x     = vminfo.h_res; | 
 | 81 | 			mi->y     = vminfo.v_res; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 82 | 			nmodes++; | 
| H. Peter Anvin | 1cac500 | 2008-01-30 13:33:02 +0100 | [diff] [blame] | 83 | 		} else if ((vminfo.mode_attr & 0x99) == 0x99 && | 
 | 84 | 			   (vminfo.memory_layout == 4 || | 
 | 85 | 			    vminfo.memory_layout == 6) && | 
 | 86 | 			   vminfo.memory_planes == 1) { | 
| Michal Januszewski | 4d31a2b | 2008-10-15 22:03:51 -0700 | [diff] [blame] | 87 | #ifdef CONFIG_FB_BOOT_VESA_SUPPORT | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 88 | 			/* Graphics mode, color, linear frame buffer | 
| H. Peter Anvin | 1cac500 | 2008-01-30 13:33:02 +0100 | [diff] [blame] | 89 | 			   supported.  Only register the mode if | 
 | 90 | 			   if framebuffer is configured, however, | 
| Michal Januszewski | 4d31a2b | 2008-10-15 22:03:51 -0700 | [diff] [blame] | 91 | 			   otherwise the user will be left without a screen. */ | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 92 | 			mi = GET_HEAP(struct mode_info, 1); | 
 | 93 | 			mi->mode = mode + VIDEO_FIRST_VESA; | 
| H. Peter Anvin | 1cac500 | 2008-01-30 13:33:02 +0100 | [diff] [blame] | 94 | 			mi->depth = vminfo.bpp; | 
 | 95 | 			mi->x = vminfo.h_res; | 
 | 96 | 			mi->y = vminfo.v_res; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 97 | 			nmodes++; | 
 | 98 | #endif | 
 | 99 | 		} | 
 | 100 | 	} | 
 | 101 |  | 
 | 102 | 	return nmodes; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 103 | } | 
 | 104 |  | 
 | 105 | static int vesa_set_mode(struct mode_info *mode) | 
 | 106 | { | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 107 | 	struct biosregs ireg, oreg; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 108 | 	int is_graphic; | 
 | 109 | 	u16 vesa_mode = mode->mode - VIDEO_FIRST_VESA; | 
 | 110 |  | 
 | 111 | 	memset(&vminfo, 0, sizeof vminfo); /* Just in case... */ | 
 | 112 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 113 | 	initregs(&ireg); | 
 | 114 | 	ireg.ax = 0x4f01; | 
 | 115 | 	ireg.cx = vesa_mode; | 
 | 116 | 	ireg.di = (size_t)&vminfo; | 
 | 117 | 	intcall(0x10, &ireg, &oreg); | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 118 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 119 | 	if (oreg.ax != 0x004f) | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 120 | 		return -1; | 
 | 121 |  | 
 | 122 | 	if ((vminfo.mode_attr & 0x15) == 0x05) { | 
 | 123 | 		/* It's a supported text mode */ | 
 | 124 | 		is_graphic = 0; | 
| Michal Januszewski | 4d31a2b | 2008-10-15 22:03:51 -0700 | [diff] [blame] | 125 | #ifdef CONFIG_FB_BOOT_VESA_SUPPORT | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 126 | 	} else if ((vminfo.mode_attr & 0x99) == 0x99) { | 
 | 127 | 		/* It's a graphics mode with linear frame buffer */ | 
 | 128 | 		is_graphic = 1; | 
 | 129 | 		vesa_mode |= 0x4000; /* Request linear frame buffer */ | 
| Michal Januszewski | 4d31a2b | 2008-10-15 22:03:51 -0700 | [diff] [blame] | 130 | #endif | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 131 | 	} else { | 
 | 132 | 		return -1;	/* Invalid mode */ | 
 | 133 | 	} | 
 | 134 |  | 
 | 135 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 136 | 	initregs(&ireg); | 
 | 137 | 	ireg.ax = 0x4f02; | 
 | 138 | 	ireg.bx = vesa_mode; | 
 | 139 | 	intcall(0x10, &ireg, &oreg); | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 140 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 141 | 	if (oreg.ax != 0x004f) | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 142 | 		return -1; | 
 | 143 |  | 
 | 144 | 	graphic_mode = is_graphic; | 
 | 145 | 	if (!is_graphic) { | 
 | 146 | 		/* Text mode */ | 
 | 147 | 		force_x = mode->x; | 
 | 148 | 		force_y = mode->y; | 
 | 149 | 		do_restore = 1; | 
 | 150 | 	} else { | 
 | 151 | 		/* Graphics mode */ | 
 | 152 | 		vesa_store_mode_params_graphics(); | 
 | 153 | 	} | 
 | 154 |  | 
 | 155 | 	return 0; | 
 | 156 | } | 
 | 157 |  | 
 | 158 |  | 
| Pavel Machek | e44b7b7 | 2008-04-10 23:28:10 +0200 | [diff] [blame] | 159 | #ifndef _WAKEUP | 
 | 160 |  | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 161 | /* Switch DAC to 8-bit mode */ | 
 | 162 | static void vesa_dac_set_8bits(void) | 
 | 163 | { | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 164 | 	struct biosregs ireg, oreg; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 165 | 	u8 dac_size = 6; | 
 | 166 |  | 
 | 167 | 	/* If possible, switch the DAC to 8-bit mode */ | 
 | 168 | 	if (vginfo.capabilities & 1) { | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 169 | 		initregs(&ireg); | 
 | 170 | 		ireg.ax = 0x4f08; | 
 | 171 | 		ireg.bh = 0x08; | 
 | 172 | 		intcall(0x10, &ireg, &oreg); | 
 | 173 | 		if (oreg.ax == 0x004f) | 
 | 174 | 			dac_size = oreg.bh; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 175 | 	} | 
 | 176 |  | 
 | 177 | 	/* Set the color sizes to the DAC size, and offsets to 0 */ | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 178 | 	boot_params.screen_info.red_size   = dac_size; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 179 | 	boot_params.screen_info.green_size = dac_size; | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 180 | 	boot_params.screen_info.blue_size  = dac_size; | 
 | 181 | 	boot_params.screen_info.rsvd_size  = dac_size; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 182 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 183 | 	boot_params.screen_info.red_pos    = 0; | 
 | 184 | 	boot_params.screen_info.green_pos  = 0; | 
 | 185 | 	boot_params.screen_info.blue_pos   = 0; | 
 | 186 | 	boot_params.screen_info.rsvd_pos   = 0; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 187 | } | 
 | 188 |  | 
 | 189 | /* Save the VESA protected mode info */ | 
 | 190 | static void vesa_store_pm_info(void) | 
 | 191 | { | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 192 | 	struct biosregs ireg, oreg; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 193 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 194 | 	initregs(&ireg); | 
 | 195 | 	ireg.ax = 0x4f0a; | 
 | 196 | 	intcall(0x10, &ireg, &oreg); | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 197 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 198 | 	if (oreg.ax != 0x004f) | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 199 | 		return; | 
 | 200 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 201 | 	boot_params.screen_info.vesapm_seg = oreg.es; | 
 | 202 | 	boot_params.screen_info.vesapm_off = oreg.di; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 203 | } | 
 | 204 |  | 
 | 205 | /* | 
 | 206 |  * Save video mode parameters for graphics mode | 
 | 207 |  */ | 
 | 208 | static void vesa_store_mode_params_graphics(void) | 
 | 209 | { | 
 | 210 | 	/* Tell the kernel we're in VESA graphics mode */ | 
| Michal Januszewski | 2407390 | 2008-10-05 12:16:04 +0200 | [diff] [blame] | 211 | 	boot_params.screen_info.orig_video_isVGA = VIDEO_TYPE_VLFB; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 212 |  | 
 | 213 | 	/* Mode parameters */ | 
 | 214 | 	boot_params.screen_info.vesa_attributes = vminfo.mode_attr; | 
 | 215 | 	boot_params.screen_info.lfb_linelength = vminfo.logical_scan; | 
 | 216 | 	boot_params.screen_info.lfb_width = vminfo.h_res; | 
 | 217 | 	boot_params.screen_info.lfb_height = vminfo.v_res; | 
 | 218 | 	boot_params.screen_info.lfb_depth = vminfo.bpp; | 
 | 219 | 	boot_params.screen_info.pages = vminfo.image_planes; | 
 | 220 | 	boot_params.screen_info.lfb_base = vminfo.lfb_ptr; | 
 | 221 | 	memcpy(&boot_params.screen_info.red_size, | 
 | 222 | 	       &vminfo.rmask, 8); | 
 | 223 |  | 
 | 224 | 	/* General parameters */ | 
 | 225 | 	boot_params.screen_info.lfb_size = vginfo.total_memory; | 
 | 226 |  | 
 | 227 | 	if (vminfo.bpp <= 8) | 
 | 228 | 		vesa_dac_set_8bits(); | 
 | 229 |  | 
 | 230 | 	vesa_store_pm_info(); | 
 | 231 | } | 
 | 232 |  | 
 | 233 | /* | 
 | 234 |  * Save EDID information for the kernel; this is invoked, separately, | 
 | 235 |  * after mode-setting. | 
 | 236 |  */ | 
 | 237 | void vesa_store_edid(void) | 
 | 238 | { | 
 | 239 | #ifdef CONFIG_FIRMWARE_EDID | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 240 | 	struct biosregs ireg, oreg; | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 241 |  | 
 | 242 | 	/* Apparently used as a nonsense token... */ | 
 | 243 | 	memset(&boot_params.edid_info, 0x13, sizeof boot_params.edid_info); | 
 | 244 |  | 
 | 245 | 	if (vginfo.version < 0x0200) | 
 | 246 | 		return;		/* EDID requires VBE 2.0+ */ | 
 | 247 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 248 | 	initregs(&ireg); | 
 | 249 | 	ireg.ax = 0x4f15;		/* VBE DDC */ | 
 | 250 | 	/* ireg.bx = 0x0000; */		/* Report DDC capabilities */ | 
 | 251 | 	/* ireg.cx = 0;	*/		/* Controller 0 */ | 
 | 252 | 	ireg.es = 0;			/* ES:DI must be 0 by spec */ | 
 | 253 | 	intcall(0x10, &ireg, &oreg); | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 254 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 255 | 	if (oreg.ax != 0x004f) | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 256 | 		return;		/* No EDID */ | 
 | 257 |  | 
 | 258 | 	/* BH = time in seconds to transfer EDD information */ | 
 | 259 | 	/* BL = DDC level supported */ | 
 | 260 |  | 
| H. Peter Anvin | cf06de7 | 2009-04-01 18:20:11 -0700 | [diff] [blame] | 261 | 	ireg.ax = 0x4f15;		/* VBE DDC */ | 
 | 262 | 	ireg.bx = 0x0001;		/* Read EDID */ | 
 | 263 | 	/* ireg.cx = 0; */		/* Controller 0 */ | 
 | 264 | 	/* ireg.dx = 0;	*/		/* EDID block number */ | 
 | 265 | 	ireg.es = ds(); | 
 | 266 | 	ireg.di =(size_t)&boot_params.edid_info; /* (ES:)Pointer to block */ | 
 | 267 | 	intcall(0x10, &ireg, &oreg); | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 268 | #endif /* CONFIG_FIRMWARE_EDID */ | 
 | 269 | } | 
 | 270 |  | 
| Pavel Machek | e44b7b7 | 2008-04-10 23:28:10 +0200 | [diff] [blame] | 271 | #endif /* not _WAKEUP */ | 
 | 272 |  | 
| roel kluin | 8bcad30 | 2008-10-21 19:49:09 -0400 | [diff] [blame] | 273 | static __videocard video_vesa = | 
| H. Peter Anvin | 5e8ddcbe | 2007-07-11 12:18:52 -0700 | [diff] [blame] | 274 | { | 
 | 275 | 	.card_name	= "VESA", | 
 | 276 | 	.probe		= vesa_probe, | 
 | 277 | 	.set_mode	= vesa_set_mode, | 
 | 278 | 	.xmode_first	= VIDEO_FIRST_VESA, | 
 | 279 | 	.xmode_n	= 0x200, | 
 | 280 | }; |