diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/edid_fill_fb.c | 195 |
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; } |