From: Mika Westerberg <mika.westerberg@linux.intel.com>
To: Grant Morphett <grant@gmorph.com>
Cc: Andreas Noever <andreas.noever@gmail.com>,
Mika Westerberg <westeri@kernel.org>,
Yehezkel Bernat <YehezkelShB@gmail.com>,
linux-usb@vger.kernel.org
Subject: Re: [PATCH v2] thunderbolt: Enable hidden DP OUT adapter on Dell TB16 dock
Date: Mon, 9 Nov 2026 06:27:17 +0100 [thread overview]
Message-ID: <20261109052717.GH176724@black.igk.intel.com> (raw)
In-Reply-To: <20261106062614.27709-1-grant@gmorph.com>
Hi,
On Fri, Nov 06, 2026 at 05:26:14PM +1100, Grant Morphett wrote:
> The Dell TB16 Thunderbolt 3 dock consists of two Alpine Ridge 4C routers
> in a chain: one in the captive cable module (DROM 00d4:b051 "Dell
> Thunderbolt Cable") and one in the dock itself (DROM 00d4:b054 "Dell
> Thunderbolt Dock"). The dock has four video connectors: mini DisplayPort,
> HDMI, full size DisplayPort and VGA.
>
> Mini DisplayPort and HDMI sit behind an MST hub on DP OUT adapter 8 of
> the dock router and work fine. Full size DisplayPort and VGA are wired
> to DP OUT adapter 8 of the cable router, but the cable router's DROM
> marks that adapter as disabled. The software connection manager
> therefore never initializes it, never tunnels DisplayPort to it, and a
> monitor on the full size DisplayPort or VGA connector stays dark. Only
> two external displays can be used with the dock, while all of them work
> with the firmware connection manager (and under Windows).
>
> The adapter is real: its adapter config space reports type DP/HDMI OUT
> (0xe0102), it raises hot plug events when a monitor is attached to the
> DisplayPort or VGA connector, and once it is enabled the DP tunnel to it
> is established and the display comes up. The same DROM also fails its
> CRC32 check (expected 0xaf438340, got 0xaf4383c0), so the port
> disabled bit is likely just wrong rather than intentional:
>
> 1: DROM data CRC32 mismatch (expected: 0xaf438340, got: 0xaf4383c0), continuing
> 1:8: disabled by eeprom
> acking hot plug event on 1:8
>
> Add a quirk for the cable router that re-enables adapter 8 if its
> config space confirms it is a DP OUT adapter. It runs in a new
> tb_check_drom_quirks() call right after the DROM has been parsed, so
> the regular port initialisation in tb_switch_add() picks the adapter up
> like any other enabled port.
>
> With the quirk applied, both DP tunnels come up at boot and all three
> external monitors work:
>
> 1:8: enabling DP OUT adapter disabled in DROM
> 1:8: DP OUT available
> 0:5 <-> 1:8 (DP): activating
> 301:8: DP OUT available
> 0:6 <-> 301:8 (DP): activating
>
> Tested on a Lenovo ThinkPad X1 Yoga Gen 8 (Raptor Lake, USB4 host
> 8087:a76d) with a TB16 at dock NVM 27.0 / cable NVM 16.0, driving three
> 1920x1080 monitors on mini DisplayPort, HDMI and full size DisplayPort.
>
> v2:
> - Add a new tb_check_drom_quirks() call right after DROM parsing, so
> the quirk corrects the wrong DROM entry before the ports are
> initialized, instead of making tb_init_port() non-static and calling
> it from the quirk (Mika Westerberg).
This should go under the '---'
>
> Assisted-by: LLM
This is not needed.
> Signed-off-by: Grant Morphett <grant@gmorph.com>
> ---
> ---
> drivers/thunderbolt/quirks.c | 62 ++++++++++++++++++++++++++++++++++++
> drivers/thunderbolt/switch.c | 1 +
> drivers/thunderbolt/tb.h | 1 +
> 3 files changed, 64 insertions(+)
>
> diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c
> index 2722918..bba10df 100644
> --- a/drivers/thunderbolt/quirks.c
> +++ b/drivers/thunderbolt/quirks.c
> @@ -65,6 +65,34 @@ static void quirk_stuck_pending(struct tb_switch *sw)
> }
> }
>
> +/*
> + * The DROM of the Dell TB16 cable module marks adapter 8 as disabled
> + * even though it is the DP OUT adapter wired to the dock's full size
> + * DisplayPort and VGA connectors. Correct the DROM entry so that the
> + * software connection manager initializes the adapter and can tunnel
> + * DisplayPort to it.
> + */
> +static void quirk_dell_tb16_dp_out(struct tb_switch *sw)
> +{
> + struct tb_port *port;
> + u32 type;
> +
> + if (sw->config.max_port_number < 8)
> + return;
> +
> + port = &sw->ports[8];
> + if (!port->disabled)
> + return;
> +
> + if (tb_port_read(port, &type, TB_CFG_PORT, 2, 1))
> + return;
> + if ((type & 0xffffff) != TB_TYPE_DP_HDMI_OUT)
> + return;
> +
> + port->disabled = false;
> + tb_port_info(port, "enabling DP OUT adapter disabled in DROM\n");
> +}
> +
> struct tb_quirk {
> u16 hw_vendor_id;
> u16 hw_device_id;
> @@ -163,3 +191,37 @@ void tb_check_quirks(struct tb_switch *sw)
> q->hook(sw);
> }
> }
> +
> +static const struct tb_quirk tb_drom_quirks[] = {
> + /* Dell TB16 cable module DROM hides the second DP OUT adapter */
> + { 0x8086, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE, 0x00d4, 0xb051,
> + quirk_dell_tb16_dp_out },
> +};
> +
> +/**
> + * tb_check_drom_quirks() - Check for quirks to apply after DROM read
> + * @sw: Thunderbolt switch
> + *
> + * Apply any quirks that correct wrong DROM entries before the ports are
> + * initialized.
> + */
> +void tb_check_drom_quirks(struct tb_switch *sw)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(tb_drom_quirks); i++) {
> + const struct tb_quirk *q = &tb_drom_quirks[i];
> +
> + if (q->hw_vendor_id && q->hw_vendor_id != sw->config.vendor_id)
> + continue;
> + if (q->hw_device_id && q->hw_device_id != sw->config.device_id)
> + continue;
> + if (q->vendor && q->vendor != sw->vendor)
> + continue;
> + if (q->device && q->device != sw->device)
> + continue;
> +
> + tb_sw_dbg(sw, "running %ps\n", q->hook);
> + q->hook(sw);
Can you make the matching as separate function (tb_apply_quirks()) that is
given the quirk table and then make this and the tb_check_quirks() call
that one? This avoids the duplication.
> + }
> +}
> diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
> index d22d8db..6268ed6 100644
> --- a/drivers/thunderbolt/switch.c
> +++ b/drivers/thunderbolt/switch.c
> @@ -3343,6 +3343,7 @@ int tb_switch_add(struct tb_switch *sw)
> ret = tb_drom_read(sw);
> if (ret)
> dev_warn(&sw->dev, "reading DROM failed: %d\n", ret);
> + tb_check_drom_quirks(sw);
> tb_sw_dbg(sw, "uid: %#llx\n", sw->uid);
>
> ret = tb_switch_set_uuid(sw);
> diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
> index e868457..7d04376 100644
> --- a/drivers/thunderbolt/tb.h
> +++ b/drivers/thunderbolt/tb.h
> @@ -1510,6 +1510,7 @@ static inline bool usb4_port_device_is_offline(const struct usb4_port *usb4)
> }
>
> void tb_check_quirks(struct tb_switch *sw);
> +void tb_check_drom_quirks(struct tb_switch *sw);
>
> #ifdef CONFIG_ACPI
> bool tb_acpi_add_links(struct tb_nhi *nhi);
> --
> 2.53.0
prev parent reply other threads:[~2026-11-09 5:27 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-01 21:23 [PATCH] thunderbolt: Enable hidden DP OUT adapter on Dell TB16 dock Grant Morphett
2026-11-01 21:29 ` sashiko-bot
2026-11-04 12:00 ` Mika Westerberg
2026-11-06 6:26 ` [PATCH v2] " Grant Morphett
2026-11-06 6:33 ` sashiko-bot
2026-11-09 5:27 ` Mika Westerberg [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261109052717.GH176724@black.igk.intel.com \
--to=mika.westerberg@linux.intel.com \
--cc=YehezkelShB@gmail.com \
--cc=andreas.noever@gmail.com \
--cc=grant@gmorph.com \
--cc=linux-usb@vger.kernel.org \
--cc=westeri@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox