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