xref: /haiku/src/system/boot/platform/bios_ia32/video.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2004-2009, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2008, Stephan Aßmus <superstippi@gmx.de>
4  * Copyright 2008, Philippe Saint-Pierre <stpere@gmail.com>
5  * Copyright 2011, Rene Gollent, rene@gollent.com.
6  * Distributed under the terms of the MIT License.
7  */
8 
9 
10 #include "video.h"
11 #include "bios.h"
12 #include "vesa.h"
13 #include "vesa_info.h"
14 #include "vga.h"
15 #include "mmu.h"
16 
17 #include <edid.h>
18 
19 #include <arch/cpu.h>
20 #include <boot/stage2.h>
21 #include <boot/platform.h>
22 #include <boot/menu.h>
23 #include <boot/kernel_args.h>
24 #include <boot/platform/generic/video.h>
25 #include <util/list.h>
26 #include <drivers/driver_settings.h>
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 
33 #define TRACE_VIDEO
34 #ifdef TRACE_VIDEO
35 #	define TRACE(x) dprintf x
36 #else
37 #	define TRACE(x) ;
38 #endif
39 
40 
41 struct video_mode {
42 	list_link	link;
43 	uint16		mode;
44 	uint16		width, height, bits_per_pixel;
45 	uint32		bytes_per_row;
46 	crtc_info_block* timing;
47 };
48 
49 static vbe_info_block sInfo;
50 static video_mode *sMode, *sDefaultMode;
51 static bool sVesaCompatible;
52 static struct list sModeList;
53 static uint32 sModeCount;
54 static addr_t sFrameBuffer;
55 static bool sModeChosen;
56 static bool sSettingsLoaded;
57 
58 
59 static int
60 compare_video_modes(video_mode *a, video_mode *b)
61 {
62 	int compare = a->width - b->width;
63 	if (compare != 0)
64 		return compare;
65 
66 	compare = a->height - b->height;
67 	if (compare != 0)
68 		return compare;
69 
70 	// TODO: compare video_mode::mode?
71 	return a->bits_per_pixel - b->bits_per_pixel;
72 }
73 
74 
75 /*!	Insert the video mode into the list, sorted by resolution and bit depth.
76 	Higher resolutions/depths come first.
77 */
78 static void
79 add_video_mode(video_mode *videoMode)
80 {
81 	video_mode *mode = NULL;
82 	while ((mode = (video_mode *)list_get_next_item(&sModeList, mode))
83 			!= NULL) {
84 		int compare = compare_video_modes(videoMode, mode);
85 		if (compare == 0) {
86 			// mode already exists
87 			return;
88 		}
89 
90 		if (compare > 0)
91 			break;
92 	}
93 
94 	list_insert_item_before(&sModeList, mode, videoMode);
95 	sModeCount++;
96 }
97 
98 
99 /*! \brief Finds a video mode with the given resolution.
100 	If \a allowPalette is true, 8-bit modes are considered, too.
101 	If \a height is \c -1, the height is ignored, and only the width
102 	matters.
103 */
104 static video_mode *
105 find_video_mode(int32 width, int32 height, bool allowPalette)
106 {
107 	video_mode *found = NULL;
108 	video_mode *mode = NULL;
109 	while ((mode = (video_mode *)list_get_next_item(&sModeList, mode))
110 			!= NULL) {
111 		if (mode->width == width && (height == -1 || mode->height == height)
112 			&& (mode->bits_per_pixel > 8 || allowPalette)) {
113 			if (found == NULL || found->bits_per_pixel < mode->bits_per_pixel)
114 				found = mode;
115 		}
116 	}
117 
118 	return found;
119 }
120 
121 
122 /*! Returns the VESA mode closest to the one specified, with a width less or
123 	equal as specified.
124 	The height as well as the depth may vary in both directions, though.
125 */
126 static video_mode *
127 closest_video_mode(int32 width, int32 height, int32 depth)
128 {
129 	video_mode *bestMode = NULL;
130 	uint32 bestDiff = 0;
131 
132 	video_mode *mode = NULL;
133 	while ((mode = (video_mode *)list_get_next_item(&sModeList, mode))
134 			!= NULL) {
135 		if (mode->width > width) {
136 			// Only choose modes with a width less or equal than the searched
137 			// one; or else it might well be that the monitor cannot keep up.
138 			continue;
139 		}
140 
141 		uint32 diff = 2 * abs(mode->width - width) + abs(mode->height - height)
142 			+ abs(mode->bits_per_pixel - depth);
143 
144 		if (bestMode == NULL || bestDiff > diff) {
145 			bestMode = mode;
146 			bestDiff = diff;
147 		}
148 	}
149 
150 	return bestMode;
151 }
152 
153 
154 static crtc_info_block*
155 get_crtc_info_block(edid1_detailed_timing& timing)
156 {
157 	// This feature is only available on chipsets supporting VBE3 and up
158 	if (sInfo.version.major < 3)
159 		return NULL;
160 
161 	// Copy timing structure to set the mode with
162 	crtc_info_block* crtcInfo = (crtc_info_block*)malloc(
163 		sizeof(crtc_info_block));
164 	if (crtcInfo == NULL)
165 		return NULL;
166 
167 	memset(crtcInfo, 0, sizeof(crtc_info_block));
168 	crtcInfo->horizontal_sync_start = timing.h_active + timing.h_sync_off;
169 	crtcInfo->horizontal_sync_end = crtcInfo->horizontal_sync_start
170 		+ timing.h_sync_width;
171 	crtcInfo->horizontal_total = timing.h_active + timing.h_blank;
172 	crtcInfo->vertical_sync_start = timing.v_active + timing.v_sync_off;
173 	crtcInfo->vertical_sync_end = crtcInfo->vertical_sync_start
174 		+ timing.v_sync_width;
175 	crtcInfo->vertical_total = timing.v_active + timing.v_blank;
176 	crtcInfo->pixel_clock = timing.pixel_clock * 10000L;
177 	crtcInfo->refresh_rate = crtcInfo->pixel_clock
178 		/ (crtcInfo->horizontal_total / 10)
179 		/ (crtcInfo->vertical_total / 10);
180 
181 	TRACE(("crtc: h %u/%u/%u, v %u/%u/%u, pixel clock %lu, refresh %u\n",
182 		crtcInfo->horizontal_sync_start, crtcInfo->horizontal_sync_end,
183 		crtcInfo->horizontal_total, crtcInfo->vertical_sync_start,
184 		crtcInfo->vertical_sync_end, crtcInfo->vertical_total,
185 		crtcInfo->pixel_clock, crtcInfo->refresh_rate));
186 
187 	crtcInfo->flags = 0;
188 	if (timing.sync == 3) {
189 		// TODO: this switches the default sync when sync != 3 (compared to
190 		// create_display_modes().
191 		if ((timing.misc & 1) == 0)
192 			crtcInfo->flags |= CRTC_NEGATIVE_HSYNC;
193 		if ((timing.misc & 2) == 0)
194 			crtcInfo->flags |= CRTC_NEGATIVE_VSYNC;
195 	}
196 	if (timing.interlaced)
197 		crtcInfo->flags |= CRTC_INTERLACED;
198 
199 	return crtcInfo;
200 }
201 
202 
203 static crtc_info_block*
204 get_crtc_info_block(edid1_std_timing& timing)
205 {
206 	// TODO: implement me!
207 	return NULL;
208 }
209 
210 
211 static video_mode*
212 find_edid_mode(edid1_info& info, bool allowPalette)
213 {
214 	video_mode *mode = NULL;
215 
216 	// try detailed timing first
217 	for (int32 i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; i++) {
218 		edid1_detailed_monitor& monitor = info.detailed_monitor[i];
219 
220 		if (monitor.monitor_desc_type == EDID1_IS_DETAILED_TIMING) {
221 			mode = find_video_mode(monitor.data.detailed_timing.h_active,
222 				monitor.data.detailed_timing.v_active, allowPalette);
223 			if (mode != NULL) {
224 				mode->timing
225 					= get_crtc_info_block(monitor.data.detailed_timing);
226 				return mode;
227 			}
228 		}
229 	}
230 
231 	int32 best = -1;
232 
233 	// try standard timings next
234 	for (int32 i = 0; i < EDID1_NUM_STD_TIMING; i++) {
235 		if (info.std_timing[i].h_size <= 256)
236 			continue;
237 
238 		video_mode* found = find_video_mode(info.std_timing[i].h_size,
239 			info.std_timing[i].v_size, allowPalette);
240 		if (found != NULL) {
241 			if (mode != NULL) {
242 				// prefer higher resolutions
243 				if (found->width > mode->width) {
244 					mode = found;
245 					best = i;
246 				}
247 			} else {
248 				mode = found;
249 				best = i;
250 			}
251 		}
252 	}
253 
254 	if (best >= 0)
255 		mode->timing = get_crtc_info_block(info.std_timing[best]);
256 
257 	return mode;
258 }
259 
260 
261 static bool
262 get_mode_from_settings(void)
263 {
264 	if (sSettingsLoaded)
265 		return true;
266 
267 	void *handle = load_driver_settings("vesa");
268 	if (handle == NULL)
269 		return false;
270 
271 	bool found = false;
272 
273 	const driver_settings *settings = get_driver_settings(handle);
274 	if (settings == NULL)
275 		goto out;
276 
277 	sSettingsLoaded = true;
278 
279 	for (int32 i = 0; i < settings->parameter_count; i++) {
280 		driver_parameter &parameter = settings->parameters[i];
281 
282 		if (!strcmp(parameter.name, "mode") && parameter.value_count > 2) {
283 			// parameter found, now get its values
284 			int32 width = strtol(parameter.values[0], NULL, 0);
285 			int32 height = strtol(parameter.values[1], NULL, 0);
286 			int32 depth = strtol(parameter.values[2], NULL, 0);
287 
288 			// search mode that fits
289 
290 			video_mode *mode = closest_video_mode(width, height, depth);
291 			if (mode != NULL) {
292 				found = true;
293 				sMode = mode;
294 			}
295 		}
296 	}
297 
298 out:
299 	unload_driver_settings(handle);
300 	return found;
301 }
302 
303 
304 //	#pragma mark - vga
305 
306 
307 static void
308 vga_set_palette(const uint8 *palette, int32 firstIndex, int32 numEntries)
309 {
310 	out8(firstIndex, VGA_COLOR_WRITE_MODE);
311 	// write VGA palette
312 	for (int32 i = firstIndex; i < numEntries; i++) {
313 		// VGA (usually) has only 6 bits per gun
314 		out8(palette[i * 3 + 0] >> 2, VGA_COLOR_DATA);
315 		out8(palette[i * 3 + 1] >> 2, VGA_COLOR_DATA);
316 		out8(palette[i * 3 + 2] >> 2, VGA_COLOR_DATA);
317 	}
318 }
319 
320 
321 static void
322 vga_enable_bright_background_colors(void)
323 {
324 	// reset attribute controller
325 	in8(VGA_INPUT_STATUS_1);
326 
327 	// select mode control register
328 	out8(0x30, VGA_ATTRIBUTE_WRITE);
329 
330 	// read mode control register, change it (we need to clear bit 3), and write it back
331 	uint8 mode = in8(VGA_ATTRIBUTE_READ) & 0xf7;
332 	out8(mode, VGA_ATTRIBUTE_WRITE);
333 }
334 
335 
336 //	#pragma mark - vesa
337 
338 
339 static status_t
340 vesa_get_edid(edid1_info *info)
341 {
342 	struct bios_regs regs;
343 	regs.eax = 0x4f15;
344 	regs.ebx = 0;
345 		// report DDC service
346 	regs.ecx = 0;
347 	regs.es = 0;
348 	regs.edi = 0;
349 	call_bios(0x10, &regs);
350 
351 	TRACE(("EDID1: %lx\n", regs.eax));
352 	// %ah contains the error code
353 	// %al determines whether or not the function is supported
354 	if (regs.eax != 0x4f)
355 		return B_NOT_SUPPORTED;
356 
357 	TRACE(("EDID2: ebx %lx\n", regs.ebx));
358 	// test if DDC is supported by the monitor
359 	if ((regs.ebx & 3) == 0)
360 		return B_NOT_SUPPORTED;
361 
362 	edid1_raw edidRaw;
363 
364 	regs.eax = 0x4f15;
365 	regs.ebx = 1;
366 		// read EDID
367 	regs.ecx = 0;
368 	regs.edx = 0;
369 	regs.es = ADDRESS_SEGMENT(&edidRaw);
370 	regs.edi = ADDRESS_OFFSET(&edidRaw);
371 	call_bios(0x10, &regs);
372 	TRACE(("EDID3: %lx\n", regs.eax));
373 
374 	if (regs.eax != 0x4f)
375 		return B_NOT_SUPPORTED;
376 
377 	// retrieved EDID - now parse it
378 	edid_decode(info, &edidRaw);
379 
380 #ifdef TRACE_VIDEO
381 	edid_dump(info);
382 #endif
383 	return B_OK;
384 }
385 
386 
387 static status_t
388 vesa_get_mode_info(uint16 mode, struct vbe_mode_info *modeInfo)
389 {
390 	memset(modeInfo, 0, sizeof(vbe_mode_info));
391 
392 	struct bios_regs regs;
393 	regs.eax = 0x4f01;
394 	regs.ecx = mode;
395 	regs.es = ADDRESS_SEGMENT(modeInfo);
396 	regs.edi = ADDRESS_OFFSET(modeInfo);
397 	call_bios(0x10, &regs);
398 
399 	// %ah contains the error code
400 	if ((regs.eax & 0xff00) != 0)
401 		return B_ENTRY_NOT_FOUND;
402 
403 	return B_OK;
404 }
405 
406 
407 static status_t
408 vesa_get_vbe_info_block(vbe_info_block *info)
409 {
410 	memset(info, 0, sizeof(vbe_info_block));
411 	info->signature = VBE2_SIGNATURE;
412 
413 	struct bios_regs regs;
414 	regs.eax = 0x4f00;
415 	regs.es = ADDRESS_SEGMENT(info);
416 	regs.edi = ADDRESS_OFFSET(info);
417 	call_bios(0x10, &regs);
418 
419 	// %ah contains the error code
420 	if ((regs.eax & 0xff00) != 0)
421 		return B_ERROR;
422 
423 	if (info->signature != VESA_SIGNATURE)
424 		return B_ERROR;
425 
426 	dprintf("VESA version = %d.%d, capabilities %lx\n", info->version.major,
427 		info->version.minor, info->capabilities);
428 
429 	if (info->version.major < 2) {
430 		dprintf("VESA support too old\n");
431 		return B_ERROR;
432 	}
433 
434 	info->oem_string = SEGMENTED_TO_LINEAR(info->oem_string);
435 	info->mode_list = SEGMENTED_TO_LINEAR(info->mode_list);
436 	dprintf("OEM string: %s\n", (const char *)info->oem_string);
437 
438 	return B_OK;
439 }
440 
441 
442 static void
443 vesa_fixups(vbe_info_block *info)
444 {
445 	const char *oem_string = (const char *)info->oem_string;
446 
447 	if (!strcmp(oem_string, "NVIDIA")) {
448 		dprintf("Disabling nvidia scaling.\n");
449 		struct bios_regs regs;
450 		regs.eax = 0x4f14;
451 		regs.ebx = 0x0102;
452 		regs.ecx = 1;	// centered unscaled
453 		call_bios(0x10, &regs);
454 	}
455 }
456 
457 
458 static status_t
459 vesa_init(vbe_info_block *info, video_mode **_standardMode)
460 {
461 	if (vesa_get_vbe_info_block(info) != B_OK)
462 		return B_ERROR;
463 
464 	vesa_fixups(info);
465 
466 	// fill mode list and find standard video mode
467 
468 	video_mode *standardMode = NULL;
469 
470 	for (int32 i = 0; true; i++) {
471 		uint16 mode = ((uint16 *)info->mode_list)[i];
472 		if (mode == 0xffff)
473 			break;
474 
475 		struct vbe_mode_info modeInfo;
476 		if (vesa_get_mode_info(mode, &modeInfo) == B_OK) {
477 			TRACE((" 0x%03x: %u x %u x %u (a = %d, mem = %d, phy = %lx, p = %d, b = %d)\n", mode,
478 				   modeInfo.width, modeInfo.height, modeInfo.bits_per_pixel, modeInfo.attributes,
479 				   modeInfo.memory_model, modeInfo.physical_base, modeInfo.num_planes,
480 				   modeInfo.num_banks));
481 			TRACE(("	mask: r: %d %d g: %d %d b: %d %d dcmi: %d\n",
482 				   modeInfo.red_mask_size, modeInfo.red_field_position,
483 				   modeInfo.green_mask_size, modeInfo.green_field_position,
484 				   modeInfo.blue_mask_size, modeInfo.blue_field_position,
485 				   modeInfo.direct_color_mode_info));
486 
487 			const uint32 requiredAttributes = MODE_ATTR_AVAILABLE
488 				| MODE_ATTR_GRAPHICS_MODE | MODE_ATTR_COLOR_MODE
489 				| MODE_ATTR_LINEAR_BUFFER;
490 
491 			if (modeInfo.width >= 640
492 				&& modeInfo.physical_base != 0
493 				&& modeInfo.num_planes == 1
494 				&& (modeInfo.memory_model == MODE_MEMORY_PACKED_PIXEL
495 					|| modeInfo.memory_model == MODE_MEMORY_DIRECT_COLOR)
496 				&& (modeInfo.attributes & requiredAttributes)
497 					== requiredAttributes) {
498 				// this mode fits our needs
499 				video_mode *videoMode = (video_mode *)malloc(
500 					sizeof(struct video_mode));
501 				if (videoMode == NULL)
502 					continue;
503 
504 				videoMode->mode = mode;
505 				videoMode->bytes_per_row = modeInfo.bytes_per_row;
506 				videoMode->width = modeInfo.width;
507 				videoMode->height = modeInfo.height;
508 				videoMode->bits_per_pixel = modeInfo.bits_per_pixel;
509 				videoMode->timing = NULL;
510 
511 				if (modeInfo.bits_per_pixel == 16
512 					&& modeInfo.red_mask_size + modeInfo.green_mask_size
513 						+ modeInfo.blue_mask_size == 15) {
514 					// this is really a 15-bit mode
515 					videoMode->bits_per_pixel = 15;
516 				}
517 
518 				add_video_mode(videoMode);
519 			}
520 		} else
521 			TRACE((" 0x%03x: (failed)\n", mode));
522 	}
523 
524 	// Choose default resolution (when no EDID information is available)
525 	const uint32 kPreferredWidth = 1024;
526 	const uint32 kFallbackWidth = 800;
527 
528 	standardMode = find_video_mode(kPreferredWidth, -1, false);
529 	if (standardMode == NULL) {
530 		standardMode = find_video_mode(kFallbackWidth, -1, false);
531 		if (standardMode == NULL) {
532 			standardMode = find_video_mode(kPreferredWidth, -1, true);
533 			if (standardMode == NULL)
534 				standardMode = find_video_mode(kFallbackWidth, -1, true);
535 		}
536 	}
537 	if (standardMode == NULL) {
538 		// just take any mode
539 		standardMode = (video_mode *)list_get_first_item(&sModeList);
540 	}
541 
542 	if (standardMode == NULL) {
543 		// no usable VESA mode found...
544 		return B_ERROR;
545 	}
546 
547 	TRACE(("Using mode 0x%03x\n", standardMode->mode));
548 	*_standardMode = standardMode;
549 	return B_OK;
550 }
551 
552 
553 #if 0
554 static status_t
555 vesa_get_mode(uint16 *_mode)
556 {
557 	struct bios_regs regs;
558 	regs.eax = 0x4f03;
559 	call_bios(0x10, &regs);
560 
561 	if ((regs.eax & 0xffff) != 0x4f)
562 		return B_ERROR;
563 
564 	*_mode = regs.ebx & 0xffff;
565 	return B_OK;
566 }
567 #endif
568 
569 
570 static status_t
571 vesa_set_mode(video_mode* mode, bool useTiming)
572 {
573 	struct bios_regs regs;
574 	regs.eax = 0x4f02;
575 	regs.ebx = (mode->mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
576 
577 	if (useTiming && mode->timing != NULL) {
578 		regs.ebx |= SET_MODE_SPECIFY_CRTC;
579 		regs.es = ADDRESS_SEGMENT(mode->timing);
580 		regs.edi = ADDRESS_OFFSET(mode->timing);
581 	}
582 
583 	call_bios(0x10, &regs);
584 
585 	if ((regs.eax & 0xffff) != 0x4f)
586 		return B_ERROR;
587 
588 #if 0
589 	// make sure we have 8 bits per color channel
590 	regs.eax = 0x4f08;
591 	regs.ebx = 8 << 8;
592 	call_bios(0x10, &regs);
593 #endif
594 
595 	return B_OK;
596 }
597 
598 
599 static status_t
600 vesa_set_palette(const uint8 *palette, int32 firstIndex, int32 numEntries)
601 {
602 	// is this an 8 bit indexed color mode?
603 	if (gKernelArgs.frame_buffer.depth != 8)
604 		return B_BAD_TYPE;
605 
606 #if 0
607 	struct bios_regs regs;
608 	regs.eax = 0x4f09;
609 	regs.ebx = 0;
610 	regs.ecx = numEntries;
611 	regs.edx = firstIndex;
612 	regs.es = (addr_t)palette >> 4;
613 	regs.edi = (addr_t)palette & 0xf;
614 	call_bios(0x10, &regs);
615 
616 	if ((regs.eax & 0xffff) != 0x4f) {
617 #endif
618 		// the VESA call does not work, just try good old VGA mechanism
619 		vga_set_palette(palette, firstIndex, numEntries);
620 #if 0
621 		return B_ERROR;
622 	}
623 #endif
624 	return B_OK;
625 }
626 
627 
628 //	#pragma mark -
629 
630 
631 bool
632 video_mode_hook(Menu *menu, MenuItem *item)
633 {
634 	// find selected mode
635 	video_mode *mode = NULL;
636 
637 	menu = item->Submenu();
638 	item = menu->FindMarked();
639 	if (item != NULL) {
640 		switch (menu->IndexOf(item)) {
641 			case 0:
642 				// "Default" mode special
643 				sMode = sDefaultMode;
644 				sModeChosen = false;
645 				return true;
646 			case 1:
647 				// "Standard VGA" mode special
648 				// sets sMode to NULL which triggers VGA mode
649 				break;
650 			default:
651 				mode = (video_mode *)item->Data();
652 				break;
653 		}
654 	}
655 
656 	if (mode != sMode) {
657 		// update standard mode
658 		// ToDo: update fb settings!
659 		sMode = mode;
660 	}
661 
662 	sModeChosen = true;
663 	return true;
664 }
665 
666 
667 Menu *
668 video_mode_menu()
669 {
670 	Menu *menu = new(nothrow) Menu(CHOICE_MENU, "Select Video Mode");
671 	MenuItem *item;
672 
673 	menu->AddItem(item = new(nothrow) MenuItem("Default"));
674 	item->SetMarked(true);
675 	item->Select(true);
676 	item->SetHelpText("The Default video mode is the one currently configured "
677 		"in the system. If there is no mode configured yet, a viable mode will "
678 		"be chosen automatically.");
679 
680 	menu->AddItem(new(nothrow) MenuItem("Standard VGA"));
681 
682 	video_mode *mode = NULL;
683 	while ((mode = (video_mode *)list_get_next_item(&sModeList, mode)) != NULL) {
684 		char label[64];
685 		snprintf(label, sizeof(label), "%ux%u %u bit", mode->width,
686 			mode->height, mode->bits_per_pixel);
687 
688 		menu->AddItem(item = new(nothrow) MenuItem(label));
689 		item->SetData(mode);
690 	}
691 
692 	menu->AddSeparatorItem();
693 	menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
694 	item->SetType(MENU_ITEM_NO_CHOICE);
695 
696 	return menu;
697 }
698 
699 
700 static void
701 set_vga_mode(void)
702 {
703 	// sets 640x480 16 colors graphics mode
704 	bios_regs regs;
705 	regs.eax = 0x0012;
706 	call_bios(0x10, &regs);
707 }
708 
709 
710 static void
711 set_text_mode(void)
712 {
713 	// sets 80x25 text console
714 	bios_regs regs;
715 	regs.eax = 0x0003;
716 	call_bios(0x10, &regs);
717 
718 	video_hide_text_cursor();
719 }
720 
721 
722 void
723 video_move_text_cursor(int x, int y)
724 {
725 	bios_regs regs;
726 	regs.eax = 0x0200;
727 	regs.ebx = 0;
728 	regs.edx = (y << 8) | x;
729 	call_bios(0x10, &regs);
730 }
731 
732 
733 void
734 video_show_text_cursor(void)
735 {
736 	bios_regs regs;
737 	regs.eax = 0x0100;
738 	regs.ecx = 0x0607;
739 	call_bios(0x10, &regs);
740 }
741 
742 
743 void
744 video_hide_text_cursor(void)
745 {
746 	bios_regs regs;
747 	regs.eax = 0x0100;
748 	regs.ecx = 0x2000;
749 	call_bios(0x10, &regs);
750 }
751 
752 
753 //	#pragma mark - blit
754 
755 
756 void
757 platform_blit4(addr_t frameBuffer, const uint8 *data,
758 	uint16 width, uint16 height, uint16 imageWidth, uint16 left, uint16 top)
759 {
760 	if (!data)
761 		return;
762 	// ToDo: no boot logo yet in VGA mode
763 #if 1
764 // this draws 16 big rectangles in all the available colors
765 	uint8 *bits = (uint8 *)frameBuffer;
766 	uint32 bytesPerRow = 80;
767 	for (int32 i = 0; i < 32; i++) {
768 		bits[9 * bytesPerRow + i + 2] = 0x55;
769 		bits[30 * bytesPerRow + i + 2] = 0xaa;
770 	}
771 
772 	for (int32 y = 10; y < 30; y++) {
773 		for (int32 i = 0; i < 16; i++) {
774 			out16((15 << 8) | 0x02, VGA_SEQUENCER_INDEX);
775 			bits[32 * bytesPerRow + i*2 + 2] = i;
776 
777 			if (i & 1) {
778 				out16((1 << 8) | 0x02, VGA_SEQUENCER_INDEX);
779 				bits[y * bytesPerRow + i*2 + 2] = 0xff;
780 				bits[y * bytesPerRow + i*2 + 3] = 0xff;
781 			}
782 			if (i & 2) {
783 				out16((2 << 8) | 0x02, VGA_SEQUENCER_INDEX);
784 				bits[y * bytesPerRow + i*2 + 2] = 0xff;
785 				bits[y * bytesPerRow + i*2 + 3] = 0xff;
786 			}
787 			if (i & 4) {
788 				out16((4 << 8) | 0x02, VGA_SEQUENCER_INDEX);
789 				bits[y * bytesPerRow + i*2 + 2] = 0xff;
790 				bits[y * bytesPerRow + i*2 + 3] = 0xff;
791 			}
792 			if (i & 8) {
793 				out16((8 << 8) | 0x02, VGA_SEQUENCER_INDEX);
794 				bits[y * bytesPerRow + i*2 + 2] = 0xff;
795 				bits[y * bytesPerRow + i*2 + 3] = 0xff;
796 			}
797 		}
798 	}
799 
800 	// enable all planes again
801 	out16((15 << 8) | 0x02, VGA_SEQUENCER_INDEX);
802 #endif
803 }
804 
805 
806 extern "C" void
807 platform_set_palette(const uint8 *palette)
808 {
809 	switch (gKernelArgs.frame_buffer.depth) {
810 		case 4:
811 			//vga_set_palette((const uint8 *)kPalette16, 0, 16);
812 			break;
813 		case 8:
814 			if (vesa_set_palette((const uint8 *)palette, 0, 256) != B_OK)
815 				dprintf("set palette failed!\n");
816 
817 			break;
818 		default:
819 			break;
820 	}
821 }
822 
823 
824 //	#pragma mark -
825 
826 extern "C" void
827 platform_switch_to_logo(void)
828 {
829 	// in debug mode, we'll never show the logo
830 	if ((platform_boot_options() & BOOT_OPTION_DEBUG_OUTPUT) != 0)
831 		return;
832 
833 	addr_t lastBase = gKernelArgs.frame_buffer.physical_buffer.start;
834 	size_t lastSize = gKernelArgs.frame_buffer.physical_buffer.size;
835 
836 	if (sVesaCompatible && sMode != NULL) {
837 		if (!sModeChosen)
838 			get_mode_from_settings();
839 
840 		// On some BIOS / chipset / monitor combinations, there seems to be a
841 		// timing issue between getting the EDID data and setting the video
842 		// mode. As such we wait here briefly to give everything enough time
843 		// to settle.
844 		spin(1000);
845 
846 		if ((sMode->timing == NULL || vesa_set_mode(sMode, true) != B_OK)
847 			&& vesa_set_mode(sMode, false) != B_OK)
848 			goto fallback;
849 
850 		struct vbe_mode_info modeInfo;
851 		if (vesa_get_mode_info(sMode->mode, &modeInfo) != B_OK)
852 			goto fallback;
853 
854 		gKernelArgs.frame_buffer.width = modeInfo.width;
855 		gKernelArgs.frame_buffer.height = modeInfo.height;
856 		gKernelArgs.frame_buffer.bytes_per_row = modeInfo.bytes_per_row;
857 		gKernelArgs.frame_buffer.depth = modeInfo.bits_per_pixel;
858 		gKernelArgs.frame_buffer.physical_buffer.size
859 			= (phys_size_t)modeInfo.bytes_per_row
860 				* (phys_size_t)gKernelArgs.frame_buffer.height;
861 		gKernelArgs.frame_buffer.physical_buffer.start = modeInfo.physical_base;
862 	} else {
863 fallback:
864 		// use standard VGA mode 640x480x4
865 		set_vga_mode();
866 
867 		gKernelArgs.frame_buffer.width = 640;
868 		gKernelArgs.frame_buffer.height = 480;
869 		gKernelArgs.frame_buffer.bytes_per_row = 80;
870 		gKernelArgs.frame_buffer.depth = 4;
871 		gKernelArgs.frame_buffer.physical_buffer.size
872 			= (phys_size_t)gKernelArgs.frame_buffer.width
873 				* (phys_size_t)gKernelArgs.frame_buffer.height / 2;
874 		gKernelArgs.frame_buffer.physical_buffer.start = 0xa0000;
875 	}
876 
877 	dprintf("video mode: %ux%ux%u\n", gKernelArgs.frame_buffer.width,
878 		gKernelArgs.frame_buffer.height, gKernelArgs.frame_buffer.depth);
879 
880 	gKernelArgs.frame_buffer.enabled = true;
881 
882 	// If the new frame buffer is either larger than the old one or located at
883 	// a different address, we need to remap it, so we first have to throw
884 	// away its previous mapping
885 	if (lastBase != 0
886 		&& (lastBase != gKernelArgs.frame_buffer.physical_buffer.start
887 			|| lastSize < gKernelArgs.frame_buffer.physical_buffer.size)) {
888 		mmu_free((void *)sFrameBuffer, lastSize);
889 		lastBase = 0;
890 	}
891 	if (lastBase == 0) {
892 		// the graphics memory has not been mapped yet!
893 		sFrameBuffer = mmu_map_physical_memory(
894 			gKernelArgs.frame_buffer.physical_buffer.start,
895 			gKernelArgs.frame_buffer.physical_buffer.size, kDefaultPageFlags);
896 	}
897 
898 	video_display_splash(sFrameBuffer);
899 }
900 
901 
902 extern "C" void
903 platform_switch_to_text_mode(void)
904 {
905 	if (!gKernelArgs.frame_buffer.enabled) {
906 		vga_enable_bright_background_colors();
907 		return;
908 	}
909 
910 	set_text_mode();
911 	gKernelArgs.frame_buffer.enabled = 0;
912 
913 	vga_enable_bright_background_colors();
914 }
915 
916 
917 extern "C" status_t
918 platform_init_video(void)
919 {
920 	gKernelArgs.frame_buffer.enabled = 0;
921 	list_init(&sModeList);
922 
923 	set_text_mode();
924 		// You may wonder why we do this here:
925 		// Obviously, some graphics card BIOS implementations don't
926 		// report all available modes unless you've done this before
927 		// getting the VESA information.
928 		// One example of those is the SiS 630 chipset in my laptop.
929 
930 	sVesaCompatible = vesa_init(&sInfo, &sDefaultMode) == B_OK;
931 	if (!sVesaCompatible) {
932 		TRACE(("No VESA compatible graphics!\n"));
933 		gKernelArgs.vesa_capabilities = 0;
934 		return B_ERROR;
935 	}
936 
937 	gKernelArgs.vesa_capabilities = sInfo.capabilities;
938 
939 	TRACE(("VESA compatible graphics!\n"));
940 
941 	// store VESA modes into kernel args
942 	vesa_mode *modes = (vesa_mode *)kernel_args_malloc(
943 		sModeCount * sizeof(vesa_mode));
944 	if (modes != NULL) {
945 		video_mode *mode = NULL;
946 		uint32 i = 0;
947 		while ((mode = (video_mode *)list_get_next_item(&sModeList, mode))
948 				!= NULL) {
949 			modes[i].mode = mode->mode;
950 			modes[i].width = mode->width;
951 			modes[i].height = mode->height;
952 			modes[i].bits_per_pixel = mode->bits_per_pixel;
953 			i++;
954 		}
955 
956 		gKernelArgs.vesa_modes = modes;
957 		gKernelArgs.vesa_modes_size = sModeCount * sizeof(vesa_mode);
958 	}
959 
960 	edid1_info info;
961 	// Note, we currently ignore EDID information for VBE2 - while the EDID
962 	// information itself seems to be reliable, older chips often seem to
963 	// use very strange default timings with higher modes.
964 	// TODO: Maybe add a setting to enable it anyway?
965 	if (sInfo.version.major >= 3 && vesa_get_edid(&info) == B_OK) {
966 		// we got EDID information from the monitor, try to find a new default
967 		// mode
968 		video_mode *defaultMode = find_edid_mode(info, false);
969 		if (defaultMode == NULL)
970 			defaultMode = find_edid_mode(info, true);
971 
972 		if (defaultMode != NULL) {
973 			// We found a new default mode to use!
974 			sDefaultMode = defaultMode;
975 		}
976 
977 		gKernelArgs.edid_info = kernel_args_malloc(sizeof(edid1_info));
978 		if (gKernelArgs.edid_info != NULL)
979 			memcpy(gKernelArgs.edid_info, &info, sizeof(edid1_info));
980 	}
981 
982 	sMode = sDefaultMode;
983 	return B_OK;
984 }
985 
986