linux-fbdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Reading the EDID block for x86 machines
@ 2003-03-11 12:49 Antonino Daplas
  2003-03-11 15:49 ` James Simmons
  2003-03-12 12:10 ` Ville Syrjälä
  0 siblings, 2 replies; 35+ messages in thread
From: Antonino Daplas @ 2003-03-11 12:49 UTC (permalink / raw)
  To: Linux Fbdev development list

[-- Attachment #1: Type: text/plain, Size: 2260 bytes --]

Hi,

I was wondering how to get the EDID block, and my first thought was to
implement DDC.  But it is tricky to implement, so why not just get the
EDID using an int 0x10 BIOS call?

The EDID block can be read before the kernel goes into protected mode
(just a short assembly code in arch/i386/boot/video.S) and this can be
saved later on as struct edid_info during the kernel's setup routine.  

One catch though is that that the "empty_zero_page" is becoming too
tight, so it will just use the same offset used by the EDD (BIOS
Enhanced Disk Drive).  So EDID reading will be disabled if EDD is
enabled under the "Processor Type and Features"  option.

Then, we can also make EDID parsing more powerful.  I've added some code
in fbmon.c to parse the EDID block in more detail.  It should be able to
parse almost everything (except the color point).  Currently, it just
prints all the information it can find.  However, I haven't tested if
parsing the Detailed Monitor information is correct since the EDID block
of the monitor I'm using is incomplete.

One of the goals is to build a video data structure that also includes a
database of video mode in fb_videomode format.  This can be dynamically
created per device using the supported VESA modes, manufacturer's mode
and detailed timings mode information in the EDID block. The EDID block
also contain information on GTF capability.  With these types of
displays, a video mode mode database may not be needed since all
possible supported modelines can be calculated.

The advantage of the above is that the driver can switch to different
modes without relying on user input (of course, user-entered modelines
are still preferred).

Anyway, I need help in testing the code (especially the Detailed
Information part).  Just apply the attached patch, disable Enhanced Disk
Driver support (under Processor Type and Features), and enable EDID
support (under Graphics option).  Finally, add the following line to the
fbdev driver that you use (anywhere, but a good place is the init code):

show_edid(get_EDID(pci_dev))

If using an x86 machine

show_edid(get_EDID(NULL)) 

should also work.

Reboot, check dmesg, and kindly post the ouput.  Thanks.

Tony

PS:  The patch is against linux-2.5.64 + fbdev.diff.gz



[-- Attachment #2: edid.diff --]
[-- Type: text/x-patch, Size: 21215 bytes --]

diff -Naur linux-2.5.64-fbdev/arch/i386/boot/compressed/misc.c linux-2.5.64/arch/i386/boot/compressed/misc.c
--- linux-2.5.64-fbdev/arch/i386/boot/compressed/misc.c	2003-02-16 00:47:53.000000000 +0000
+++ linux-2.5.64/arch/i386/boot/compressed/misc.c	2003-03-11 02:39:51.000000000 +0000
@@ -91,6 +91,7 @@
 #define ALT_MEM_K   (*(unsigned long *)(real_mode + 0x1e0))
 #endif
 #define SCREEN_INFO (*(struct screen_info *)(real_mode+0))
+#define EDID_INFO   (*(struct edid_info   *)(real_mode+0x600))
 
 extern char input_data[];
 extern int input_len;
diff -Naur linux-2.5.64-fbdev/arch/i386/boot/video.S linux-2.5.64/arch/i386/boot/video.S
--- linux-2.5.64-fbdev/arch/i386/boot/video.S	2002-12-16 02:07:50.000000000 +0000
+++ linux-2.5.64/arch/i386/boot/video.S	2003-03-11 02:39:53.000000000 +0000
@@ -135,6 +135,7 @@
 #endif /* CONFIG_VIDEO_RETAIN */
 #endif /* CONFIG_VIDEO_SELECT */
 	call	mode_params			# Store mode parameters
+	call	store_edid
 	popw	%ds				# Restore original DS
 	ret
 
@@ -1887,6 +1888,50 @@
 	popw	%ax
 	ret
 
+#if defined(CONFIG_EDID)
+	store_edid:
+	pushw	%es				# just save all affected 
+	pushw	%ax				# affected registers
+	pushw	%bx
+	pushw   %cx
+	pushw	%dx
+	pushw   %di
+
+	pushw	%fs                             
+	popw    %es
+
+	call	store_edid_magic               
+	call	read_edid
+
+	popw	%di				# restore all registers        
+	popw	%dx
+	popw	%cx
+	popw	%bx
+	popw	%ax
+	popw	%es	
+	ret
+
+store_edid_magic:	
+	movb	$0x13, %al 
+	movw    $128, %cx
+	movw	$0x600, %di
+	cld
+	rep 
+	stosb  
+	ret
+read_edid:	
+	movw	$0x4f15, %ax
+	movw	$0x01, %bx
+	movw	$0x00, %cx
+	movw    $0x01, %dx
+	movw	$0x600, %di
+	int	$0x10	
+	ret
+#else
+store_edid:
+	ret
+#endif
+
 # VIDEO_SELECT-only variables
 mt_end:		.word	0	# End of video mode table if built
 edit_buf:	.space	6	# Line editor buffer
diff -Naur linux-2.5.64-fbdev/arch/i386/kernel/setup.c linux-2.5.64/arch/i386/kernel/setup.c
--- linux-2.5.64-fbdev/arch/i386/kernel/setup.c	2003-03-11 02:30:07.000000000 +0000
+++ linux-2.5.64/arch/i386/kernel/setup.c	2003-03-11 02:52:09.000000000 +0000
@@ -80,6 +80,7 @@
  */
 struct drive_info_struct { char dummy[32]; } drive_info;
 struct screen_info screen_info;
+struct edid_info   edid_info;
 struct apm_info apm_info;
 struct sys_desc_table_struct {
 	unsigned short length;
@@ -495,6 +496,23 @@
 #define copy_edd() do {} while (0)
 #endif
 
+#if defined(CONFIG_EDID)
+/*
+ * Since empty_zero_page has a limited size, we are going to use the
+ * same offset as EDD.  Thus, copying of the EDID block will only be
+ * enabled if EDD is disabled.  
+ */
+static inline void copy_edid(void)
+{
+	edid_info = EDID_INFO;
+}
+#else
+static inline void copy_edid(void)
+{
+	memset(&edid_info, 0x13, 128);
+}
+#endif
+
 /*
  * Do NOT EVER look at the BIOS memory size location.
  * It does not work on many machines.
@@ -862,6 +880,7 @@
  	ROOT_DEV = ORIG_ROOT_DEV;
  	drive_info = DRIVE_INFO;
  	screen_info = SCREEN_INFO;
+	copy_edid();
 	apm_info.bios = APM_BIOS_INFO;
 	saved_videomode = VIDEO_MODE;
 	printk("Video mode to be used for restore is %lx\n", saved_videomode);
diff -Naur linux-2.5.64-fbdev/drivers/video/Kconfig linux-2.5.64/drivers/video/Kconfig
--- linux-2.5.64-fbdev/drivers/video/Kconfig	2003-03-11 02:30:31.000000000 +0000
+++ linux-2.5.64/drivers/video/Kconfig	2003-03-11 02:41:18.000000000 +0000
@@ -4,6 +4,10 @@
 
 menu "Graphics support"
 
+config EDID
+	bool "Get EDID block from VBE"
+	depends on X86 && !EDD
+          
 config FB
 	bool "Support for frame buffer devices"
 	---help---
diff -Naur linux-2.5.64-fbdev/drivers/video/fbmon.c linux-2.5.64/drivers/video/fbmon.c
--- linux-2.5.64-fbdev/drivers/video/fbmon.c	2003-03-11 02:30:32.000000000 +0000
+++ linux-2.5.64/drivers/video/fbmon.c	2003-03-11 11:39:19.000000000 +0000
@@ -11,9 +11,6 @@
 #include <linux/tty.h>
 #include <linux/fb.h>
 #include <linux/module.h>
-#ifdef CONFIG_PCI
-#include <linux/pci.h>
-#endif
 #ifdef CONFIG_ALL_PPC
 #include <asm/prom.h>
 #endif
@@ -41,11 +38,18 @@
 #define EDID_STRUCT_VERSION			0x12
 #define EDID_STRUCT_REVISION			0x13
 
+#define EDID_STRUCT_DISPLAY                     0x14
+
 #define DPMS_FLAGS				0x18
 #define ESTABLISHED_TIMING_1			0x23
 #define ESTABLISHED_TIMING_2			0x24
 #define MANUFACTURERS_TIMINGS			0x25
 
+/* standard timings supported */
+#define STD_TIMING                              8
+#define STD_TIMING_DESCRIPTION_SIZE             2
+#define STD_TIMING_DESCRIPTIONS_START           0x26
+
 #define DETAILED_TIMING_DESCRIPTIONS_START	0x36
 #define DETAILED_TIMING_DESCRIPTION_SIZE	18
 #define NO_DETAILED_TIMING_DESCRIPTIONS		4
@@ -124,11 +128,32 @@
 #define HSYNC_POSITIVE     (FLAGS & 4)
 #define VSYNC_POSITIVE     (FLAGS & 2)
 
+#define V_MIN_RATE              block[ 5 ]
+#define V_MAX_RATE              block[ 6 ]
+#define H_MIN_RATE              block[ 7 ]
+#define H_MAX_RATE              block[ 8 ]
+#define MAX_PIXEL_CLOCK         (((int)block[ 9 ]) * 10)
+#define GTF_SUPPORT		block[10]
+
+#define DPMS_ACTIVE_OFF		(1 << 5)
+#define DPMS_SUSPEND		(1 << 6)
+#define DPMS_STANDBY		(1 << 7)
+
 const unsigned char edid_v1_header[] = { 0x00, 0xff, 0xff, 0xff,
 	0xff, 0xff, 0xff, 0x00
 };
 const unsigned char edid_v1_descriptor_flag[] = { 0x00, 0x00 };
 
+static void copy_string(unsigned char *c, unsigned char *s)
+{
+  int i;
+  c = c + 5;
+  for (i = 0; (i < 13 && *c != 0x0A); i++)
+    *(s++) = *(c++);
+  *s = 0;
+  while (i-- && (*--s == 0x20)) *s = 0;
+}
+
 static int edid_checksum(unsigned char *edid)
 {
 	unsigned char i, csum = 0;
@@ -157,37 +182,144 @@
 	return 1;
 }
 
-
-static char *edid_get_vendor(unsigned char *block)
+static void parse_vendor_block(unsigned char *block)
 {
-	static char sign[4];
-	unsigned short h;
-
-	h = COMBINE_HI_8LO(block[0], block[1]);
-	sign[0] = ((h >> 10) & 0x1f) + 'A' - 1;
-	sign[1] = ((h >> 5) & 0x1f) + 'A' - 1;
-	sign[2] = (h & 0x1f) + 'A' - 1;
-	sign[3] = 0;
+	unsigned char c[4];
 
-	return sign;
+	c[0] = ((block[0] & 0x7c) >> 2) + '@';
+	c[1] = ((block[0] & 0x03) << 3) + ((block[1] & 0xe0) >> 5) + '@';
+	c[2] = (block[1] & 0x1f) + '@';
+	c[3] = 0;
+	printk("   Manufacturer: %s ", c);
+	printk("Model: %x ", block[2] + (block[3] << 8));
+	printk("Serial#: %u\n", block[4] + (block[5] << 8) + 
+	       (block[6] << 16) + (block[7] << 24));
+	printk("   Year: %u Week %u\n", block[9] + 1990, block[8]);
+}
+
+static void parse_dpms_capabilities(unsigned char flags)
+{
+	printk("      DPMS: Active %s, Suspend %s, Standby %s\n",
+	       (flags & DPMS_ACTIVE_OFF) ? "yes" : "no",
+	       (flags & DPMS_SUSPEND)    ? "yes" : "no",
+	       (flags & DPMS_STANDBY)    ? "yes" : "no");
 }
-
-static char *edid_get_monitor(unsigned char *block)
+	
+static void parse_display_block(unsigned char *block)
 {
-	static char name[13];
-	unsigned i;
-	const unsigned char *ptr = block + DESCRIPTOR_DATA;
+	unsigned char c;
 
-	for (i = 0; i < 13; i++, ptr++) {
-		if (*ptr == 0xa) {
-			name[i] = 0x00;
-			return name;
+	c = (block[0] & 0x80) >> 7;
+	if (c) 
+		printk("      Digital Display Input");
+	else {
+		printk("      Analog Display Input: Input Voltage - ");
+		switch ((block[0] & 0x60) >> 5) {
+		case 0:
+			printk("0.700V/0.300V");
+			break;
+		case 1:
+			printk("0.714V/0.286V");
+			break;
+		case 2:
+			printk("1.000V/0.400V");
+			break;
+		case 3:
+			printk("0.700V/0.000V");
+			break;
+		default:
+			printk("unknown");
 		}
-		name[i] = *ptr;
+		printk("\n");
 	}
-	return name;
-}
+	c = (block[0] & 0x10) >> 4;
+	if (c)
+		printk("      Configurable signal level\n");
+	printk("      Sync: ");
+	c = block[0] & 0x0f;
+	if (c & 0x08)
+		printk("Separate ");
+	if (c & 0x04)
+		printk("Composite ");
+	if (c & 0x02)
+		printk("Sync on Green ");
+	if (c & 0x01)
+		printk("Serration on ");
+	printk("\n");
+
+	printk("      Max H-size in cm: ");
+	c = block[1];
+	if (c) 
+		printk("%d\n", c);
+	else
+		printk("variable\n");
+	
+	printk("      Max V-size in cm: ");
+	c = block[2];
+	if (c)
+		printk("%d\n", c);
+	else
+		printk("variable\n");
 
+	c = block[3];
+	printk("      Gamma: ");
+	printk("%d.%d\n", (c + 100)/100, (c+100) % 100);
+
+	parse_dpms_capabilities(block[4]);
+
+	switch ((block[4] & 0x18) >> 3) {
+	case 0:
+		printk("      Monochrome/Grayscale\n");
+		break;
+	case 1:
+		printk("      RGB Color Display\n");
+		break;
+	case 2:
+		printk("      Non-RGB Multicolor Display\n");
+		break;
+	default:
+		printk("      Unknown\n");
+		break;
+	}
+	c = block[4] & 0x7;
+	if (c & 0x04)
+		printk("      Default color format is primary\n");
+	if (c & 0x02)
+		printk("      First DETAILED Timing is preferred\n");
+	if (c & 0x01)
+		printk("      Display is GTF capable\n");
+}
+
+static void parse_std_md_block(unsigned char *block)
+{
+	unsigned char c;
+
+	c = block[0];
+	if (c&0x80) printk("      720x400@70Hz\n");
+	if (c&0x40) printk("      720x400@88Hz\n");
+	if (c&0x20) printk("      640x480@60Hz\n");
+	if (c&0x10) printk("      640x480@67Hz\n");
+	if (c&0x08) printk("      640x480@72Hz\n");
+	if (c&0x04) printk("      640x480@75Hz\n");
+	if (c&0x02) printk("      800x600@56Hz\n");
+	if (c&0x01) printk("      800x600@60Hz\n");
+
+	c = block[1];
+	if (c&0x80) printk("      800x600@72Hz\n");
+	if (c&0x40) printk("      800x600@75Hz\n");
+	if (c&0x20) printk("      832x624@75Hz\n");
+	if (c&0x10) printk("      1024x768@87Hz (interlaced)\n");
+	if (c&0x08) printk("      1024x768@60Hz\n");
+	if (c&0x04) printk("      1024x768@70Hz\n");
+	if (c&0x02) printk("      1024x768@75Hz\n");
+	if (c&0x01) printk("      1280x1024@75Hz\n");
+
+	c = block[2];
+	if (c&0x80) printk("      1152x870@75Hz\n");
+	printk("      Manufacturer's mask: %x\n",c&0x7F);
+}
+		
+		
 static int edid_is_timing_block(unsigned char *block)
 {
 	if ((block[0] == 0x00) && (block[1] == 0x00))
@@ -196,6 +328,30 @@
 		return 1;
 }
 
+static int edid_is_serial_block(unsigned char *block)
+{
+	if ((block[0] == 0x00) && (block[1] == 0x00) && (block[3] == 0xff))
+		return 1;
+	else
+		return 0;
+}
+
+static int edid_is_ascii_block(unsigned char *block)
+{
+	if ((block[0] == 0x00) && (block[1] == 0x00) && (block[3] == 0xfe))
+		return 1;
+	else
+		return 0;
+}
+
+static int edid_is_limits_block(unsigned char *block)
+{
+	if ((block[0] == 0x00) && (block[1] == 0x00) && (block[3] == 0xfd))
+		return 1;
+	else
+		return 0;
+}
+
 static int edid_is_monitor_block(unsigned char *block)
 {
 	if ((block[0] == 0x00) && (block[1] == 0x00) && (block[3] == 0xfc))
@@ -204,65 +360,219 @@
 		return 0;
 }
 
-static void parse_timing_block(unsigned char *block,
-			       struct fb_var_screeninfo *var)
+static int edid_is_color_block(unsigned char *block)
+{
+	if ((block[0] == 0x00) && (block[1] == 0x00) && (block[3] == 0xfb))
+		return 1;
+	else
+		return 0;
+}
+
+static int edid_is_std_timings_block(unsigned char *block)
+{
+	if ((block[0] == 0x00) && (block[1] == 0x00) && (block[3] == 0xfa))
+		return 1;
+	else
+		return 0;
+}
+
+static void parse_serial_block(unsigned char *block)
+{
+	unsigned char c[13];
+	
+	copy_string(block, c);
+	printk("      Serial No: %s\n", c);
+}
+
+static void parse_ascii_block(unsigned char *block)
+{
+	unsigned char c[13];
+	
+	copy_string(block, c);
+	printk("      %s\n", c);
+}
+
+static void parse_limits_block(unsigned char *block)
+{
+	printk("      HorizSync  : %d-%d KHz\n", H_MIN_RATE, H_MAX_RATE);
+	printk("      VertRefresh: %d-%d Hz\n", V_MIN_RATE, V_MAX_RATE);
+	if (MAX_PIXEL_CLOCK != 10*0xff)
+		printk("      Max Pixelclock %d MHz\n", (int) MAX_PIXEL_CLOCK);
+}
+
+static void parse_monitor_block(unsigned char *block)
+{
+	unsigned char c[13];
+	
+	copy_string(block, c);
+	printk("      Monitor Name: %s\n", c);
+}
+
+static void parse_color_block(unsigned char *block)
+{
+	printk("      Color Point: unimplemented\n");
+}
+
+static void parse_std_timing_block(unsigned char *block)
 {
-	var->xres = var->xres_virtual = H_ACTIVE;
-	var->yres = var->yres_virtual = V_ACTIVE;
-	var->height = var->width = -1;
-	var->right_margin = H_SYNC_OFFSET;
-	var->left_margin = (H_ACTIVE + H_BLANKING) -
-	    (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
-	var->upper_margin = V_BLANKING - V_SYNC_OFFSET - V_SYNC_WIDTH;
-	var->lower_margin = V_SYNC_OFFSET;
-	var->hsync_len = H_SYNC_WIDTH;
-	var->vsync_len = V_SYNC_WIDTH;
-	var->pixclock = PIXEL_CLOCK;
-	var->pixclock /= 1000;
-	var->pixclock = KHZ2PICOS(var->pixclock);
-
-	if (HSYNC_POSITIVE)
-		var->sync |= FB_SYNC_HOR_HIGH_ACT;
-	if (VSYNC_POSITIVE)
-		var->sync |= FB_SYNC_VERT_HIGH_ACT;
+	int xres, yres = 0, refresh, ratio, err = 1;
+	
+	xres = (block[0] + 31) * 8;
+	ratio = (block[1] & 0xc0) >> 6;
+	switch (ratio) {
+	case 1:
+		yres = xres;
+		break;
+	case 2:
+		yres = (xres * 3)/4;
+		break;
+	case 3:
+		yres = (xres * 4)/5;
+		break;
+	case 4:
+		yres = (xres * 9)/16;
+		break;
+	}
+	refresh = (block[1] & 0x3f) + 60;
+	printk("      %dx%d@%dHz\n", xres, yres, refresh);
+	err = 0;
+}
+
+static void parse_detailed_timing_block(unsigned char *block)
+{
+	printk("      %d MHz ",  PIXEL_CLOCK);
+	printk("%d %d %d %d ", H_ACTIVE, H_ACTIVE + H_SYNC_OFFSET, 
+	       H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH, H_ACTIVE + H_BLANKING);
+	printk("%d %d %d %d ", V_ACTIVE, V_ACTIVE + V_SYNC_OFFSET, 
+	       V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH, V_ACTIVE + V_BLANKING);
+	printk("%sHSync %sVSync\n\n", (HSYNC_POSITIVE) ? "+" : "-", 
+	       (VSYNC_POSITIVE) ? "+" : "-");
 }
 
 int parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
 {
-	unsigned char *block, *vendor, *monitor = NULL;
 	int i;
+	unsigned char *block;
+
+	if (edid == NULL || var == NULL)
+		return 1;
 
 	if (!(edid_checksum(edid)))
-		return 0;
+		return 1;
 
 	if (!(edid_check_header(edid)))
-		return 0;
-
-	printk("EDID ver %d rev %d\n", (int) edid[EDID_STRUCT_VERSION],
-	       (int) edid[EDID_STRUCT_REVISION]);
-
-	vendor = edid_get_vendor(edid + ID_MANUFACTURER_NAME);
+		return 1;
 
 	block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
 
 	for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
-		if (edid_is_monitor_block(block)) {
-			monitor = edid_get_monitor(block);
+		if (edid_is_timing_block(block)) {
+			var->xres = var->xres_virtual = H_ACTIVE;
+			var->yres = var->yres_virtual = V_ACTIVE;
+			var->height = var->width = -1;
+			var->right_margin = H_SYNC_OFFSET;
+			var->left_margin = (H_ACTIVE + H_BLANKING) -
+				(H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH);
+			var->upper_margin = V_BLANKING - V_SYNC_OFFSET - 
+				V_SYNC_WIDTH;
+			var->lower_margin = V_SYNC_OFFSET;
+			var->hsync_len = H_SYNC_WIDTH;
+			var->vsync_len = V_SYNC_WIDTH;
+			var->pixclock = PIXEL_CLOCK;
+			var->pixclock /= 1000;
+			var->pixclock = KHZ2PICOS(var->pixclock);
+
+			if (HSYNC_POSITIVE)
+				var->sync |= FB_SYNC_HOR_HIGH_ACT;
+			if (VSYNC_POSITIVE)
+				var->sync |= FB_SYNC_VERT_HIGH_ACT;
+			return 0;
 		}
 	}
+	return 1;
+}
 
-	printk("EDID: detected %s %s\n", vendor, monitor);
+int get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs)
+{
+	int i;
+	unsigned char *block;
+
+	if (edid == NULL || specs == NULL)
+		return 1;
+
+	if (!(edid_checksum(edid)))
+		return 1;
+
+	if (!(edid_check_header(edid)))
+		return 1;
 
 	block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
 
 	for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
-		if (edid_is_timing_block(block)) {
-			parse_timing_block(block, var);
+		if (edid_is_limits_block(block)) {
+			specs->hfmin = H_MIN_RATE * 1000;
+			specs->hfmax = H_MAX_RATE * 1000;
+			specs->vfmin = V_MIN_RATE;
+			specs->vfmax = V_MAX_RATE;
+			specs->dclkmax = (MAX_PIXEL_CLOCK != 10*0xff) ?
+				MAX_PIXEL_CLOCK * 1000000 : 0;
+			specs->gtf = (GTF_SUPPORT) ? 1 : 0;
+			specs->dpms = edid[DPMS_FLAGS];
+			return 0;
 		}
 	}
 	return 1;
 }
 
+void show_edid(unsigned char *edid)
+{
+	unsigned char *block;
+	int i;
+
+	if (edid == NULL)
+		return;
+
+	if (!(edid_checksum(edid)))
+		return;
+
+	if (!(edid_check_header(edid)))
+		return;
+	printk("========================================\n");
+	printk("Display Information (EDID)\n");
+	printk("========================================\n");
+	printk("   EDID Version %d.%d\n", (int) edid[EDID_STRUCT_VERSION],
+	       (int) edid[EDID_STRUCT_REVISION]);
+
+	parse_vendor_block(edid + ID_MANUFACTURER_NAME);
+
+	printk("   Display Characteristics:\n");
+	parse_display_block(edid + EDID_STRUCT_DISPLAY);
+
+	printk("   Supported VESA Modes\n");
+	parse_std_md_block(edid + ESTABLISHED_TIMING_1);
+
+	printk("   Detailed Monitor Information\n");
+	block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+	for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
+		if (edid_is_serial_block(block)) {
+			parse_serial_block(block);
+		} else if (edid_is_ascii_block(block)) {
+			parse_ascii_block(block);
+		} else if (edid_is_limits_block(block)) {
+			parse_limits_block(block);
+		} else if (edid_is_monitor_block(block)) {
+			parse_monitor_block(block);
+		} else if (edid_is_color_block(block)) {
+			parse_color_block(block);
+		} else if (edid_is_std_timings_block(block)) {
+			parse_std_timing_block(block);
+		} else if (edid_is_timing_block(block)) {
+			parse_detailed_timing_block(block);
+		}
+	}
+	printk("========================================\n");
+}
+
 #ifdef CONFIG_PCI
 char *get_EDID(struct pci_dev *pdev)
 {
@@ -273,6 +583,8 @@
 	struct device_node *dp;
 	int i;
 
+	if (pdev == NULL)
+		return NULL;
 	dp = pci_device_to_OF_node(pdev);
 	while (dp != NULL) {
 		for (i = 0; propnames[i] != NULL; ++i) {
@@ -286,6 +598,9 @@
 		dp = dp->child;
 	}
 	return pedid;
+#elif defined (CONFIG_EDID)
+	return (edid_checksum((char *) &edid_info)) ?
+		(char *) &edid_info : NULL;
 #else
 	return NULL;
 #endif
@@ -576,6 +891,10 @@
 			timings.vfreq = vfmax;
 			fb_timings_vfreq(&timings);
 		}
+		if (timings.dclk > dclkmax) {
+			timings.dclk = dclkmax;
+			fb_timings_dclk(&timings);
+		}
 		break;
 	case FB_VSYNCTIMINGS: /* vrefresh driven */
 		timings.vfreq = val;
@@ -669,13 +988,17 @@
 	vfreq = hfreq/vtotal;
 
 	return (vfreq < vfmin || vfreq > vfmax || 
-		hfreq < hfmin || hfreq > hfmax) ?
+		hfreq < hfmin || hfreq > hfmax ||
+		pixclock < dclkmin || pixclock > dclkmax) ?
 		-EINVAL : 0;
 }
 
 EXPORT_SYMBOL(parse_edid);
+EXPORT_SYMBOL(show_edid);
 #ifdef CONFIG_PCI
 EXPORT_SYMBOL(get_EDID);
 #endif
+
+EXPORT_SYMBOL(get_monitor_limits);
 EXPORT_SYMBOL(fb_get_mode);
 EXPORT_SYMBOL(fb_validate_mode);
diff -Naur linux-2.5.64-fbdev/include/asm-i386/setup.h linux-2.5.64/include/asm-i386/setup.h
--- linux-2.5.64-fbdev/include/asm-i386/setup.h	2002-12-16 02:08:12.000000000 +0000
+++ linux-2.5.64/include/asm-i386/setup.h	2003-03-11 02:39:51.000000000 +0000
@@ -39,6 +39,8 @@
 #define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c))
 #define EDD_NR     (*(unsigned char *) (PARAM+EDDNR))
 #define EDD_BUF     ((struct edd_info *) (PARAM+EDDBUF))
+/* Note: EDID_INFO uses the same offset as EDD_BUF */
+#define EDID_INFO   (*(struct edid_info *) (PARAM+EDDBUF))
 #define COMMAND_LINE ((char *) (PARAM+2048))
 #define COMMAND_LINE_SIZE 256
 
diff -Naur linux-2.5.64-fbdev/include/linux/fb.h linux-2.5.64/include/linux/fb.h
--- linux-2.5.64-fbdev/include/linux/fb.h	2003-03-11 02:30:33.000000000 +0000
+++ linux-2.5.64/include/linux/fb.h	2003-03-11 11:39:05.000000000 +0000
@@ -486,6 +486,10 @@
 extern int num_registered_fb;
 
 /* drivers/video/fbmon.c */
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+
 #define FB_MAXTIMINGS       0
 #define FB_VSYNCTIMINGS     1
 #define FB_HSYNCTIMINGS     2
@@ -499,6 +503,10 @@
 		       struct fb_info *info);
 extern int fb_validate_mode(struct fb_var_screeninfo *var,
 			    struct fb_info *info);
+extern int parse_edid(unsigned char *edid, struct fb_var_screeninfo *var);
+extern char *get_EDID(struct pci_dev *pdev);
+extern void show_edid(unsigned char *edid);
+extern int get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs);
 
 /* drivers/video/fbcmap.c */
 extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp);
