This patch adds the ability for the Bochs VGA ROM to return information about a HD wide monitor.
It is constructed from defitions at run time.
Inspired by this patch I digged out from the vgabios mailing list: http://old.nabble.com/VBE-BIOS-support-for-monitor-information-(EDID)-td2295...
Signed-off-by: Hiroshi Miura miurahr@linux.com --- Makefile | 3 +- vgasrc/bochsedid.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++++ vgasrc/bochsedid.h | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ vgasrc/vgahw.h | 5 ++ 4 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 vgasrc/bochsedid.c create mode 100644 vgasrc/bochsedid.h
diff --git a/Makefile b/Makefile index 45ea48a..d3e22b9 100644 --- a/Makefile +++ b/Makefile @@ -189,7 +189,8 @@ $(OUT)bios.bin.elf $(OUT)bios.bin: $(OUT)rom.o tools/checkrom.py SRCVGA=src/output.c src/util.c src/pci.c \ vgasrc/vgabios.c vgasrc/vgafb.c vgasrc/vgafonts.c vgasrc/vbe.c \ vgasrc/stdvga.c vgasrc/stdvgamodes.c vgasrc/stdvgaio.c \ - vgasrc/clext.c vgasrc/bochsvga.c vgasrc/geodevga.c + vgasrc/clext.c vgasrc/bochsvga.c vgasrc/geodevga.c \ + vgasrc/bochsedid.c
CFLAGS16VGA = $(CFLAGS16INC) -Isrc
diff --git a/vgasrc/bochsedid.c b/vgasrc/bochsedid.c new file mode 100644 index 0000000..5a40593 --- /dev/null +++ b/vgasrc/bochsedid.c @@ -0,0 +1,147 @@ +// VESA VBE EDID capability for virtual display +// +// Copyright (C) 2012 Hiroshi Miura miurahr@linux.com +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "vbe.h" // VBE_CAPABILITY_8BIT_DAC +#include "biosvar.h" // GET_GLOBAL +#include "util.h" // dprintf +#include "config.h" // CONFIG_* +#include "bochsedid.h" + +int bochs_get_ddc_capabilities(u16 unit) +{ + if (unit != 0) + return -1; + + return (1 << 8) | VBE_DDC1_PROTOCOL_SUPPORTED; +} + +u8 most_chromaticity[8] VAR16 = {0xA6,0x55,0x48,0x9B,0x26,0x12,0x50,0x54}; +unsigned char vgabios_name[] VAR16 = "Sea VGABIOS"; +struct edid_detailed_timing edid_dtd_1280x1024 VAR16 = { + 0x2a30, 0x00, 0x98, 0x51, 0x00, 0x2A, + 0x40, 0x30, 0x70, 0x13, 0x00, 0x2C, 0xE1, 0x10, + 0x00, 0x00, 0x1E}; +struct edid_detailed_timing edid_dtd_1152x864 VAR16 = { + 0x2a30, 0x80, 0xC0, 0x41, 0x60, 0x24, + 0x30, 0x40, 0x80, 0x13, 0x00, 0x2C, 0xE1, 0x10, + 0x00, 0x00, 0x1E}; +struct edid_detailed_timing edid_dtd_1600x1200 VAR16 = { + 0x3c8c, 0x40, 0x30, 0x62, 0xB0, 0x32, + 0x40, 0x40, 0xC0, 0x13, 0x00, 0x2C, 0xE1, 0x10, + 0x00, 0x00, 0x1E}; +struct edid_detailed_timing edid_dtd_1920x1200 VAR16 = { + 0x4B7D, 0xB0, 0xA0, 0x72, 0xB0, 0x2D, + 0x40, 0x88, 0xC8, 0x55, 0x75, 0x21, + 0x00, 0x00, 0x1C}; +struct edid_monitor_range_limit edid_range_limit VAR16 = { + {0x00,0x00,0x00,0xfD,0x00}, + 50,150, /* Vertical 50Hz - 150Hz */ + 31,135, /* Horiz 31kHz - 135kHz */ + 20, /* clock 200MHz */ + 0, /* no ext */ + 0x0A,0x20,0x20, + 0x2020, + 0x20,0x20 /* padding */ + }; + +int bochs_read_edid_block0(u16 unit, u16 block, u16 seg, void *data, u8 next) +{ + struct edid_info *info = data; + int i; + + memset_far(seg, info, 0, sizeof(*info)); + /* header */ + SET_FARVAR(seg, info->header[0], 0); + for (i = 1; i < 7; i++) { + SET_FARVAR(seg, info->header[i], 0xFF); + } + SET_FARVAR(seg, info->header[7], 0); + /* Vendor/Product/Serial/Date */ + SET_FARVAR(seg, info->vendor, 0x2104); + SET_FARVAR(seg, info->product,0xCDAB); + SET_FARVAR(seg, info->serial, 0x00000000); + /* date/version */ + SET_FARVAR(seg, info->week,36); + SET_FARVAR(seg, info->year,22); /* 2012 */ + SET_FARVAR(seg, info->major_version,1); + SET_FARVAR(seg, info->minor_version,3); /* 1.3 */ + /* video prameters */ + SET_FARVAR(seg, info->video_setup,0x0F); + /* Video signal interface (analogue, 0.700 : 0.300 : 1.000 V p-p, + Video Setup: Blank Level = Black Level, Separate Sync H & V Signals are + supported, Composite Sync Signal on Horizontal is supported, Composite + Sync Signal on Green Video is supported, Serration on the Vertical Sync + is supported) */ + SET_FARVAR(seg, info->screen_width,0x21); + SET_FARVAR(seg, info->screen_height,0x19); /* 330 mm * 250 mm */ + SET_FARVAR(seg, info->gamma,0x78); /* 2.2 */ + SET_FARVAR(seg, info->feature_flag,0x0F); /* no DMPS states, RGB, display is continuous frequency, prefered mode */ + SET_FARVAR(seg, info->least_chromaticity[0],0x78); + SET_FARVAR(seg, info->least_chromaticity[1],0xF5); + memcpy_far(seg, info->most_chromaticity, get_global_seg(), most_chromaticity, + sizeof (most_chromaticity)); + + SET_FARVAR(seg, info->established_timing[0], 0xFF); + SET_FARVAR(seg, info->established_timing[1], 0xEF); + SET_FARVAR(seg, info->established_timing[2], 0x80); + /* 720x400@70Hz, 720x400@88Hz, 640x480@60Hz, 640x480@67Hz, 640x480@72Hz, 640x480@75Hz, + 800x600@56Hz, 800x600@60Hz, 800x600@72Hz, 800x600@75Hz, 832x624@75Hz, 1152x870@75Hz, + not 1024x768@87Hz(I), 1024x768@60Hz, 1024x768@70Hz, 1024x768@75Hz, 1280x1024@75Hz */ + /* standard timings */ + SET_FARVAR(seg, info->standard_timing[0], EDID_STD_640x480_85Hz); + SET_FARVAR(seg, info->standard_timing[1], EDID_STD_800x600_85Hz); + SET_FARVAR(seg, info->standard_timing[2], EDID_STD_1024x768_85Hz); + SET_FARVAR(seg, info->standard_timing[3], EDID_STD_1280x720_70Hz); + SET_FARVAR(seg, info->standard_timing[4], EDID_STD_1280x960_60Hz); + SET_FARVAR(seg, info->standard_timing[5], EDID_STD_1440x900_60Hz); + SET_FARVAR(seg, info->standard_timing[6], EDID_STD_1600x1200_60Hz); + SET_FARVAR(seg, info->standard_timing[7], EDID_STD_NOP); + /* detailed timing blocks */ + memcpy_far(seg, &(info->desc[0].dtd), get_global_seg(), &edid_dtd_1600x1200, + sizeof (edid_dtd_1600x1200)); + /* monitor range */ + memcpy_far(seg, &(info->desc[1].mrld), get_global_seg(), &edid_range_limit, + sizeof(edid_range_limit)); + /* serial */ + for (i = 0; i < 5; i++) { + SET_FARVAR(seg, info->desc[2].mtxtd.header[i], 0); + } + SET_FARVAR(seg, info->desc[2].mtxtd.header[3], 0xFF); + for (i = 0; i < 10; i++) { + SET_FARVAR(seg, info->desc[2].mtxtd.text[i], i+0x30); + } + SET_FARVAR(seg, info->desc[2].mtxtd.text[10], 0x0A); + SET_FARVAR(seg, info->desc[2].mtxtd.text[11], 0x20); + SET_FARVAR(seg, info->desc[2].mtxtd.text[12], 0x20); + /* monitor name */ + for (i = 0; i < 5; i++) { + SET_FARVAR(seg, info->desc[3].mtxtd.header[i], 0); + } + SET_FARVAR(seg, info->desc[3].mtxtd.header[3], 0xFC); + memcpy_far(seg, info->desc[3].mtxtd.text, get_global_seg(), vgabios_name, 12); + SET_FARVAR(seg, info->desc[3].mtxtd.text[12], 0x0A); + /* ext */ + SET_FARVAR(seg, info->extensions, next); + + /* checksum */ + u8 sum = -checksum_far(get_global_seg(), info, sizeof(info)); + SET_FARVAR(seg, info->checksum, sum); + + return 0; +} + +int bochs_read_edid(u16 unit, u16 block, u16 seg, void *data) +{ + if (unit != 0) + return -1; + + switch (block) { + case 0: + return bochs_read_edid_block0(unit, block, seg, data, 0); + default: + return -1; + } +} diff --git a/vgasrc/bochsedid.h b/vgasrc/bochsedid.h new file mode 100644 index 0000000..0f925df --- /dev/null +++ b/vgasrc/bochsedid.h @@ -0,0 +1,136 @@ +#ifndef __BOCHEDID_H +#define __BOCHEDID_H + +/* EDID infomation definitions */ +/* Detailed Timing Descriptor */ +struct edid_detailed_timing { + u16 pixel_clock; + u8 horizontal_addressable_low; + u8 horizontal_blanking_low; + u8 horizontal_high; + u8 vertical_addressable_low; + u8 vertical_blanking_low; + u8 vertical_high; + u8 horizontal_front_porch_low; + u8 horizontal_sync_pulse_low; + u8 vertical_low4; + u8 horizontal_vertical_sync_hi; + u8 horizontal_video_image_low; + u8 vertical_video_image_low; + u8 video_image_high; + u8 horizontal_border; + u8 vertical_border; + u8 features; +} PACKED; + +/* Other Monitor Descriptors */ +/* + 0xFF: Monitor serial number (text) + 0xFE: Unspecified text (text) + 0xFC: Monitor name (text) + */ +struct edid_monitor_text { + u8 header[5]; + u8 text[13]; +} PACKED; + +/* Monitor Range Limits Descriptor */ +/* + 0xFD: Monitor range limits. 6- or 13-byte binary descriptor. + */ +struct edid_monitor_range_limit { + u8 header[5]; + u8 vertical_rate_min; + u8 vertical_rate_max; + u8 horizontal_rate_min; + u8 horizontal_rate_max; + u8 pixel_clock_max; + u8 extended_timing_type; + u8 reserved; + u8 start_frequency; + u8 gtf_c_val; + u16 gtf_m_val; + u8 gtf_k_val; + u8 gtf_j_val; +} PACKED; + +/* White point Descriptor */ +/* + 0xFB: Additional white point data. 2Ã 5-byte descriptors, padded with 0A 20 20. + 0xFA: Additional standard timing identifiers. 6Ã 2-byte descriptors, padded with 0A + */ +struct edid_wp_desc { + u8 index; + u8 least; + u8 x; + u8 y; + u8 gamma; +} PACKED; + +struct edid_white_point { + u8 header[5]; + struct edid_wp_desc desc[2]; + u8 padding[3]; +} PACKED; + +struct edid_additional_timing { + u8 header[5]; + u16 standard_timing[6]; + u8 padding; +} PACKED; + +union edid_descriptors { + struct edid_detailed_timing dtd; + struct edid_monitor_range_limit mrld; + struct edid_monitor_text mtxtd; + struct edid_white_point wpd; + struct edid_additional_timing astd; +} PACKED; + +struct edid_info { + u8 header[8]; + u16 vendor; + u16 product; + u32 serial; + u8 week; + u8 year; + u8 major_version; + u8 minor_version; + u8 video_setup; + u8 screen_width; + u8 screen_height; + u8 gamma; + u8 feature_flag; + u8 least_chromaticity[2]; + u8 most_chromaticity[8]; + u8 established_timing[3]; + u16 standard_timing[8]; + union edid_descriptors desc[4]; + u8 extensions; + u8 checksum; +} PACKED; + +/* EDID Standard Timing Description */ + +/* First byte: X resolution, divided by 8, less 31 (256â2288 pixels) */ +/* bit 7-6, X:Y pixel ratio: 00=16:10; 01=4:3; 10=5:4; 11=16:9 */ +/* bit 5-0, Vertical frequency, less 60 (60â123 Hz), nop 01 01 in Big Endian*/ +#define EDID_STD_640x480_85Hz 0x5931 +#define EDID_STD_800x600_85Hz 0x5945 +#define EDID_STD_1024x768_85Hz 0x5961 +#define EDID_STD_1152x864_70Hz 0x4A71 +#define EDID_STD_1280x720_70Hz 0xCA81 +#define EDID_STD_1280x800_70Hz 0x0A81 +#define EDID_STD_1280x960_60Hz 0x4081 +#define EDID_STD_1280x1024_60Hz 0x8081 +#define EDID_STD_1440x900_60Hz 0x0095 +#define EDID_STD_1600x1200_60Hz 0x40A9 +#define EDID_STD_1600x900_60Hz 0xC0A9 +#define EDID_STD_1680x1050_60Hz 0x00B3 +#define EDID_STD_1920x1080_60Hz 0xC0D1 +#define EDID_STD_1920x1200_60Hz 0x00D1 +#define EDID_STD_NOP 0x0101 + +int bochs_get_ddc_capabilities(u16 unit); +int bochs_read_edid(u16 unit, u16 block, u16 seg, void *data); +#endif /* bochedid.h */ diff --git a/vgasrc/vgahw.h b/vgasrc/vgahw.h index 01d2609..130462c 100644 --- a/vgasrc/vgahw.h +++ b/vgasrc/vgahw.h @@ -8,6 +8,7 @@ #include "bochsvga.h" // bochsvga_set_mode #include "stdvga.h" // stdvga_set_mode #include "geodevga.h" // geodevga_init +#include "bochsedid.h"
static inline struct vgamode_s *vgahw_find_mode(int mode) { if (CONFIG_VGA_CIRRUS) @@ -130,10 +131,14 @@ static inline int vgahw_restore_state(u16 seg, void *data, int states) { }
static inline int vgahw_get_ddc_capabilities(u16 unit) { + if (CONFIG_VGA_BOCHS) + return bochs_get_ddc_capabilities(unit); return stdvga_get_ddc_capabilities(unit); }
static inline int vgahw_read_edid(u16 unit, u16 block, u16 seg, void *data) { + if (CONFIG_VGA_BOCHS) + return bochs_read_edid(unit, block, seg, data); return stdvga_read_edid(unit, block, seg, data); }