efi/gop: Allow automatically choosing the best mode

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>
This commit is contained in:
Arvind Sankar 2020-03-28 12:06:01 -04:00 committed by Ard Biesheuvel
parent 9a1663bc4d
commit 45d97a749e
2 changed files with 89 additions and 1 deletions

View 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>

View 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;
}