xref: /openbmc/linux/drivers/firmware/efi/libstub/gop.c (revision d49fd4bb)
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>
9fc372064SArd Biesheuvel #include <linux/efi.h>
10fc372064SArd Biesheuvel #include <linux/screen_info.h>
11fc372064SArd Biesheuvel #include <asm/efi.h>
12fc372064SArd Biesheuvel #include <asm/setup.h>
13fc372064SArd Biesheuvel 
142fcdad2aSArd Biesheuvel #include "efistub.h"
152fcdad2aSArd Biesheuvel 
169867fc9dSArvind Sankar static void find_bits(u32 mask, u8 *pos, u8 *size)
17fc372064SArd Biesheuvel {
189867fc9dSArvind Sankar 	if (!mask) {
199867fc9dSArvind Sankar 		*pos = *size = 0;
209867fc9dSArvind Sankar 		return;
21fc372064SArd Biesheuvel 	}
22fc372064SArd Biesheuvel 
239867fc9dSArvind Sankar 	/* UEFI spec guarantees that the set bits are contiguous */
249867fc9dSArvind Sankar 	*pos  = __ffs(mask);
259867fc9dSArvind Sankar 	*size = __fls(mask) - *pos + 1;
26fc372064SArd Biesheuvel }
27fc372064SArd Biesheuvel 
28fc372064SArd Biesheuvel static void
29fc372064SArd Biesheuvel setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
3044c84b4aSArvind Sankar 		 efi_pixel_bitmask_t pixel_info, int pixel_format)
31fc372064SArd Biesheuvel {
32d49fd4bbSArvind Sankar 	if (pixel_format == PIXEL_BIT_MASK) {
33d49fd4bbSArvind Sankar 		find_bits(pixel_info.red_mask,
34d49fd4bbSArvind Sankar 			  &si->red_pos, &si->red_size);
35d49fd4bbSArvind Sankar 		find_bits(pixel_info.green_mask,
36d49fd4bbSArvind Sankar 			  &si->green_pos, &si->green_size);
37d49fd4bbSArvind Sankar 		find_bits(pixel_info.blue_mask,
38d49fd4bbSArvind Sankar 			  &si->blue_pos, &si->blue_size);
39d49fd4bbSArvind Sankar 		find_bits(pixel_info.reserved_mask,
40d49fd4bbSArvind Sankar 			  &si->rsvd_pos, &si->rsvd_size);
41fc372064SArd Biesheuvel 		si->lfb_depth = si->red_size + si->green_size +
42fc372064SArd Biesheuvel 			si->blue_size + si->rsvd_size;
43fc372064SArd Biesheuvel 		si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
44fc372064SArd Biesheuvel 	} else {
45d49fd4bbSArvind Sankar 		if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
46fc372064SArd Biesheuvel 			si->red_pos   = 0;
47d49fd4bbSArvind Sankar 			si->blue_pos  = 16;
48d49fd4bbSArvind Sankar 		} else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ {
49fc372064SArd Biesheuvel 			si->blue_pos  = 0;
50d49fd4bbSArvind Sankar 			si->red_pos   = 16;
51d49fd4bbSArvind Sankar 		}
52d49fd4bbSArvind Sankar 
53d49fd4bbSArvind Sankar 		si->green_pos = 8;
54d49fd4bbSArvind Sankar 		si->rsvd_pos  = 24;
55d49fd4bbSArvind Sankar 		si->red_size = si->green_size =
56d49fd4bbSArvind Sankar 			si->blue_size = si->rsvd_size = 8;
57d49fd4bbSArvind Sankar 
58d49fd4bbSArvind Sankar 		si->lfb_depth = 32;
59d49fd4bbSArvind Sankar 		si->lfb_linelength = pixels_per_scan_line * 4;
60fc372064SArd Biesheuvel 	}
61fc372064SArd Biesheuvel }
62fc372064SArd Biesheuvel 
63ecf53091SArvind Sankar static efi_graphics_output_protocol_t *
64ecf53091SArvind Sankar find_gop(efi_guid_t *proto, unsigned long size, void **handles)
65fc372064SArd Biesheuvel {
66e484c594SArvind Sankar 	efi_graphics_output_protocol_t *first_gop;
672732ea0dSArd Biesheuvel 	efi_handle_t h;
68fc372064SArd Biesheuvel 	int i;
69fc372064SArd Biesheuvel 
70fc372064SArd Biesheuvel 	first_gop = NULL;
71fc372064SArd Biesheuvel 
722732ea0dSArd Biesheuvel 	for_each_efi_handle(h, handles, size, i) {
73e484c594SArvind Sankar 		efi_status_t status;
74e484c594SArvind Sankar 
75e484c594SArvind Sankar 		efi_graphics_output_protocol_t *gop;
76e484c594SArvind Sankar 		efi_graphics_output_protocol_mode_t *mode;
77e484c594SArvind Sankar 		efi_graphics_output_mode_info_t *info;
78e484c594SArvind Sankar 
79fc372064SArd Biesheuvel 		efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
80fc372064SArd Biesheuvel 		void *dummy = NULL;
81fc372064SArd Biesheuvel 
82966291f6SArd Biesheuvel 		status = efi_bs_call(handle_protocol, h, proto, (void **)&gop);
83fc372064SArd Biesheuvel 		if (status != EFI_SUCCESS)
84fc372064SArd Biesheuvel 			continue;
85fc372064SArd Biesheuvel 
868cd20797SArvind Sankar 		mode = efi_table_attr(gop, mode);
878cd20797SArvind Sankar 		info = efi_table_attr(mode, info);
88d49fd4bbSArvind Sankar 		if (info->pixel_format == PIXEL_BLT_ONLY ||
89d49fd4bbSArvind Sankar 		    info->pixel_format >= PIXEL_FORMAT_MAX)
908cd20797SArvind Sankar 			continue;
918cd20797SArvind Sankar 
92fc372064SArd Biesheuvel 		/*
93fc372064SArd Biesheuvel 		 * Systems that use the UEFI Console Splitter may
94fc372064SArd Biesheuvel 		 * provide multiple GOP devices, not all of which are
95fc372064SArd Biesheuvel 		 * backed by real hardware. The workaround is to search
96fc372064SArd Biesheuvel 		 * for a GOP implementing the ConOut protocol, and if
97fc372064SArd Biesheuvel 		 * one isn't found, to just fall back to the first GOP.
986327e6d0SArvind Sankar 		 *
99fc372064SArd Biesheuvel 		 * Once we've found a GOP supporting ConOut,
100fc372064SArd Biesheuvel 		 * don't bother looking any further.
101fc372064SArd Biesheuvel 		 */
1028e0a22e2SArvind Sankar 		status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy);
1038e0a22e2SArvind Sankar 		if (status == EFI_SUCCESS)
1048e0a22e2SArvind Sankar 			return gop;
1058e0a22e2SArvind Sankar 
1068e0a22e2SArvind Sankar 		if (!first_gop)
1078de8788dSArvind Sankar 			first_gop = gop;
108fc372064SArd Biesheuvel 	}
109fc372064SArd Biesheuvel 
110ecf53091SArvind Sankar 	return first_gop;
111ecf53091SArvind Sankar }
112ecf53091SArvind Sankar 
113ecf53091SArvind Sankar static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
114ecf53091SArvind Sankar 			      unsigned long size, void **handles)
115ecf53091SArvind Sankar {
116ecf53091SArvind Sankar 	efi_graphics_output_protocol_t *gop;
117ecf53091SArvind Sankar 	efi_graphics_output_protocol_mode_t *mode;
118e484c594SArvind Sankar 	efi_graphics_output_mode_info_t *info;
119ecf53091SArvind Sankar 	efi_physical_addr_t fb_base;
120ecf53091SArvind Sankar 
121ecf53091SArvind Sankar 	gop = find_gop(proto, size, handles);
122ecf53091SArvind Sankar 
123fc372064SArd Biesheuvel 	/* Did we find any GOPs? */
124ecf53091SArvind Sankar 	if (!gop)
1256fc3cec3SArvind Sankar 		return EFI_NOT_FOUND;
126fc372064SArd Biesheuvel 
127fc372064SArd Biesheuvel 	/* EFI framebuffer */
128ecf53091SArvind Sankar 	mode = efi_table_attr(gop, mode);
1296327e6d0SArvind Sankar 	info = efi_table_attr(mode, info);
1306327e6d0SArvind Sankar 
131fc372064SArd Biesheuvel 	si->orig_video_isVGA = VIDEO_TYPE_EFI;
132fc372064SArd Biesheuvel 
1336327e6d0SArvind Sankar 	si->lfb_width  = info->horizontal_resolution;
1346327e6d0SArvind Sankar 	si->lfb_height = info->vertical_resolution;
135fc372064SArd Biesheuvel 
1366327e6d0SArvind Sankar 	fb_base		 = efi_table_attr(mode, frame_buffer_base);
137f1d1853bSArvind Sankar 	si->lfb_base	 = lower_32_bits(fb_base);
138f1d1853bSArvind Sankar 	si->ext_lfb_base = upper_32_bits(fb_base);
1396327e6d0SArvind Sankar 	if (si->ext_lfb_base)
140fc372064SArd Biesheuvel 		si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
141fc372064SArd Biesheuvel 
142fc372064SArd Biesheuvel 	si->pages = 1;
143fc372064SArd Biesheuvel 
1446327e6d0SArvind Sankar 	setup_pixel_info(si, info->pixels_per_scan_line,
1456327e6d0SArvind Sankar 			     info->pixel_information, info->pixel_format);
146fc372064SArd Biesheuvel 
147fc372064SArd Biesheuvel 	si->lfb_size = si->lfb_linelength * si->lfb_height;
148fc372064SArd Biesheuvel 
149fc372064SArd Biesheuvel 	si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
1506fc3cec3SArvind Sankar 
151dbd89c30SArvind Sankar 	return EFI_SUCCESS;
152fc372064SArd Biesheuvel }
153fc372064SArd Biesheuvel 
154fc372064SArd Biesheuvel /*
155fc372064SArd Biesheuvel  * See if we have Graphics Output Protocol
156fc372064SArd Biesheuvel  */
157cd33a5c1SArd Biesheuvel efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
158fc372064SArd Biesheuvel 			   unsigned long size)
159fc372064SArd Biesheuvel {
160fc372064SArd Biesheuvel 	efi_status_t status;
161fc372064SArd Biesheuvel 	void **gop_handle = NULL;
162fc372064SArd Biesheuvel 
163966291f6SArd Biesheuvel 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
164966291f6SArd Biesheuvel 			     (void **)&gop_handle);
165fc372064SArd Biesheuvel 	if (status != EFI_SUCCESS)
166fc372064SArd Biesheuvel 		return status;
167fc372064SArd Biesheuvel 
168966291f6SArd Biesheuvel 	status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, proto, NULL,
169966291f6SArd Biesheuvel 			     &size, gop_handle);
170fc372064SArd Biesheuvel 	if (status != EFI_SUCCESS)
171fc372064SArd Biesheuvel 		goto free_handle;
172fc372064SArd Biesheuvel 
173cd33a5c1SArd Biesheuvel 	status = setup_gop(si, proto, size, gop_handle);
174fc372064SArd Biesheuvel 
175fc372064SArd Biesheuvel free_handle:
176966291f6SArd Biesheuvel 	efi_bs_call(free_pool, gop_handle);
177fc372064SArd Biesheuvel 	return status;
178fc372064SArd Biesheuvel }
179