* [PATCH] Preferred resolution detection for VBE
@ 2010-12-14 16:38 Colin Watson
2010-12-14 18:13 ` Colin Watson
0 siblings, 1 reply; 4+ messages in thread
From: Colin Watson @ 2010-12-14 16:38 UTC (permalink / raw)
To: grub-devel
I'd like to use GRUB_GFXMODE=auto as a default. It's more appropriate
on more platforms than the current default of 640x480, and generally
speaking I think we should be trying to use something as close to the
native panel resolution as we can.
I'm a bit nervous of doing this right now, though. It seems safe on
most drivers, but on vbe it just picks the largest mode it can find.
The mode list comes from the VESA BIOS on the graphics card, and may not
bear much resemblance to what the monitor can do. I realise most
current monitors are pretty robust, but I've heard too many horror
stories of people managing to blow up CRTs with excessive modes to be
entirely comfortable with this, and even aside from that it probably
just won't look very good if we try to do 1600x1200 on a 14" screen, if
it shows anything at all.
VBE offers a couple of better ways to do this. They aren't supported
quite everywhere, but they aren't that unusual these days. Firstly,
there's EDID
(http://en.wikipedia.org/wiki/Extended_display_identification_data),
which gets information from the connected monitor. Version 1.3 and
above provides a preferred mode. Secondly, there are the Flat Panel
extensions (VBE/FP - you can find at least a draft spec on Google,
though it seems tricky to find a final version), which has a nice simple
function to get the panel properties. The X.org VESA driver tries the
former followed by the latter, so this should be a safe approach. We
can then fall back to 640x480 to make sure that "auto" is a safe
default.
I've pushed this to
bzr+ssh://bzr.sv.gnu.org/grub/branches/vbe-autodetect/, currently based
on my previous parse-color branch since it was convenient to test both
at once.
2010-12-14 Colin Watson <cjwatson@ubuntu.com>
Preferred resolution detection for VBE.
* 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_edid_checksum): Likewise.
(grub_vbe_get_preferred_mode): Likewise. Try EDID followed by the
Flat Panel extension, in line with the X.org VESA driver.
(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.
* include/grub/i386/pc/vbe.h (struct grub_vbe_flat_panel_info): New
structure.
(struct grub_vbe_edid_info): Likewise.
(grub_vbe_bios_get_flat_panel_info): Add prototype.
(grub_vbe_bios_get_ddc_capabilities): Likewise.
(grub_vbe_bios_read_edid): Likewise.
* 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/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 16:22:19 +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_vbe_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,70 @@ grub_vbe_probe (struct grub_vbe_info_blo
return GRUB_ERR_NONE;
}
+static grub_err_t
+grub_vbe_edid_checksum (struct grub_vbe_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;
+}
+
+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_vbe_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)
+ {
+ status = grub_vbe_bios_read_edid (&edid_info);
+ /* Bit 1 in the Feature Support field indicates that the first
+ Detailed Timing Description is the preferred timing mode. */
+ if (status == GRUB_VBE_STATUS_OK
+ && grub_vbe_edid_checksum (&edid_info) == GRUB_ERR_NONE
+ && edid_info.version == 1 /* we don't understand later versions */
+ && (edid_info.feature_support
+ & GRUB_VBE_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;
+ }
+ }
+
+ 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 +809,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 +873,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)
=== 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 16:22:19 +0000
@@ -169,6 +169,81 @@ 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));
+
+struct grub_vbe_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_VBE_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));
+
/* Prototypes for helper functions. */
/* Call VESA BIOS 0x4f00 to get VBE Controller Information, return status. */
grub_vbe_status_t
@@ -197,6 +272,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_vbe_edid_info *edid_data);
grub_vbe_status_t grub_vbe_bios_getset_dac_palette_width (int set, int *width);
=== 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:22:19 +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]
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] Preferred resolution detection for VBE
2010-12-14 16:38 [PATCH] Preferred resolution detection for VBE Colin Watson
@ 2010-12-14 18:13 ` Colin Watson
2010-12-14 19:04 ` Colin Watson
0 siblings, 1 reply; 4+ messages in thread
From: Colin Watson @ 2010-12-14 18:13 UTC (permalink / raw)
To: grub-devel
On Tue, Dec 14, 2010 at 04:38:36PM +0000, Colin Watson wrote:
> 2010-12-14 Colin Watson <cjwatson@ubuntu.com>
>
> Preferred resolution detection for VBE.
Updated version following comments from Vladimir on IRC.
2010-12-14 Colin Watson <cjwatson@ubuntu.com>
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 <width>x<height>[x<depth>]*/
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 <grub/video.h>
+
/* 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]
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] Preferred resolution detection for VBE
2010-12-14 18:13 ` Colin Watson
@ 2010-12-14 19:04 ` Colin Watson
2010-12-18 18:44 ` Vladimir 'φ-coder/phcoder' Serbinenko
0 siblings, 1 reply; 4+ messages in thread
From: Colin Watson @ 2010-12-14 19:04 UTC (permalink / raw)
To: grub-devel
Oops, slight typo (missing NULL check on adapter->get_edid).
2010-12-14 Colin Watson <cjwatson@ubuntu.com>
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 19:03:01 +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 && 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 <width>x<height>[x<depth>]*/
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 <grub/video.h>
+
/* 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]
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] Preferred resolution detection for VBE
2010-12-14 19:04 ` Colin Watson
@ 2010-12-18 18:44 ` Vladimir 'φ-coder/phcoder' Serbinenko
0 siblings, 0 replies; 4+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-12-18 18:44 UTC (permalink / raw)
To: grub-devel
[-- Attachment #1: Type: text/plain, Size: 3515 bytes --]
> +/* 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)
>
I would prefer all new grub_vbe_bios_* to be static and not global since
they are adapter-specific and also require special considerations when
used (pointer has to be to lowmem).
> +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;
>
You implicitly use with these 2 variables that the stack is in lowmem.
It's so now but may change in the future if we need to increase stack
size or move it out of current region for any reason. Could you use
GRUB_MEMORY_MACHINE_SCRATCH_ADDR instead?
> +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");
> +
>
get_edid shouldn't require any particular placement of edid_info but
currently it works only if edid_info is in lowmem.
> +
> + if (checksum != 0)
> + return grub_error (GRUB_ERR_BAD_DEVICE,
> + "invalid EDID checksum %d", checksum);
> +
> + grub_errno = GRUB_ERR_NONE;
>
Why did you need to reset grub_errno here? If it is really needed we
probably have a problem somewhere else
> + 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;
>
In such cases the usual construction is:
err = ....;
if (err)
return err;
> === 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 <grub/video.h>
> +
> /* 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));
> +
>
Is this structure VBE specific? If it's not it should be moved to
generic headers.
On IRC I raised concern about possible endiannness issue but this patch
isn't actually affected: it seems to use only 8-bit fields.
--
Regards
Vladimir 'φ-coder/phcoder' Serbinenko
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 294 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2010-12-18 18:46 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-12-14 16:38 [PATCH] Preferred resolution detection for VBE Colin Watson
2010-12-14 18:13 ` Colin Watson
2010-12-14 19:04 ` Colin Watson
2010-12-18 18:44 ` Vladimir 'φ-coder/phcoder' Serbinenko
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).