diff options
| author | James Simmons <jsimmons@kozmo.(none)> | 2003-04-09 21:20:26 -0700 |
|---|---|---|
| committer | James Simmons <jsimmons@kozmo.(none)> | 2003-04-09 21:20:26 -0700 |
| commit | f31521dd9b824bb4635c2f5bf689b18fc6c0f2d6 (patch) | |
| tree | 6fda1d1a95ebeda3bee9e958145d0515b53638d2 | |
| parent | 32f5ad40c868b3e3a6a4ee65340ced6f899e3fe0 (diff) | |
[FBDEV] EDID support from OpenFirmware on PPC platoforms and from the BIOS on intel platforms.
| -rw-r--r-- | arch/i386/boot/compressed/misc.c | 2 | ||||
| -rw-r--r-- | arch/i386/boot/video.S | 34 | ||||
| -rw-r--r-- | arch/i386/kernel/setup.c | 4 | ||||
| -rw-r--r-- | drivers/video/edid.h | 138 | ||||
| -rw-r--r-- | drivers/video/fbmon.c | 926 | ||||
| -rw-r--r-- | drivers/video/modedb.c | 105 | ||||
| -rw-r--r-- | drivers/video/vesafb.c | 23 | ||||
| -rw-r--r-- | include/asm-i386/setup.h | 1 | ||||
| -rw-r--r-- | include/linux/fb.h | 9 | ||||
| -rw-r--r-- | include/video/edid.h | 27 |
10 files changed, 1089 insertions, 180 deletions
diff --git a/arch/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c index b0483a3c867a..d55ca2805c75 100644 --- a/arch/i386/boot/compressed/misc.c +++ b/arch/i386/boot/compressed/misc.c @@ -12,6 +12,7 @@ #include <linux/linkage.h> #include <linux/vmalloc.h> #include <linux/tty.h> +#include <video/edid.h> #include <asm/io.h> /* @@ -91,6 +92,7 @@ static unsigned char *real_mode; /* Pointer to real-mode data */ #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+0x440)) extern char input_data[]; extern int input_len; diff --git a/arch/i386/boot/video.S b/arch/i386/boot/video.S index 2c7f07eeace8..2e1f38ba9424 100644 --- a/arch/i386/boot/video.S +++ b/arch/i386/boot/video.S @@ -135,6 +135,7 @@ vid1: #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,39 @@ skip10: movb %ah, %al popw %ax ret +store_edid: + pushw %es # just save all registers + pushw %ax + pushw %bx + pushw %cx + pushw %dx + pushw %di + + pushw %fs + popw %es + + movl $0x13131313, %eax # memset block with 0x13 + movw $32, %cx + movw $0x440, %di + cld + rep + stosl + + movw $0x4f15, %ax # do VBE/DDC + movw $0x01, %bx + movw $0x00, %cx + movw $0x01, %dx + movw $0x440, %di + int $0x10 + + popw %di # restore all registers + popw %dx + popw %cx + popw %bx + popw %ax + popw %es + ret + # VIDEO_SELECT-only variables mt_end: .word 0 # End of video mode table if built edit_buf: .space 6 # Line editor buffer diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index c3fb8f0602cd..968a71ec009e 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -37,6 +37,7 @@ #include <linux/console.h> #include <linux/root_dev.h> #include <linux/highmem.h> +#include <video/edid.h> #include <asm/e820.h> #include <asm/mpspec.h> #include <asm/edd.h> @@ -85,7 +86,7 @@ struct sys_desc_table_struct { unsigned short length; unsigned char table[0]; }; - +struct edid_info edid_info; struct e820map e820; unsigned char aux_device_present; @@ -883,6 +884,7 @@ void __init setup_arch(char **cmdline_p) ROOT_DEV = ORIG_ROOT_DEV; drive_info = DRIVE_INFO; screen_info = SCREEN_INFO; + edid_info = EDID_INFO; apm_info.bios = APM_BIOS_INFO; saved_videomode = VIDEO_MODE; printk("Video mode to be used for restore is %lx\n", saved_videomode); diff --git a/drivers/video/edid.h b/drivers/video/edid.h new file mode 100644 index 000000000000..bd89fb3be8c2 --- /dev/null +++ b/drivers/video/edid.h @@ -0,0 +1,138 @@ +/* + * drivers/video/edid.h - EDID/DDC Header + * + * Based on: + * 1. XFree86 4.3.0, edid.h + * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE> + * + * 2. John Fremlin <vii@users.sourceforge.net> and + * Ani Joshi <ajoshi@unixbox.com> + * + * DDC is a Trademark of VESA (Video Electronics Standard Association). + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. +*/ + +#ifndef __EDID_H__ +#define __EDID_H__ + +#define EDID_LENGTH 0x80 +#define EDID_HEADER 0x00 +#define EDID_HEADER_END 0x07 + +#define ID_MANUFACTURER_NAME 0x08 +#define ID_MANUFACTURER_NAME_END 0x09 +#define ID_MODEL 0x0a + +#define ID_SERIAL_NUMBER 0x0c + +#define MANUFACTURE_WEEK 0x10 +#define MANUFACTURE_YEAR 0x11 + +#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 + +#define DETAILED_TIMING_DESCRIPTION_1 0x36 +#define DETAILED_TIMING_DESCRIPTION_2 0x48 +#define DETAILED_TIMING_DESCRIPTION_3 0x5a +#define DETAILED_TIMING_DESCRIPTION_4 0x6c + +#define DESCRIPTOR_DATA 5 + +#define UPPER_NIBBLE( x ) \ + (((128|64|32|16) & (x)) >> 4) + +#define LOWER_NIBBLE( x ) \ + ((1|2|4|8) & (x)) + +#define COMBINE_HI_8LO( hi, lo ) \ + ( (((unsigned)hi) << 8) | (unsigned)lo ) + +#define COMBINE_HI_4LO( hi, lo ) \ + ( (((unsigned)hi) << 4) | (unsigned)lo ) + +#define PIXEL_CLOCK_LO (unsigned)block[ 0 ] +#define PIXEL_CLOCK_HI (unsigned)block[ 1 ] +#define PIXEL_CLOCK (COMBINE_HI_8LO( PIXEL_CLOCK_HI,PIXEL_CLOCK_LO )*10000) +#define H_ACTIVE_LO (unsigned)block[ 2 ] +#define H_BLANKING_LO (unsigned)block[ 3 ] +#define H_ACTIVE_HI UPPER_NIBBLE( (unsigned)block[ 4 ] ) +#define H_ACTIVE COMBINE_HI_8LO( H_ACTIVE_HI, H_ACTIVE_LO ) +#define H_BLANKING_HI LOWER_NIBBLE( (unsigned)block[ 4 ] ) +#define H_BLANKING COMBINE_HI_8LO( H_BLANKING_HI, H_BLANKING_LO ) + +#define V_ACTIVE_LO (unsigned)block[ 5 ] +#define V_BLANKING_LO (unsigned)block[ 6 ] +#define V_ACTIVE_HI UPPER_NIBBLE( (unsigned)block[ 7 ] ) +#define V_ACTIVE COMBINE_HI_8LO( V_ACTIVE_HI, V_ACTIVE_LO ) +#define V_BLANKING_HI LOWER_NIBBLE( (unsigned)block[ 7 ] ) +#define V_BLANKING COMBINE_HI_8LO( V_BLANKING_HI, V_BLANKING_LO ) + +#define H_SYNC_OFFSET_LO (unsigned)block[ 8 ] +#define H_SYNC_WIDTH_LO (unsigned)block[ 9 ] + +#define V_SYNC_OFFSET_LO UPPER_NIBBLE( (unsigned)block[ 10 ] ) +#define V_SYNC_WIDTH_LO LOWER_NIBBLE( (unsigned)block[ 10 ] ) + +#define V_SYNC_WIDTH_HI ((unsigned)block[ 11 ] & (1|2)) +#define V_SYNC_OFFSET_HI (((unsigned)block[ 11 ] & (4|8)) >> 2) + +#define H_SYNC_WIDTH_HI (((unsigned)block[ 11 ] & (16|32)) >> 4) +#define H_SYNC_OFFSET_HI (((unsigned)block[ 11 ] & (64|128)) >> 6) + +#define V_SYNC_WIDTH COMBINE_HI_4LO( V_SYNC_WIDTH_HI, V_SYNC_WIDTH_LO ) +#define V_SYNC_OFFSET COMBINE_HI_4LO( V_SYNC_OFFSET_HI, V_SYNC_OFFSET_LO ) + +#define H_SYNC_WIDTH COMBINE_HI_4LO( H_SYNC_WIDTH_HI, H_SYNC_WIDTH_LO ) +#define H_SYNC_OFFSET COMBINE_HI_4LO( H_SYNC_OFFSET_HI, H_SYNC_OFFSET_LO ) + +#define H_SIZE_LO (unsigned)block[ 12 ] +#define V_SIZE_LO (unsigned)block[ 13 ] + +#define H_SIZE_HI UPPER_NIBBLE( (unsigned)block[ 14 ] ) +#define V_SIZE_HI LOWER_NIBBLE( (unsigned)block[ 14 ] ) + +#define H_SIZE COMBINE_HI_8LO( H_SIZE_HI, H_SIZE_LO ) +#define V_SIZE COMBINE_HI_8LO( V_SIZE_HI, V_SIZE_LO ) + +#define H_BORDER (unsigned)block[ 15 ] +#define V_BORDER (unsigned)block[ 16 ] + +#define FLAGS (unsigned)block[ 17 ] + +#define INTERLACED (FLAGS&128) +#define SYNC_TYPE (FLAGS&3<<3) /* bits 4,3 */ +#define SYNC_SEPARATE (3<<3) +#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) + +#endif /* __EDID_H__ */ diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 58f2fe27b1a3..5f3aa905a16c 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -1,7 +1,25 @@ /* - * linux/drivers/video/fbmon.c + * linux/drivers/video/fbmon.c * - * Copyright (C) 2002 James Simmons <jsimmons@users.sf.net> + * Copyright (C) 2002 James Simmons <jsimmons@users.sf.net> + * + * Credits: + * + * The EDID Parser is a conglomeration from the following sources: + * + * 1. SciTech SNAP Graphics Architecture + * Copyright (C) 1991-2002 SciTech Software, Inc. All rights reserved. + * + * 2. XFree86 4.3.0, interpret_edid.c + * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE> + * + * 3. John Fremlin <vii@users.sourceforge.net> and + * Ani Joshi <ajoshi@unixbox.com> + * + * Generalized Timing Formula is derived from: + * + * GTF Spreadsheet by Andy Morrish (1/5/97) + * available at http://www.vesa.org * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive @@ -11,124 +29,32 @@ #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 <linux/pci.h> #include <asm/prom.h> #endif +#include <video/edid.h> +#include "edid.h" /* * EDID parser - * - * portions of this file were based on the EDID parser by - * John Fremlin <vii@users.sourceforge.net> and Ani Joshi <ajoshi@unixbox.com> */ -#define EDID_LENGTH 0x80 -#define EDID_HEADER 0x00 -#define EDID_HEADER_END 0x07 - -#define ID_MANUFACTURER_NAME 0x08 -#define ID_MANUFACTURER_NAME_END 0x09 -#define ID_MODEL 0x0a - -#define ID_SERIAL_NUMBER 0x0c - -#define MANUFACTURE_WEEK 0x10 -#define MANUFACTURE_YEAR 0x11 - -#define EDID_STRUCT_VERSION 0x12 -#define EDID_STRUCT_REVISION 0x13 - -#define DPMS_FLAGS 0x18 -#define ESTABLISHED_TIMING_1 0x23 -#define ESTABLISHED_TIMING_2 0x24 -#define MANUFACTURERS_TIMINGS 0x25 - -#define DETAILED_TIMING_DESCRIPTIONS_START 0x36 -#define DETAILED_TIMING_DESCRIPTION_SIZE 18 -#define NO_DETAILED_TIMING_DESCRIPTIONS 4 - -#define DETAILED_TIMING_DESCRIPTION_1 0x36 -#define DETAILED_TIMING_DESCRIPTION_2 0x48 -#define DETAILED_TIMING_DESCRIPTION_3 0x5a -#define DETAILED_TIMING_DESCRIPTION_4 0x6c - -#define DESCRIPTOR_DATA 5 - -#define UPPER_NIBBLE( x ) \ - (((128|64|32|16) & (x)) >> 4) - -#define LOWER_NIBBLE( x ) \ - ((1|2|4|8) & (x)) - -#define COMBINE_HI_8LO( hi, lo ) \ - ( (((unsigned)hi) << 8) | (unsigned)lo ) - -#define COMBINE_HI_4LO( hi, lo ) \ - ( (((unsigned)hi) << 4) | (unsigned)lo ) - -#define PIXEL_CLOCK_LO (unsigned)block[ 0 ] -#define PIXEL_CLOCK_HI (unsigned)block[ 1 ] -#define PIXEL_CLOCK (COMBINE_HI_8LO( PIXEL_CLOCK_HI,PIXEL_CLOCK_LO )*1000) -#define H_ACTIVE_LO (unsigned)block[ 2 ] -#define H_BLANKING_LO (unsigned)block[ 3 ] -#define H_ACTIVE_HI UPPER_NIBBLE( (unsigned)block[ 4 ] ) -#define H_ACTIVE COMBINE_HI_8LO( H_ACTIVE_HI, H_ACTIVE_LO ) -#define H_BLANKING_HI LOWER_NIBBLE( (unsigned)block[ 4 ] ) -#define H_BLANKING COMBINE_HI_8LO( H_BLANKING_HI, H_BLANKING_LO ) - -#define V_ACTIVE_LO (unsigned)block[ 5 ] -#define V_BLANKING_LO (unsigned)block[ 6 ] -#define V_ACTIVE_HI UPPER_NIBBLE( (unsigned)block[ 7 ] ) -#define V_ACTIVE COMBINE_HI_8LO( V_ACTIVE_HI, V_ACTIVE_LO ) -#define V_BLANKING_HI LOWER_NIBBLE( (unsigned)block[ 7 ] ) -#define V_BLANKING COMBINE_HI_8LO( V_BLANKING_HI, V_BLANKING_LO ) - -#define H_SYNC_OFFSET_LO (unsigned)block[ 8 ] -#define H_SYNC_WIDTH_LO (unsigned)block[ 9 ] - -#define V_SYNC_OFFSET_LO UPPER_NIBBLE( (unsigned)block[ 10 ] ) -#define V_SYNC_WIDTH_LO LOWER_NIBBLE( (unsigned)block[ 10 ] ) - -#define V_SYNC_WIDTH_HI ((unsigned)block[ 11 ] & (1|2)) -#define V_SYNC_OFFSET_HI (((unsigned)block[ 11 ] & (4|8)) >> 2) - -#define H_SYNC_WIDTH_HI (((unsigned)block[ 11 ] & (16|32)) >> 4) -#define H_SYNC_OFFSET_HI (((unsigned)block[ 11 ] & (64|128)) >> 6) - -#define V_SYNC_WIDTH COMBINE_HI_4LO( V_SYNC_WIDTH_HI, V_SYNC_WIDTH_LO ) -#define V_SYNC_OFFSET COMBINE_HI_4LO( V_SYNC_OFFSET_HI, V_SYNC_OFFSET_LO ) - -#define H_SYNC_WIDTH COMBINE_HI_4LO( H_SYNC_WIDTH_HI, H_SYNC_WIDTH_LO ) -#define H_SYNC_OFFSET COMBINE_HI_4LO( H_SYNC_OFFSET_HI, H_SYNC_OFFSET_LO ) - -#define H_SIZE_LO (unsigned)block[ 12 ] -#define V_SIZE_LO (unsigned)block[ 13 ] - -#define H_SIZE_HI UPPER_NIBBLE( (unsigned)block[ 14 ] ) -#define V_SIZE_HI LOWER_NIBBLE( (unsigned)block[ 14 ] ) - -#define H_SIZE COMBINE_HI_8LO( H_SIZE_HI, H_SIZE_LO ) -#define V_SIZE COMBINE_HI_8LO( V_SIZE_HI, V_SIZE_LO ) - -#define H_BORDER (unsigned)block[ 15 ] -#define V_BORDER (unsigned)block[ 16 ] - -#define FLAGS (unsigned)block[ 17 ] - -#define INTERLACED (FLAGS&128) -#define SYNC_TYPE (FLAGS&3<<3) /* bits 4,3 */ -#define SYNC_SEPARATE (3<<3) -#define HSYNC_POSITIVE (FLAGS & 4) -#define VSYNC_POSITIVE (FLAGS & 2) - 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,138 +83,785 @@ static int edid_check_header(unsigned char *edid) 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; - - return sign; + unsigned char c[4]; + + 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 char *edid_get_monitor(unsigned char *block) +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 void print_chroma(unsigned char *block) { - static char name[13]; - unsigned i; - const unsigned char *ptr = block + DESCRIPTOR_DATA; + int tmp; + + /* Chromaticity data */ + printk(" Chromaticity: "); + tmp = ((block[5] & (3 << 6)) >> 6) | (block[0x7] << 2); + tmp *= 1000; + tmp += 512; + printk("RedX: 0.%03d ", tmp/1024); + + tmp = ((block[5] & (3 << 4)) >> 4) | (block[0x8] << 2); + tmp *= 1000; + tmp += 512; + printk("RedY: 0.%03d\n", tmp/1024); + + tmp = ((block[5] & (3 << 2)) >> 2) | (block[0x9] << 2); + tmp *= 1000; + tmp += 512; + printk(" GreenX: 0.%03d ", tmp/1024); + + tmp = (block[5] & 3) | (block[0xa] << 2); + tmp *= 1000; + tmp += 512; + printk("GreenY: 0.%03d\n", tmp/1024); + + tmp = ((block[6] & (3 << 6)) >> 6) | (block[0xb] << 2); + tmp *= 1000; + tmp += 512; + printk(" BlueX: 0.%03d ", tmp/1024); + + tmp = ((block[6] & (3 << 4)) >> 4) | (block[0xc] << 2); + tmp *= 1000; + tmp += 512; + printk("BlueY: 0.%03d\n", tmp/1024); + + tmp = ((block[6] & (3 << 2)) >> 2) | (block[0xd] << 2); + tmp *= 1000; + tmp += 512; + printk(" WhiteX: 0.%03d ", tmp/1024); + + tmp = (block[6] & 3) | (block[0xe] << 2); + tmp *= 1000; + tmp += 512; + printk("WhiteY: 0.%03d\n", tmp/1024); +} - for (i = 0; i < 13; i++, ptr++) { - if (*ptr == 0xa) { - name[i] = 0x00; - return name; +static void parse_display_block(unsigned char *block) +{ + unsigned char c; + + 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"); + } + c = (block[0] & 0x10) >> 4; + if (c) + printk(" Configurable signal level\n"); + printk(" Sync: "); + c = block[0] & 0x0f; + if (c & 0x10) + printk("Blank to Blank "); + 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; } - return name; + + print_chroma(block); + + 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)) + if ((block[0] != 0x00) || (block[1] != 0x00) || + (block[2] != 0x00) || (block[4] != 0x00)) + return 1; + else return 0; +} + +static int edid_is_serial_block(unsigned char *block) +{ + if ((block[0] == 0x00) && (block[1] == 0x00) && + (block[2] == 0x00) && (block[3] == 0xff) && + (block[4] == 0x00)) + return 1; + else + return 0; +} + +static int edid_is_ascii_block(unsigned char *block) +{ + if ((block[0] == 0x00) && (block[1] == 0x00) && + (block[2] == 0x00) && (block[3] == 0xfe) && + (block[4] == 0x00)) + return 1; else + return 0; +} + +static int edid_is_limits_block(unsigned char *block) +{ + if ((block[0] == 0x00) && (block[1] == 0x00) && + (block[2] == 0x00) && (block[3] == 0xfd) && + (block[4] == 0x00)) return 1; + else + return 0; } static int edid_is_monitor_block(unsigned char *block) { - if ((block[0] == 0x00) && (block[1] == 0x00) && (block[3] == 0xfc)) + if ((block[0] == 0x00) && (block[1] == 0x00) && + (block[2] == 0x00) && (block[3] == 0xfc) && + (block[4] == 0x00)) return 1; else return 0; } -static void parse_timing_block(unsigned char *block, - struct fb_var_screeninfo *var) +static int edid_is_color_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 ((block[0] == 0x00) && (block[1] == 0x00) && + (block[2] == 0x00) && (block[3] == 0xfb) && + (block[4] == 0x00)) + return 1; + else + return 0; +} - if (HSYNC_POSITIVE) - var->sync |= FB_SYNC_HOR_HIGH_ACT; - if (VSYNC_POSITIVE) - var->sync |= FB_SYNC_VERT_HIGH_ACT; +static int edid_is_std_timings_block(unsigned char *block) +{ + if ((block[0] == 0x00) && (block[1] == 0x00) && + (block[2] == 0x00) && (block[3] == 0xfa) && + (block[4] == 0x00)) + 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) +{ + int xres, yres = 0, refresh, ratio, err = 1; + + xres = (block[0] + 31) * 8; + if (xres <= 256) + return; + + ratio = (block[1] & 0xc0) >> 6; + switch (ratio) { + case 0: + yres = xres; + break; + case 1: + yres = (xres * 3)/4; + break; + case 2: + yres = (xres * 4)/5; + break; + case 3: + yres = (xres * 9)/16; + break; + } + refresh = (block[1] & 0x3f) + 60; + printk(" %dx%d@%dHz\n", xres, yres, refresh); + err = 0; +} + +static void parse_dst_timing_block(unsigned char *block) +{ + int i; + + block += 5; + for (i = 0; i < 5; i++, block += STD_TIMING_DESCRIPTION_SIZE) + parse_std_timing_block(block); +} + +static void parse_detailed_timing_block(unsigned char *block) +{ + printk(" %d MHz ", PIXEL_CLOCK/1000000); + 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 1; + + block = edid + DETAILED_TIMING_DESCRIPTIONS_START; + + for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { + 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; +} + +static void calc_mode_timings(int xres, int yres, int refresh, struct fb_videomode *mode) +{ + struct fb_var_screeninfo var; + struct fb_info info; + + var.xres = xres; + var.yres = yres; + fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, + refresh, &var, &info); + mode->xres = xres; + mode->yres = yres; + mode->pixclock = var.pixclock; + mode->refresh = refresh; + mode->left_margin = var.left_margin; + mode->right_margin = var.right_margin; + mode->upper_margin = var.upper_margin; + mode->lower_margin = var.lower_margin; + mode->hsync_len = var.hsync_len; + mode->vsync_len = var.vsync_len; + mode->vmode = 0; + mode->sync = 0; +} + +static int get_est_timing(unsigned char *block, struct fb_videomode *mode) +{ + int num = 0; + unsigned char c; + + c = block[0]; + if (c&0x80) + calc_mode_timings(720, 400, 70, &mode[num++]); + if (c&0x40) + calc_mode_timings(720, 400, 88, &mode[num++]); + if (c&0x20) + mode[num++] = vesa_modes[3]; + if (c&0x10) + calc_mode_timings(640, 480, 67, &mode[num++]); + if (c&0x08) + mode[num++] = vesa_modes[4]; + if (c&0x04) + mode[num++] = vesa_modes[5]; + if (c&0x02) + mode[num++] = vesa_modes[7]; + if (c&0x01) + mode[num++] = vesa_modes[8]; + + c = block[1]; + if (c&0x80) + mode[num++] = vesa_modes[9]; + if (c&0x40) + mode[num++] = vesa_modes[10]; + if (c&0x20) + calc_mode_timings(832, 624, 75, &mode[num++]); + if (c&0x10) + mode[num++] = vesa_modes[12]; + if (c&0x08) + mode[num++] = vesa_modes[13]; + if (c&0x04) + mode[num++] = vesa_modes[14]; + if (c&0x02) + mode[num++] = vesa_modes[15]; + if (c&0x01) + mode[num++] = vesa_modes[21]; + + c = block[2]; + if (c&0x80) + mode[num++] = vesa_modes[17]; + + return num; +} + +static int get_std_timing(unsigned char *block, struct fb_videomode *mode) +{ + int xres, yres = 0, refresh, ratio, i; + + xres = (block[0] + 31) * 8; + if (xres <= 256) return 0; - printk("EDID ver %d rev %d\n", (int) edid[EDID_STRUCT_VERSION], - (int) edid[EDID_STRUCT_REVISION]); + ratio = (block[1] & 0xc0) >> 6; + switch (ratio) { + case 0: + yres = xres; + break; + case 1: + yres = (xres * 3)/4; + break; + case 2: + yres = (xres * 4)/5; + break; + case 3: + yres = (xres * 9)/16; + break; + } + refresh = (block[1] & 0x3f) + 60; + + for (i = 0; i < VESA_MODEDB_SIZE; i++) { + if (vesa_modes[i].xres == xres && + vesa_modes[i].yres == yres && + vesa_modes[i].refresh == refresh) { + *mode = vesa_modes[i]; + break; + } else { + calc_mode_timings(xres, yres, refresh, mode); + break; + } + } + return 1; +} - vendor = edid_get_vendor(edid + ID_MANUFACTURER_NAME); +static int get_dst_timing(unsigned char *block, + struct fb_videomode *mode) +{ + int j, num = 0; - block = edid + DETAILED_TIMING_DESCRIPTIONS_START; + for (j = 0; j < 6; j++, block+= STD_TIMING_DESCRIPTION_SIZE) + num += get_std_timing(block, &mode[num]); - for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) { - if (edid_is_monitor_block(block)) { - monitor = edid_get_monitor(block); + return num; +} + +static void get_detailed_timing(unsigned char *block, + struct fb_videomode *mode) +{ + mode->xres = H_ACTIVE; + mode->yres = V_ACTIVE; + mode->pixclock = PIXEL_CLOCK; + mode->pixclock /= 1000; + mode->pixclock = KHZ2PICOS(mode->pixclock); + mode->right_margin = H_SYNC_OFFSET; + mode->left_margin = (H_ACTIVE + H_BLANKING) - + (H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH); + mode->upper_margin = V_BLANKING - V_SYNC_OFFSET - + V_SYNC_WIDTH; + mode->lower_margin = V_SYNC_OFFSET; + mode->hsync_len = H_SYNC_WIDTH; + mode->vsync_len = V_SYNC_WIDTH; + if (HSYNC_POSITIVE) + mode->sync |= FB_SYNC_HOR_HIGH_ACT; + if (VSYNC_POSITIVE) + mode->sync |= FB_SYNC_VERT_HIGH_ACT; + mode->refresh = PIXEL_CLOCK/((H_ACTIVE + H_BLANKING) * + (V_ACTIVE + V_BLANKING)); + mode->vmode = 0; +} + +/** + * fb_create_modedb - create video mode database + * @edid: EDID data + * @dbsize: database size + * + * RETURNS: struct fb_videomode, @dbsize contains length of database + * + * DESCRIPTION: + * This function builds a mode database using the contents of the EDID + * data + */ +struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) +{ + struct fb_videomode *mode, *m; + unsigned char *block; + int num = 0, i; + + mode = kmalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL); + if (mode == NULL) + return NULL; + memset(mode, 0, 50 * sizeof(struct fb_videomode)); + + if (edid == NULL || !edid_checksum(edid) || + !edid_check_header(edid)) { + kfree(mode); + return NULL; + } + + *dbsize = 0; + + block = edid + ESTABLISHED_TIMING_1; + num += get_est_timing(block, &mode[num]); + + block = edid + STD_TIMING_DESCRIPTIONS_START; + for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE) + num += get_std_timing(block, &mode[num]); + + block = edid + DETAILED_TIMING_DESCRIPTIONS_START; + for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) { + if (block[0] == 0x00 && block[1] == 0x00) { + if (block[3] == 0xfa) { + num += get_dst_timing(block + 5, &mode[num]); + } + } else { + get_detailed_timing(block, &mode[num]); + num++; } } + + /* Yikes, EDID data is totally useless */ + if (!num) { + kfree(mode); + return NULL; + } + + *dbsize = num; + m = kmalloc(num * sizeof(struct fb_videomode), GFP_KERNEL); + if (!m) + return mode; + memmove(m, mode, num * sizeof(struct fb_videomode)); + kfree(mode); + return m; +} + +/** + * fb_destroy_modedb - destroys mode database + * @modedb: mode database to destroy + * + * DESCRIPTION: + * Destroy mode database created by fb_create_modedb + */ +void fb_destroy_modedb(struct fb_videomode *modedb) +{ + if (modedb) + kfree(modedb); +} - printk("EDID: detected %s %s\n", vendor, monitor); +/** + * fb_get_monitor_limits - get monitor operating limits + * @edid: EDID data + * @specs: fb_monspecs structure pointer + * + * DESCRIPTION: + * Gets monitor operating limits from EDID data and places them in + * @specs + */ +int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs) +{ + int i, retval = 1; + unsigned char *block; + + if (edid == NULL || specs == NULL) + return 1; + if (!(edid_checksum(edid))) + return 1; + + if (!(edid_check_header(edid))) + return 1; + + memset(specs, 0, sizeof(struct fb_monspecs)); block = edid + DETAILED_TIMING_DESCRIPTIONS_START; + printk("Monitor Operating Limits: "); 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]; + retval = 0; + printk("From EDID\n"); + break; } } - return 1; + + /* estimate monitor limits based on modes supported */ + if (retval) { + struct fb_videomode *modes; + int num_modes, i, hz, hscan, pixclock; + + modes = fb_create_modedb(edid, &num_modes); + if (!modes) { + printk("None Available\n"); + return 1; + } + + retval = 0; + for (i = 0; i < num_modes; i++) { + hz = modes[i].refresh; + pixclock = PICOS2KHZ(modes[i].pixclock) * 1000; + hscan = (modes[i].yres * 105 * hz + 5000)/100; + + if (specs->dclkmax == 0 || specs->dclkmax < pixclock) + specs->dclkmax = pixclock; + if (specs->dclkmin == 0 || specs->dclkmin > pixclock) + specs->dclkmin = pixclock; + if (specs->hfmax == 0 || specs->hfmax < hscan) + specs->hfmax = hscan; + if (specs->hfmin == 0 || specs->hfmin > hscan) + specs->hfmin = hscan; + if (specs->vfmax == 0 || specs->vfmax < hz) + specs->vfmax = hz; + if (specs->vfmin == 0 || specs->vfmin > hz) + specs->vfmin = hz; + } + printk("Extrapolated\n"); + fb_destroy_modedb(modes); + } + printk(" H: %d-%dKHz V: %d-%dHz DCLK: %dMHz\n", specs->hfmin/1000, specs->hfmax/1000, + specs->vfmin, specs->vfmax, specs->dclkmax/1000000); + return retval; } -#ifdef CONFIG_PCI -char *get_EDID(struct pci_dev *pdev) +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(" Standard Timings\n"); + block = edid + STD_TIMING_DESCRIPTIONS_START; + for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE) + parse_std_timing_block(block); + + 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_dst_timing_block(block); + } else if (edid_is_timing_block(block)) { + parse_detailed_timing_block(block); + } + } + printk("========================================\n"); +} + #ifdef CONFIG_ALL_PPC +char *get_EDID_from_OF(struct pci_dev *pdev) +{ static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID", "EDID1", NULL }; unsigned char *pedid = NULL; 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) { - pedid = - (unsigned char *) get_property(dp, - propnames[i], - NULL); + pedid = (unsigned char *) get_property(dp, propnames[i], NULL); if (pedid != NULL) return pedid; } dp = dp->child; } + show_edid(pedid); return pedid; -#else - return NULL; +} #endif + +#ifdef CONFIG_X86 +char *get_EDID_from_BIOS(void *dummy) +{ + unsigned char *pedid = edid_info.dummy; + + if (!pedid) + return NULL; + show_edid(pedid); + return pedid; } #endif @@ -679,8 +1252,15 @@ int fb_validate_mode(struct fb_var_screeninfo *var, struct fb_info *info) } EXPORT_SYMBOL(parse_edid); -#ifdef CONFIG_PCI -EXPORT_SYMBOL(get_EDID); +EXPORT_SYMBOL(show_edid); +#ifdef CONFIG_X86 +EXPORT_SYMBOL(get_EDID_from_BIOS); +#endif +#ifdef CONFIG_ALL_PPC +EXPORT_SYMBOL(get_EDID_from_OF); #endif +EXPORT_SYMBOL(fb_get_monitor_limits); EXPORT_SYMBOL(fb_get_mode); EXPORT_SYMBOL(fb_validate_mode); +EXPORT_SYMBOL(fb_create_modedb); +EXPORT_SYMBOL(fb_destroy_modedb); diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index 60332e32d87d..eb60cbd1d30a 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c @@ -251,6 +251,110 @@ static const struct fb_videomode modedb[] __initdata = { }, }; +const struct fb_videomode vesa_modes[] = { + /* 0 640x350-85 VESA */ + { NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3, + FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 1 640x400-85 VESA */ + { NULL, 85, 640, 400, 31746, 96, 32, 41, 01, 64, 3, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 2 720x400-85 VESA */ + { NULL, 85, 721, 400, 28169, 108, 36, 42, 01, 72, 3, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 3 640x480-60 VESA */ + { NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2, + 0, FB_VMODE_NONINTERLACED }, + /* 4 640x480-72 VESA */ + { NULL, 72, 640, 480, 31746, 128, 24, 29, 9, 40, 2, + 0, FB_VMODE_NONINTERLACED }, + /* 5 640x480-75 VESA */ + { NULL, 75, 640, 480, 31746, 120, 16, 16, 01, 64, 3, + 0, FB_VMODE_NONINTERLACED }, + /* 6 640x480-85 VESA */ + { NULL, 85, 640, 480, 27777, 80, 56, 25, 01, 56, 3, + 0, FB_VMODE_NONINTERLACED }, + /* 7 800x600-56 VESA */ + { NULL, 56, 800, 600, 27777, 128, 24, 22, 01, 72, 2, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 8 800x600-60 VESA */ + { NULL, 60, 800, 600, 25000, 88, 40, 23, 01, 128, 4, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 9 800x600-72 VESA */ + { NULL, 72, 800, 600, 20000, 64, 56, 23, 37, 120, 6, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 10 800x600-75 VESA */ + { NULL, 75, 800, 600, 20202, 160, 16, 21, 01, 80, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 11 800x600-85 VESA */ + { NULL, 85, 800, 600, 17761, 152, 32, 27, 01, 64, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 12 1024x768i-43 VESA */ + { NULL, 53, 1024, 768, 22271, 56, 8, 41, 0, 176, 8, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_INTERLACED }, + /* 13 1024x768-60 VESA */ + { NULL, 60, 1024, 768, 15384, 160, 24, 29, 3, 136, 6, + 0, FB_VMODE_NONINTERLACED }, + /* 14 1024x768-70 VESA */ + { NULL, 70, 1024, 768, 13333, 144, 24, 29, 3, 136, 6, + 0, FB_VMODE_NONINTERLACED }, + /* 15 1024x768-75 VESA */ + { NULL, 75, 1024, 768, 12690, 176, 16, 28, 1, 96, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 16 1024x768-85 VESA */ + { NULL, 85, 1024, 768, 10582, 208, 48, 36, 1, 96, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 17 1152x864-75 VESA */ + { NULL, 75, 1153, 864, 9259, 256, 64, 32, 1, 128, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 18 1280x960-60 VESA */ + { NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 19 1280x960-85 VESA */ + { NULL, 85, 1280, 960, 6734, 224, 64, 47, 1, 160, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 20 1280x1024-60 VESA */ + { NULL, 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 21 1280x1024-75 VESA */ + { NULL, 75, 1280, 1024, 7407, 248, 16, 38, 1, 144, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 22 1280x1024-85 VESA */ + { NULL, 85, 1280, 1024, 6349, 224, 64, 44, 1, 160, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 23 1600x1200-60 VESA */ + { NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 24 1600x1200-65 VESA */ + { NULL, 65, 1600, 1200, 5698, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 25 1600x1200-70 VESA */ + { NULL, 70, 1600, 1200, 5291, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 26 1600x1200-75 VESA */ + { NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 27 1600x1200-85 VESA */ + { NULL, 85, 1600, 1200, 4357, 304, 64, 46, 1, 192, 3, + FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 28 1792x1344-60 VESA */ + { NULL, 60, 1792, 1344, 4882, 328, 128, 46, 1, 200, 3, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 29 1792x1344-75 VESA */ + { NULL, 75, 1792, 1344, 3831, 352, 96, 69, 1, 216, 3, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 30 1856x1392-60 VESA */ + { NULL, 60, 1856, 1392, 4580, 352, 96, 43, 1, 224, 3, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 31 1856x1392-75 VESA */ + { NULL, 75, 1856, 1392, 3472, 352, 128, 104, 1, 224, 3, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 32 1920x1440-60 VESA */ + { NULL, 60, 1920, 1440, 4273, 344, 128, 56, 1, 200, 3, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, + /* 33 1920x1440-75 VESA */ + { NULL, 60, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED }, +}; static int __init my_atoi(const char *name) { @@ -432,3 +536,4 @@ done: } EXPORT_SYMBOL(__fb_try_mode); +EXPORT_SYMBOL(vesa_modes); diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c index 9b9f7cc44bf3..1f70b15405ec 100644 --- a/drivers/video/vesafb.c +++ b/drivers/video/vesafb.c @@ -19,7 +19,9 @@ #include <linux/fb.h> #include <linux/ioport.h> #include <linux/init.h> - +#ifdef __i386__ +#include <video/edid.h> +#endif #include <asm/io.h> #include <asm/mtrr.h> @@ -213,6 +215,7 @@ int __init vesafb_setup(char *options) int __init vesafb_init(void) { int video_cmap_len; + char *edid = 0; int i; if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB) @@ -296,12 +299,20 @@ int __init vesafb_init(void) vesafb_defined.yres_virtual = vesafb_defined.yres; ypan = 0; } - - /* some dummy values for timing to make fbset happy */ - vesafb_defined.pixclock = 10000000 / vesafb_defined.xres * 1000 / vesafb_defined.yres; - vesafb_defined.left_margin = (vesafb_defined.xres / 8) & 0xf8; - vesafb_defined.hsync_len = (vesafb_defined.xres / 8) & 0xf8; +#ifdef __i386__ + edid = get_EDID_from_BIOS(0); + if (edid) + parse_edid(edid, &vesafb_defined); + else +#endif + { + /* some dummy values for timing to make fbset happy */ + vesafb_defined.pixclock = 10000000 / vesafb_defined.xres * 1000 / vesafb_defined.yres; + vesafb_defined.left_margin = (vesafb_defined.xres / 8) & 0xf8; + vesafb_defined.hsync_len = (vesafb_defined.xres / 8) & 0xf8; + } + if (vesafb_defined.bits_per_pixel > 8) { vesafb_defined.red.offset = screen_info.red_pos; vesafb_defined.red.length = screen_info.red_size; diff --git a/include/asm-i386/setup.h b/include/asm-i386/setup.h index a89487b409b8..3160b947d2bd 100644 --- a/include/asm-i386/setup.h +++ b/include/asm-i386/setup.h @@ -37,6 +37,7 @@ #define KERNEL_START (*(unsigned long *) (PARAM+0x214)) #define INITRD_START (*(unsigned long *) (PARAM+0x218)) #define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c)) +#define EDID_INFO (*(struct edid_info *) (PARAM+0x440)) #define EDD_NR (*(unsigned char *) (PARAM+EDDNR)) #define EDD_BUF ((struct edd_info *) (PARAM+EDDBUF)) #define COMMAND_LINE ((char *) (PARAM+2048)) diff --git a/include/linux/fb.h b/include/linux/fb.h index 5815d65d630b..654e85f12a16 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -504,6 +504,15 @@ extern int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, 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 int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs); +extern struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize); +extern void fb_destroy_modedb(struct fb_videomode *modedb); +extern void show_edid(unsigned char *edid); + +/* drivers/video/modedb.c */ +#define VESA_MODEDB_SIZE 34 +extern const struct fb_videomode vesa_modes[]; /* drivers/video/fbcmap.c */ extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp); diff --git a/include/video/edid.h b/include/video/edid.h new file mode 100644 index 000000000000..ae23acc7db28 --- /dev/null +++ b/include/video/edid.h @@ -0,0 +1,27 @@ +#ifndef __linux_video_edid_h__ +#define __linux_video_edid_h__ + +#ifdef __KERNEL__ + +#include <linux/config.h> +#ifdef CONFIG_ALL_PPC +#include <linux/pci.h> +#endif + +#ifdef CONFIG_X86 +struct edid_info { + unsigned char dummy[128]; +}; + +extern struct edid_info edid_info; +extern char *get_EDID_from_BIOS(void *); + +#endif /* CONFIG_X86 */ + +#ifdef CONFIG_ALL_PPC +extern char *get_EDID_from_OF(struct pci_dev *pdev); +#endif + +#endif /* __KERNEL__ */ + +#endif /* __linux_video_edid_h__ */ |
