firmware/sysfb: Fix EFI/VESA format selection

Select color format for EFI/VESA firmware scanout buffer from the
number of bits per pixel and the position of the individual color
components. Fixes the selected format for the buffer in several odd
cases. For example, XRGB1555 has been reported as ARGB1555 because
of the different use of depth and transparency in VESA and Linux.

Bits-per-pixel is always the pixel's raw number of bits; including
alpha and filler bits. It is preferred over color depth, which has a
different meaning among various components and standards.

Also do not compare reserved bits and transparency bits to each other.
These values have different meanings, as reserved bits include filler
bits while transparency does not.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20230102112927.26565-2-tzimmermann@suse.de
This commit is contained in:
Thomas Zimmermann 2023-01-02 12:29:15 +01:00
parent 2591939e88
commit f35cd3fa77
1 changed files with 37 additions and 6 deletions

View File

@ -27,25 +27,56 @@ static const struct simplefb_format formats[] = SIMPLEFB_FORMATS;
__init bool sysfb_parse_mode(const struct screen_info *si, __init bool sysfb_parse_mode(const struct screen_info *si,
struct simplefb_platform_data *mode) struct simplefb_platform_data *mode)
{ {
const struct simplefb_format *f;
__u8 type; __u8 type;
u32 bits_per_pixel;
unsigned int i; unsigned int i;
type = si->orig_video_isVGA; type = si->orig_video_isVGA;
if (type != VIDEO_TYPE_VLFB && type != VIDEO_TYPE_EFI) if (type != VIDEO_TYPE_VLFB && type != VIDEO_TYPE_EFI)
return false; 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) { for (i = 0; i < ARRAY_SIZE(formats); ++i) {
f = &formats[i]; const struct simplefb_format *f = &formats[i];
if (si->lfb_depth == f->bits_per_pixel &&
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_size == f->red.length &&
si->red_pos == f->red.offset && si->red_pos == f->red.offset &&
si->green_size == f->green.length && si->green_size == f->green.length &&
si->green_pos == f->green.offset && si->green_pos == f->green.offset &&
si->blue_size == f->blue.length && si->blue_size == f->blue.length &&
si->blue_pos == f->blue.offset && si->blue_pos == f->blue.offset) {
si->rsvd_size == f->transp.length &&
si->rsvd_pos == f->transp.offset) {
mode->format = f->name; mode->format = f->name;
mode->width = si->lfb_width; mode->width = si->lfb_width;
mode->height = si->lfb_height; mode->height = si->lfb_height;