linux-surface / surface-pro-x

Tracking and meta repository for Surface Pro X support.
83 stars 6 forks source link

Support for SAM USB-C Events #35

Open qzed opened 2 years ago

qzed commented 2 years ago

Quoting myself from #33:

There is indeed some SAM stuff going on in the SurfaceUsbCMuxAcpiFilterDriver.sys using the USC subsystem. There seem to be two methods, specifically. A FilterGetPortNumbers and a FilterSendUSBCAck. In addition to that, there's a PdEventInfoUpdateFromSamToSoc function, which might be some part of a PD event handler by its name.

In fact all three functions are called in the same ProcessUsbCResponse function, first getting the port number(s?), then updating the PD event info, then sending the ACK back. I think what's happening is that SAM might send a PD event when a USB-C source is plugged in. This is then handled by that process-response function.

I think we might need to have to log SAM events on Windows to get a better idea on what's going on with that.

Turns out we can enable and receive events on Linux (using the standard SAM event registry) when I plug in my phone for charging, but I should probably get some USB-C power meter and a powerful enough USB-C charger to see if there are any changes. Also I don't know yet what to send back to SAM via the ACK command.

Interestingly, when I enable SAM events, the port seems to do something (at very least cut power for a brief moment) as my phone screen turns on. Phone says "fast charging" regardless of events being enabled/disabled though...

gus33000 commented 2 years ago

The SurfaceUsbCMuxAcpiFilterDriver.sys filter driver is designed to sit in the middle of ACPI.sys IOCTL events.

In order to enable Display Port for Windows GPU driver normally the UCSI Driver from qualcomm notifies either via an interface or via an ACPI notify function the GPU device that a cable got inserted at this port number in this orientation. And it does so by getting notified from PmicGlink (missing on the Surface Pro X)

Given the Pro X doesn't make use of PmicGlink/BattMngr/PTCC in ADSP.mbn and instead uses SAM this is broken.

So the filter driver serves as getting notifications from SAM and then making qualcomm's driver react by notifying it of an pmicglink event (even though there's no pmicglink), so the GPU driver can setup display port output.

This is one of the functionality of that driver that I gathered, but I'm not sure if it does anything else.

qzed commented 2 years ago

Thanks for your insights!

I've experimented a bit more with various USB devices, but I don't have a powerful enough USB-C charger here, so I probably can't look at power-delivery stuff properly.

Events have 11 bytes and start off with a sequence number. Also, if events are not ACKed, the last event repeats for up to a total of 5 events being sent for one action (all with the same sequence number, which is simply incremented for new events).

So far, I've gathered the following structure. I've tested the display-port part of this with the Surface USB-C hub and its HDMI output, so this might be a bit different for more/other display connections.

struct usbc_event {
    u32 sequence_number;   // sequence number, increases with each new event (might also be smaller than 32 bit)
    u8 port_id;            // ID/index of the USB-C port, top=1, bottom=0
    u8 cable_orientation;  // 0=normal, 1=flipped, 2=disconnect
    u8 event_type;         // 0=disconnect, 1=connect, 3=dp_link_change?
    u8 unknown1;           // 2 on USB-C disconnect, 0 for everything else observed so far
    u8 dp_link_prop;       // some DP link ID?, only set on DP change, 4 or 10 for HDMI port (depending on orientation)
    u8 dp_link_conn;       // 1=connect_display, 0=disconnect_display
    u8 unknown2;           // seems to be zero
};

I guess the event payload might be different for different (sub-)types of events.

@gus33000 You wouldn't happen to know what data specifically needs to be sent via glink (and/or could fill in the gaps of the struct above) and to which specific endpoints? I assume the display-port controller (mdss-mdp) is one?

qzed commented 2 years ago

The ACK command sent back to SAM seems to have the following properties: Target category 0x1b, target ID 0x01, instance ID 0x01, command ID 0x03. Multiple (re-)tries are attempted when the command fails.

The command payload is 12 bytes with the following structure

