18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------- 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2011 Intel Corporation; author Matt Fleming 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * ----------------------------------------------------------------------- */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bitops.h> 98c2ecf20Sopenharmony_ci#include <linux/ctype.h> 108c2ecf20Sopenharmony_ci#include <linux/efi.h> 118c2ecf20Sopenharmony_ci#include <linux/screen_info.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <asm/efi.h> 148c2ecf20Sopenharmony_ci#include <asm/setup.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "efistub.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cienum efi_cmdline_option { 198c2ecf20Sopenharmony_ci EFI_CMDLINE_NONE, 208c2ecf20Sopenharmony_ci EFI_CMDLINE_MODE_NUM, 218c2ecf20Sopenharmony_ci EFI_CMDLINE_RES, 228c2ecf20Sopenharmony_ci EFI_CMDLINE_AUTO, 238c2ecf20Sopenharmony_ci EFI_CMDLINE_LIST 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic struct { 278c2ecf20Sopenharmony_ci enum efi_cmdline_option option; 288c2ecf20Sopenharmony_ci union { 298c2ecf20Sopenharmony_ci u32 mode; 308c2ecf20Sopenharmony_ci struct { 318c2ecf20Sopenharmony_ci u32 width, height; 328c2ecf20Sopenharmony_ci int format; 338c2ecf20Sopenharmony_ci u8 depth; 348c2ecf20Sopenharmony_ci } res; 358c2ecf20Sopenharmony_ci }; 368c2ecf20Sopenharmony_ci} cmdline = { .option = EFI_CMDLINE_NONE }; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic bool parse_modenum(char *option, char **next) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci u32 m; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (!strstarts(option, "mode=")) 438c2ecf20Sopenharmony_ci return false; 448c2ecf20Sopenharmony_ci option += strlen("mode="); 458c2ecf20Sopenharmony_ci m = simple_strtoull(option, &option, 0); 468c2ecf20Sopenharmony_ci if (*option && *option++ != ',') 478c2ecf20Sopenharmony_ci return false; 488c2ecf20Sopenharmony_ci cmdline.option = EFI_CMDLINE_MODE_NUM; 498c2ecf20Sopenharmony_ci cmdline.mode = m; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci *next = option; 528c2ecf20Sopenharmony_ci return true; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic bool parse_res(char *option, char **next) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci u32 w, h, d = 0; 588c2ecf20Sopenharmony_ci int pf = -1; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (!isdigit(*option)) 618c2ecf20Sopenharmony_ci return false; 628c2ecf20Sopenharmony_ci w = simple_strtoull(option, &option, 10); 638c2ecf20Sopenharmony_ci if (*option++ != 'x' || !isdigit(*option)) 648c2ecf20Sopenharmony_ci return false; 658c2ecf20Sopenharmony_ci h = simple_strtoull(option, &option, 10); 668c2ecf20Sopenharmony_ci if (*option == '-') { 678c2ecf20Sopenharmony_ci option++; 688c2ecf20Sopenharmony_ci if (strstarts(option, "rgb")) { 698c2ecf20Sopenharmony_ci option += strlen("rgb"); 708c2ecf20Sopenharmony_ci pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR; 718c2ecf20Sopenharmony_ci } else if (strstarts(option, "bgr")) { 728c2ecf20Sopenharmony_ci option += strlen("bgr"); 738c2ecf20Sopenharmony_ci pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR; 748c2ecf20Sopenharmony_ci } else if (isdigit(*option)) 758c2ecf20Sopenharmony_ci d = simple_strtoull(option, &option, 10); 768c2ecf20Sopenharmony_ci else 778c2ecf20Sopenharmony_ci return false; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci if (*option && *option++ != ',') 808c2ecf20Sopenharmony_ci return false; 818c2ecf20Sopenharmony_ci cmdline.option = EFI_CMDLINE_RES; 828c2ecf20Sopenharmony_ci cmdline.res.width = w; 838c2ecf20Sopenharmony_ci cmdline.res.height = h; 848c2ecf20Sopenharmony_ci cmdline.res.format = pf; 858c2ecf20Sopenharmony_ci cmdline.res.depth = d; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci *next = option; 888c2ecf20Sopenharmony_ci return true; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic bool parse_auto(char *option, char **next) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci if (!strstarts(option, "auto")) 948c2ecf20Sopenharmony_ci return false; 958c2ecf20Sopenharmony_ci option += strlen("auto"); 968c2ecf20Sopenharmony_ci if (*option && *option++ != ',') 978c2ecf20Sopenharmony_ci return false; 988c2ecf20Sopenharmony_ci cmdline.option = EFI_CMDLINE_AUTO; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci *next = option; 1018c2ecf20Sopenharmony_ci return true; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic bool parse_list(char *option, char **next) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci if (!strstarts(option, "list")) 1078c2ecf20Sopenharmony_ci return false; 1088c2ecf20Sopenharmony_ci option += strlen("list"); 1098c2ecf20Sopenharmony_ci if (*option && *option++ != ',') 1108c2ecf20Sopenharmony_ci return false; 1118c2ecf20Sopenharmony_ci cmdline.option = EFI_CMDLINE_LIST; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci *next = option; 1148c2ecf20Sopenharmony_ci return true; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_civoid efi_parse_option_graphics(char *option) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci while (*option) { 1208c2ecf20Sopenharmony_ci if (parse_modenum(option, &option)) 1218c2ecf20Sopenharmony_ci continue; 1228c2ecf20Sopenharmony_ci if (parse_res(option, &option)) 1238c2ecf20Sopenharmony_ci continue; 1248c2ecf20Sopenharmony_ci if (parse_auto(option, &option)) 1258c2ecf20Sopenharmony_ci continue; 1268c2ecf20Sopenharmony_ci if (parse_list(option, &option)) 1278c2ecf20Sopenharmony_ci continue; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci while (*option && *option++ != ',') 1308c2ecf20Sopenharmony_ci ; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci efi_status_t status; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci efi_graphics_output_protocol_mode_t *mode; 1398c2ecf20Sopenharmony_ci efi_graphics_output_mode_info_t *info; 1408c2ecf20Sopenharmony_ci unsigned long info_size; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci u32 max_mode, cur_mode; 1438c2ecf20Sopenharmony_ci int pf; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci mode = efi_table_attr(gop, mode); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci cur_mode = efi_table_attr(mode, mode); 1488c2ecf20Sopenharmony_ci if (cmdline.mode == cur_mode) 1498c2ecf20Sopenharmony_ci return cur_mode; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci max_mode = efi_table_attr(mode, max_mode); 1528c2ecf20Sopenharmony_ci if (cmdline.mode >= max_mode) { 1538c2ecf20Sopenharmony_ci efi_err("Requested mode is invalid\n"); 1548c2ecf20Sopenharmony_ci return cur_mode; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci status = efi_call_proto(gop, query_mode, cmdline.mode, 1588c2ecf20Sopenharmony_ci &info_size, &info); 1598c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) { 1608c2ecf20Sopenharmony_ci efi_err("Couldn't get mode information\n"); 1618c2ecf20Sopenharmony_ci return cur_mode; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci pf = info->pixel_format; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci efi_bs_call(free_pool, info); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) { 1698c2ecf20Sopenharmony_ci efi_err("Invalid PixelFormat\n"); 1708c2ecf20Sopenharmony_ci return cur_mode; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return cmdline.mode; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci if (pixel_format == PIXEL_BIT_MASK) { 1798c2ecf20Sopenharmony_ci u32 mask = pixel_info.red_mask | pixel_info.green_mask | 1808c2ecf20Sopenharmony_ci pixel_info.blue_mask | pixel_info.reserved_mask; 1818c2ecf20Sopenharmony_ci if (!mask) 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci return __fls(mask) - __ffs(mask) + 1; 1848c2ecf20Sopenharmony_ci } else 1858c2ecf20Sopenharmony_ci return 32; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic u32 choose_mode_res(efi_graphics_output_protocol_t *gop) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci efi_status_t status; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci efi_graphics_output_protocol_mode_t *mode; 1938c2ecf20Sopenharmony_ci efi_graphics_output_mode_info_t *info; 1948c2ecf20Sopenharmony_ci unsigned long info_size; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci u32 max_mode, cur_mode; 1978c2ecf20Sopenharmony_ci int pf; 1988c2ecf20Sopenharmony_ci efi_pixel_bitmask_t pi; 1998c2ecf20Sopenharmony_ci u32 m, w, h; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci mode = efi_table_attr(gop, mode); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci cur_mode = efi_table_attr(mode, mode); 2048c2ecf20Sopenharmony_ci info = efi_table_attr(mode, info); 2058c2ecf20Sopenharmony_ci pf = info->pixel_format; 2068c2ecf20Sopenharmony_ci pi = info->pixel_information; 2078c2ecf20Sopenharmony_ci w = info->horizontal_resolution; 2088c2ecf20Sopenharmony_ci h = info->vertical_resolution; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (w == cmdline.res.width && h == cmdline.res.height && 2118c2ecf20Sopenharmony_ci (cmdline.res.format < 0 || cmdline.res.format == pf) && 2128c2ecf20Sopenharmony_ci (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi))) 2138c2ecf20Sopenharmony_ci return cur_mode; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci max_mode = efi_table_attr(mode, max_mode); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci for (m = 0; m < max_mode; m++) { 2188c2ecf20Sopenharmony_ci if (m == cur_mode) 2198c2ecf20Sopenharmony_ci continue; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci status = efi_call_proto(gop, query_mode, m, 2228c2ecf20Sopenharmony_ci &info_size, &info); 2238c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 2248c2ecf20Sopenharmony_ci continue; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci pf = info->pixel_format; 2278c2ecf20Sopenharmony_ci pi = info->pixel_information; 2288c2ecf20Sopenharmony_ci w = info->horizontal_resolution; 2298c2ecf20Sopenharmony_ci h = info->vertical_resolution; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci efi_bs_call(free_pool, info); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) 2348c2ecf20Sopenharmony_ci continue; 2358c2ecf20Sopenharmony_ci if (w == cmdline.res.width && h == cmdline.res.height && 2368c2ecf20Sopenharmony_ci (cmdline.res.format < 0 || cmdline.res.format == pf) && 2378c2ecf20Sopenharmony_ci (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi))) 2388c2ecf20Sopenharmony_ci return m; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci efi_err("Couldn't find requested mode\n"); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return cur_mode; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic u32 choose_mode_auto(efi_graphics_output_protocol_t *gop) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci efi_status_t status; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci efi_graphics_output_protocol_mode_t *mode; 2518c2ecf20Sopenharmony_ci efi_graphics_output_mode_info_t *info; 2528c2ecf20Sopenharmony_ci unsigned long info_size; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci u32 max_mode, cur_mode, best_mode, area; 2558c2ecf20Sopenharmony_ci u8 depth; 2568c2ecf20Sopenharmony_ci int pf; 2578c2ecf20Sopenharmony_ci efi_pixel_bitmask_t pi; 2588c2ecf20Sopenharmony_ci u32 m, w, h, a; 2598c2ecf20Sopenharmony_ci u8 d; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci mode = efi_table_attr(gop, mode); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci cur_mode = efi_table_attr(mode, mode); 2648c2ecf20Sopenharmony_ci max_mode = efi_table_attr(mode, max_mode); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci info = efi_table_attr(mode, info); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci pf = info->pixel_format; 2698c2ecf20Sopenharmony_ci pi = info->pixel_information; 2708c2ecf20Sopenharmony_ci w = info->horizontal_resolution; 2718c2ecf20Sopenharmony_ci h = info->vertical_resolution; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci best_mode = cur_mode; 2748c2ecf20Sopenharmony_ci area = w * h; 2758c2ecf20Sopenharmony_ci depth = pixel_bpp(pf, pi); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci for (m = 0; m < max_mode; m++) { 2788c2ecf20Sopenharmony_ci if (m == cur_mode) 2798c2ecf20Sopenharmony_ci continue; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci status = efi_call_proto(gop, query_mode, m, 2828c2ecf20Sopenharmony_ci &info_size, &info); 2838c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 2848c2ecf20Sopenharmony_ci continue; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci pf = info->pixel_format; 2878c2ecf20Sopenharmony_ci pi = info->pixel_information; 2888c2ecf20Sopenharmony_ci w = info->horizontal_resolution; 2898c2ecf20Sopenharmony_ci h = info->vertical_resolution; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci efi_bs_call(free_pool, info); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) 2948c2ecf20Sopenharmony_ci continue; 2958c2ecf20Sopenharmony_ci a = w * h; 2968c2ecf20Sopenharmony_ci if (a < area) 2978c2ecf20Sopenharmony_ci continue; 2988c2ecf20Sopenharmony_ci d = pixel_bpp(pf, pi); 2998c2ecf20Sopenharmony_ci if (a > area || d > depth) { 3008c2ecf20Sopenharmony_ci best_mode = m; 3018c2ecf20Sopenharmony_ci area = a; 3028c2ecf20Sopenharmony_ci depth = d; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return best_mode; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic u32 choose_mode_list(efi_graphics_output_protocol_t *gop) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci efi_status_t status; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci efi_graphics_output_protocol_mode_t *mode; 3148c2ecf20Sopenharmony_ci efi_graphics_output_mode_info_t *info; 3158c2ecf20Sopenharmony_ci unsigned long info_size; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci u32 max_mode, cur_mode; 3188c2ecf20Sopenharmony_ci int pf; 3198c2ecf20Sopenharmony_ci efi_pixel_bitmask_t pi; 3208c2ecf20Sopenharmony_ci u32 m, w, h; 3218c2ecf20Sopenharmony_ci u8 d; 3228c2ecf20Sopenharmony_ci const char *dstr; 3238c2ecf20Sopenharmony_ci bool valid; 3248c2ecf20Sopenharmony_ci efi_input_key_t key; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci mode = efi_table_attr(gop, mode); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci cur_mode = efi_table_attr(mode, mode); 3298c2ecf20Sopenharmony_ci max_mode = efi_table_attr(mode, max_mode); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci efi_printk("Available graphics modes are 0-%u\n", max_mode-1); 3328c2ecf20Sopenharmony_ci efi_puts(" * = current mode\n" 3338c2ecf20Sopenharmony_ci " - = unusable mode\n"); 3348c2ecf20Sopenharmony_ci for (m = 0; m < max_mode; m++) { 3358c2ecf20Sopenharmony_ci status = efi_call_proto(gop, query_mode, m, 3368c2ecf20Sopenharmony_ci &info_size, &info); 3378c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 3388c2ecf20Sopenharmony_ci continue; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci pf = info->pixel_format; 3418c2ecf20Sopenharmony_ci pi = info->pixel_information; 3428c2ecf20Sopenharmony_ci w = info->horizontal_resolution; 3438c2ecf20Sopenharmony_ci h = info->vertical_resolution; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci efi_bs_call(free_pool, info); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX); 3488c2ecf20Sopenharmony_ci d = 0; 3498c2ecf20Sopenharmony_ci switch (pf) { 3508c2ecf20Sopenharmony_ci case PIXEL_RGB_RESERVED_8BIT_PER_COLOR: 3518c2ecf20Sopenharmony_ci dstr = "rgb"; 3528c2ecf20Sopenharmony_ci break; 3538c2ecf20Sopenharmony_ci case PIXEL_BGR_RESERVED_8BIT_PER_COLOR: 3548c2ecf20Sopenharmony_ci dstr = "bgr"; 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci case PIXEL_BIT_MASK: 3578c2ecf20Sopenharmony_ci dstr = ""; 3588c2ecf20Sopenharmony_ci d = pixel_bpp(pf, pi); 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci case PIXEL_BLT_ONLY: 3618c2ecf20Sopenharmony_ci dstr = "blt"; 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci default: 3648c2ecf20Sopenharmony_ci dstr = "xxx"; 3658c2ecf20Sopenharmony_ci break; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n", 3698c2ecf20Sopenharmony_ci m, 3708c2ecf20Sopenharmony_ci m == cur_mode ? '*' : ' ', 3718c2ecf20Sopenharmony_ci !valid ? '-' : ' ', 3728c2ecf20Sopenharmony_ci w, h, dstr, d); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci efi_puts("\nPress any key to continue (or wait 10 seconds)\n"); 3768c2ecf20Sopenharmony_ci status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key); 3778c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS && status != EFI_TIMEOUT) { 3788c2ecf20Sopenharmony_ci efi_err("Unable to read key, continuing in 10 seconds\n"); 3798c2ecf20Sopenharmony_ci efi_bs_call(stall, 10 * EFI_USEC_PER_SEC); 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return cur_mode; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic void set_mode(efi_graphics_output_protocol_t *gop) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci efi_graphics_output_protocol_mode_t *mode; 3888c2ecf20Sopenharmony_ci u32 cur_mode, new_mode; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci switch (cmdline.option) { 3918c2ecf20Sopenharmony_ci case EFI_CMDLINE_MODE_NUM: 3928c2ecf20Sopenharmony_ci new_mode = choose_mode_modenum(gop); 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci case EFI_CMDLINE_RES: 3958c2ecf20Sopenharmony_ci new_mode = choose_mode_res(gop); 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci case EFI_CMDLINE_AUTO: 3988c2ecf20Sopenharmony_ci new_mode = choose_mode_auto(gop); 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci case EFI_CMDLINE_LIST: 4018c2ecf20Sopenharmony_ci new_mode = choose_mode_list(gop); 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci default: 4048c2ecf20Sopenharmony_ci return; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci mode = efi_table_attr(gop, mode); 4088c2ecf20Sopenharmony_ci cur_mode = efi_table_attr(mode, mode); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (new_mode == cur_mode) 4118c2ecf20Sopenharmony_ci return; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS) 4148c2ecf20Sopenharmony_ci efi_err("Failed to set requested mode\n"); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic void find_bits(u32 mask, u8 *pos, u8 *size) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci if (!mask) { 4208c2ecf20Sopenharmony_ci *pos = *size = 0; 4218c2ecf20Sopenharmony_ci return; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* UEFI spec guarantees that the set bits are contiguous */ 4258c2ecf20Sopenharmony_ci *pos = __ffs(mask); 4268c2ecf20Sopenharmony_ci *size = __fls(mask) - *pos + 1; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic void 4308c2ecf20Sopenharmony_cisetup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line, 4318c2ecf20Sopenharmony_ci efi_pixel_bitmask_t pixel_info, int pixel_format) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci if (pixel_format == PIXEL_BIT_MASK) { 4348c2ecf20Sopenharmony_ci find_bits(pixel_info.red_mask, 4358c2ecf20Sopenharmony_ci &si->red_pos, &si->red_size); 4368c2ecf20Sopenharmony_ci find_bits(pixel_info.green_mask, 4378c2ecf20Sopenharmony_ci &si->green_pos, &si->green_size); 4388c2ecf20Sopenharmony_ci find_bits(pixel_info.blue_mask, 4398c2ecf20Sopenharmony_ci &si->blue_pos, &si->blue_size); 4408c2ecf20Sopenharmony_ci find_bits(pixel_info.reserved_mask, 4418c2ecf20Sopenharmony_ci &si->rsvd_pos, &si->rsvd_size); 4428c2ecf20Sopenharmony_ci si->lfb_depth = si->red_size + si->green_size + 4438c2ecf20Sopenharmony_ci si->blue_size + si->rsvd_size; 4448c2ecf20Sopenharmony_ci si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8; 4458c2ecf20Sopenharmony_ci } else { 4468c2ecf20Sopenharmony_ci if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) { 4478c2ecf20Sopenharmony_ci si->red_pos = 0; 4488c2ecf20Sopenharmony_ci si->blue_pos = 16; 4498c2ecf20Sopenharmony_ci } else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ { 4508c2ecf20Sopenharmony_ci si->blue_pos = 0; 4518c2ecf20Sopenharmony_ci si->red_pos = 16; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci si->green_pos = 8; 4558c2ecf20Sopenharmony_ci si->rsvd_pos = 24; 4568c2ecf20Sopenharmony_ci si->red_size = si->green_size = 4578c2ecf20Sopenharmony_ci si->blue_size = si->rsvd_size = 8; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci si->lfb_depth = 32; 4608c2ecf20Sopenharmony_ci si->lfb_linelength = pixels_per_scan_line * 4; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic efi_graphics_output_protocol_t * 4658c2ecf20Sopenharmony_cifind_gop(efi_guid_t *proto, unsigned long size, void **handles) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci efi_graphics_output_protocol_t *first_gop; 4688c2ecf20Sopenharmony_ci efi_handle_t h; 4698c2ecf20Sopenharmony_ci int i; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci first_gop = NULL; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci for_each_efi_handle(h, handles, size, i) { 4748c2ecf20Sopenharmony_ci efi_status_t status; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci efi_graphics_output_protocol_t *gop; 4778c2ecf20Sopenharmony_ci efi_graphics_output_protocol_mode_t *mode; 4788c2ecf20Sopenharmony_ci efi_graphics_output_mode_info_t *info; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; 4818c2ecf20Sopenharmony_ci void *dummy = NULL; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci status = efi_bs_call(handle_protocol, h, proto, (void **)&gop); 4848c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 4858c2ecf20Sopenharmony_ci continue; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci mode = efi_table_attr(gop, mode); 4888c2ecf20Sopenharmony_ci info = efi_table_attr(mode, info); 4898c2ecf20Sopenharmony_ci if (info->pixel_format == PIXEL_BLT_ONLY || 4908c2ecf20Sopenharmony_ci info->pixel_format >= PIXEL_FORMAT_MAX) 4918c2ecf20Sopenharmony_ci continue; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci /* 4948c2ecf20Sopenharmony_ci * Systems that use the UEFI Console Splitter may 4958c2ecf20Sopenharmony_ci * provide multiple GOP devices, not all of which are 4968c2ecf20Sopenharmony_ci * backed by real hardware. The workaround is to search 4978c2ecf20Sopenharmony_ci * for a GOP implementing the ConOut protocol, and if 4988c2ecf20Sopenharmony_ci * one isn't found, to just fall back to the first GOP. 4998c2ecf20Sopenharmony_ci * 5008c2ecf20Sopenharmony_ci * Once we've found a GOP supporting ConOut, 5018c2ecf20Sopenharmony_ci * don't bother looking any further. 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_ci status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy); 5048c2ecf20Sopenharmony_ci if (status == EFI_SUCCESS) 5058c2ecf20Sopenharmony_ci return gop; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (!first_gop) 5088c2ecf20Sopenharmony_ci first_gop = gop; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return first_gop; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, 5158c2ecf20Sopenharmony_ci unsigned long size, void **handles) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci efi_graphics_output_protocol_t *gop; 5188c2ecf20Sopenharmony_ci efi_graphics_output_protocol_mode_t *mode; 5198c2ecf20Sopenharmony_ci efi_graphics_output_mode_info_t *info; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci gop = find_gop(proto, size, handles); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* Did we find any GOPs? */ 5248c2ecf20Sopenharmony_ci if (!gop) 5258c2ecf20Sopenharmony_ci return EFI_NOT_FOUND; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* Change mode if requested */ 5288c2ecf20Sopenharmony_ci set_mode(gop); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* EFI framebuffer */ 5318c2ecf20Sopenharmony_ci mode = efi_table_attr(gop, mode); 5328c2ecf20Sopenharmony_ci info = efi_table_attr(mode, info); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci si->orig_video_isVGA = VIDEO_TYPE_EFI; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci si->lfb_width = info->horizontal_resolution; 5378c2ecf20Sopenharmony_ci si->lfb_height = info->vertical_resolution; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci efi_set_u64_split(efi_table_attr(mode, frame_buffer_base), 5408c2ecf20Sopenharmony_ci &si->lfb_base, &si->ext_lfb_base); 5418c2ecf20Sopenharmony_ci if (si->ext_lfb_base) 5428c2ecf20Sopenharmony_ci si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci si->pages = 1; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci setup_pixel_info(si, info->pixels_per_scan_line, 5478c2ecf20Sopenharmony_ci info->pixel_information, info->pixel_format); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci si->lfb_size = si->lfb_linelength * si->lfb_height; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci return EFI_SUCCESS; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci/* 5578c2ecf20Sopenharmony_ci * See if we have Graphics Output Protocol 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ciefi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto, 5608c2ecf20Sopenharmony_ci unsigned long size) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci efi_status_t status; 5638c2ecf20Sopenharmony_ci void **gop_handle = NULL; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size, 5668c2ecf20Sopenharmony_ci (void **)&gop_handle); 5678c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 5688c2ecf20Sopenharmony_ci return status; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, proto, NULL, 5718c2ecf20Sopenharmony_ci &size, gop_handle); 5728c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 5738c2ecf20Sopenharmony_ci goto free_handle; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci status = setup_gop(si, proto, size, gop_handle); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cifree_handle: 5788c2ecf20Sopenharmony_ci efi_bs_call(free_pool, gop_handle); 5798c2ecf20Sopenharmony_ci return status; 5808c2ecf20Sopenharmony_ci} 581