[PATCH AUTOSEL 6.19-6.12] drm/amd/display: Add USB-C DP Alt Mode lane limitation in DCN32

Sasha Levin
2026-02-14T01:04:06Z

From: LinCheng Ku <lincheng.ku@amd.com>

[ Upstream commit cea573a8e1ed83840a2173d153dd68e172849d44 ]

[Why]
USB-C DisplayPort Alt Mode with concurrent USB data needs lane count
limitation to prevent incorrect 4-lane DP configuration when only 2 lanes
are available due to hardware lane sharing between DP and USB3.

[How]
Query DMUB for Alt Mode status (is_dp_alt_disable, is_usb, is_dp4) in
dcn32_link_encoder_get_max_link_cap() and cap DP to 2 lanes when USB is
active on USB-C port. Added inline documentation explaining the USB-C
lane sharing constraint.

Reviewed-by: PeiChen Huang <peichen.huang@amd.com>
Signed-off-by: LinCheng Ku <lincheng.ku@amd.com>
Signed-off-by: Chenyu Chen <chen-yu.chen@amd.com>
Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

Now I have a comprehensive picture. Let me summarize my analysis.

## Detailed Analysis

### 1. Commit Message Analysis

The commit adds a missing `is_dp_alt_disable == 0` check to
`dcn32_link_encoder_get_max_link_cap()`. The [Why] section explains that
USB-C DP Alt Mode with concurrent USB data needs lane count limitation,
but the key fix is actually **preventing incorrect lane limiting when DP
Alt Mode is NOT active** (i.e., on native DP ports).

### 2. Code Change Analysis — The Bug

The actual code change is minimal — adding one condition
`cmd.query_dp_alt.data.is_dp_alt_disable == 0` to an existing `if`
statement, plus inline documentation.

**Before (buggy):**

```191:193:drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encod
er.c
if (cmd.query_dp_alt.data.is_usb &&
cmd.query_dp_alt.data.is_dp4 == 0)
link_settings->lane_count = MIN(LANE_COUNT_TWO,
link_settings->lane_count);
```

**After (fixed):** Adds `is_dp_alt_disable == 0 &&` to only limit lanes
when DP Alt Mode is actually active.

### 3. Root Cause — Regression in `7d1e9d0369e4d`