struct usbc_ack_to_sam {
    u32 sequence_number;  // the sequence number of the event to ACK
    u32 unknown1;         // seems to be port ID cast to u32?
    u32 unknown2;         // some bool (or byte?) returned from PdEventInfoUpdateFromSamToSoc or zero
};

Interestingly, only the LSB of unknown2 seems to be set. So could be that the rest is just padding, but that's fairly unusual for SAM communication.

gus33000 commented 2 years ago

@gus33000 You wouldn't happen to know what data specifically needs to be sent via glink (and/or could fill in the gaps of the struct above) and to which specific endpoints? I assume the display-port controller (mdss-mdp) is one?

Actually the filter doesn't send anything via Glink, the Surface Pro X does not provide the Pmic Glink interface at all (so no USB-C handled by ADSP, no battery management handled by ADSP). But due to how the Qualcomm GPU DirectX driver is built they needed to reuse that UCSI driver to make DisplayPort connections work.

I'm currently working on the same sort of thing for the Surface Duo. The UCSI driver is able to make calls directly to the GPU driver to tell it, I detected a cable in DP AltMode with this pin assignment and this orientation, then the GPU driver enables the DisplayPort connection and sets up MDSS / DP PHY etc. It also tells the GPU driver which port was detected (this ultimately depends on the amount of EXTERNAL connections defined in ACPI for the GPU device) The UCSI driver is basically a shim, listening to the QcPmicGlink8180.sys driver (not present on the Pro X), and that PmicGlink driver sends IOCTLs to the UCSI driver to notify it of changes. The role of that filter driver is to send IOCTLs to the UCSI driver when needed so it can tell the GPU driver a DP connection needs to be setup. (essentially re-implementing missing functionality due to the Qualcomm Pmic Glink stack not being present on this device)

Unfortunately I can't help much with SAM events as my device doesn't use them so I can't really complete the structure for you, but I do actually need to not use PmicGlink (just like on the Surface Pro X) because Android platforms of the time also did not have the USB-C/charging code in ADSP instead implementing it directly in the kernel/HLOS. Previous Qualcomm Windows Platforms (SDM845/Snapdragon 850) did not use PmicGlink either.

In a way it makes sense they would not use ADSP/PmicGlink here, they already have SAM for this.

From what I can see though you also have a Surface UCSI over HID driver (SurfaceUcmUcsiHidClient.sys) that likely uses the same events the filter uses to notify the GPU driver, but for providing standard USB-C UCSI functionality (for the other ports), so I'm not saying it's only used for that purpose, just that PmicGlink is out of reach on the Surface Pro X.

qzed commented 2 years ago

Ah sorry, I misunderstood earlier then (I thought only the Glink endpoint for USB-C was not present). Thanks for clearing that up. @andersson is working on Display Ports for the Lenovo Flex 5G, which uses the same SC8180X SoC. That apparently uses Glink, so I can then hopefully see what needs to be replaced and gauge the remaining fields.

From what I can see though you also have a Surface UCSI over HID driver (SurfaceUcmUcsiHidClient.sys) that likely uses the same events the filter uses to notify the GPU driver, but for providing standard USB-C UCSI functionality (for the other ports), so I'm not saying it's only used for that purpose, just that PmicGlink is out of reach on the Surface Pro X.

As far as I know, that driver is also present on a bunch of other x86 Surface devices as well. The respective device is some SAM-provided HID device, at least according to the Windows Device Manager. Unfortunately, I haven't been able to receive any input events from that, so I'm not sure whether it provides some (e.g. when devices are connected/disconnected), whether the command to enable events is wrong, or whether it just doesn't provide any. Also, the SPX doesn't have any other USB-C ports, so if it plays a similar role as the SAM notifications, it might not be used at all...

Here's the descriptor for that device: t1i0.c.txt. However, I'm not even sure if that's the right device since that reports an id of 045e:0922 (whereas the .inf for the driver has 045e:ff00 and 045e:f900?). But that's the only other HID device I've found via SAM. So could be that it's some virtual device.