summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/edid_fill_fb.c195
1 files changed, 145 insertions, 50 deletions
diff --git a/src/lib/edid_fill_fb.c b/src/lib/edid_fill_fb.c
index 422feaf9e7..1e8cc5a5e7 100644
--- a/src/lib/edid_fill_fb.c
+++ b/src/lib/edid_fill_fb.c
@@ -3,75 +3,170 @@
#include <console/console.h>
#include <edid.h>
#include <boot/coreboot_tables.h>
+#include <framebuffer_info.h>
+#include <string.h>
+#include <stdlib.h>
+#include <bootsplash.h>
+#include <list.h>
-static int fb_valid;
-static struct lb_framebuffer edid_fb;
+struct fb_info {
+ struct list_node node;
+ struct lb_framebuffer fb;
+};
+static struct list_node list;
/*
- * Take an edid, and create a framebuffer. Set fb_valid to 1.
+ * Allocate a new framebuffer info struct on heap.
+ * Returns NULL on error.
*/
-void set_vbe_mode_info_valid(const struct edid *edid, uintptr_t fb_addr)
+static struct fb_info *fb_new_framebuffer_info(void)
{
- edid_fb.physical_address = fb_addr;
- edid_fb.x_resolution = edid->x_resolution;
- edid_fb.y_resolution = edid->y_resolution;
- edid_fb.bytes_per_line = edid->bytes_per_line;
- /* In the case of (e.g.) 24 framebuffer bits per pixel, the convention
- * nowadays seems to be to round it up to the nearest reasonable
- * boundary, because otherwise the byte-packing is hideous.
- * So, for example, in RGB with no alpha, the bytes are still
- * packed into 32-bit words, the so-called 32bpp-no-alpha mode.
- * Or, in 5:6:5 mode, the bytes are also packed into 32-bit words,
- * and in 4:4:4 mode, they are packed into 16-bit words.
- * Good call on the hardware guys part.
- * It's not clear we're covering all cases here, but
- * I'm not sure with grahpics you ever can.
- */
- edid_fb.bits_per_pixel = edid->framebuffer_bits_per_pixel;
- edid_fb.reserved_mask_pos = 0;
- edid_fb.reserved_mask_size = 0;
- switch (edid->framebuffer_bits_per_pixel) {
+ struct fb_info *ret;
+ ret = malloc(sizeof(struct fb_info));
+ if (ret)
+ memset(ret, 0, sizeof(struct fb_info));
+
+ return ret;
+}
+
+/*
+ * Fills a provided framebuffer info struct and adds it to the internal list if it's
+ * valid. Returns NULL on error.
+ */
+struct fb_info *
+fb_add_framebuffer_info_ex(const struct lb_framebuffer *fb)
+{
+ struct fb_info *info;
+ uint8_t bpp_mask;
+
+ /* Validate input */
+ if (!fb || !fb->x_resolution || !fb->y_resolution || !fb->bytes_per_line ||
+ !fb->bits_per_pixel) {
+ printk(BIOS_ERR, "%s: Invalid framebuffer data provided\n", __func__);
+ return NULL;
+ }
+
+ bpp_mask = fb->blue_mask_size + fb->green_mask_size + fb->red_mask_size +
+ fb->reserved_mask_size;
+ if (fb->bits_per_pixel != bpp_mask) {
+ printk(BIOS_ERR, "%s: BPP=%d and channel bit mask=%d doesn't match."
+ " This is a driver bug.\n", __func__, fb->bits_per_pixel, bpp_mask);
+ return NULL;
+ }
+
+ info = fb_new_framebuffer_info();
+ if (!info)
+ return NULL;
+
+ printk(BIOS_INFO, "framebuffer_info: bytes_per_line: %d, bits_per_pixel: %d\n "
+ " x_res x y_res: %d x %d, size: %d at 0x%llx\n",
+ fb->bytes_per_line, fb->bits_per_pixel, fb->x_resolution,
+ fb->y_resolution, (fb->bytes_per_line * fb->y_resolution),
+ fb->physical_address);
+
+ /* Update */
+ info->fb = *fb;
+
+ list_insert_after(&info->node, &list);
+
+ return info;
+}
+
+/*
+ * Allocates a new framebuffer info struct and fills it for 32/24/16bpp framebuffers.
+ * Intended for drivers that only support reporting the current information or have a single
+ * modeset invocation.
+ *
+ * Complex drivers should use fb_add_framebuffer_info_ex() instead.
+ */
+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)
+{
+ struct fb_info *info = NULL;
+
+ switch (bits_per_pixel) {
case 32:
- case 24:
+ case 24: {
+ /* FIXME: 24 BPP might be RGB8 or XRGB8 */
/* packed into 4-byte words */
- edid_fb.reserved_mask_pos = 24;
- edid_fb.reserved_mask_size = 8;
- edid_fb.red_mask_pos = 16;
- edid_fb.red_mask_size = 8;
- edid_fb.green_mask_pos = 8;
- edid_fb.green_mask_size = 8;
- edid_fb.blue_mask_pos = 0;
- edid_fb.blue_mask_size = 8;
+
+ const struct lb_framebuffer fb = {
+ .physical_address = fb_addr,
+ .x_resolution = x_resolution,
+ .y_resolution = y_resolution,
+ .bytes_per_line = bytes_per_line,
+ .bits_per_pixel = bits_per_pixel,
+ .red_mask_pos = 16,
+ .red_mask_size = 8,
+ .green_mask_pos = 8,
+ .green_mask_size = 8,
+ .blue_mask_pos = 0,
+ .blue_mask_size = 8,
+ .reserved_mask_pos = 24,
+ .reserved_mask_size = 8,
+ .orientation = LB_FB_ORIENTATION_NORMAL,
+ };
+
+ info = fb_add_framebuffer_info_ex(&fb);
break;
- case 16:
+ }
+ case 16: {
/* packed into 2-byte words */
- edid_fb.red_mask_pos = 11;
- edid_fb.red_mask_size = 5;
- edid_fb.green_mask_pos = 5;
- edid_fb.green_mask_size = 6;
- edid_fb.blue_mask_pos = 0;
- edid_fb.blue_mask_size = 5;
+ const struct lb_framebuffer fb = {
+ .physical_address = fb_addr,
+ .x_resolution = x_resolution,
+ .y_resolution = y_resolution,
+ .bytes_per_line = bytes_per_line,
+ .bits_per_pixel = 16,
+ .red_mask_pos = 11,
+ .red_mask_size = 5,
+ .green_mask_pos = 5,
+ .green_mask_size = 6,
+ .blue_mask_pos = 0,
+ .blue_mask_size = 5,
+ .reserved_mask_pos = 0,
+ .reserved_mask_size = 0,
+ .orientation = LB_FB_ORIENTATION_NORMAL,
+ };
+ info = fb_add_framebuffer_info_ex(&fb);
break;
+ }
default:
- printk(BIOS_SPEW, "%s: unsupported BPP %d\n", __func__,
- edid->framebuffer_bits_per_pixel);
- return;
+ printk(BIOS_ERR, "%s: unsupported BPP %d\n", __func__, bits_per_pixel);
}
+ if (!info)
+ printk(BIOS_ERR, "%s: failed to add framebuffer info\n", __func__);
- fb_valid = 1;
+ return info;
}
-void set_vbe_framebuffer_orientation(enum lb_fb_orientation orientation)
+void fb_set_orientation(struct fb_info *info, enum lb_fb_orientation orientation)
{
- edid_fb.orientation = orientation;
+ if (!info)
+ return;
+
+ info->fb.orientation = orientation;
}
-int fill_lb_framebuffer(struct lb_framebuffer *framebuffer)
+/*
+ * Take an edid, and create a framebuffer.
+ */
+struct fb_info *set_vbe_mode_info_valid(const struct edid *edid, uintptr_t fb_addr)
{
- if (!fb_valid)
- return -1;
+ return fb_add_framebuffer_info(fb_addr, edid->x_resolution, edid->y_resolution,
+ edid->bytes_per_line, edid->framebuffer_bits_per_pixel);
+}
- *framebuffer = edid_fb;
+int fill_lb_framebuffer(struct lb_framebuffer *framebuffer)
+{
+ struct fb_info *i;
- return 0;
+ list_for_each(i, list, node) {
+ //TODO: Add support for advertising all framebuffers in this list
+ *framebuffer = i->fb;
+ return 0;
+ }
+ return -1;
}