Opened 11 months ago

Last modified 11 months ago

#18440 new enhancement

Support multiple displays in EFI framebuffer driver

Reported by: pulkomandy Owned by:
Priority: normal Milestone: Unscheduled
Component: Drivers/Graphics/framebuffer Version: R1/beta4
Keywords: Cc:
Blocked By: Blocking:
Platform: All

Description (last modified by pulkomandy)

It looks like we should be able to support at least "clone" mode relatively easily, and real multiple displays should also be possible.

Documentation is here: https://uefi.org/specs/UEFI/2.9_A/12_Protocols_Console_Support.html#rules-for-pci-agp-devices

We are currently using LocateProtocol to find the graphics protocol (this happens in src/system/boot/platform/efi/video.cpp). According to this: https://stackoverflow.com/questions/57487924/what-is-the-correct-way-to-load-a-uefi-protocol , we only get the first instance when doing this. But there should be one instance of the protocol per combination of displays (one for each display plus one for each pair of displays that have at least one videomode in common).

From there we have two possible options:

  • Locate one of the "display pairs" instances, and use that to set up the hardware in a way that both displays will show the same framebuffer
  • Locate each "single display" instance, and allocate a separate framebuffer to each of them. Then we can have true multiple displays.

This may also help with the intel_extreme driver: if displays are already initialized during boot, the intel driver can skip all the lowlevel parts of modesetting (DDI link training) and use the already configured display, but add the ability to change videomodes (and anything else the intel driver may support later).

Change History (17)

comment:1 by pulkomandy, 11 months ago

Description: modified (diff)

comment:2 by tqh, 11 months ago

Yes, it looks doable, never seen this part before. An interesting point when trying it out that you may need to call SetMode:

"Start() function optionally initializes video frame buffer hardware. The EFI driver has the option of delaying this operation until SetMode() is called."

comment:3 by tqh, 11 months ago

Here is a simple test that I would like people to test in UEFI shell to see what different firmwares and combinations report:

  • Launch UEFI Shell (Leaving out info on how to set it up as it varies on different hw, but it is an UEFI app)
  • Type the command: dh -p GraphicsOutput
  • Check how many entries you get. On a laptop without external display attached I get one entry starting with:

11A: SimpleTextOut EDIActive(EDIActive) GraphicsOutput(GraphicsOutput) ...

comment:4 by tqh, 11 months ago

I get the same result with external display connected (UEFI 2.3.1) so not possible on that hardware.

comment:5 by pulkomandy, 11 months ago

On my laptop I think I get two entries:

286: UGADraw GraphicsOutput(GraphicsOutput) SimpleTextOut
2A2: ConsoleOut SimpleTextOut … (some unknown uuids, edid and graphicsoutput)

I don't know what I can try from there?

I learnt about this possibility from Redox OS release notes, it seems they got it working on some machines at least

Last edited 11 months ago by pulkomandy (previous) (diff)

comment:6 by tqh, 11 months ago

That looks promising. If both have graphicsoutput then you have two handles for graphics. Not sure what can be done in UEFI shell probably use help to see.

Here is how I use UEFI shell:

setmode # to list screen modes
setmode <cols> <rows> # to get more screen space
help -b # list commands paginated
help <interesting command>
<interesting command with args>

Another way is to write small test programs.

Here is a small program when I was investigating EFI: https://github.com/tqh/efi-example/blob/master/gfx_example.c
(The repo has info on how to build. The programs were done before UEFI docs were free, so might do weird things as they were done by trial and error.)

Last edited 11 months ago by tqh (previous) (diff)

comment:7 by pulkomandy, 11 months ago

The code in Redox OS that does this:

The latter two parts are "nothing special". The first one is where the enumeration happens, and it searches for displays that already have an associated framebuffer.

However, it is possible to enumerate displays as well, even if they don't have a framebuffer yet.

On my machine, this looks interesting:

dh -p EDIDDiscovered

lists two displays, but only one has ConsoleOut, SimpleTextOut, EDIDActive and GraphicsOutput.

Can we force both displays to have GraphicsOutput? Maybe with the "EDID override" protocol? https://uefi.org/specs/UEFI/2.9_A/12_Protocols_Console_Support.html#efi-edid-override-protocol-getedid

I'm not too familiar with the vocabulary in the UEFI spec, is the "Start() function" something we implement or something done by the firmware? Is "platform" something we can provide, or something provided by the firmware?

comment:8 by pulkomandy, 11 months ago

I have updated my BIOS and I gained some new features :D

I can now switch active displays at runtime (while in EFI shell) by opening and closing the laptop lid. When the lid is closed, the external displays is enabled. The output of dh -p EDIDDiscovered changes when I unplug the external display and when I close or open the lid. EDIDActive and GraphicsOutput are on the device that has the ACPIAdr associated with the display.

The list is also updated when I unplug or plug the DVI port.

Now all that's left to do is figuring out if we can put GraphicsOutput on both at the same time or on some kind of combined device.

comment:9 by tqh, 11 months ago

