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