]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Preferred resolution detection for VBE.
authorColin Watson <cjwatson@ubuntu.com>
Tue, 14 Dec 2010 16:22:19 +0000 (16:22 +0000)
committerColin Watson <cjwatson@ubuntu.com>
Tue, 14 Dec 2010 16:22:19 +0000 (16:22 +0000)
* 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.

ChangeLog.vbe-autodetect [new file with mode: 0644]
grub-core/video/i386/pc/vbe.c
include/grub/i386/pc/vbe.h
util/grub.d/00_header.in

diff --git a/ChangeLog.vbe-autodetect b/ChangeLog.vbe-autodetect
new file mode 100644 (file)
index 0000000..7d16ebe
--- /dev/null
@@ -0,0 +1,25 @@
+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.
index 2ddb4ca8085be47bd811446b0a57380820500adb..1794addae32ad0625ff278cdd91af6c0104b77be 100644 (file)
@@ -273,6 +273,56 @@ grub_vbe_bios_get_pm_interface (grub_uint16_t *segment, grub_uint16_t *offset,
   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, &regs);
+  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, &regs);
+
+  *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, &regs);
+  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_block *info_block)
   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, unsigned int height,
   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, unsigned int height,
        /* 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)
index fba3ee642fae91f95e1881bc220c932cf9fcd04b..5be34fdeccc1d96c0917f3592ecca95ae3ba9482 100644 (file)
@@ -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_uint32_t *length);
 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);
 
index a596e9c4ac54ca107aee364a38c23cea37750f5a..55c686296e3d2a9078a2379e3eebe57b3edd9b75 100644 (file)
@@ -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