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);

Reply via email to