/* SPDX-License-Identifier: MIT */ #include <console/console.h> #include <edid.h> #include <boot/coreboot_tables.h> #include <framebuffer_info.h> #include <string.h> #include <stdlib.h> #include <commonlib/list.h> struct fb_info { struct list_node node; struct lb_framebuffer fb; }; static struct list_node list; /* * Allocate a new framebuffer info struct on heap. * Returns NULL on error. */ static struct fb_info *fb_new_framebuffer_info(void) { 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 (bpp_mask > fb->bits_per_pixel) { printk(BIOS_ERR, "%s: channel bit mask=%d is greater than BPP=%d ." " This is a driver bug. Framebuffer is invalid.\n", __func__, bpp_mask, fb->bits_per_pixel); return NULL; } else if (bpp_mask != fb->bits_per_pixel) { printk(BIOS_WARNING, "%s: channel bit mask=%d and BPP=%d don't match." " This is a driver bug.\n", __func__, bpp_mask, fb->bits_per_pixel); } 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: { /* FIXME: 24 BPP might be RGB8 or XRGB8 */ /* packed into 4-byte words */ 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: { /* packed into 2-byte words */ 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_ERR, "%s: unsupported BPP %d\n", __func__, bits_per_pixel); } if (!info) printk(BIOS_ERR, "%s: failed to add framebuffer info\n", __func__); return info; } /* Wrapper for Ada to have a simpler function signature */ int fb_add_framebuffer_info_simple(uintptr_t fb_addr, uint32_t x_res, uint32_t y_res, uint32_t bytes_per_line, uint8_t bits_per_pixel) { return fb_add_framebuffer_info(fb_addr, x_res, y_res, bytes_per_line, bits_per_pixel) != NULL; } void fb_set_orientation(struct fb_info *info, enum lb_fb_orientation orientation) { if (!info) return; info->fb.orientation = orientation; } /* * Take an edid, and create a framebuffer. */ struct fb_info *fb_new_framebuffer_info_from_edid(const struct edid *edid, uintptr_t fb_addr) { return fb_add_framebuffer_info(fb_addr, edid->x_resolution, edid->y_resolution, edid->bytes_per_line, edid->framebuffer_bits_per_pixel); } int fill_lb_framebuffer(struct lb_framebuffer *framebuffer) { struct fb_info *i; list_for_each(i, list, node) { //TODO: Add support for advertising all framebuffers in this list *framebuffer = i->fb; return 0; } return -1; }