diff options
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/sysfb_simplefb.c | 43 |
1 files changed, 37 insertions, 6 deletions
diff --git a/drivers/firmware/sysfb_simplefb.c b/drivers/firmware/sysfb_simplefb.c index a353e27f83f5..ce9c007ed66f 100644 --- a/drivers/firmware/sysfb_simplefb.c +++ b/drivers/firmware/sysfb_simplefb.c @@ -27,25 +27,56 @@ static const struct simplefb_format formats[] = SIMPLEFB_FORMATS; __init bool sysfb_parse_mode(const struct screen_info *si, struct simplefb_platform_data *mode) { - const struct simplefb_format *f; __u8 type; + u32 bits_per_pixel; unsigned int i; type = si->orig_video_isVGA; if (type != VIDEO_TYPE_VLFB && type != VIDEO_TYPE_EFI) return false; + /* + * The meaning of depth and bpp for direct-color formats is + * inconsistent: + * + * - DRM format info specifies depth as the number of color + * bits; including alpha, but not including filler bits. + * - Linux' EFI platform code computes lfb_depth from the + * individual color channels, including the reserved bits. + * - VBE 1.1 defines lfb_depth for XRGB1555 as 16, but later + * versions use 15. + * - On the kernel command line, 'bpp' of 32 is usually + * XRGB8888 including the filler bits, but 15 is XRGB1555 + * not including the filler bit. + * + * It's not easily possible to fix this in struct screen_info, + * as this could break UAPI. The best solution is to compute + * bits_per_pixel here and ignore lfb_depth. In the loop below, + * ignore simplefb formats with alpha bits, as EFI and VESA + * don't specify alpha channels. + */ + if (si->lfb_depth > 8) { + bits_per_pixel = max(max3(si->red_size + si->red_pos, + si->green_size + si->green_pos, + si->blue_size + si->blue_pos), + si->rsvd_size + si->rsvd_pos); + } else { + bits_per_pixel = si->lfb_depth; + } + for (i = 0; i < ARRAY_SIZE(formats); ++i) { - f = &formats[i]; - if (si->lfb_depth == f->bits_per_pixel && + const struct simplefb_format *f = &formats[i]; + + if (f->transp.length) + continue; /* transparent formats are unsupported by VESA/EFI */ + + if (bits_per_pixel == f->bits_per_pixel && si->red_size == f->red.length && si->red_pos == f->red.offset && si->green_size == f->green.length && si->green_pos == f->green.offset && si->blue_size == f->blue.length && - si->blue_pos == f->blue.offset && - si->rsvd_size == f->transp.length && - si->rsvd_pos == f->transp.offset) { + si->blue_pos == f->blue.offset) { mode->format = f->name; mode->width = si->lfb_width; mode->height = si->lfb_height; |