xref: /openbmc/linux/drivers/firmware/efi/libstub/gop.c (revision 61eac6d9)
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,
2245d97a74SArvind Sankar 	EFI_CMDLINE_AUTO
23fffb6804SArvind Sankar };
24fffb6804SArvind Sankar 
25fffb6804SArvind Sankar static struct {
26fffb6804SArvind Sankar 	enum efi_cmdline_option option;
27d9ff0323SArvind Sankar 	union {
28fffb6804SArvind Sankar 		u32 mode;
29d9ff0323SArvind Sankar 		struct {
30d9ff0323SArvind Sankar 			u32 width, height;
319a1663bcSArvind Sankar 			int format;
329a1663bcSArvind Sankar 			u8 depth;
33d9ff0323SArvind Sankar 		} res;
34d9ff0323SArvind Sankar 	};
3554439370SArvind Sankar } cmdline = { .option = EFI_CMDLINE_NONE };
36fffb6804SArvind Sankar 
37fffb6804SArvind Sankar static bool parse_modenum(char *option, char **next)
38fffb6804SArvind Sankar {
39fffb6804SArvind Sankar 	u32 m;
40fffb6804SArvind Sankar 
41fffb6804SArvind Sankar 	if (!strstarts(option, "mode="))
42fffb6804SArvind Sankar 		return false;
43fffb6804SArvind Sankar 	option += strlen("mode=");
44fffb6804SArvind Sankar 	m = simple_strtoull(option, &option, 0);
45fffb6804SArvind Sankar 	if (*option && *option++ != ',')
46fffb6804SArvind Sankar 		return false;
47fffb6804SArvind Sankar 	cmdline.option = EFI_CMDLINE_MODE_NUM;
48fffb6804SArvind Sankar 	cmdline.mode   = m;
49fffb6804SArvind Sankar 
50fffb6804SArvind Sankar 	*next = option;
51fffb6804SArvind Sankar 	return true;
52fffb6804SArvind Sankar }
53fffb6804SArvind Sankar 
54d9ff0323SArvind Sankar static bool parse_res(char *option, char **next)
55d9ff0323SArvind Sankar {
569a1663bcSArvind Sankar 	u32 w, h, d = 0;
579a1663bcSArvind Sankar 	int pf = -1;
58d9ff0323SArvind Sankar 
59d9ff0323SArvind Sankar 	if (!isdigit(*option))
60d9ff0323SArvind Sankar 		return false;
61d9ff0323SArvind Sankar 	w = simple_strtoull(option, &option, 10);
62d9ff0323SArvind Sankar 	if (*option++ != 'x' || !isdigit(*option))
63d9ff0323SArvind Sankar 		return false;
64d9ff0323SArvind Sankar 	h = simple_strtoull(option, &option, 10);
659a1663bcSArvind Sankar 	if (*option == '-') {
669a1663bcSArvind Sankar 		option++;
679a1663bcSArvind Sankar 		if (strstarts(option, "rgb")) {
689a1663bcSArvind Sankar 			option += strlen("rgb");
699a1663bcSArvind Sankar 			pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR;
709a1663bcSArvind Sankar 		} else if (strstarts(option, "bgr")) {
719a1663bcSArvind Sankar 			option += strlen("bgr");
729a1663bcSArvind Sankar 			pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR;
739a1663bcSArvind Sankar 		} else if (isdigit(*option))
749a1663bcSArvind Sankar 			d = simple_strtoull(option, &option, 10);
759a1663bcSArvind Sankar 		else
769a1663bcSArvind Sankar 			return false;
779a1663bcSArvind Sankar 	}
78d9ff0323SArvind Sankar 	if (*option && *option++ != ',')
79d9ff0323SArvind Sankar 		return false;
80d9ff0323SArvind Sankar 	cmdline.option     = EFI_CMDLINE_RES;
81d9ff0323SArvind Sankar 	cmdline.res.width  = w;
82d9ff0323SArvind Sankar 	cmdline.res.height = h;
839a1663bcSArvind Sankar 	cmdline.res.format = pf;
849a1663bcSArvind Sankar 	cmdline.res.depth  = d;
85d9ff0323SArvind Sankar 
86d9ff0323SArvind Sankar 	*next = option;
87d9ff0323SArvind Sankar 	return true;
88d9ff0323SArvind Sankar }
89d9ff0323SArvind Sankar 
9045d97a74SArvind Sankar static bool parse_auto(char *option, char **next)
9145d97a74SArvind Sankar {
9245d97a74SArvind Sankar 	if (!strstarts(option, "auto"))
9345d97a74SArvind Sankar 		return false;
9445d97a74SArvind Sankar 	option += strlen("auto");
9545d97a74SArvind Sankar 	if (*option && *option++ != ',')
9645d97a74SArvind Sankar 		return false;
9745d97a74SArvind Sankar 	cmdline.option = EFI_CMDLINE_AUTO;
9845d97a74SArvind Sankar 
9945d97a74SArvind Sankar 	*next = option;
10045d97a74SArvind Sankar 	return true;
10145d97a74SArvind Sankar }
10245d97a74SArvind Sankar 
103fffb6804SArvind Sankar void efi_parse_option_graphics(char *option)
104fffb6804SArvind Sankar {
105fffb6804SArvind Sankar 	while (*option) {
106fffb6804SArvind Sankar 		if (parse_modenum(option, &option))
107fffb6804SArvind Sankar 			continue;
108d9ff0323SArvind Sankar 		if (parse_res(option, &option))
109d9ff0323SArvind Sankar 			continue;
11045d97a74SArvind Sankar 		if (parse_auto(option, &option))
11145d97a74SArvind Sankar 			continue;
112fffb6804SArvind Sankar 
113fffb6804SArvind Sankar 		while (*option && *option++ != ',')
114fffb6804SArvind Sankar 			;
115fffb6804SArvind Sankar 	}
116fffb6804SArvind Sankar }
117fffb6804SArvind Sankar 
118fffb6804SArvind Sankar static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
119fffb6804SArvind Sankar {
120fffb6804SArvind Sankar 	efi_status_t status;
121fffb6804SArvind Sankar 
122fffb6804SArvind Sankar 	efi_graphics_output_protocol_mode_t *mode;
123fffb6804SArvind Sankar 	efi_graphics_output_mode_info_t *info;
124fffb6804SArvind Sankar 	unsigned long info_size;
125fffb6804SArvind Sankar 
126fffb6804SArvind Sankar 	u32 max_mode, cur_mode;
127fffb6804SArvind Sankar 	int pf;
128fffb6804SArvind Sankar 
129fffb6804SArvind Sankar 	mode = efi_table_attr(gop, mode);
130fffb6804SArvind Sankar 
131fffb6804SArvind Sankar 	cur_mode = efi_table_attr(mode, mode);
132fffb6804SArvind Sankar 	if (cmdline.mode == cur_mode)
133fffb6804SArvind Sankar 		return cur_mode;
134fffb6804SArvind Sankar 
135fffb6804SArvind Sankar 	max_mode = efi_table_attr(mode, max_mode);
136fffb6804SArvind Sankar 	if (cmdline.mode >= max_mode) {
13761eac6d9SArvind Sankar 		efi_err("Requested mode is invalid\n");
138fffb6804SArvind Sankar 		return cur_mode;
139fffb6804SArvind Sankar 	}
140fffb6804SArvind Sankar 
141fffb6804SArvind Sankar 	status = efi_call_proto(gop, query_mode, cmdline.mode,
142fffb6804SArvind Sankar 				&info_size, &info);
143fffb6804SArvind Sankar 	if (status != EFI_SUCCESS) {
14461eac6d9SArvind Sankar 		efi_err("Couldn't get mode information\n");
145fffb6804SArvind Sankar 		return cur_mode;
146fffb6804SArvind Sankar 	}
147fffb6804SArvind Sankar 
148fffb6804SArvind Sankar 	pf = info->pixel_format;
149fffb6804SArvind Sankar 
150fffb6804SArvind Sankar 	efi_bs_call(free_pool, info);
151fffb6804SArvind Sankar 
152fffb6804SArvind Sankar 	if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
15361eac6d9SArvind Sankar 		efi_err("Invalid PixelFormat\n");
154fffb6804SArvind Sankar 		return cur_mode;
155fffb6804SArvind Sankar 	}
156fffb6804SArvind Sankar 
157fffb6804SArvind Sankar 	return cmdline.mode;
158fffb6804SArvind Sankar }
159fffb6804SArvind Sankar 
1609a1663bcSArvind Sankar static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
1619a1663bcSArvind Sankar {
1629a1663bcSArvind Sankar 	if (pixel_format == PIXEL_BIT_MASK) {
1639a1663bcSArvind Sankar 		u32 mask = pixel_info.red_mask | pixel_info.green_mask |
1649a1663bcSArvind Sankar 			   pixel_info.blue_mask | pixel_info.reserved_mask;
1659a1663bcSArvind Sankar 		if (!mask)
1669a1663bcSArvind Sankar 			return 0;
1679a1663bcSArvind Sankar 		return __fls(mask) - __ffs(mask) + 1;
1689a1663bcSArvind Sankar 	} else
1699a1663bcSArvind Sankar 		return 32;
1709a1663bcSArvind Sankar }
1719a1663bcSArvind Sankar 
172d9ff0323SArvind Sankar static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
173d9ff0323SArvind Sankar {
174d9ff0323SArvind Sankar 	efi_status_t status;
175d9ff0323SArvind Sankar 
176d9ff0323SArvind Sankar 	efi_graphics_output_protocol_mode_t *mode;
177d9ff0323SArvind Sankar 	efi_graphics_output_mode_info_t *info;
178d9ff0323SArvind Sankar 	unsigned long info_size;
179d9ff0323SArvind Sankar 
180d9ff0323SArvind Sankar 	u32 max_mode, cur_mode;
181d9ff0323SArvind Sankar 	int pf;
1829a1663bcSArvind Sankar 	efi_pixel_bitmask_t pi;
183d9ff0323SArvind Sankar 	u32 m, w, h;
184d9ff0323SArvind Sankar 
185d9ff0323SArvind Sankar 	mode = efi_table_attr(gop, mode);
186d9ff0323SArvind Sankar 
187d9ff0323SArvind Sankar 	cur_mode = efi_table_attr(mode, mode);
188d9ff0323SArvind Sankar 	info = efi_table_attr(mode, info);
1899a1663bcSArvind Sankar 	pf = info->pixel_format;
1909a1663bcSArvind Sankar 	pi = info->pixel_information;
191d9ff0323SArvind Sankar 	w  = info->horizontal_resolution;
192d9ff0323SArvind Sankar 	h  = info->vertical_resolution;
193d9ff0323SArvind Sankar 
1949a1663bcSArvind Sankar 	if (w == cmdline.res.width && h == cmdline.res.height &&
1959a1663bcSArvind Sankar 	    (cmdline.res.format < 0 || cmdline.res.format == pf) &&
1969a1663bcSArvind Sankar 	    (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
197d9ff0323SArvind Sankar 		return cur_mode;
198d9ff0323SArvind Sankar 
199d9ff0323SArvind Sankar 	max_mode = efi_table_attr(mode, max_mode);
200d9ff0323SArvind Sankar 
201d9ff0323SArvind Sankar 	for (m = 0; m < max_mode; m++) {
202d9ff0323SArvind Sankar 		if (m == cur_mode)
203d9ff0323SArvind Sankar 			continue;
204d9ff0323SArvind Sankar 
205d9ff0323SArvind Sankar 		status = efi_call_proto(gop, query_mode, m,
206d9ff0323SArvind Sankar 					&info_size, &info);
207d9ff0323SArvind Sankar 		if (status != EFI_SUCCESS)
208d9ff0323SArvind Sankar 			continue;
209d9ff0323SArvind Sankar 
210d9ff0323SArvind Sankar 		pf = info->pixel_format;
2119a1663bcSArvind Sankar 		pi = info->pixel_information;
212d9ff0323SArvind Sankar 		w  = info->horizontal_resolution;
213d9ff0323SArvind Sankar 		h  = info->vertical_resolution;
214d9ff0323SArvind Sankar 
215d9ff0323SArvind Sankar 		efi_bs_call(free_pool, info);
216d9ff0323SArvind Sankar 
217d9ff0323SArvind Sankar 		if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
218d9ff0323SArvind Sankar 			continue;
2199a1663bcSArvind Sankar 		if (w == cmdline.res.width && h == cmdline.res.height &&
2209a1663bcSArvind Sankar 		    (cmdline.res.format < 0 || cmdline.res.format == pf) &&
2219a1663bcSArvind Sankar 		    (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
222d9ff0323SArvind Sankar 			return m;
223d9ff0323SArvind Sankar 	}
224d9ff0323SArvind Sankar 
22561eac6d9SArvind Sankar 	efi_err("Couldn't find requested mode\n");
226d9ff0323SArvind Sankar 
227d9ff0323SArvind Sankar 	return cur_mode;
228d9ff0323SArvind Sankar }
229d9ff0323SArvind Sankar 
23045d97a74SArvind Sankar static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
23145d97a74SArvind Sankar {
23245d97a74SArvind Sankar 	efi_status_t status;
23345d97a74SArvind Sankar 
23445d97a74SArvind Sankar 	efi_graphics_output_protocol_mode_t *mode;
23545d97a74SArvind Sankar 	efi_graphics_output_mode_info_t *info;
23645d97a74SArvind Sankar 	unsigned long info_size;
23745d97a74SArvind Sankar 
23845d97a74SArvind Sankar 	u32 max_mode, cur_mode, best_mode, area;
23945d97a74SArvind Sankar 	u8 depth;
24045d97a74SArvind Sankar 	int pf;
24145d97a74SArvind Sankar 	efi_pixel_bitmask_t pi;
24245d97a74SArvind Sankar 	u32 m, w, h, a;
24345d97a74SArvind Sankar 	u8 d;
24445d97a74SArvind Sankar 
24545d97a74SArvind Sankar 	mode = efi_table_attr(gop, mode);
24645d97a74SArvind Sankar 
24745d97a74SArvind Sankar 	cur_mode = efi_table_attr(mode, mode);
24845d97a74SArvind Sankar 	max_mode = efi_table_attr(mode, max_mode);
24945d97a74SArvind Sankar 
25045d97a74SArvind Sankar 	info = efi_table_attr(mode, info);
25145d97a74SArvind Sankar 
25245d97a74SArvind Sankar 	pf = info->pixel_format;
25345d97a74SArvind Sankar 	pi = info->pixel_information;
25445d97a74SArvind Sankar 	w  = info->horizontal_resolution;
25545d97a74SArvind Sankar 	h  = info->vertical_resolution;
25645d97a74SArvind Sankar 
25745d97a74SArvind Sankar 	best_mode = cur_mode;
25845d97a74SArvind Sankar 	area = w * h;
25945d97a74SArvind Sankar 	depth = pixel_bpp(pf, pi);
26045d97a74SArvind Sankar 
26145d97a74SArvind Sankar 	for (m = 0; m < max_mode; m++) {
26245d97a74SArvind Sankar 		if (m == cur_mode)
26345d97a74SArvind Sankar 			continue;
26445d97a74SArvind Sankar 
26545d97a74SArvind Sankar 		status = efi_call_proto(gop, query_mode, m,
26645d97a74SArvind Sankar 					&info_size, &info);
26745d97a74SArvind Sankar 		if (status != EFI_SUCCESS)
26845d97a74SArvind Sankar 			continue;
26945d97a74SArvind Sankar 
27045d97a74SArvind Sankar 		pf = info->pixel_format;
27145d97a74SArvind Sankar 		pi = info->pixel_information;
27245d97a74SArvind Sankar 		w  = info->horizontal_resolution;
27345d97a74SArvind Sankar 		h  = info->vertical_resolution;
27445d97a74SArvind Sankar 
27545d97a74SArvind Sankar 		efi_bs_call(free_pool, info);
27645d97a74SArvind Sankar 
27745d97a74SArvind Sankar 		if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
27845d97a74SArvind Sankar 			continue;
27945d97a74SArvind Sankar 		a = w * h;
28045d97a74SArvind Sankar 		if (a < area)
28145d97a74SArvind Sankar 			continue;
28245d97a74SArvind Sankar 		d = pixel_bpp(pf, pi);
28345d97a74SArvind Sankar 		if (a > area || d > depth) {
28445d97a74SArvind Sankar 			best_mode = m;
28545d97a74SArvind Sankar 			area = a;
28645d97a74SArvind Sankar 			depth = d;
28745d97a74SArvind Sankar 		}
28845d97a74SArvind Sankar 	}
28945d97a74SArvind Sankar 
29045d97a74SArvind Sankar 	return best_mode;
29145d97a74SArvind Sankar }
29245d97a74SArvind Sankar 
293fffb6804SArvind Sankar static void set_mode(efi_graphics_output_protocol_t *gop)
294fffb6804SArvind Sankar {
295fffb6804SArvind Sankar 	efi_graphics_output_protocol_mode_t *mode;
296fffb6804SArvind Sankar 	u32 cur_mode, new_mode;
297fffb6804SArvind Sankar 
298fffb6804SArvind Sankar 	switch (cmdline.option) {
299fffb6804SArvind Sankar 	case EFI_CMDLINE_MODE_NUM:
300fffb6804SArvind Sankar 		new_mode = choose_mode_modenum(gop);
301fffb6804SArvind Sankar 		break;
302d9ff0323SArvind Sankar 	case EFI_CMDLINE_RES:
303d9ff0323SArvind Sankar 		new_mode = choose_mode_res(gop);
304d9ff0323SArvind Sankar 		break;
30545d97a74SArvind Sankar 	case EFI_CMDLINE_AUTO:
30645d97a74SArvind Sankar 		new_mode = choose_mode_auto(gop);
30745d97a74SArvind Sankar 		break;
308fffb6804SArvind Sankar 	default:
309fffb6804SArvind Sankar 		return;
310fffb6804SArvind Sankar 	}
311fffb6804SArvind Sankar 
312fffb6804SArvind Sankar 	mode = efi_table_attr(gop, mode);
313fffb6804SArvind Sankar 	cur_mode = efi_table_attr(mode, mode);
314fffb6804SArvind Sankar 
315fffb6804SArvind Sankar 	if (new_mode == cur_mode)
316fffb6804SArvind Sankar 		return;
317fffb6804SArvind Sankar 
318fffb6804SArvind Sankar 	if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS)
31961eac6d9SArvind Sankar 		efi_err("Failed to set requested mode\n");
320fffb6804SArvind Sankar }
321fffb6804SArvind Sankar 
3229867fc9dSArvind Sankar static void find_bits(u32 mask, u8 *pos, u8 *size)
323fc372064SArd Biesheuvel {
3249867fc9dSArvind Sankar 	if (!mask) {
3259867fc9dSArvind Sankar 		*pos = *size = 0;
3269867fc9dSArvind Sankar 		return;
327fc372064SArd Biesheuvel 	}
328fc372064SArd Biesheuvel 
3299867fc9dSArvind Sankar 	/* UEFI spec guarantees that the set bits are contiguous */
3309867fc9dSArvind Sankar 	*pos  = __ffs(mask);
3319867fc9dSArvind Sankar 	*size = __fls(mask) - *pos + 1;
332fc372064SArd Biesheuvel }
333fc372064SArd Biesheuvel 
334fc372064SArd Biesheuvel static void
335fc372064SArd Biesheuvel setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
33644c84b4aSArvind Sankar 		 efi_pixel_bitmask_t pixel_info, int pixel_format)
337fc372064SArd Biesheuvel {
338d49fd4bbSArvind Sankar 	if (pixel_format == PIXEL_BIT_MASK) {
339d49fd4bbSArvind Sankar 		find_bits(pixel_info.red_mask,
340d49fd4bbSArvind Sankar 			  &si->red_pos, &si->red_size);
341d49fd4bbSArvind Sankar 		find_bits(pixel_info.green_mask,
342d49fd4bbSArvind Sankar 			  &si->green_pos, &si->green_size);
343d49fd4bbSArvind Sankar 		find_bits(pixel_info.blue_mask,
344d49fd4bbSArvind Sankar 			  &si->blue_pos, &si->blue_size);
345d49fd4bbSArvind Sankar 		find_bits(pixel_info.reserved_mask,
346d49fd4bbSArvind Sankar 			  &si->rsvd_pos, &si->rsvd_size);
347fc372064SArd Biesheuvel 		si->lfb_depth = si->red_size + si->green_size +
348fc372064SArd Biesheuvel 			si->blue_size + si->rsvd_size;
349fc372064SArd Biesheuvel 		si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
350fc372064SArd Biesheuvel 	} else {
351d49fd4bbSArvind Sankar 		if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
352fc372064SArd Biesheuvel 			si->red_pos   = 0;
353d49fd4bbSArvind Sankar 			si->blue_pos  = 16;
354d49fd4bbSArvind Sankar 		} else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ {
355fc372064SArd Biesheuvel 			si->blue_pos  = 0;
356d49fd4bbSArvind Sankar 			si->red_pos   = 16;
357d49fd4bbSArvind Sankar 		}
358d49fd4bbSArvind Sankar 
359d49fd4bbSArvind Sankar 		si->green_pos = 8;
360d49fd4bbSArvind Sankar 		si->rsvd_pos  = 24;
361d49fd4bbSArvind Sankar 		si->red_size = si->green_size =
362d49fd4bbSArvind Sankar 			si->blue_size = si->rsvd_size = 8;
363d49fd4bbSArvind Sankar 
364d49fd4bbSArvind Sankar 		si->lfb_depth = 32;
365d49fd4bbSArvind Sankar 		si->lfb_linelength = pixels_per_scan_line * 4;
366fc372064SArd Biesheuvel 	}
367fc372064SArd Biesheuvel }
368fc372064SArd Biesheuvel 
369ecf53091SArvind Sankar static efi_graphics_output_protocol_t *
370ecf53091SArvind Sankar find_gop(efi_guid_t *proto, unsigned long size, void **handles)
371fc372064SArd Biesheuvel {
372e484c594SArvind Sankar 	efi_graphics_output_protocol_t *first_gop;
3732732ea0dSArd Biesheuvel 	efi_handle_t h;
374fc372064SArd Biesheuvel 	int i;
375fc372064SArd Biesheuvel 
376fc372064SArd Biesheuvel 	first_gop = NULL;
377fc372064SArd Biesheuvel 
3782732ea0dSArd Biesheuvel 	for_each_efi_handle(h, handles, size, i) {
379e484c594SArvind Sankar 		efi_status_t status;
380e484c594SArvind Sankar 
381e484c594SArvind Sankar 		efi_graphics_output_protocol_t *gop;
382e484c594SArvind Sankar 		efi_graphics_output_protocol_mode_t *mode;
383e484c594SArvind Sankar 		efi_graphics_output_mode_info_t *info;
384e484c594SArvind Sankar 
385fc372064SArd Biesheuvel 		efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
386fc372064SArd Biesheuvel 		void *dummy = NULL;
387fc372064SArd Biesheuvel 
388966291f6SArd Biesheuvel 		status = efi_bs_call(handle_protocol, h, proto, (void **)&gop);
389fc372064SArd Biesheuvel 		if (status != EFI_SUCCESS)
390fc372064SArd Biesheuvel 			continue;
391fc372064SArd Biesheuvel 
3928cd20797SArvind Sankar 		mode = efi_table_attr(gop, mode);
3938cd20797SArvind Sankar 		info = efi_table_attr(mode, info);
394d49fd4bbSArvind Sankar 		if (info->pixel_format == PIXEL_BLT_ONLY ||
395d49fd4bbSArvind Sankar 		    info->pixel_format >= PIXEL_FORMAT_MAX)
3968cd20797SArvind Sankar 			continue;
3978cd20797SArvind Sankar 
398fc372064SArd Biesheuvel 		/*
399fc372064SArd Biesheuvel 		 * Systems that use the UEFI Console Splitter may
400fc372064SArd Biesheuvel 		 * provide multiple GOP devices, not all of which are
401fc372064SArd Biesheuvel 		 * backed by real hardware. The workaround is to search
402fc372064SArd Biesheuvel 		 * for a GOP implementing the ConOut protocol, and if
403fc372064SArd Biesheuvel 		 * one isn't found, to just fall back to the first GOP.
4046327e6d0SArvind Sankar 		 *
405fc372064SArd Biesheuvel 		 * Once we've found a GOP supporting ConOut,
406fc372064SArd Biesheuvel 		 * don't bother looking any further.
407fc372064SArd Biesheuvel 		 */
4088e0a22e2SArvind Sankar 		status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy);
4098e0a22e2SArvind Sankar 		if (status == EFI_SUCCESS)
4108e0a22e2SArvind Sankar 			return gop;
4118e0a22e2SArvind Sankar 
4128e0a22e2SArvind Sankar 		if (!first_gop)
4138de8788dSArvind Sankar 			first_gop = gop;
414fc372064SArd Biesheuvel 	}
415fc372064SArd Biesheuvel 
416ecf53091SArvind Sankar 	return first_gop;
417ecf53091SArvind Sankar }
418ecf53091SArvind Sankar 
419ecf53091SArvind Sankar static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
420ecf53091SArvind Sankar 			      unsigned long size, void **handles)
421ecf53091SArvind Sankar {
422ecf53091SArvind Sankar 	efi_graphics_output_protocol_t *gop;
423ecf53091SArvind Sankar 	efi_graphics_output_protocol_mode_t *mode;
424e484c594SArvind Sankar 	efi_graphics_output_mode_info_t *info;
425ecf53091SArvind Sankar 
426ecf53091SArvind Sankar 	gop = find_gop(proto, size, handles);
427ecf53091SArvind Sankar 
428fc372064SArd Biesheuvel 	/* Did we find any GOPs? */
429ecf53091SArvind Sankar 	if (!gop)
4306fc3cec3SArvind Sankar 		return EFI_NOT_FOUND;
431fc372064SArd Biesheuvel 
432fffb6804SArvind Sankar 	/* Change mode if requested */
433fffb6804SArvind Sankar 	set_mode(gop);
434fffb6804SArvind Sankar 
435fc372064SArd Biesheuvel 	/* EFI framebuffer */
436ecf53091SArvind Sankar 	mode = efi_table_attr(gop, mode);
4376327e6d0SArvind Sankar 	info = efi_table_attr(mode, info);
4386327e6d0SArvind Sankar 
439fc372064SArd Biesheuvel 	si->orig_video_isVGA = VIDEO_TYPE_EFI;
440fc372064SArd Biesheuvel 
4416327e6d0SArvind Sankar 	si->lfb_width  = info->horizontal_resolution;
4426327e6d0SArvind Sankar 	si->lfb_height = info->vertical_resolution;
443fc372064SArd Biesheuvel 
444eed4e019SArvind Sankar 	efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
445eed4e019SArvind Sankar 			  &si->lfb_base, &si->ext_lfb_base);
4466327e6d0SArvind Sankar 	if (si->ext_lfb_base)
447fc372064SArd Biesheuvel 		si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
448fc372064SArd Biesheuvel 
449fc372064SArd Biesheuvel 	si->pages = 1;
450fc372064SArd Biesheuvel 
4516327e6d0SArvind Sankar 	setup_pixel_info(si, info->pixels_per_scan_line,
4526327e6d0SArvind Sankar 			     info->pixel_information, info->pixel_format);
453fc372064SArd Biesheuvel 
454fc372064SArd Biesheuvel 	si->lfb_size = si->lfb_linelength * si->lfb_height;
455fc372064SArd Biesheuvel 
456fc372064SArd Biesheuvel 	si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
4576fc3cec3SArvind Sankar 
458dbd89c30SArvind Sankar 	return EFI_SUCCESS;
459fc372064SArd Biesheuvel }
460fc372064SArd Biesheuvel 
461fc372064SArd Biesheuvel /*
462fc372064SArd Biesheuvel  * See if we have Graphics Output Protocol
463fc372064SArd Biesheuvel  */
464cd33a5c1SArd Biesheuvel efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
465fc372064SArd Biesheuvel 			   unsigned long size)
466fc372064SArd Biesheuvel {
467fc372064SArd Biesheuvel 	efi_status_t status;
468fc372064SArd Biesheuvel 	void **gop_handle = NULL;
469fc372064SArd Biesheuvel 
470966291f6SArd Biesheuvel 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
471966291f6SArd Biesheuvel 			     (void **)&gop_handle);
472fc372064SArd Biesheuvel 	if (status != EFI_SUCCESS)
473fc372064SArd Biesheuvel 		return status;
474fc372064SArd Biesheuvel 
475966291f6SArd Biesheuvel 	status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, proto, NULL,
476966291f6SArd Biesheuvel 			     &size, gop_handle);
477fc372064SArd Biesheuvel 	if (status != EFI_SUCCESS)
478fc372064SArd Biesheuvel 		goto free_handle;
479fc372064SArd Biesheuvel 
480cd33a5c1SArd Biesheuvel 	status = setup_gop(si, proto, size, gop_handle);
481fc372064SArd Biesheuvel 
482fc372064SArd Biesheuvel free_handle:
483966291f6SArd Biesheuvel 	efi_bs_call(free_pool, gop_handle);
484fc372064SArd Biesheuvel 	return status;
485fc372064SArd Biesheuvel }
486