| /* 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; |
| } |
| |
| 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; |
| } |