QueryMode seems interesting to try, UEFI 2.7 chapter 2.10 (2017) says:

This summary assumes the graphics controller supports a single frame buffer. If a graphics device supports multiple frame buffers, then handles for the frame buffers must be created first, and then the handles for the video output devices can be created as children of the frame buffer handles.

[...]

  • Start() retrieves the one ChildHandle that represents the entire set of active video output devices. If this set is a single video output device, then this ChildHandle will be the same as the one used in the previous step. If this set is a combination of video output devices, then this will not be one of the ChildHandles used in the previous two steps. The EFI_GRAPHICS_OUTPUT_PROTOCOL is installed onto this ChildHandle.
  • The QueryMode() service of the EFI_GRAPHICS_OUTPUT_PROTOCOL returns the set of modes that both the graphics controller and the set of active video output devices all support. If a different set of active video output device is selected, then a different set of modes will likely be produced by QueryMode().

It is also good to check which spec your hw supports, and read the spec for that hw.

comment:10 by tqh, 11 months ago

Start() is not something we call. It is internal and UEFI's job to call. As it was so long ago I worked with it, I can't give more details though.

comment:11 by tqh, 11 months ago

Redox iterates over graphics protocols, looks for framebuffer != 0, and has active edi info.
It can probably be improved by trying to SetMode if framebuffer == 0.

comment:12 by pulkomandy, 11 months ago

The devtree command allows to show the EFI device tree and in it I found that I have a device for "EDP1 + DP5" under my graphics card. If I could somehow enable this one, I guess I would get dual screen output.

Printing it in "dh" (with the added -d and -v option to get more info) shows that it is "Managed by: <None>" whereas the currently active display is managed by the drivers for "Graphics Console Driver", "Console Splitter Driver" and "Platform Console Management Driver". I tried to use "connect" to attach the graphics console driver to that display, but I didn't manage to do that, I can only "connect" to the parent device (the graphics card).

The active display also has a child "Primary Console Output Device" node.

Listing the graphics card with dh -d -v also clearly shows the 3 children devices (one for each display and one for the two displays together), with the first one marked [ACTIVE] but I don't know how I can change that from the shell.

comment:13 by tqh, 11 months ago

That sounds promising. Can you call QueryMode somehow on GraphicsOutputProtocol from the shell? I think setmode is just console. According to specs QueryMode should list combos, and then it would be SetMode for the combo you want.

comment:14 by pulkomandy, 11 months ago

It looks like the selection of which child device is active is made at this step:

Start() retrieves the one ChildHandle that represents the entire set of active video output devices. If this set is a single video output device, then this ChildHandle will be the same as the one used in the previous step. If this set is a combination of video output devices, then this will not be one of the ChildHandles used in the previous two steps. The EFI_GRAPHICS_OUTPUT_PROTOCOL is installed onto this ChildHandle.

Without this, there is no EFI_GRAPHICS_OUTPUT_PROTOCOL on the other displays/configurations, and so I don't think I can use QueryMode to get out of that. First I have to make sure the GOP is attached to the device configuration(s) I want to use.

The "active video output devices" is in turn is determined early in the Start() process:

“If RemainingDevicePath is NULL , then a default set of active video output devices are selected by the driver.

So, do we have some control on that RemainingDevicePath to force selection of some display?

If I understand correctly, we could do that from ConnectController (in EFI boot services). Is that the same thing as the "connect" command in EFI shell? That command does not seem to allow setting a "RemainingDevicePath" option.

comment:15 by pulkomandy, 11 months ago

I found some docs here: https://uefi.org/sites/default/files/resources/UEFI_Plugfest_2011Q4_P7_AMD.pdf

If I understand this correctly, the idea is that the GOP driver is started multiple times: once with NULL, to enumerate the available displays, and then with a specific device path to select that display.

comment:16 by tqh, 11 months ago

The Start() IIRC is the internal init, spec just lists what is expected of certified UEFI drivers. It is only interesting for us because it tells us how UEFI will act. It is never exposed in the public structs.

From my understanding UEFI lists active outputs, creates device paths for those and for the combo of the active outputs. Then it sets the GOP to point to the combo of active outputs. From there it will list modes for outputs of individual or combos. This is how I read it. Most is from my QueryMode post above, but I cut away some of the initial setup info about paths.

You may have a chance to change GOP through display hotkeys, firmware config options, attaching more displays or (a long shot) GOP->SetMode(). We already know enabling CSM limits modes, so if you have CSM enabled, disable that.

comment:17 by tqh, 11 months ago

I think AMD docs says same:

If the RemainingDevicePath assignment changes in Start() and the function returns successfully, the ModeList from GraphicsOutputProtocol will be changed accordingly to reflect the new modes supported by newly assigned video output devices.

[...]

The actual switch to a new display set happens in SetMode(), not after the call to Start (new display devices)

Checking QueryMode() should show you the modelist and what you can switch to. GOP will probably never change, and the "active EDI" would list connected displays.

Note: See TracTickets for help on using tickets.