From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with archive (Exim 4.43) id 1PSZNR-0002S5-NK for mharc-grub-devel@gnu.org; Tue, 14 Dec 2010 13:13:29 -0500 Received: from [140.186.70.92] (port=40463 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PSZNO-0002PA-TO for grub-devel@gnu.org; Tue, 14 Dec 2010 13:13:28 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PSZNM-0004z2-T2 for grub-devel@gnu.org; Tue, 14 Dec 2010 13:13:26 -0500 Received: from smarthost03.mail.zen.net.uk ([212.23.3.142]:56103) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PSZNM-0004yp-Fb for grub-devel@gnu.org; Tue, 14 Dec 2010 13:13:24 -0500 Received: from [82.69.40.219] (helo=riva.pelham.vpn.ucam.org) by smarthost03.mail.zen.net.uk with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.63) (envelope-from ) id 1PSZNK-0005Vp-KQ for grub-devel@gnu.org; Tue, 14 Dec 2010 18:13:23 +0000 Received: from cjwatson by riva.pelham.vpn.ucam.org with local (Exim 4.69) (envelope-from ) id 1PSZNJ-00072k-7m for grub-devel@gnu.org; Tue, 14 Dec 2010 18:13:21 +0000 Date: Tue, 14 Dec 2010 18:13:20 +0000 From: Colin Watson To: grub-devel@gnu.org Message-ID: <20101214181320.GA27013@riva.ucam.org> References: <20101214163836.GR21862@riva.ucam.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20101214163836.GR21862@riva.ucam.org> User-Agent: Mutt/1.5.18 (2008-05-17) X-Originating-Smarthost03-IP: [82.69.40.219] X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) Subject: Re: [PATCH] Preferred resolution detection for VBE X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: The development of GNU GRUB List-Id: The development of GNU GRUB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 14 Dec 2010 18:13:28 -0000 On Tue, Dec 14, 2010 at 04:38:36PM +0000, Colin Watson wrote: > 2010-12-14 Colin Watson > > Preferred resolution detection for VBE. Updated version following comments from Vladimir on IRC. 2010-12-14 Colin Watson Preferred resolution detection for VBE. * grub-core/video/video.c (grub_video_edid_checksum): New function. (grub_video_get_edid): Likewise. (grub_video_edid_preferred_mode): Likewise. Try EDID followed by the Flat Panel extension, in line with the X.org VESA driver. * grub-core/video/i386/pc/vbe.c (grub_vbe_bios_get_flat_panel_info): New function. (grub_vbe_bios_get_ddc_capabilities): Likewise. (grub_vbe_bios_read_edid): Likewise. (grub_vbe_get_preferred_mode): Likewise. (grub_video_vbe_setup): When the mode is "auto", try to get the preferred mode from VBE, and use the largest mode that is no larger than the preferred mode (some BIOSes expose a preferred mode that is not in their mode list!). If this fails, fall back to 640x480 as a safe conservative choice. (grub_video_vbe_get_edid): New function. (grub_video_vbe_adapter): Add get_edid. * include/grub/video.h (struct grub_vbe_edid_info): New structure. (struct grub_video_adapter): Add get_edid. (grub_video_edid_checksum): Add prototype. (grub_video_get_edid): Likewise. (grub_video_edid_preferred_mode): Likewise. * include/grub/i386/pc/vbe.h (struct grub_vbe_flat_panel_info): New structure. (grub_vbe_bios_get_flat_panel_info): Add prototype. (grub_vbe_bios_get_ddc_capabilities): Likewise. (grub_vbe_bios_read_edid): Likewise. * grub-core/commands/videoinfo.c (print_edid): New function. (grub_cmd_videoinfo): Print EDID if available. * util/grub.d/00_header.in (GRUB_GFXMODE): Default to "auto". This is more appropriate on a wider range of platforms than 640x480. === modified file 'grub-core/commands/videoinfo.c' --- grub-core/commands/videoinfo.c 2010-09-15 12:37:28 +0000 +++ grub-core/commands/videoinfo.c 2010-12-14 17:59:42 +0000 @@ -77,6 +77,30 @@ hook (const struct grub_video_mode_info return 0; } +static void +print_edid (struct grub_video_edid_info *edid_info) +{ + unsigned int edid_width, edid_height; + + if (grub_video_edid_checksum (edid_info)) + { + grub_printf (" EDID checksum invalid\n"); + grub_errno = GRUB_ERR_NONE; + return; + } + + grub_printf (" EDID version: %u.%u\n", + edid_info->version, edid_info->revision); + if (grub_video_edid_preferred_mode (edid_info, &edid_width, &edid_height) + == GRUB_ERR_NONE) + grub_printf (" Preferred mode: %ux%u\n", edid_width, edid_height); + else + { + grub_printf (" No preferred mode available\n"); + grub_errno = GRUB_ERR_NONE; + } +} + static grub_err_t grub_cmd_videoinfo (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) @@ -120,6 +144,8 @@ grub_cmd_videoinfo (grub_command_t cmd _ FOR_VIDEO_ADAPTERS (adapter) { + struct grub_video_edid_info edid_info; + grub_printf ("Adapter '%s':\n", adapter->name); if (!adapter->iterate) @@ -143,6 +169,11 @@ grub_cmd_videoinfo (grub_command_t cmd _ adapter->iterate (hook); + if (adapter->get_edid (&edid_info) == GRUB_ERR_NONE) + print_edid (&edid_info); + else + grub_errno = GRUB_ERR_NONE; + if (adapter->id != id) { if (adapter->fini ()) === modified file 'grub-core/video/i386/pc/vbe.c' --- grub-core/video/i386/pc/vbe.c 2010-09-15 22:37:30 +0000 +++ grub-core/video/i386/pc/vbe.c 2010-12-14 17:31:20 +0000 @@ -273,6 +273,56 @@ grub_vbe_bios_get_pm_interface (grub_uin return regs.eax & 0xffff; } +/* Call VESA BIOS 0x4f11 to get flat panel information, return status. */ +grub_vbe_status_t +grub_vbe_bios_get_flat_panel_info (struct grub_vbe_flat_panel_info *flat_panel_info) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f11; + regs.ebx = 0x0001; + regs.es = (((grub_addr_t) flat_panel_info) & 0xffff0000) >> 4; + regs.edi = ((grub_addr_t) flat_panel_info) & 0xffff; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f15 to get DDC availability, return status. */ +grub_vbe_status_t +grub_vbe_bios_get_ddc_capabilities (grub_uint8_t *level) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f15; + regs.ebx = 0x0000; + regs.ecx = 0x0000; + regs.es = 0x0000; + regs.edi = 0x0000; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + + *level = regs.ebx & 0xff; + return regs.eax & 0xffff; +} + +/* Call VESA BIOS 0x4f15 to read EDID information, return status. */ +grub_vbe_status_t +grub_vbe_bios_read_edid (struct grub_video_edid_info *edid_info) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x4f15; + regs.ebx = 0x0001; + regs.ecx = 0x0000; + regs.edx = 0x0000; + regs.es = (((grub_addr_t) edid_info) & 0xffff0000) >> 4; + regs.edi = ((grub_addr_t) edid_info) & 0xffff; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x10, ®s); + return regs.eax & 0xffff; +} + grub_err_t grub_vbe_probe (struct grub_vbe_info_block *info_block) @@ -327,6 +377,37 @@ grub_vbe_probe (struct grub_vbe_info_blo return GRUB_ERR_NONE; } +static grub_err_t +grub_vbe_get_preferred_mode (unsigned int *width, unsigned int *height) +{ + grub_vbe_status_t status; + grub_uint8_t ddc_level; + struct grub_video_edid_info edid_info; + struct grub_vbe_flat_panel_info flat_panel_info; + + if (controller_info.version >= 0x200 + && (grub_vbe_bios_get_ddc_capabilities (&ddc_level) & 0xff) + == GRUB_VBE_STATUS_OK) + { + if (grub_video_get_edid (&edid_info) == GRUB_ERR_NONE + && grub_video_edid_preferred_mode (&edid_info, width, height) + == GRUB_ERR_NONE) + return GRUB_ERR_NONE; + + grub_errno = GRUB_ERR_NONE; + } + + status = grub_vbe_bios_get_flat_panel_info (&flat_panel_info); + if (status == GRUB_VBE_STATUS_OK) + { + *width = flat_panel_info.horizontal_size; + *height = flat_panel_info.vertical_size; + return GRUB_ERR_NONE; + } + + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "cannot get preferred mode"); +} + grub_err_t grub_vbe_set_video_mode (grub_uint32_t vbe_mode, struct grub_vbe_mode_info_block *vbe_mode_info) @@ -695,11 +776,28 @@ grub_video_vbe_setup (unsigned int width struct grub_vbe_mode_info_block best_vbe_mode_info; grub_uint32_t best_vbe_mode = 0; int depth; + int preferred_mode = 0; /* Decode depth from mode_type. If it is zero, then autodetect. */ depth = (mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS; + if (width == 0 && height == 0) + { + grub_vbe_get_preferred_mode (&width, &height); + if (grub_errno == GRUB_ERR_NONE) + preferred_mode = 1; + else + { + /* Fall back to 640x480. This is conservative, but the largest + mode supported by the graphics card may not be safe for the + display device. */ + grub_errno = GRUB_ERR_NONE; + width = 640; + height = 480; + } + } + /* Walk thru mode list and try to find matching mode. */ for (p = vbe_mode_list; *p != 0xFFFF; p++) { @@ -742,10 +840,21 @@ grub_video_vbe_setup (unsigned int width /* Unsupported bitdepth . */ continue; - if (((vbe_mode_info.x_resolution != width) - || (vbe_mode_info.y_resolution != height)) && width != 0 && height != 0) - /* Non matching resolution. */ - continue; + if (preferred_mode) + { + if (vbe_mode_info.x_resolution > width + || vbe_mode_info.y_resolution > height) + /* Resolution exceeds that of preferred mode. */ + continue; + } + else + { + if (((vbe_mode_info.x_resolution != width) + || (vbe_mode_info.y_resolution != height)) + && width != 0 && height != 0) + /* Non matching resolution. */ + continue; + } /* Check if user requested RGB or index color mode. */ if ((mode_mask & GRUB_VIDEO_MODE_TYPE_COLOR_MASK) != 0) @@ -855,6 +964,15 @@ grub_video_vbe_get_info_and_fini (struct return grub_video_fb_get_info_and_fini (mode_info, framebuf); } +static grub_err_t +grub_video_vbe_get_edid (struct grub_video_edid_info *edid_info) +{ + if (grub_vbe_bios_read_edid (edid_info) != GRUB_VBE_STATUS_OK) + return grub_error (GRUB_ERR_BAD_DEVICE, "EDID information not available"); + + return GRUB_ERR_NONE; +} + static void grub_video_vbe_print_adapter_specific_info (void) { @@ -899,6 +1017,7 @@ static struct grub_video_adapter grub_vi .set_active_render_target = grub_video_fb_set_active_render_target, .get_active_render_target = grub_video_fb_get_active_render_target, .iterate = grub_video_vbe_iterate, + .get_edid = grub_video_vbe_get_edid, .print_adapter_specific_info = grub_video_vbe_print_adapter_specific_info, .next = 0 === modified file 'grub-core/video/video.c' --- grub-core/video/video.c 2010-06-24 19:22:40 +0000 +++ grub-core/video/video.c 2010-12-14 17:51:19 +0000 @@ -374,6 +374,68 @@ grub_video_get_active_render_target (str return grub_video_adapter_active->get_active_render_target (target); } +grub_err_t +grub_video_edid_checksum (struct grub_video_edid_info *edid_info) +{ + const char *edid_bytes = (const char *) edid_info; + int i; + char checksum = 0; + + /* Check EDID checksum. */ + for (i = 0; i < 128; ++i) + checksum += edid_bytes[i]; + + if (checksum != 0) + return grub_error (GRUB_ERR_BAD_DEVICE, + "invalid EDID checksum %d", checksum); + + grub_errno = GRUB_ERR_NONE; + return grub_errno; +} + +grub_err_t +grub_video_get_edid (struct grub_video_edid_info *edid_info) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "no video mode activated"); + + if (! grub_video_adapter_active->get_edid) + return grub_error (GRUB_ERR_BAD_DEVICE, + "EDID information unavailable for this video mode"); + + if (grub_video_adapter_active->get_edid (edid_info) != GRUB_ERR_NONE) + return grub_errno; + if (grub_video_edid_checksum (edid_info) != GRUB_ERR_NONE) + return grub_errno; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_video_edid_preferred_mode (struct grub_video_edid_info *edid_info, + unsigned int *width, unsigned int *height) +{ + /* Bit 1 in the Feature Support field indicates that the first + Detailed Timing Description is the preferred timing mode. */ + if (edid_info->version == 1 /* we don't understand later versions */ + && (edid_info->feature_support + & GRUB_VIDEO_EDID_FEATURE_PREFERRED_TIMING_MODE) + && edid_info->detailed_timings[0].pixel_clock) + { + *width = edid_info->detailed_timings[0].horizontal_active_lo + | (((unsigned int) + (edid_info->detailed_timings[0].horizontal_hi & 0xf0)) + << 4); + *height = edid_info->detailed_timings[0].vertical_active_lo + | (((unsigned int) + (edid_info->detailed_timings[0].vertical_hi & 0xf0)) + << 4); + return GRUB_ERR_NONE; + } + + return grub_error (GRUB_ERR_BAD_DEVICE, "no preferred mode available"); +} + /* Parse x[x]*/ static grub_err_t parse_modespec (const char *current_mode, int *width, int *height, int *depth) === modified file 'include/grub/i386/pc/vbe.h' --- include/grub/i386/pc/vbe.h 2010-09-15 22:37:30 +0000 +++ include/grub/i386/pc/vbe.h 2010-12-14 17:03:13 +0000 @@ -19,6 +19,8 @@ #ifndef GRUB_VBE_MACHINE_HEADER #define GRUB_VBE_MACHINE_HEADER 1 +#include + /* Default video mode to be used. */ #define GRUB_VBE_DEFAULT_VIDEO_MODE 0x101 @@ -169,6 +171,21 @@ struct grub_vbe_palette_data grub_uint8_t alignment; } __attribute__ ((packed)); +struct grub_vbe_flat_panel_info +{ + grub_uint16_t horizontal_size; + grub_uint16_t vertical_size; + grub_uint16_t panel_type; + grub_uint8_t red_bpp; + grub_uint8_t green_bpp; + grub_uint8_t blue_bpp; + grub_uint8_t reserved_bpp; + grub_uint32_t reserved_offscreen_mem_size; + grub_vbe_farptr_t reserved_offscreen_mem_ptr; + + grub_uint8_t reserved[14]; +} __attribute__ ((packed)); + /* Prototypes for helper functions. */ /* Call VESA BIOS 0x4f00 to get VBE Controller Information, return status. */ grub_vbe_status_t @@ -197,6 +214,15 @@ grub_vbe_bios_get_scanline_length (grub_ grub_vbe_status_t grub_vbe_bios_get_display_start (grub_uint32_t *x, grub_uint32_t *y); +/* Call VESA BIOS 0x4f11 to get flat panel information, return status. */ +grub_vbe_status_t +grub_vbe_bios_get_flat_panel_info (struct grub_vbe_flat_panel_info *flat_panel_info); +/* Call VESA BIOS 0x4f15 to get DDC availability, return status. */ +grub_vbe_status_t +grub_vbe_bios_get_ddc_capabilities (grub_uint8_t *level); +/* Call VESA BIOS 0x4f15 to read EDID information, return status. */ +grub_vbe_status_t +grub_vbe_bios_read_edid (struct grub_video_edid_info *edid_data); grub_vbe_status_t grub_vbe_bios_getset_dac_palette_width (int set, int *width); === modified file 'include/grub/video.h' --- include/grub/video.h 2010-12-10 16:45:58 +0000 +++ include/grub/video.h 2010-12-14 17:49:35 +0000 @@ -210,6 +210,66 @@ struct grub_video_palette_data grub_uint8_t a; /* Reserved bits value (0-255). */ }; +struct grub_video_edid_info +{ + grub_uint8_t header[8]; + grub_uint16_t manufacturer_id; + grub_uint16_t product_id; + grub_uint32_t serial_number; + grub_uint8_t week_of_manufacture; + grub_uint8_t year_of_manufacture; + grub_uint8_t version; + grub_uint8_t revision; + + grub_uint8_t video_input_definition; + grub_uint8_t max_horizontal_image_size; + grub_uint8_t max_vertical_image_size; + grub_uint8_t display_gamma; + grub_uint8_t feature_support; +#define GRUB_VIDEO_EDID_FEATURE_PREFERRED_TIMING_MODE (1 << 1) + + grub_uint8_t red_green_lo; + grub_uint8_t blue_white_lo; + grub_uint8_t red_x_hi; + grub_uint8_t red_y_hi; + grub_uint8_t green_x_hi; + grub_uint8_t green_y_hi; + grub_uint8_t blue_x_hi; + grub_uint8_t blue_y_hi; + grub_uint8_t white_x_hi; + grub_uint8_t white_y_hi; + + grub_uint8_t established_timings_1; + grub_uint8_t established_timings_2; + grub_uint8_t manufacturer_reserved_timings; + + grub_uint16_t standard_timings[8]; + + struct { + grub_uint16_t pixel_clock; + /* Only valid if the pixel clock is non-null. */ + grub_uint8_t horizontal_active_lo; + grub_uint8_t horizontal_blanking_lo; + grub_uint8_t horizontal_hi; + grub_uint8_t vertical_active_lo; + grub_uint8_t vertical_blanking_lo; + grub_uint8_t vertical_hi; + grub_uint8_t horizontal_sync_offset_lo; + grub_uint8_t horizontal_sync_pulse_width_lo; + grub_uint8_t vertical_sync_lo; + grub_uint8_t sync_hi; + grub_uint8_t horizontal_image_size_lo; + grub_uint8_t vertical_image_size_lo; + grub_uint8_t image_size_hi; + grub_uint8_t horizontal_border; + grub_uint8_t vertical_border; + grub_uint8_t flags; + } detailed_timings[4]; + + grub_uint8_t extension_flag; + grub_uint8_t checksum; +} __attribute__ ((packed)); + typedef enum grub_video_driver_id { GRUB_VIDEO_DRIVER_NONE, @@ -311,6 +371,8 @@ struct grub_video_adapter int (*iterate) (int (*hook) (const struct grub_video_mode_info *info)); + grub_err_t (*get_edid) (struct grub_video_edid_info *edid_info); + void (*print_adapter_specific_info) (void); }; typedef struct grub_video_adapter *grub_video_adapter_t; @@ -423,6 +485,12 @@ grub_err_t EXPORT_FUNC (grub_video_set_a grub_err_t grub_video_get_active_render_target (struct grub_video_render_target **target); +grub_err_t grub_video_edid_checksum (struct grub_video_edid_info *edid_info); +grub_err_t grub_video_get_edid (struct grub_video_edid_info *edid_info); +grub_err_t grub_video_edid_preferred_mode (struct grub_video_edid_info *edid_info, + unsigned int *width, + unsigned int *height); + grub_err_t EXPORT_FUNC (grub_video_set_mode) (const char *modestring, unsigned int modemask, unsigned int modevalue); === modified file 'util/grub.d/00_header.in' --- util/grub.d/00_header.in 2010-12-10 11:45:08 +0000 +++ util/grub.d/00_header.in 2010-12-14 16:20:51 +0000 @@ -36,7 +36,7 @@ done if [ "x${GRUB_DEFAULT}" = "x" ] ; then GRUB_DEFAULT=0 ; fi if [ "x${GRUB_DEFAULT}" = "xsaved" ] ; then GRUB_DEFAULT='${saved_entry}' ; fi if [ "x${GRUB_TIMEOUT}" = "x" ] ; then GRUB_TIMEOUT=5 ; fi -if [ "x${GRUB_GFXMODE}" = "x" ] ; then GRUB_GFXMODE=640x480 ; fi +if [ "x${GRUB_GFXMODE}" = "x" ] ; then GRUB_GFXMODE=auto ; fi if [ "x${GRUB_DEFAULT_BUTTON}" = "x" ] ; then GRUB_DEFAULT_BUTTON="$GRUB_DEFAULT" ; fi if [ "x${GRUB_DEFAULT_BUTTON}" = "xsaved" ] ; then GRUB_DEFAULT_BUTTON='${saved_entry}' ; fi -- Colin Watson [cjwatson@ubuntu.com]