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