On Tue, 2 Dec 2025 at 15:10, Tvrtko Ursulin <[email protected]> wrote:
>
> Valve Steam Deck has a 800x1280 portrait screen installed in a landscape
> orientation. The firmware offers a software rotated 1280x800 mode which
> GRUB can be made to switch to when displaying a boot menu. If this mode
> was selected frame buffer drivers will see this fake mode and fbcon
> rendering will be corrupted.
>
> Lets therefore add a selective quirk inside the current "swap with and
> height" handling, which will detect this exact mode and fix it up back to
> the native one.
>
> This will allow the DRM based frame buffer drivers to detect the correct
> mode and, apply the existing panel orientation quirk, and render the
> console in landscape mode with no corruption.
>
> Signed-off-by: Tvrtko Ursulin <[email protected]>
> Cc: Thomas Zimmermann <[email protected]>
> Cc: Ard Biesheuvel <[email protected]>
> Cc: Melissa Wen <[email protected]>
> Cc: [email protected]
> ---
> drivers/firmware/efi/sysfb_efi.c | 69 +++++++++++++++++++++++++++++---
> 1 file changed, 63 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/firmware/efi/sysfb_efi.c
> b/drivers/firmware/efi/sysfb_efi.c
> index 2dea98395784..6458b3193093 100644
> --- a/drivers/firmware/efi/sysfb_efi.c
> +++ b/drivers/firmware/efi/sysfb_efi.c
> @@ -231,6 +231,27 @@ static const struct dmi_system_id
> efifb_dmi_system_table[] __initconst = {
> {},
> };
>
> +struct efifb_mode_fixup {
> + unsigned int width;
> + unsigned int height;
> + unsigned int pitch;
> +};
> +
> +static const struct efifb_mode_fixup efifb_steamdeck_mode_fixup = {
> + /*
> + * Valve Steam Deck has a 800x1280 portrait screen installed in a
> + * landscape orientation. The firmware offers a software rotated
> + * 1280x800 mode which GRUB can be made to switch to when displaying a
> + * boot menu. If this mode was selected we need to fix it up back to
> the
> + * native mode so frame buffer drivers can correctly probe, detect the
> + * panel orientation quirk based on it, and the console renders with
> no
> + * corruption in the software rotated mode.
> + */
I don't think we need this wall of text. The only material difference
between this quirk and the other ones is that the pitch is not simply
4x the width, but I suspect that in the other cases, it also only
works when using the Blit() interface, and I guess this is what
Windows uses.
> + .width = 1280,
> + .height = 800,
> + .pitch = 3328,
Can we call this 'linelength' instead?
> +};
> +
> /*
> * Some devices have a portrait LCD but advertise a landscape resolution (and
> * pitch). We simply swap width and height for these devices so that we can
> @@ -281,6 +302,24 @@ static const struct dmi_system_id
> efifb_dmi_swap_width_height[] __initconst = {
> DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X91"),
> },
> },
> + {
> + /* Valve Steam Deck (Jupiter) */
> + .matches = {
> + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Valve"),
> + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Jupiter"),
> + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "1"),
> + },
> + .driver_data = (void *)&efifb_steamdeck_mode_fixup,
> + },
> + {
> + /* Valve Steam Deck (Galileo) */
> + .matches = {
> + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Valve"),
> + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galileo"),
> + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "1"),
> + },
> + .driver_data = (void *)&efifb_steamdeck_mode_fixup,
> + },
> {},
> };
>
> @@ -351,16 +390,34 @@ static struct fwnode_handle efifb_fwnode;
>
> __init void sysfb_apply_efi_quirks(void)
> {
> + const struct dmi_system_id *match;
> +
> if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI ||
> !(screen_info.capabilities & VIDEO_CAPABILITY_SKIP_QUIRKS))
> dmi_check_system(efifb_dmi_system_table);
>
> - if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI &&
> - dmi_check_system(efifb_dmi_swap_width_height)) {
> - swap(screen_info.lfb_width, screen_info.lfb_height);
> - screen_info.lfb_linelength = (unsigned)screen_info.lfb_depth *
> - screen_info.lfb_width /
> - BITS_PER_BYTE;
> + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
> + return;
> +
> + for (match = dmi_first_match(efifb_dmi_swap_width_height);
> + match;
> + match = dmi_first_match(match + 1)) {
> + const struct efifb_mode_fixup *data = match->driver_data;
> +
> + if (!data ||
> + (data->width == screen_info.lfb_width &&
> + data->height == screen_info.lfb_height)) {
> + swap(screen_info.lfb_width, screen_info.lfb_height);
> +
> + if (data && data->pitch) {
> + screen_info.lfb_linelength = data->pitch;
> + screen_info.lfb_size = data->pitch *
> data->width;
> + } else {
> + screen_info.lfb_linelength =
> (unsigned)screen_info.lfb_depth *
Please don't use bare 'unsigned' as a type. Is it really needed in the
first place? Is it because it gets promoted to (signed int) otherwise?
> +
> screen_info.lfb_width /
> + BITS_PER_BYTE;
> + }
> + }
I'd prefer to avoid all this boilerplate, and use a callback instead
(but perhaps combined with the previous fix):
static int __init efifb_swap_width_height(const struct dmi_system_id *id)
{
swap(screen_info.lfb_width, screen_info.lfb_height);
screen_info.lfb_linelength = screen_info.lfb_depth *
screen_info.lfb_width / BITS_PER_BYTE;
return 1;
}
and then add the handling of id->data in this patch.
You'll need to add .callback fields to the dmi_system_id array,
though, but this only takes up space that is already statically
allocated anyway.
Then, the code in sysfb_apply_efi_quirks() just becomes
if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI)
dmi_check_system(efifb_dmi_swap_width_height);