efi/gop: Allow automatically choosing the best mode
authorArvind Sankar <nivedita@alum.mit.edu>
Sat, 28 Mar 2020 16:06:01 +0000 (12:06 -0400)
committerArd Biesheuvel <ardb@kernel.org>
Thu, 23 Apr 2020 18:15:06 +0000 (20:15 +0200)
Add the ability to automatically pick the highest resolution video mode
(defined as the product of vertical and horizontal resolution) by using
a command-line argument of the form
video=efifb:auto

If there are multiple modes with the highest resolution, pick one with
the highest color depth.

Signed-off-by: Arvind Sankar <nivedita@alum.mit.edu>
Link: https://lore.kernel.org/r/20200328160601.378299-2-nivedita@alum.mit.edu
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Documentation/fb/efifb.rst
drivers/firmware/efi/libstub/gop.c

index eca38466487a4ba0cc7703b4c26a506222172652..519550517fd49c55c99abf60874299e5e0c9a837 100644 (file)
@@ -57,4 +57,10 @@ mode=n
         "rgb" or "bgr" to match specifically those pixel formats, or a number
         for a mode with matching bits per pixel.
 
+auto
+        The EFI stub will choose the mode with the highest resolution (product
+        of horizontal and vertical resolution). If there are multiple modes
+        with the highest resolution, it will choose one with the highest color
+        depth.
+
 Edgar Hucek <gimli@dark-green.com>
index 848cb605a9c4e9df7a4fd7a9e7d11fbd7b40c1cc..fa05a0b0adfdd077d1062ade473dd0341d8b51a3 100644 (file)
@@ -18,7 +18,8 @@
 enum efi_cmdline_option {
        EFI_CMDLINE_NONE,
        EFI_CMDLINE_MODE_NUM,
-       EFI_CMDLINE_RES
+       EFI_CMDLINE_RES,
+       EFI_CMDLINE_AUTO
 };
 
 static struct {
@@ -86,6 +87,19 @@ static bool parse_res(char *option, char **next)
        return true;
 }
 
+static bool parse_auto(char *option, char **next)
+{
+       if (!strstarts(option, "auto"))
+               return false;
+       option += strlen("auto");
+       if (*option && *option++ != ',')
+               return false;
+       cmdline.option = EFI_CMDLINE_AUTO;
+
+       *next = option;
+       return true;
+}
+
 void efi_parse_option_graphics(char *option)
 {
        while (*option) {
@@ -93,6 +107,8 @@ void efi_parse_option_graphics(char *option)
                        continue;
                if (parse_res(option, &option))
                        continue;
+               if (parse_auto(option, &option))
+                       continue;
 
                while (*option && *option++ != ',')
                        ;
@@ -211,6 +227,69 @@ static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
        return cur_mode;
 }
 
+static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
+{
+       efi_status_t status;
+
+       efi_graphics_output_protocol_mode_t *mode;
+       efi_graphics_output_mode_info_t *info;
+       unsigned long info_size;
+
+       u32 max_mode, cur_mode, best_mode, area;
+       u8 depth;
+       int pf;
+       efi_pixel_bitmask_t pi;
+       u32 m, w, h, a;
+       u8 d;
+
+       mode = efi_table_attr(gop, mode);
+
+       cur_mode = efi_table_attr(mode, mode);
+       max_mode = efi_table_attr(mode, max_mode);
+
+       info = efi_table_attr(mode, info);
+
+       pf = info->pixel_format;
+       pi = info->pixel_information;
+       w  = info->horizontal_resolution;
+       h  = info->vertical_resolution;
+
+       best_mode = cur_mode;
+       area = w * h;
+       depth = pixel_bpp(pf, pi);
+
+       for (m = 0; m < max_mode; m++) {
+               if (m == cur_mode)
+                       continue;
+
+               status = efi_call_proto(gop, query_mode, m,
+                                       &info_size, &info);
+               if (status != EFI_SUCCESS)
+                       continue;
+
+               pf = info->pixel_format;
+               pi = info->pixel_information;
+               w  = info->horizontal_resolution;
+               h  = info->vertical_resolution;
+
+               efi_bs_call(free_pool, info);
+
+               if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
+                       continue;
+               a = w * h;
+               if (a < area)
+                       continue;
+               d = pixel_bpp(pf, pi);
+               if (a > area || d > depth) {
+                       best_mode = m;
+                       area = a;
+                       depth = d;
+               }
+       }
+
+       return best_mode;
+}
+
 static void set_mode(efi_graphics_output_protocol_t *gop)
 {
        efi_graphics_output_protocol_mode_t *mode;
@@ -223,6 +302,9 @@ static void set_mode(efi_graphics_output_protocol_t *gop)
        case EFI_CMDLINE_RES:
                new_mode = choose_mode_res(gop);
                break;
+       case EFI_CMDLINE_AUTO:
+               new_mode = choose_mode_auto(gop);
+               break;
        default:
                return;
        }