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