Commit `7d1e9d0369e4d` ("drm/amd/display: Check DP Alt mode DPCS state
via DMUB", merged in v6.9) refactored DCN32 to query the DMUB
microcontroller instead of reading hardware registers. Before this
refactor, the old code correctly guarded the lane-limiting logic behind
an `is_in_alt_mode()` call:

```c
// OLD (correct):
if (enc->funcs->is_in_alt_mode && enc->funcs->is_in_alt_mode(enc)) {
REG_GET(RDPCSPIPE_PHY_CNTL6, RDPCS_PHY_DPALT_DP4,
&is_in_usb_c_dp4_mode);
if (!is_in_usb_c_dp4_mode)
link_settings->lane_count = MIN(LANE_COUNT_TWO,
link_settings->lane_count);
}
```

The refactor to DMUB queries **dropped** the equivalent alt-mode check
(`is_dp_alt_disable == 0`), while the DCN31 version at
`dcn31_dio_link_encoder.c:656` correctly includes it:

```656:659:drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encod
er.c
if (cmd.query_dp_alt.data.is_dp_alt_disable == 0 &&
cmd.query_dp_alt.data.is_usb &&
cmd.query_dp_alt.data.is_dp4 == 0)
link_settings->lane_count = MIN(LANE_COUNT_TWO,
link_settings->lane_count);
```

This is exactly the same bug pattern previously fixed for DCN20 in
commit `8ccf0e20769d9` ("determine USB C DP2 mode only when USB DP Alt
is enabled") which stated: *"When display is connected with a native DP
port, DP2 mode register value is a don't care. Driver mistakenly reduce
max supported lane count to 2 lane based on the don't care value."*

### 4. User Impact

Without this fix, on DCN32 hardware (AMD Radeon RX 7000 series / RDNA3
GPUs), when a display is connected via a **native DP port** (not USB-C),
the `is_usb` and `is_dp4` DMUB register values are "don't care" garbage.
If `is_usb` happens to be set and `is_dp4` is 0, the driver incorrectly
caps the lane count to 2 (instead of 4), halving the available
bandwidth. This results in:
- Reduced maximum resolution
- Reduced refresh rate
- The user cannot get their display's full capabilities

This affects a very popular GPU family (RDNA3) and is triggered on every
mode set.

### 5. Scope and Risk

- **Size**: Adds 1 condition to an existing if-statement + 5 lines of
comments. Extremely small.
- **Risk**: Very low. This change only makes the lane-limiting logic
**more conservative** (requires an additional condition to be true
before limiting). If the condition was already true before, behavior
is unchanged. If it was false (native DP), it prevents the incorrect
lane limiting.
- **Dependencies**: None. The `is_dp_alt_disable` field is already used
in `dcn32_link_encoder_is_in_alt_mode()` in the same file (line 178),
and the struct definition exists in `dmub_cmd.h`.
- **Backport path**: The buggy commit `7d1e9d0369e4d` was first included
in v6.9. Note that the file was moved from `dc/dcn32/` to
`dc/dio/dcn32/` in a later refactor (`2d62bb450ed18`, v6.15 cycle), so
for older stable trees (6.12.y, 6.11.y, 6.10.y, 6.9.y) the path would
need adjusting, but the code change is trivially applicable.

### 6. Stability Indicators

- **Reviewed-by: PeiChen Huang** (AMD display engineer)
- **Tested-by: Daniel Wheeler** (AMD QA)
- **Signed-off-by: Alex Deucher** (AMD display maintainer)
- The fix aligns DCN32 with the already-correct DCN31 implementation,
providing strong confidence in correctness.

### 7. Classification

This is a **hardware bug fix / regression fix** — it corrects incorrect
DP lane count limiting on native DP ports for DCN32 hardware, a
regression introduced when the code was refactored to use DMUB queries.
It does NOT add new features, APIs, or change userspace behavior. It
restores the correct pre-refactor behavior.

### Conclusion

This commit fixes a real, user-visible regression (reduced display
resolution/refresh rate) on widely-used AMD RDNA3 GPUs. The fix is small
(1 condition added), obviously correct (matches DCN31 and pre-refactor
DCN32 behavior), well-reviewed, well-tested, and has zero risk of
introducing new issues. It meets all stable kernel criteria.

**YES**

.../display/dc/dio/dcn32/dcn32_dio_link_encoder.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.c
index 06907e8a4eda1..ddc736af776c9 100644
--- a/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/dio/dcn32/dcn32_dio_link_encoder.c
@@ -188,9 +188,18 @@ void dcn32_link_encoder_get_max_link_cap(struct link_encoder *enc,
if (!query_dp_alt_from_dmub(enc, &cmd))
return;

- if (cmd.query_dp_alt.data.is_usb &&
- cmd.query_dp_alt.data.is_dp4 == 0)
- link_settings->lane_count = MIN(LANE_COUNT_TWO, link_settings->lane_count);
+ /*
+ * USB-C DisplayPort Alt Mode lane count limitation logic:
+ * When USB and DP share the same USB-C connector, hardware must allocate
+ * some lanes for USB data, limiting DP to maximum 2 lanes instead of 4.
+ * This ensures USB functionality remains available while DP is active.
+ */
+ if (cmd.query_dp_alt.data.is_dp_alt_disable == 0 &&
+ cmd.query_dp_alt.data.is_usb &&
+ cmd.query_dp_alt.data.is_dp4 == 0) {
+ link_settings->lane_count =
+ MIN(LANE_COUNT_TWO, link_settings->lane_count);
+ }
}

--
2.51.0