From b4c8ccc37273f10de5212ec7c7199fff43054339 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Axel=20D=C3=B6rfler?= <axeld@pinc-software.de>
Date: Fri, 31 Oct 2008 09:52:57 +0000
Subject: [PATCH] * The boot loader now creates a CRTCInfoBlock structure when
 detailed EDID   mode informations are available. * This is passed to the
 graphics card when the mode is set in the hopes that it   will be more
 conforming. * Not yet tested on real hardware, though, therefore the VESA
 driver doesn't   do anything like this yet. I will test next, but please
 report any problems   with this nonetheless.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28390 a95241bf-73f2-0310-859d-f6bbb57e9c96
---
 headers/private/graphics/vesa/vesa.h         | 28 +++++-
 src/system/boot/platform/bios_ia32/video.cpp | 89 +++++++++++++++++---
 2 files changed, 104 insertions(+), 13 deletions(-)

diff --git a/headers/private/graphics/vesa/vesa.h b/headers/private/graphics/vesa/vesa.h
index 069bb2b0dc..55a17052af 100644
--- a/headers/private/graphics/vesa/vesa.h
+++ b/headers/private/graphics/vesa/vesa.h
@@ -1,7 +1,7 @@
 /*
-** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
-** Distributed under the terms of the OpenBeOS License.
-*/
+ * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
+ * Distributed under the terms of the MIT License.
+ */
 #ifndef VESA_H
 #define VESA_H
 
@@ -119,6 +119,28 @@ struct vbe_mode_info {
 #define SET_MODE_DONT_CLEAR_MEMORY	(1 << 15)
 
 
+/* CRTC info block structure */
+
+struct crtc_info_block {
+	uint16	horizontal_total;
+	uint16	horizontal_sync_start;
+	uint16	horizontal_sync_end;
+	uint16	vertical_total;
+	uint16	vertical_sync_start;
+	uint16	vertical_sync_end;
+	uint8	flags;
+	uint32	pixel_clock;		// in Hz
+	uint16	refresh_rate;		// in 0.01 Hz
+
+	uint8	_reserved[40];
+} _PACKED;
+
+#define CRTC_DOUBLE_SCAN		0x01
+#define CRTC_INTERLACED			0x02
+#define CRTC_NEGATIVE_HSYNC		0x04
+#define CRTC_NEGATIVE_VSYNC		0x08
+
+
 /* VBE 3.0 protected mode interface
  * The BIOS area can be scanned for the protected mode
  * signature that identifies the structure below.
diff --git a/src/system/boot/platform/bios_ia32/video.cpp b/src/system/boot/platform/bios_ia32/video.cpp
index 3a3eefd311..d4720ee061 100644
--- a/src/system/boot/platform/bios_ia32/video.cpp
+++ b/src/system/boot/platform/bios_ia32/video.cpp
@@ -42,6 +42,7 @@ struct video_mode {
 	uint16		mode;
 	uint16		width, height, bits_per_pixel;
 	uint32		bytes_per_row;
+	crtc_info_block* timing;
 };
 
 static vbe_info_block sInfo;
@@ -149,40 +150,99 @@ closest_video_mode(int32 width, int32 height, int32 depth)
 }
 
 
-static video_mode *
-find_edid_mode(edid1_info &info, bool allowPalette)
+static crtc_info_block*
+get_crtc_info_block(edid1_detailed_timing& timing)
+{
+	// Copy timing structure to set the mode with
+	crtc_info_block* crtcInfo = (crtc_info_block*)malloc(
+		sizeof(crtc_info_block));
+	if (crtcInfo == NULL)
+		return NULL;
+
+	memset(crtcInfo, 0, sizeof(crtc_info_block));
+	crtcInfo->horizontal_sync_start = timing.h_active + timing.h_sync_off;
+	crtcInfo->horizontal_sync_end = crtcInfo->horizontal_sync_start
+		+ timing.h_sync_width;
+	crtcInfo->horizontal_total = timing.h_active + timing.h_blank;
+	crtcInfo->vertical_sync_start = timing.v_active + timing.v_sync_off;
+	crtcInfo->vertical_sync_end = crtcInfo->vertical_sync_start
+		+ timing.v_sync_width;
+	crtcInfo->vertical_total = timing.v_active + timing.v_blank;
+	crtcInfo->pixel_clock = timing.pixel_clock * 10000L;
+	crtcInfo->refresh_rate = crtcInfo->pixel_clock
+		/ (crtcInfo->horizontal_total / 10)
+		/ (crtcInfo->vertical_total / 10);
+
+	crtcInfo->flags = 0;
+	if (timing.sync == 3) {
+		// TODO: this switches the default sync when sync != 3 (compared to
+		// create_display_modes().
+		if ((timing.misc & 1) == 0)
+			crtcInfo->flags |= CRTC_NEGATIVE_HSYNC;
+		if ((timing.misc & 2) == 0)
+			crtcInfo->flags |= CRTC_NEGATIVE_VSYNC;
+	}
+	if (timing.interlaced)
+		crtcInfo->flags |= CRTC_INTERLACED;
+
+	return crtcInfo;
+}
+
+
+static crtc_info_block*
+get_crtc_info_block(edid1_std_timing& timing)
+{
+	// TODO: implement me!
+	return NULL;
+}
+
+
+static video_mode*
+find_edid_mode(edid1_info& info, bool allowPalette)
 {
 	video_mode *mode = NULL;
 
 	// try detailed timing first
 	for (int32 i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; i++) {
-		edid1_detailed_monitor &monitor = info.detailed_monitor[i];
+		edid1_detailed_monitor& monitor = info.detailed_monitor[i];
 
 		if (monitor.monitor_desc_type == EDID1_IS_DETAILED_TIMING) {
 			mode = find_video_mode(monitor.data.detailed_timing.h_active,
 				monitor.data.detailed_timing.v_active, allowPalette);
-			if (mode != NULL)
+			if (mode != NULL) {
+				mode->timing
+					= get_crtc_info_block(monitor.data.detailed_timing);
 				return mode;
+			}
 		}
 	}
 
+	int32 best = -1;
+
 	// try standard timings next
 	for (int32 i = 0; i < EDID1_NUM_STD_TIMING; i++) {
 		if (info.std_timing[i].h_size <= 256)
 			continue;
 
-		video_mode *found = find_video_mode(info.std_timing[i].h_size,
+		video_mode* found = find_video_mode(info.std_timing[i].h_size,
 			info.std_timing[i].v_size, allowPalette);
 		if (found != NULL) {
 			if (mode != NULL) {
 				// prefer higher resolutions
-				if (found->width > mode->width)
+				if (found->width > mode->width) {
 					mode = found;
-			} else
+					best = i;
+				}
+			} else {
 				mode = found;
+				best = i;
+			}
 		}
 	}
 
+	if (best >= 0)
+		mode->timing = get_crtc_info_block(info.std_timing[best]);
+
 	return mode;
 }
 
@@ -413,6 +473,8 @@ vesa_init(vbe_info_block *info, video_mode **_standardMode)
 				videoMode->width = modeInfo.width;
 				videoMode->height = modeInfo.height;
 				videoMode->bits_per_pixel = modeInfo.bits_per_pixel;
+				videoMode->timing = NULL;
+
 				if (modeInfo.bits_per_pixel == 16
 					&& modeInfo.red_mask_size + modeInfo.green_mask_size
 						+ modeInfo.blue_mask_size == 15) {
@@ -472,11 +534,18 @@ vesa_get_mode(uint16 *_mode)
 
 
 static status_t
-vesa_set_mode(uint16 mode)
+vesa_set_mode(video_mode* mode)
 {
 	struct bios_regs regs;
 	regs.eax = 0x4f02;
-	regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
+	regs.ebx = (mode->mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
+
+	if (mode->timing != NULL) {
+		regs.ebx |= SET_MODE_SPECIFY_CRTC;
+		regs.es = ADDRESS_SEGMENT(mode->timing);
+		regs.edi = ADDRESS_OFFSET(mode->timing);
+	}
+
 	call_bios(0x10, &regs);
 
 	if ((regs.eax & 0xffff) != 0x4f)
@@ -904,7 +973,7 @@ platform_switch_to_logo(void)
 		// getting the EDID data and setting the video mode. As such we wait here briefly to give
 		// everything enough time to settle.
 		spin(1000);
-		if (vesa_set_mode(sMode->mode) != B_OK)
+		if (vesa_set_mode(sMode) != B_OK)
 			goto fallback;
 
 		struct vbe_mode_info modeInfo;