diff -Naur linux-2.5.64-fbdev/include/linux/tty.h linux-2.5.64/include/linux/tty.h
--- linux-2.5.64-fbdev/include/linux/tty.h	2003-02-16 00:48:02.000000000 +0000
+++ linux-2.5.64/include/linux/tty.h	2003-03-11 02:39:50.000000000 +0000
@@ -93,7 +93,12 @@
 						/* 0x36 -- 0x3f reserved for future expansion */
 };
 
+struct edid_info {
+	 unsigned char data[128];
+};
+
 extern struct screen_info screen_info;
+extern struct edid_info   edid_info;
 
 #define ORIG_X			(screen_info.orig_x)
 #define ORIG_Y			(screen_info.orig_y)

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2003-03-19  6:07 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-03-11 12:49 Reading the EDID block for x86 machines Antonino Daplas
2003-03-11 15:49 ` James Simmons
2003-03-11 20:07   ` Antonino Daplas
2003-03-11 21:33   ` Antonino Daplas
2003-03-11 21:47     ` Antonino Daplas
2003-03-11 22:05     ` Jon Smirl
2003-03-11 22:33       ` Antonino Daplas
2003-03-11 22:54         ` Jon Smirl
2003-03-11 23:02           ` Antonino Daplas
2003-03-11 23:42             ` Jon Smirl
2003-03-12 17:38               ` Antonino Daplas
2003-03-12 18:16                 ` Jon Smirl
2003-03-12 22:38                   ` Antonino Daplas
2003-03-12 23:36                     ` Jon Smirl
2003-03-12 23:47                     ` Jon Smirl
2003-03-13  6:50                     ` Geert Uytterhoeven
2003-03-13 15:42                       ` Jon Smirl
2003-03-16 23:00                       ` Antonino Daplas
2003-03-17  4:00                         ` Jon Smirl
2003-03-17  7:00                           ` Antonino Daplas
2003-03-17 19:33                             ` Jon Smirl
2003-03-17 21:38                               ` Antonino Daplas
2003-03-17 22:02                                 ` Jon Smirl
2003-03-17 22:29                                   ` Antonino Daplas
2003-03-17 23:41                                     ` Jon Smirl
2003-03-18  9:06                                       ` Geert Uytterhoeven
2003-03-18 10:00                                       ` Antonino Daplas
2003-03-18 17:07                                         ` Jon Smirl
2003-03-19  5:15                                           ` Antonino Daplas
2003-03-19  6:07                                             ` Jon Smirl
2003-03-18  0:00                                     ` Jon Smirl
2003-03-11 23:54             ` Jon Smirl
2003-03-12 12:10 ` Ville Syrjälä
2003-03-12 17:38   ` Antonino Daplas
2003-03-12 18:47     ` Ville Syrjälä

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).