14febfb8dSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0
2fc372064SArd Biesheuvel /* -----------------------------------------------------------------------
3fc372064SArd Biesheuvel *
4fc372064SArd Biesheuvel * Copyright 2011 Intel Corporation; author Matt Fleming
5fc372064SArd Biesheuvel *
6fc372064SArd Biesheuvel * ----------------------------------------------------------------------- */
7fc372064SArd Biesheuvel
89867fc9dSArvind Sankar #include <linux/bitops.h>
9d9ff0323SArvind Sankar #include <linux/ctype.h>
10fc372064SArd Biesheuvel #include <linux/efi.h>
11fc372064SArd Biesheuvel #include <linux/screen_info.h>
12fffb6804SArvind Sankar #include <linux/string.h>
13fc372064SArd Biesheuvel #include <asm/efi.h>
14fc372064SArd Biesheuvel #include <asm/setup.h>
15fc372064SArd Biesheuvel
162fcdad2aSArd Biesheuvel #include "efistub.h"
172fcdad2aSArd Biesheuvel
18fffb6804SArvind Sankar enum efi_cmdline_option {
19fffb6804SArvind Sankar EFI_CMDLINE_NONE,
20fffb6804SArvind Sankar EFI_CMDLINE_MODE_NUM,
2145d97a74SArvind Sankar EFI_CMDLINE_RES,
2214c574f3SArvind Sankar EFI_CMDLINE_AUTO,
2314c574f3SArvind Sankar EFI_CMDLINE_LIST
24fffb6804SArvind Sankar };
25fffb6804SArvind Sankar
26fffb6804SArvind Sankar static struct {
27fffb6804SArvind Sankar enum efi_cmdline_option option;
28d9ff0323SArvind Sankar union {
29fffb6804SArvind Sankar u32 mode;
30d9ff0323SArvind Sankar struct {
31d9ff0323SArvind Sankar u32 width, height;
329a1663bcSArvind Sankar int format;
339a1663bcSArvind Sankar u8 depth;
34d9ff0323SArvind Sankar } res;
35d9ff0323SArvind Sankar };
3654439370SArvind Sankar } cmdline = { .option = EFI_CMDLINE_NONE };
37fffb6804SArvind Sankar
parse_modenum(char * option,char ** next)38fffb6804SArvind Sankar static bool parse_modenum(char *option, char **next)
39fffb6804SArvind Sankar {
40fffb6804SArvind Sankar u32 m;
41fffb6804SArvind Sankar
42fffb6804SArvind Sankar if (!strstarts(option, "mode="))
43fffb6804SArvind Sankar return false;
44fffb6804SArvind Sankar option += strlen("mode=");
45fffb6804SArvind Sankar m = simple_strtoull(option, &option, 0);
46fffb6804SArvind Sankar if (*option && *option++ != ',')
47fffb6804SArvind Sankar return false;
48fffb6804SArvind Sankar cmdline.option = EFI_CMDLINE_MODE_NUM;
49fffb6804SArvind Sankar cmdline.mode = m;
50fffb6804SArvind Sankar
51fffb6804SArvind Sankar *next = option;
52fffb6804SArvind Sankar return true;
53fffb6804SArvind Sankar }
54fffb6804SArvind Sankar
parse_res(char * option,char ** next)55d9ff0323SArvind Sankar static bool parse_res(char *option, char **next)
56d9ff0323SArvind Sankar {
579a1663bcSArvind Sankar u32 w, h, d = 0;
589a1663bcSArvind Sankar int pf = -1;
59d9ff0323SArvind Sankar
60d9ff0323SArvind Sankar if (!isdigit(*option))
61d9ff0323SArvind Sankar return false;
62d9ff0323SArvind Sankar w = simple_strtoull(option, &option, 10);
63d9ff0323SArvind Sankar if (*option++ != 'x' || !isdigit(*option))
64d9ff0323SArvind Sankar return false;
65d9ff0323SArvind Sankar h = simple_strtoull(option, &option, 10);
669a1663bcSArvind Sankar if (*option == '-') {
679a1663bcSArvind Sankar option++;
689a1663bcSArvind Sankar if (strstarts(option, "rgb")) {
699a1663bcSArvind Sankar option += strlen("rgb");
709a1663bcSArvind Sankar pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR;
719a1663bcSArvind Sankar } else if (strstarts(option, "bgr")) {
729a1663bcSArvind Sankar option += strlen("bgr");
739a1663bcSArvind Sankar pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR;
749a1663bcSArvind Sankar } else if (isdigit(*option))
759a1663bcSArvind Sankar d = simple_strtoull(option, &option, 10);
769a1663bcSArvind Sankar else
779a1663bcSArvind Sankar return false;
789a1663bcSArvind Sankar }
79d9ff0323SArvind Sankar if (*option && *option++ != ',')
80d9ff0323SArvind Sankar return false;
81d9ff0323SArvind Sankar cmdline.option = EFI_CMDLINE_RES;
82d9ff0323SArvind Sankar cmdline.res.width = w;
83d9ff0323SArvind Sankar cmdline.res.height = h;
849a1663bcSArvind Sankar cmdline.res.format = pf;
859a1663bcSArvind Sankar cmdline.res.depth = d;
86d9ff0323SArvind Sankar
87d9ff0323SArvind Sankar *next = option;
88d9ff0323SArvind Sankar return true;
89d9ff0323SArvind Sankar }
90d9ff0323SArvind Sankar
parse_auto(char * option,char ** next)9145d97a74SArvind Sankar static bool parse_auto(char *option, char **next)
9245d97a74SArvind Sankar {
9345d97a74SArvind Sankar if (!strstarts(option, "auto"))
9445d97a74SArvind Sankar return false;
9545d97a74SArvind Sankar option += strlen("auto");
9645d97a74SArvind Sankar if (*option && *option++ != ',')
9745d97a74SArvind Sankar return false;
9845d97a74SArvind Sankar cmdline.option = EFI_CMDLINE_AUTO;
9945d97a74SArvind Sankar
10045d97a74SArvind Sankar *next = option;
10145d97a74SArvind Sankar return true;
10245d97a74SArvind Sankar }
10345d97a74SArvind Sankar
parse_list(char * option,char ** next)10414c574f3SArvind Sankar static bool parse_list(char *option, char **next)
10514c574f3SArvind Sankar {
10614c574f3SArvind Sankar if (!strstarts(option, "list"))
10714c574f3SArvind Sankar return false;
10814c574f3SArvind Sankar option += strlen("list");
10914c574f3SArvind Sankar if (*option && *option++ != ',')
11014c574f3SArvind Sankar return false;
11114c574f3SArvind Sankar cmdline.option = EFI_CMDLINE_LIST;
11214c574f3SArvind Sankar
11314c574f3SArvind Sankar *next = option;
11414c574f3SArvind Sankar return true;
11514c574f3SArvind Sankar }
11614c574f3SArvind Sankar
efi_parse_option_graphics(char * option)117fffb6804SArvind Sankar void efi_parse_option_graphics(char *option)
118fffb6804SArvind Sankar {
119fffb6804SArvind Sankar while (*option) {
120fffb6804SArvind Sankar if (parse_modenum(option, &option))
121fffb6804SArvind Sankar continue;
122d9ff0323SArvind Sankar if (parse_res(option, &option))
123d9ff0323SArvind Sankar continue;
12445d97a74SArvind Sankar if (parse_auto(option, &option))
12545d97a74SArvind Sankar continue;
12614c574f3SArvind Sankar if (parse_list(option, &option))
12714c574f3SArvind Sankar continue;
128fffb6804SArvind Sankar
129fffb6804SArvind Sankar while (*option && *option++ != ',')
130fffb6804SArvind Sankar ;
131fffb6804SArvind Sankar }
132fffb6804SArvind Sankar }
133fffb6804SArvind Sankar
choose_mode_modenum(efi_graphics_output_protocol_t * gop)134fffb6804SArvind Sankar static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
135fffb6804SArvind Sankar {
136fffb6804SArvind Sankar efi_status_t status;
137fffb6804SArvind Sankar
138fffb6804SArvind Sankar efi_graphics_output_protocol_mode_t *mode;
139fffb6804SArvind Sankar efi_graphics_output_mode_info_t *info;
140fffb6804SArvind Sankar unsigned long info_size;
141fffb6804SArvind Sankar
142fffb6804SArvind Sankar u32 max_mode, cur_mode;
143fffb6804SArvind Sankar int pf;
144fffb6804SArvind Sankar
145fffb6804SArvind Sankar mode = efi_table_attr(gop, mode);
146fffb6804SArvind Sankar
147fffb6804SArvind Sankar cur_mode = efi_table_attr(mode, mode);
148fffb6804SArvind Sankar if (cmdline.mode == cur_mode)
149fffb6804SArvind Sankar return cur_mode;
150fffb6804SArvind Sankar
151fffb6804SArvind Sankar max_mode = efi_table_attr(mode, max_mode);
152fffb6804SArvind Sankar if (cmdline.mode >= max_mode) {
15361eac6d9SArvind Sankar efi_err("Requested mode is invalid\n");
154fffb6804SArvind Sankar return cur_mode;
155fffb6804SArvind Sankar }
156fffb6804SArvind Sankar
157fffb6804SArvind Sankar status = efi_call_proto(gop, query_mode, cmdline.mode,
158fffb6804SArvind Sankar &info_size, &info);
159fffb6804SArvind Sankar if (status != EFI_SUCCESS) {
16061eac6d9SArvind Sankar efi_err("Couldn't get mode information\n");
161fffb6804SArvind Sankar return cur_mode;
162fffb6804SArvind Sankar }
163fffb6804SArvind Sankar
164fffb6804SArvind Sankar pf = info->pixel_format;
165fffb6804SArvind Sankar
166fffb6804SArvind Sankar efi_bs_call(free_pool, info);
167fffb6804SArvind Sankar
168fffb6804SArvind Sankar if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
16961eac6d9SArvind Sankar efi_err("Invalid PixelFormat\n");
170fffb6804SArvind Sankar return cur_mode;
171fffb6804SArvind Sankar }
172fffb6804SArvind Sankar
173fffb6804SArvind Sankar return cmdline.mode;
174fffb6804SArvind Sankar }
175fffb6804SArvind Sankar
pixel_bpp(int pixel_format,efi_pixel_bitmask_t pixel_info)1769a1663bcSArvind Sankar static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
1779a1663bcSArvind Sankar {
1789a1663bcSArvind Sankar if (pixel_format == PIXEL_BIT_MASK) {
1799a1663bcSArvind Sankar u32 mask = pixel_info.red_mask | pixel_info.green_mask |
1809a1663bcSArvind Sankar pixel_info.blue_mask | pixel_info.reserved_mask;
1819a1663bcSArvind Sankar if (!mask)
1829a1663bcSArvind Sankar return 0;
1839a1663bcSArvind Sankar return __fls(mask) - __ffs(mask) + 1;
1849a1663bcSArvind Sankar } else
1859a1663bcSArvind Sankar return 32;
1869a1663bcSArvind Sankar }
1879a1663bcSArvind Sankar
choose_mode_res(efi_graphics_output_protocol_t * gop)188d9ff0323SArvind Sankar static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
189d9ff0323SArvind Sankar {
190d9ff0323SArvind Sankar efi_status_t status;
191d9ff0323SArvind Sankar
192d9ff0323SArvind Sankar efi_graphics_output_protocol_mode_t *mode;
193d9ff0323SArvind Sankar efi_graphics_output_mode_info_t *info;
194d9ff0323SArvind Sankar unsigned long info_size;
195d9ff0323SArvind Sankar
196d9ff0323SArvind Sankar u32 max_mode, cur_mode;
197d9ff0323SArvind Sankar int pf;
1989a1663bcSArvind Sankar efi_pixel_bitmask_t pi;
199d9ff0323SArvind Sankar u32 m, w, h;
200d9ff0323SArvind Sankar
201d9ff0323SArvind Sankar mode = efi_table_attr(gop, mode);
202d9ff0323SArvind Sankar
203d9ff0323SArvind Sankar cur_mode = efi_table_attr(mode, mode);
204d9ff0323SArvind Sankar info = efi_table_attr(mode, info);
2059a1663bcSArvind Sankar pf = info->pixel_format;
2069a1663bcSArvind Sankar pi = info->pixel_information;
207d9ff0323SArvind Sankar w = info->horizontal_resolution;
208d9ff0323SArvind Sankar h = info->vertical_resolution;
209d9ff0323SArvind Sankar
2109a1663bcSArvind Sankar if (w == cmdline.res.width && h == cmdline.res.height &&
2119a1663bcSArvind Sankar (cmdline.res.format < 0 || cmdline.res.format == pf) &&
2129a1663bcSArvind Sankar (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
213d9ff0323SArvind Sankar return cur_mode;
214d9ff0323SArvind Sankar
215d9ff0323SArvind Sankar max_mode = efi_table_attr(mode, max_mode);
216d9ff0323SArvind Sankar
217d9ff0323SArvind Sankar for (m = 0; m < max_mode; m++) {
218d9ff0323SArvind Sankar if (m == cur_mode)
219d9ff0323SArvind Sankar continue;
220d9ff0323SArvind Sankar
221d9ff0323SArvind Sankar status = efi_call_proto(gop, query_mode, m,
222d9ff0323SArvind Sankar &info_size, &info);
223d9ff0323SArvind Sankar if (status != EFI_SUCCESS)
224d9ff0323SArvind Sankar continue;
225d9ff0323SArvind Sankar
226d9ff0323SArvind Sankar pf = info->pixel_format;
2279a1663bcSArvind Sankar pi = info->pixel_information;
228d9ff0323SArvind Sankar w = info->horizontal_resolution;
229d9ff0323SArvind Sankar h = info->vertical_resolution;
230d9ff0323SArvind Sankar
231d9ff0323SArvind Sankar efi_bs_call(free_pool, info);
232d9ff0323SArvind Sankar
233d9ff0323SArvind Sankar if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
234d9ff0323SArvind Sankar continue;
2359a1663bcSArvind Sankar if (w == cmdline.res.width && h == cmdline.res.height &&
2369a1663bcSArvind Sankar (cmdline.res.format < 0 || cmdline.res.format == pf) &&
2379a1663bcSArvind Sankar (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
238d9ff0323SArvind Sankar return m;
239d9ff0323SArvind Sankar }
240d9ff0323SArvind Sankar
24161eac6d9SArvind Sankar efi_err("Couldn't find requested mode\n");
242d9ff0323SArvind Sankar
243d9ff0323SArvind Sankar return cur_mode;
244d9ff0323SArvind Sankar }
245d9ff0323SArvind Sankar
choose_mode_auto(efi_graphics_output_protocol_t * gop)24645d97a74SArvind Sankar static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
24745d97a74SArvind Sankar {
24845d97a74SArvind Sankar efi_status_t status;
24945d97a74SArvind Sankar
25045d97a74SArvind Sankar efi_graphics_output_protocol_mode_t *mode;
25145d97a74SArvind Sankar efi_graphics_output_mode_info_t *info;
25245d97a74SArvind Sankar unsigned long info_size;
25345d97a74SArvind Sankar
25445d97a74SArvind Sankar u32 max_mode, cur_mode, best_mode, area;
25545d97a74SArvind Sankar u8 depth;
25645d97a74SArvind Sankar int pf;
25745d97a74SArvind Sankar efi_pixel_bitmask_t pi;
25845d97a74SArvind Sankar u32 m, w, h, a;
25945d97a74SArvind Sankar u8 d;
26045d97a74SArvind Sankar
26145d97a74SArvind Sankar mode = efi_table_attr(gop, mode);
26245d97a74SArvind Sankar
26345d97a74SArvind Sankar cur_mode = efi_table_attr(mode, mode);
26445d97a74SArvind Sankar max_mode = efi_table_attr(mode, max_mode);
26545d97a74SArvind Sankar
26645d97a74SArvind Sankar info = efi_table_attr(mode, info);
26745d97a74SArvind Sankar
26845d97a74SArvind Sankar pf = info->pixel_format;
26945d97a74SArvind Sankar pi = info->pixel_information;
27045d97a74SArvind Sankar w = info->horizontal_resolution;
27145d97a74SArvind Sankar h = info->vertical_resolution;
27245d97a74SArvind Sankar
27345d97a74SArvind Sankar best_mode = cur_mode;
27445d97a74SArvind Sankar area = w * h;
27545d97a74SArvind Sankar depth = pixel_bpp(pf, pi);
27645d97a74SArvind Sankar
27745d97a74SArvind Sankar for (m = 0; m < max_mode; m++) {
27845d97a74SArvind Sankar if (m == cur_mode)
27945d97a74SArvind Sankar continue;
28045d97a74SArvind Sankar
28145d97a74SArvind Sankar status = efi_call_proto(gop, query_mode, m,
28245d97a74SArvind Sankar &info_size, &info);
28345d97a74SArvind Sankar if (status != EFI_SUCCESS)
28445d97a74SArvind Sankar continue;
28545d97a74SArvind Sankar
28645d97a74SArvind Sankar pf = info->pixel_format;
28745d97a74SArvind Sankar pi = info->pixel_information;
28845d97a74SArvind Sankar w = info->horizontal_resolution;
28945d97a74SArvind Sankar h = info->vertical_resolution;
29045d97a74SArvind Sankar
29145d97a74SArvind Sankar efi_bs_call(free_pool, info);
29245d97a74SArvind Sankar
29345d97a74SArvind Sankar if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
29445d97a74SArvind Sankar continue;
29545d97a74SArvind Sankar a = w * h;
29645d97a74SArvind Sankar if (a < area)
29745d97a74SArvind Sankar continue;
29845d97a74SArvind Sankar d = pixel_bpp(pf, pi);
29945d97a74SArvind Sankar if (a > area || d > depth) {
30045d97a74SArvind Sankar best_mode = m;
30145d97a74SArvind Sankar area = a;
30245d97a74SArvind Sankar depth = d;
30345d97a74SArvind Sankar }
30445d97a74SArvind Sankar }
30545d97a74SArvind Sankar
30645d97a74SArvind Sankar return best_mode;
30745d97a74SArvind Sankar }
30845d97a74SArvind Sankar
choose_mode_list(efi_graphics_output_protocol_t * gop)30914c574f3SArvind Sankar static u32 choose_mode_list(efi_graphics_output_protocol_t *gop)
31014c574f3SArvind Sankar {
31114c574f3SArvind Sankar efi_status_t status;
31214c574f3SArvind Sankar
31314c574f3SArvind Sankar efi_graphics_output_protocol_mode_t *mode;
31414c574f3SArvind Sankar efi_graphics_output_mode_info_t *info;
31514c574f3SArvind Sankar unsigned long info_size;
31614c574f3SArvind Sankar
31714c574f3SArvind Sankar u32 max_mode, cur_mode;
31814c574f3SArvind Sankar int pf;
31914c574f3SArvind Sankar efi_pixel_bitmask_t pi;
32014c574f3SArvind Sankar u32 m, w, h;
32114c574f3SArvind Sankar u8 d;
32214c574f3SArvind Sankar const char *dstr;
32314c574f3SArvind Sankar bool valid;
32414c574f3SArvind Sankar efi_input_key_t key;
32514c574f3SArvind Sankar
32614c574f3SArvind Sankar mode = efi_table_attr(gop, mode);
32714c574f3SArvind Sankar
32814c574f3SArvind Sankar cur_mode = efi_table_attr(mode, mode);
32914c574f3SArvind Sankar max_mode = efi_table_attr(mode, max_mode);
33014c574f3SArvind Sankar
33114c574f3SArvind Sankar efi_printk("Available graphics modes are 0-%u\n", max_mode-1);
33214c574f3SArvind Sankar efi_puts(" * = current mode\n"
33314c574f3SArvind Sankar " - = unusable mode\n");
33414c574f3SArvind Sankar for (m = 0; m < max_mode; m++) {
33514c574f3SArvind Sankar status = efi_call_proto(gop, query_mode, m,
33614c574f3SArvind Sankar &info_size, &info);
33714c574f3SArvind Sankar if (status != EFI_SUCCESS)
33814c574f3SArvind Sankar continue;
33914c574f3SArvind Sankar
34014c574f3SArvind Sankar pf = info->pixel_format;
34114c574f3SArvind Sankar pi = info->pixel_information;
34214c574f3SArvind Sankar w = info->horizontal_resolution;
34314c574f3SArvind Sankar h = info->vertical_resolution;
34414c574f3SArvind Sankar
34514c574f3SArvind Sankar efi_bs_call(free_pool, info);
34614c574f3SArvind Sankar
34714c574f3SArvind Sankar valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
34814c574f3SArvind Sankar d = 0;
34914c574f3SArvind Sankar switch (pf) {
35014c574f3SArvind Sankar case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
35114c574f3SArvind Sankar dstr = "rgb";
35214c574f3SArvind Sankar break;
35314c574f3SArvind Sankar case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
35414c574f3SArvind Sankar dstr = "bgr";
35514c574f3SArvind Sankar break;
35614c574f3SArvind Sankar case PIXEL_BIT_MASK:
35714c574f3SArvind Sankar dstr = "";
35814c574f3SArvind Sankar d = pixel_bpp(pf, pi);
35914c574f3SArvind Sankar break;
36014c574f3SArvind Sankar case PIXEL_BLT_ONLY:
36114c574f3SArvind Sankar dstr = "blt";
36214c574f3SArvind Sankar break;
36314c574f3SArvind Sankar default:
36414c574f3SArvind Sankar dstr = "xxx";
36514c574f3SArvind Sankar break;
36614c574f3SArvind Sankar }
36714c574f3SArvind Sankar
36814c574f3SArvind Sankar efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
36914c574f3SArvind Sankar m,
37014c574f3SArvind Sankar m == cur_mode ? '*' : ' ',
37114c574f3SArvind Sankar !valid ? '-' : ' ',
37214c574f3SArvind Sankar w, h, dstr, d);
37314c574f3SArvind Sankar }
37414c574f3SArvind Sankar
37514c574f3SArvind Sankar efi_puts("\nPress any key to continue (or wait 10 seconds)\n");
37614c574f3SArvind Sankar status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key);
37714c574f3SArvind Sankar if (status != EFI_SUCCESS && status != EFI_TIMEOUT) {
37814c574f3SArvind Sankar efi_err("Unable to read key, continuing in 10 seconds\n");
37914c574f3SArvind Sankar efi_bs_call(stall, 10 * EFI_USEC_PER_SEC);
38014c574f3SArvind Sankar }
38114c574f3SArvind Sankar
38214c574f3SArvind Sankar return cur_mode;
38314c574f3SArvind Sankar }
38414c574f3SArvind Sankar
set_mode(efi_graphics_output_protocol_t * gop)385fffb6804SArvind Sankar static void set_mode(efi_graphics_output_protocol_t *gop)
386fffb6804SArvind Sankar {
387fffb6804SArvind Sankar efi_graphics_output_protocol_mode_t *mode;
388fffb6804SArvind Sankar u32 cur_mode, new_mode;
389fffb6804SArvind Sankar
390fffb6804SArvind Sankar switch (cmdline.option) {
391fffb6804SArvind Sankar case EFI_CMDLINE_MODE_NUM:
392fffb6804SArvind Sankar new_mode = choose_mode_modenum(gop);
393fffb6804SArvind Sankar break;
394d9ff0323SArvind Sankar case EFI_CMDLINE_RES:
395d9ff0323SArvind Sankar new_mode = choose_mode_res(gop);
396d9ff0323SArvind Sankar break;
39745d97a74SArvind Sankar case EFI_CMDLINE_AUTO:
39845d97a74SArvind Sankar new_mode = choose_mode_auto(gop);
39945d97a74SArvind Sankar break;
40014c574f3SArvind Sankar case EFI_CMDLINE_LIST:
40114c574f3SArvind Sankar new_mode = choose_mode_list(gop);
40214c574f3SArvind Sankar break;
403fffb6804SArvind Sankar default:
404fffb6804SArvind Sankar return;
405fffb6804SArvind Sankar }
406fffb6804SArvind Sankar
407fffb6804SArvind Sankar mode = efi_table_attr(gop, mode);
408fffb6804SArvind Sankar cur_mode = efi_table_attr(mode, mode);
409fffb6804SArvind Sankar
410fffb6804SArvind Sankar if (new_mode == cur_mode)
411fffb6804SArvind Sankar return;
412fffb6804SArvind Sankar
413fffb6804SArvind Sankar if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS)
41461eac6d9SArvind Sankar efi_err("Failed to set requested mode\n");
415fffb6804SArvind Sankar }
416fffb6804SArvind Sankar
find_bits(u32 mask,u8 * pos,u8 * size)4179867fc9dSArvind Sankar static void find_bits(u32 mask, u8 *pos, u8 *size)
418fc372064SArd Biesheuvel {
4199867fc9dSArvind Sankar if (!mask) {
4209867fc9dSArvind Sankar *pos = *size = 0;
4219867fc9dSArvind Sankar return;
422fc372064SArd Biesheuvel }
423fc372064SArd Biesheuvel
4249867fc9dSArvind Sankar /* UEFI spec guarantees that the set bits are contiguous */
4259867fc9dSArvind Sankar *pos = __ffs(mask);
4269867fc9dSArvind Sankar *size = __fls(mask) - *pos + 1;
427fc372064SArd Biesheuvel }
428fc372064SArd Biesheuvel
429fc372064SArd Biesheuvel static void
setup_pixel_info(struct screen_info * si,u32 pixels_per_scan_line,efi_pixel_bitmask_t pixel_info,int pixel_format)430fc372064SArd Biesheuvel setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
43144c84b4aSArvind Sankar efi_pixel_bitmask_t pixel_info, int pixel_format)
432fc372064SArd Biesheuvel {
433d49fd4bbSArvind Sankar if (pixel_format == PIXEL_BIT_MASK) {
434d49fd4bbSArvind Sankar find_bits(pixel_info.red_mask,
435d49fd4bbSArvind Sankar &si->red_pos, &si->red_size);
436d49fd4bbSArvind Sankar find_bits(pixel_info.green_mask,
437d49fd4bbSArvind Sankar &si->green_pos, &si->green_size);
438d49fd4bbSArvind Sankar find_bits(pixel_info.blue_mask,
439d49fd4bbSArvind Sankar &si->blue_pos, &si->blue_size);
440d49fd4bbSArvind Sankar find_bits(pixel_info.reserved_mask,
441d49fd4bbSArvind Sankar &si->rsvd_pos, &si->rsvd_size);
442fc372064SArd Biesheuvel si->lfb_depth = si->red_size + si->green_size +
443fc372064SArd Biesheuvel si->blue_size + si->rsvd_size;
444fc372064SArd Biesheuvel si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
445fc372064SArd Biesheuvel } else {
446d49fd4bbSArvind Sankar if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
447fc372064SArd Biesheuvel si->red_pos = 0;
448d49fd4bbSArvind Sankar si->blue_pos = 16;
449d49fd4bbSArvind Sankar } else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ {
450fc372064SArd Biesheuvel si->blue_pos = 0;
451d49fd4bbSArvind Sankar si->red_pos = 16;
452d49fd4bbSArvind Sankar }
453d49fd4bbSArvind Sankar
454d49fd4bbSArvind Sankar si->green_pos = 8;
455d49fd4bbSArvind Sankar si->rsvd_pos = 24;
456d49fd4bbSArvind Sankar si->red_size = si->green_size =
457d49fd4bbSArvind Sankar si->blue_size = si->rsvd_size = 8;
458d49fd4bbSArvind Sankar
459d49fd4bbSArvind Sankar si->lfb_depth = 32;
460d49fd4bbSArvind Sankar si->lfb_linelength = pixels_per_scan_line * 4;
461fc372064SArd Biesheuvel }
462fc372064SArd Biesheuvel }
463fc372064SArd Biesheuvel
464ecf53091SArvind Sankar static efi_graphics_output_protocol_t *
find_gop(efi_guid_t * proto,unsigned long size,void ** handles)465ecf53091SArvind Sankar find_gop(efi_guid_t *proto, unsigned long size, void **handles)
466fc372064SArd Biesheuvel {
467e484c594SArvind Sankar efi_graphics_output_protocol_t *first_gop;
4682732ea0dSArd Biesheuvel efi_handle_t h;
469fc372064SArd Biesheuvel int i;
470fc372064SArd Biesheuvel
471fc372064SArd Biesheuvel first_gop = NULL;
472fc372064SArd Biesheuvel
4732732ea0dSArd Biesheuvel for_each_efi_handle(h, handles, size, i) {
474e484c594SArvind Sankar efi_status_t status;
475e484c594SArvind Sankar
476e484c594SArvind Sankar efi_graphics_output_protocol_t *gop;
477e484c594SArvind Sankar efi_graphics_output_protocol_mode_t *mode;
478e484c594SArvind Sankar efi_graphics_output_mode_info_t *info;
479e484c594SArvind Sankar
480fc372064SArd Biesheuvel efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
481fc372064SArd Biesheuvel void *dummy = NULL;
482fc372064SArd Biesheuvel
483966291f6SArd Biesheuvel status = efi_bs_call(handle_protocol, h, proto, (void **)&gop);
484fc372064SArd Biesheuvel if (status != EFI_SUCCESS)
485fc372064SArd Biesheuvel continue;
486fc372064SArd Biesheuvel
4878cd20797SArvind Sankar mode = efi_table_attr(gop, mode);
4888cd20797SArvind Sankar info = efi_table_attr(mode, info);
489d49fd4bbSArvind Sankar if (info->pixel_format == PIXEL_BLT_ONLY ||
490d49fd4bbSArvind Sankar info->pixel_format >= PIXEL_FORMAT_MAX)
4918cd20797SArvind Sankar continue;
4928cd20797SArvind Sankar
493fc372064SArd Biesheuvel /*
494fc372064SArd Biesheuvel * Systems that use the UEFI Console Splitter may
495fc372064SArd Biesheuvel * provide multiple GOP devices, not all of which are
496fc372064SArd Biesheuvel * backed by real hardware. The workaround is to search
497fc372064SArd Biesheuvel * for a GOP implementing the ConOut protocol, and if
498fc372064SArd Biesheuvel * one isn't found, to just fall back to the first GOP.
4996327e6d0SArvind Sankar *
500fc372064SArd Biesheuvel * Once we've found a GOP supporting ConOut,
501fc372064SArd Biesheuvel * don't bother looking any further.
502fc372064SArd Biesheuvel */
5038e0a22e2SArvind Sankar status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy);
5048e0a22e2SArvind Sankar if (status == EFI_SUCCESS)
5058e0a22e2SArvind Sankar return gop;
5068e0a22e2SArvind Sankar
5078e0a22e2SArvind Sankar if (!first_gop)
5088de8788dSArvind Sankar first_gop = gop;
509fc372064SArd Biesheuvel }
510fc372064SArd Biesheuvel
511ecf53091SArvind Sankar return first_gop;
512ecf53091SArvind Sankar }
513ecf53091SArvind Sankar
setup_gop(struct screen_info * si,efi_guid_t * proto,unsigned long size,void ** handles)514ecf53091SArvind Sankar static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
515ecf53091SArvind Sankar unsigned long size, void **handles)
516ecf53091SArvind Sankar {
517ecf53091SArvind Sankar efi_graphics_output_protocol_t *gop;
518ecf53091SArvind Sankar efi_graphics_output_protocol_mode_t *mode;
519e484c594SArvind Sankar efi_graphics_output_mode_info_t *info;
520ecf53091SArvind Sankar
521ecf53091SArvind Sankar gop = find_gop(proto, size, handles);
522ecf53091SArvind Sankar
523fc372064SArd Biesheuvel /* Did we find any GOPs? */
524ecf53091SArvind Sankar if (!gop)
5256fc3cec3SArvind Sankar return EFI_NOT_FOUND;
526fc372064SArd Biesheuvel
527fffb6804SArvind Sankar /* Change mode if requested */
528fffb6804SArvind Sankar set_mode(gop);
529fffb6804SArvind Sankar
530fc372064SArd Biesheuvel /* EFI framebuffer */
531ecf53091SArvind Sankar mode = efi_table_attr(gop, mode);
5326327e6d0SArvind Sankar info = efi_table_attr(mode, info);
5336327e6d0SArvind Sankar
534fc372064SArd Biesheuvel si->orig_video_isVGA = VIDEO_TYPE_EFI;
535fc372064SArd Biesheuvel
5366327e6d0SArvind Sankar si->lfb_width = info->horizontal_resolution;
5376327e6d0SArvind Sankar si->lfb_height = info->vertical_resolution;
538fc372064SArd Biesheuvel
539eed4e019SArvind Sankar efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
540eed4e019SArvind Sankar &si->lfb_base, &si->ext_lfb_base);
5416327e6d0SArvind Sankar if (si->ext_lfb_base)
542fc372064SArd Biesheuvel si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
543fc372064SArd Biesheuvel
544fc372064SArd Biesheuvel si->pages = 1;
545fc372064SArd Biesheuvel
5466327e6d0SArvind Sankar setup_pixel_info(si, info->pixels_per_scan_line,
5476327e6d0SArvind Sankar info->pixel_information, info->pixel_format);
548fc372064SArd Biesheuvel
549fc372064SArd Biesheuvel si->lfb_size = si->lfb_linelength * si->lfb_height;
550fc372064SArd Biesheuvel
551fc372064SArd Biesheuvel si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
5526fc3cec3SArvind Sankar
553dbd89c30SArvind Sankar return EFI_SUCCESS;
554fc372064SArd Biesheuvel }
555fc372064SArd Biesheuvel
556fc372064SArd Biesheuvel /*
557fc372064SArd Biesheuvel * See if we have Graphics Output Protocol
558fc372064SArd Biesheuvel */
efi_setup_gop(struct screen_info * si,efi_guid_t * proto,unsigned long size)559cd33a5c1SArd Biesheuvel efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
560fc372064SArd Biesheuvel unsigned long size)
561fc372064SArd Biesheuvel {
562fc372064SArd Biesheuvel efi_status_t status;
563fc372064SArd Biesheuvel void **gop_handle = NULL;
564fc372064SArd Biesheuvel
565966291f6SArd Biesheuvel status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
566966291f6SArd Biesheuvel (void **)&gop_handle);
567fc372064SArd Biesheuvel if (status != EFI_SUCCESS)
568fc372064SArd Biesheuvel return status;
569fc372064SArd Biesheuvel
570966291f6SArd Biesheuvel status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, proto, NULL,
571966291f6SArd Biesheuvel &size, gop_handle);
572fc372064SArd Biesheuvel if (status != EFI_SUCCESS)
573fc372064SArd Biesheuvel goto free_handle;
574fc372064SArd Biesheuvel
575cd33a5c1SArd Biesheuvel status = setup_gop(si, proto, size, gop_handle);
576fc372064SArd Biesheuvel
577fc372064SArd Biesheuvel free_handle:
578966291f6SArd Biesheuvel efi_bs_call(free_pool, gop_handle);
579fc372064SArd Biesheuvel return status;
580fc372064SArd Biesheuvel }
581