Linux USB
 help / color / mirror / Atom feed
* [BUG] ucsi_acpi: unstable GET_ALTERNATE_MODES(SOP) response prevents DP Alt Mode binding on ASUS K3605ZV
@ 2026-09-07 17:18 Ismail Tarim
  2026-09-09 13:59 ` Heikki Krogerus
  0 siblings, 1 reply; 2+ messages in thread
From: Ismail Tarim @ 2026-09-07 17:18 UTC (permalink / raw)
  To: linux-usb; +Cc: Heikki Krogerus

Hi,

On an ASUS Vivobook K3605ZV (Alder Lake-P, UCSI over ACPI), USB-C
DisplayPort Alt Mode is never established on hot-plug, while attaching
the cable before boot gives a working display.

Repeated GET_ALTERNATE_MODES queries for the SOP recipient return two
different six-byte payloads in exact alternation. Under the record
layout Linux expects, these produce two distinct driver-visible failure
modes, but they may well have a single underlying ordering or
serialisation cause - I describe an alternative reading of the same
bytes below and I cannot currently decide between them.

The payload instability is reproducible through UCSI debugfs without a
patched kernel. I used a kretprobe on ucsi_send_command() only to
confirm that the CCI data length was 6 on every call.


System
------

  Machine        ASUSTeK Vivobook_ASUSLaptop K3605ZV_K3605ZV
  BIOS           K3605ZV.317, 05/12/2026
  CPU            Intel Core i7-12650H (Alder Lake-P)
  Kernel         7.2.3 (Arch), CONFIG_TYPEC_UCSI=m, CONFIG_UCSI_ACPI=m,
                 CONFIG_TYPEC_DP_ALTMODE=m
  UCSI transport ucsi_acpi, platform device USBC000:00
  Type-C port    port0, data role host, bound to usb3-port3,
                 usb2-port1 and usb4_port1 (connector_ops [thunderbolt])
  Display sink   Gigabyte M27UA (3840x2160). Connected with a cable
                 whose DisplayPort end plugs into the monitor and whose
                 USB-C end plugs into the laptop, so the laptop is the
                 DP source over USB-C.
  External DRM   all on the Intel iGPU (card1: DP-1, DP-2, HDMI-A-1)

Loaded Type-C modules: typec, typec_ucsi, ucsi_acpi, typec_displayport,
roles, thunderbolt. I can supply GET_CAPABILITY output or anything else
on request.


Symptom
-------

Hot-plugging the USB-C cable never brings up DP-1: the connector stays
"disconnected" with a 0-byte EDID and typec_displayport binds to
nothing.

In a controlled test, attaching the same cable before boot resulted in
a working 3840x2160 display. Separately, 71 retained boot journals
report DP-1 connected; those historical records do not by themselves
establish the attachment sequence for each boot.


Observation 1: the SOP payload alternates between two values
------------------------------------------------------------

Issuing the same GET_ALTERNATE_MODES(recipient=SOP, connector=1,
offset=0) repeatedly through /sys/kernel/debug/usb/ucsi/USBC000:00/
returns two payloads in strict alternation. 40 reads per inter-call
spacing, 120 reads in one continuous run, zero deviation:

  back-to-back : ABABABABABABABABABABABABABABABABABABABAB
  200 ms apart : ABABABABABABABABABABABABABABABABABABABAB
  1 s apart    : ABABABABABABABABABABABABABABABABABABABAB

  A = 01 ff 00 00 01 ff
  B = 05 04 00 00 01 ff

Only bytes 0..1 differ; bytes 2..5 are identical. Every one of the 120
calls returned a CCI data length of 6.

The corresponding query with recipient=CON returned one identical
six-byte response in 40 reads. So the alternation was not observed in
this same-sized control, though that does not by itself locate the
fault between the PPM and the ACPI transport.

Limitation of the spacing test: the response stays locked to
transaction parity across the tested 0-1 s inter-call intervals, which
makes a simple wall-clock or probabilistic race less likely. It does
NOT distinguish PPM payload state from a deterministic
completion/ACK/MESSAGE_IN ordering problem, because those delays occur
after ucsi_send_command() has already read MESSAGE_IN. Testing that
boundary would need a delay between command completion and
read_message_in(), or two reads of MESSAGE_IN after a single
completion. Also, the three blocks were consecutive and of even length,
so they are effectively one 120-call sequence rather than three
independent phase trials.

The second value is not constant across attachments: in an attach
session five minutes earlier it was 0a 00 00 00 01 ff instead of
05 04 00 00 01 ff. It was stable within each session.


Observation 2: two readings of the same bytes
---------------------------------------------

Parsed as the packed record Linux uses,
struct ucsi_altmode { u16 svid; u32 mid; } __packed :

  A -> svid=0xff01  mid=0xff010000
  B -> svid=0x0405  mid=0xff010000
  (earlier session) -> svid=0x000a  mid=0xff010000

so the SVID alternates and the MID is constant but useless as a DP
capability VDO.

Parsed with the two members in the opposite order, { u32 mid; u16 svid }:

  A -> mid=0x0000ff01  svid=0xff01
  B -> mid=0x00000405  svid=0xff01
  (earlier session) -> mid=0x0000000a  svid=0xff01

Under that reading the SVID is 0xff01 in every payload I have ever
captured, and it is the MID that alternates. 0x00000405 decodes as a
plausible DP sink capability: UFP_D capable, DP v1.3 signalling, UFP_D
pin assignment C. 0x0000ff01 looks instead like the SVID duplicated
into the MID slot.

I cannot decide between these. The CON record is unambiguously in the
Linux/UCSI order (reading it reversed yields svid=0x0000, i.e. an
end-of-list marker, which is nonsense given it is the DP mode), so the
producer is at least inconsistent between recipients under the reversed
hypothesis. Bytes past the CCI length of 6 are not part of the response
and cannot disambiguate it.


Effect on the driver
--------------------

ucsi_register_altmodes() registers whichever payload it happened to
read, and the SOP early-out

  if (recipient == UCSI_RECIPIENT_SOP && con->partner_altmode[0])
          return 0;

then prevents any further read for the lifetime of the partner, so the
first result is latched.

If the registered SVID is not 0xff01, the Type-C bus match against
typec_displayport's typec:idFF01 alias never succeeds and
dp_altmode_probe() is not reached. This is the state after a hot-plug,
captured before I issued any debugfs command, so it is not an artefact
of my probing:

  /sys/class/typec/port0-partner/port0-partner.0/svid   = 0405
  /sys/class/typec/port0-partner/port0-partner.0/vdo    = 0xff010000
  /sys/class/typec/port0-partner/port0-partner.0/driver : absent
  /sys/class/drm/card1-DP-1/status                      : disconnected

If instead the payload with svid=0xff01 is registered, the driver
matches but dp_altmode_probe() rejects it. With port0.0/vdo =
0x001c1c46 and the partner VDO as Linux parses it (0xff010000, whose
raw DFP_D pin-assignment field bits 15:8 = 0x00 and raw UFP_D
pin-assignment field bits 23:16 = 0x01, with the receptacle bit clear,
giving Linux's helper results UFP_D=0x00 and DFP_D=0x01):

  DP_CAP_PIN_ASSIGN_DFP_D(0x001c1c46) & DP_CAP_PIN_ASSIGN_UFP_D(0xff010000)
        = 0x1c & 0x00 = 0
  DP_CAP_PIN_ASSIGN_UFP_D(0x001c1c46) & DP_CAP_PIN_ASSIGN_DFP_D(0xff010000)
        = 0x1c & 0x01 = 0
  -> -ENODEV

So neither of the two alternating payloads leads to a bound DP alt
mode.

For reference, the raw CON response to the same command is

  01 ff 06 1c 00 00        len=6

and ucsi_register_displayport() turns mid=0x00001c06 into
0x1c06 | DP_CAP_RECEPTACLE | 0x1c<<8 | 0x1c<<16 = 0x001c1c46, which is
what port0.0/vdo reads. That sanity-checks my byte decoding and shows
how the current kernel transforms the CON record; it does not establish
that the SOP producer uses the same member ordering.


Other observations
------------------

  * The PPM returned one six-byte record at offset 0 and zero length at
    offsets 1 and 2.

  * After a hot-plug, GET_CURRENT_CAM(connector=1) returned 0x01 rather
    than the 0xff "none" sentinel, while GET_CAM_SUPPORTED returned
    0x01. Those two are mutually inconsistent under Linux's zero-based
    index interpretation. I mention it only as a further sign that this
    PPM's alt-mode reporting is unreliable.

  * No intel_pmc_mux device can bind on this firmware: neither INTC105C
    nor the Alder Lake IOM INTC1079 is present in ACPI, and
    /sys/class/typec_mux is empty. I therefore do not know whether
    correcting the alt-mode registration alone would be enough to light
    the display, or whether the PPM would also have to be driven into
    the CAM.


Regression status: not established
----------------------------------

The user-visible history is that hot-plug used to work, so I looked for
a kernel change.

The relevant struct ucsi_altmode layout and the dp_altmode_probe()
rejection logic are unchanged across every kernel this machine has run
(v7.0.5 through v7.2.3), and I found no direct change to the UCSI
alt-mode read path between 7.1.9 and 7.2.3 - the changes there are the
msg_out/write_message_out plumbing, an unregister refactor and lockdep
annotations.

This is only a source audit. Indirect timing or ordering changes
elsewhere, and firmware state, remain possible. I have not yet run the
same unplugged-boot-then-hot-plug procedure under an older kernel, so I
am not claiming a regression and I have not copied
regressions@lists.linux.dev.


Reproduction
------------

With the cable attached, as root:

  D=/sys/kernel/debug/usb/ucsi/USBC000:00
  for i in $(seq 1 10); do
      echo 0x101000c > $D/command   # GET_ALTERNATE_MODES, SOP, con 1, off 0
      cat $D/response
  done

The responses alternate. Substituting 0x100000c (recipient CON) gives a
stable response. The debugfs "status" file only records negative
errors, so the positive return value (the data length) is not exposed
there; I used a kretprobe on ucsi_send_command() for that.


Questions
---------

  1. Has an SOP-only member ordering, or an alternating offset-0
     response, been seen from other UCSI PPMs? Is there a reading of
     these six bytes that I have missed?

  2. Which capture would best distinguish PPM payload corruption from
     an ACPI completion/MESSAGE_IN ordering problem: a debug patch that
     delays or repeats read_message_in() after a single completion, a
     PD Discover Modes trace from an analyser, or data from the working
     boot-attached case?

  3. If the correct partner record can be established independently,
     would a DMI-scoped fixup in ucsi_acpi's sync_control be the right
     layer, or should this stay a firmware-only issue?

I have the machine available and am happy to run debug patches or
further captures.

Thanks,
Ismail Tarim

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-09-09 13:59 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-07 17:18 [BUG] ucsi_acpi: unstable GET_ALTERNATE_MODES(SOP) response prevents DP Alt Mode binding on ASUS K3605ZV Ismail Tarim
2026-09-09 13:59 ` Heikki Krogerus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox