xref: /aosp_15_r20/external/coreboot/src/lib/edid_fill_fb.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: MIT */
2 
3 #include <console/console.h>
4 #include <edid.h>
5 #include <boot/coreboot_tables.h>
6 #include <framebuffer_info.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <commonlib/list.h>
10 
11 struct fb_info {
12 	struct list_node node;
13 	struct lb_framebuffer fb;
14 };
15 static struct list_node list;
16 
17 /*
18  * Allocate a new framebuffer info struct on heap.
19  * Returns NULL on error.
20  */
fb_new_framebuffer_info(void)21 static struct fb_info *fb_new_framebuffer_info(void)
22 {
23 	struct fb_info *ret;
24 	ret = malloc(sizeof(struct fb_info));
25 	if (ret)
26 		memset(ret, 0, sizeof(struct fb_info));
27 
28 	return ret;
29 }
30 
31 /*
32  * Fills a provided framebuffer info struct and adds it to the internal list if it's
33  * valid. Returns NULL on error.
34  */
35 struct fb_info *
fb_add_framebuffer_info_ex(const struct lb_framebuffer * fb)36 fb_add_framebuffer_info_ex(const struct lb_framebuffer *fb)
37 {
38 	struct fb_info *info;
39 	uint8_t bpp_mask;
40 
41 	/* Validate input */
42 	if (!fb || !fb->x_resolution || !fb->y_resolution || !fb->bytes_per_line ||
43 	    !fb->bits_per_pixel) {
44 		printk(BIOS_ERR, "%s: Invalid framebuffer data provided\n", __func__);
45 		return NULL;
46 	}
47 
48 	bpp_mask = fb->blue_mask_size + fb->green_mask_size + fb->red_mask_size +
49 		fb->reserved_mask_size;
50 	if (bpp_mask > fb->bits_per_pixel) {
51 		printk(BIOS_ERR,
52 		       "%s: channel bit mask=%d is greater than BPP=%d ."
53 		       " This is a driver bug. Framebuffer is invalid.\n",
54 		       __func__, bpp_mask, fb->bits_per_pixel);
55 		return NULL;
56 	} else if (bpp_mask != fb->bits_per_pixel) {
57 		printk(BIOS_WARNING,
58 		       "%s: channel bit mask=%d and BPP=%d don't match."
59 		       " This is a driver bug.\n",
60 		       __func__, bpp_mask, fb->bits_per_pixel);
61 	}
62 
63 	info = fb_new_framebuffer_info();
64 	if (!info)
65 		return NULL;
66 
67 	printk(BIOS_INFO, "framebuffer_info: bytes_per_line: %d, bits_per_pixel: %d\n "
68 			  "                  x_res x y_res: %d x %d, size: %d at 0x%llx\n",
69 			fb->bytes_per_line, fb->bits_per_pixel, fb->x_resolution,
70 			fb->y_resolution, (fb->bytes_per_line * fb->y_resolution),
71 			fb->physical_address);
72 
73 	/* Update */
74 	info->fb = *fb;
75 
76 	list_insert_after(&info->node, &list);
77 
78 	return info;
79 }
80 
81 /*
82  * Allocates a new framebuffer info struct and fills it for 32/24/16bpp framebuffers.
83  * Intended for drivers that only support reporting the current information or have a single
84  * modeset invocation.
85  *
86  * Complex drivers should use fb_add_framebuffer_info_ex() instead.
87  */
88 struct fb_info *
fb_add_framebuffer_info(uintptr_t fb_addr,uint32_t x_resolution,uint32_t y_resolution,uint32_t bytes_per_line,uint8_t bits_per_pixel)89 fb_add_framebuffer_info(uintptr_t fb_addr, uint32_t x_resolution,
90 			uint32_t y_resolution, uint32_t bytes_per_line,
91 			uint8_t bits_per_pixel)
92 {
93 	struct fb_info *info = NULL;
94 
95 	switch (bits_per_pixel) {
96 	case 32:
97 	case 24: {
98 		/* FIXME: 24 BPP might be RGB8 or XRGB8 */
99 		/* packed into 4-byte words */
100 
101 		const struct lb_framebuffer fb = {
102 			.physical_address    = fb_addr,
103 			.x_resolution        = x_resolution,
104 			.y_resolution        = y_resolution,
105 			.bytes_per_line      = bytes_per_line,
106 			.bits_per_pixel      = bits_per_pixel,
107 			.red_mask_pos        = 16,
108 			.red_mask_size       = 8,
109 			.green_mask_pos      = 8,
110 			.green_mask_size     = 8,
111 			.blue_mask_pos       = 0,
112 			.blue_mask_size      = 8,
113 			.reserved_mask_pos   = 24,
114 			.reserved_mask_size  = 8,
115 			.orientation         = LB_FB_ORIENTATION_NORMAL,
116 		};
117 
118 		info = fb_add_framebuffer_info_ex(&fb);
119 		break;
120 	}
121 	case 16: {
122 		/* packed into 2-byte words */
123 		const struct lb_framebuffer fb = {
124 			.physical_address   = fb_addr,
125 			.x_resolution       = x_resolution,
126 			.y_resolution       = y_resolution,
127 			.bytes_per_line     = bytes_per_line,
128 			.bits_per_pixel     = 16,
129 			.red_mask_pos       = 11,
130 			.red_mask_size      = 5,
131 			.green_mask_pos     = 5,
132 			.green_mask_size    = 6,
133 			.blue_mask_pos      = 0,
134 			.blue_mask_size     = 5,
135 			.reserved_mask_pos  = 0,
136 			.reserved_mask_size = 0,
137 			.orientation        = LB_FB_ORIENTATION_NORMAL,
138 		};
139 		info = fb_add_framebuffer_info_ex(&fb);
140 		break;
141 	}
142 	default:
143 		printk(BIOS_ERR, "%s: unsupported BPP %d\n", __func__, bits_per_pixel);
144 	}
145 	if (!info)
146 		printk(BIOS_ERR, "%s: failed to add framebuffer info\n", __func__);
147 
148 	return info;
149 }
150 
fb_set_orientation(struct fb_info * info,enum lb_fb_orientation orientation)151 void fb_set_orientation(struct fb_info *info, enum lb_fb_orientation orientation)
152 {
153 	if (!info)
154 		return;
155 
156 	info->fb.orientation = orientation;
157 }
158 
159 /*
160  * Take an edid, and create a framebuffer.
161  */
fb_new_framebuffer_info_from_edid(const struct edid * edid,uintptr_t fb_addr)162 struct fb_info *fb_new_framebuffer_info_from_edid(const struct edid *edid,
163 							 uintptr_t fb_addr)
164 {
165 	return fb_add_framebuffer_info(fb_addr, edid->x_resolution, edid->y_resolution,
166 		edid->bytes_per_line, edid->framebuffer_bits_per_pixel);
167 }
168 
fill_lb_framebuffer(struct lb_framebuffer * framebuffer)169 int fill_lb_framebuffer(struct lb_framebuffer *framebuffer)
170 {
171 	struct fb_info *i;
172 
173 	list_for_each(i, list, node) {
174 		//TODO: Add support for advertising all framebuffers in this list
175 		*framebuffer = i->fb;
176 		return 0;
177 	}
178 	return -1;
179 }
180