At least some DELL monitors (P2715Q) with DPCD_REV 1.2 return corrupted DPCD register values when reading from the 0xF0000- LTTPR range with an AUX transaction block size bigger than 1. The DP standard requires 0 to be returned - as for any other reserved/invalid addresses - but these monitors return the DPCD_REV register value repeated in each byte of the read buffer. This will in turn corrupt the values returned by the LTTPRs between the source and the monitor: LTTPRs must adjust the values they read from the downstream DPRX, for instance right-shift/init the downstream DP_PHY_REPEATER_CNT value. Since the value returned by the monitor's DPRX is non-zero the adjusted values will be corrupt. Reading the LTTPR registers one-by-one instead of reading all of them with a single AUX transfer works around the issue. According to the DP standard's 0xF0000 register description: "LTTPR-related registers at DPCD Addresses F0000h through F02FFh are valid only for DPCD r1.4 (or higher)." While it's unclear if DPCD r1.4 refers to the DPCD_REV or to the LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV register (tickets filed at the VESA site to clarify this haven't been addressed), one possibility is that it's a restriction due to non-compliant monitors described above. Disabling the non-transparent LTTPR mode for all such monitors is not a viable solution: the transparent LTTPR mode has its own issue causing link training failures and this would affect a lot of monitors in use with DPCD_REV < 1.4. Instead this patch works around the problem by reading the LTTPR common and PHY cap registers one-by-one for any monitor with a DPCD_REV < 1.4. The standard requires the DPCD capabilities to be read after the LTTPR common capabilities are read, so re-read the DPCD capabilities after the LTTPR common and PHY caps were read out. v2: - Use for instead of a while loop. (Ville) - Add to code comment the monitor model with the problem.
Unified · Split
| @@ -2400,9 +2400,36 @@ int drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_S · 2399 unmodified lines | ||
| 2400 | 2400 | } |
| 2401 | 2401 | EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs); |
| 2402 | 2402 | |
| 2403 | +static int drm_dp_read_lttpr_regs(struct drm_dp_aux *aux, | |
| 2404 | + const u8 dpcd[DP_RECEIVER_CAP_SIZE], int address, | |
| 2405 | + u8 *buf, int buf_size) | |
| 2406 | +{ | |
| 2407 | + /* | |
| 2408 | + * At least the DELL P2715Q monitor with a DPCD_REV < 0x14 returns | |
| 2409 | + * corrupted values when reading from the 0xF0000- range with a block | |
| 2410 | + * size bigger than 1. | |
| 2411 | + */ | |
| 2412 | + int block_size = dpcd[DP_DPCD_REV] < 0x14 ? 1 : buf_size; | |
| 2413 | + int offset; | |
| 2414 | + int ret; | |
| 2415 | + | |
| 2416 | + for (offset = 0; offset < buf_size; offset += block_size) { | |
| 2417 | + ret = drm_dp_dpcd_read(aux, | |
| 2418 | + address + offset, | |
| 2419 | + &buf[offset], block_size); | |
| 2420 | + if (ret < 0) | |
| 2421 | + return ret; | |
| 2422 | + | |
| 2423 | + WARN_ON(ret != block_size); | |
| 2424 | + } | |
| 2425 | + | |
| 2426 | + return 0; | |
| 2427 | +} | |
| 2428 | + | |
| 2403 | 2429 | /** |
| 2404 | 2430 | * drm_dp_read_lttpr_common_caps - read the LTTPR common capabilities |
| 2405 | 2431 | * @aux: DisplayPort AUX channel |
| 2432 | + * @dpcd: DisplayPort configuration data | |
| 2406 | 2433 | * @caps: buffer to return the capability info in |
| 2407 | 2434 | * |
| 2408 | 2435 | * Read capabilities common to all LTTPRs. |
| @@ -2410,25 +2437,19 @@ EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs); · 1 unmodified line | ||
| 2410 | 2437 | * Returns 0 on success or a negative error code on failure. |
| 2411 | 2438 | */ |
| 2412 | 2439 | int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux, |
| 2440 | + const u8 dpcd[DP_RECEIVER_CAP_SIZE], | |
| 2413 | 2441 | u8 caps[DP_LTTPR_COMMON_CAP_SIZE]) |
| 2414 | 2442 | { |
| 2415 | − int ret; | |
| 2416 | − | |
| 2417 | − ret = drm_dp_dpcd_read(aux, | |
| 2418 | − DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV, | |
| 2419 | − caps, DP_LTTPR_COMMON_CAP_SIZE); | |
| 2420 | − if (ret < 0) | |
| 2421 | − return ret; | |
| 2422 | − | |
| 2423 | − WARN_ON(ret != DP_LTTPR_COMMON_CAP_SIZE); | |
| 2424 | − | |
| 2425 | − return 0; | |
| 2443 | + return drm_dp_read_lttpr_regs(aux, dpcd, | |
| 2444 | + DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV, | |
| 2445 | + caps, DP_LTTPR_COMMON_CAP_SIZE); | |
| 2426 | 2446 | } |
| 2427 | 2447 | EXPORT_SYMBOL(drm_dp_read_lttpr_common_caps); |
| 2428 | 2448 | |
| 2429 | 2449 | /** |
| 2430 | 2450 | * drm_dp_read_lttpr_phy_caps - read the capabilities for a given LTTPR PHY |
| 2431 | 2451 | * @aux: DisplayPort AUX channel |
| 2452 | + * @dpcd: DisplayPort configuration data | |
| 2432 | 2453 | * @dp_phy: LTTPR PHY to read the capabilities for |
| 2433 | 2454 | * @caps: buffer to return the capability info in |
| 2434 | 2455 | * |
| @@ -2437,20 +2458,13 @@ EXPORT_SYMBOL(drm_dp_read_lttpr_common_caps); · 2 unmodified lines | ||
| 2437 | 2458 | * Returns 0 on success or a negative error code on failure. |
| 2438 | 2459 | */ |
| 2439 | 2460 | int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux, |
| 2461 | + const u8 dpcd[DP_RECEIVER_CAP_SIZE], | |
| 2440 | 2462 | enum drm_dp_phy dp_phy, |
| 2441 | 2463 | u8 caps[DP_LTTPR_PHY_CAP_SIZE]) |
| 2442 | 2464 | { |
| 2443 | − int ret; | |
| 2444 | − | |
| 2445 | − ret = drm_dp_dpcd_read(aux, | |
| 2446 | − DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy), | |
| 2447 | − caps, DP_LTTPR_PHY_CAP_SIZE); | |
| 2448 | − if (ret < 0) | |
| 2449 | − return ret; | |
| 2450 | − | |
| 2451 | − WARN_ON(ret != DP_LTTPR_PHY_CAP_SIZE); | |
| 2452 | − | |
| 2453 | − return 0; | |
| 2465 | + return drm_dp_read_lttpr_regs(aux, dpcd, | |
| 2466 | + DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy), | |
| 2467 | + caps, DP_LTTPR_PHY_CAP_SIZE); | |
| 2454 | 2468 | } |
| 2455 | 2469 | EXPORT_SYMBOL(drm_dp_read_lttpr_phy_caps); |
| 2456 | 2470 | |
| @@ -55,6 +55,7 @@ static u8 *intel_dp_lttpr_phy_caps(struct intel_dp *intel_dp, · 54 unmodified lines | ||
| 55 | 55 | } |
| 56 | 56 | |
| 57 | 57 | static void intel_dp_read_lttpr_phy_caps(struct intel_dp *intel_dp, |
| 58 | + const u8 dpcd[DP_RECEIVER_CAP_SIZE], | |
| 58 | 59 | enum drm_dp_phy dp_phy) |
| 59 | 60 | { |
| 60 | 61 | struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; |
| @@ -63,7 +64,7 @@ static void intel_dp_read_lttpr_phy_caps(struct intel_dp *intel_dp, · 2 unmodified lines | ||
| 63 | 64 | |
| 64 | 65 | intel_dp_phy_name(dp_phy, phy_name, sizeof(phy_name)); |
| 65 | 66 | |
| 66 | − if (drm_dp_read_lttpr_phy_caps(&intel_dp->aux, dp_phy, phy_caps) < 0) { | |
| 67 | + if (drm_dp_read_lttpr_phy_caps(&intel_dp->aux, dpcd, dp_phy, phy_caps) < 0) { | |
| 67 | 68 | drm_dbg_kms(&dp_to_i915(intel_dp)->drm, |
| 68 | 69 | "[ENCODER:%d:%s][%s] failed to read the PHY caps\n", |
| 69 | 70 | encoder->base.base.id, encoder->base.name, phy_name); |
| @@ -77,10 +78,12 @@ static void intel_dp_read_lttpr_phy_caps(struct intel_dp *intel_dp, · 7 unmodified lines | ||
| 77 | 78 | phy_caps); |
| 78 | 79 | } |
| 79 | 80 | |
| 80 | −static bool intel_dp_read_lttpr_common_caps(struct intel_dp *intel_dp) | |
| 81 | +static bool intel_dp_read_lttpr_common_caps(struct intel_dp *intel_dp, | |
| 82 | + const u8 dpcd[DP_RECEIVER_CAP_SIZE]) | |
| 81 | 83 | { |
| 82 | 84 | struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; |
| 83 | 85 | struct drm_i915_private *i915 = to_i915(encoder->base.dev); |
| 86 | + int ret; | |
| 84 | 87 | |
| 85 | 88 | if (intel_dp_is_edp(intel_dp)) |
| 86 | 89 | return false; |
| @@ -92,8 +95,9 @@ static bool intel_dp_read_lttpr_common_caps(struct intel_dp *intel_dp) · 5 unmodified lines | ||
| 92 | 95 | if (DISPLAY_VER(i915) < 10 || IS_GEMINILAKE(i915)) |
| 93 | 96 | return false; |
| 94 | 97 | |
| 95 | − if (drm_dp_read_lttpr_common_caps(&intel_dp->aux, | |
| 96 | − intel_dp->lttpr_common_caps) < 0) | |
| 98 | + ret = drm_dp_read_lttpr_common_caps(&intel_dp->aux, dpcd, | |
| 99 | + intel_dp->lttpr_common_caps); | |
| 100 | + if (ret < 0) | |
| 97 | 101 | goto reset_caps; |
| 98 | 102 | |
| 99 | 103 | drm_dbg_kms(&dp_to_i915(intel_dp)->drm, |
| @@ -122,14 +126,14 @@ intel_dp_set_lttpr_transparent_mode(struct intel_dp *intel_dp, bool enable) · 22 unmodified lines | ||
| 122 | 126 | return drm_dp_dpcd_write(&intel_dp->aux, DP_PHY_REPEATER_MODE, &val, 1) == 1; |
| 123 | 127 | } |
| 124 | 128 | |
| 125 | −static int intel_dp_init_lttpr(struct intel_dp *intel_dp) | |
| 129 | +static int intel_dp_init_lttpr(struct intel_dp *intel_dp, const u8 dpcd[DP_RECEIVER_CAP_SIZE]) | |
| 126 | 130 | { |
| 127 | 131 | struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; |
| 128 | 132 | struct drm_i915_private *i915 = to_i915(encoder->base.dev); |
| 129 | 133 | int lttpr_count; |
| 130 | 134 | int i; |
| 131 | 135 | |
| 132 | − if (!intel_dp_read_lttpr_common_caps(intel_dp)) | |
| 136 | + if (!intel_dp_read_lttpr_common_caps(intel_dp, dpcd)) | |
| 133 | 137 | return 0; |
| 134 | 138 | |
| 135 | 139 | lttpr_count = drm_dp_lttpr_count(intel_dp->lttpr_common_caps); |
| @@ -168,7 +172,7 @@ static int intel_dp_init_lttpr(struct intel_dp *intel_dp) · 32 unmodified lines | ||
| 168 | 172 | } |
| 169 | 173 | |
| 170 | 174 | for (i = 0; i < lttpr_count; i++) |
| 171 | − intel_dp_read_lttpr_phy_caps(intel_dp, DP_PHY_LTTPR(i)); | |
| 175 | + intel_dp_read_lttpr_phy_caps(intel_dp, dpcd, DP_PHY_LTTPR(i)); | |
| 172 | 176 | |
| 173 | 177 | return lttpr_count; |
| 174 | 178 | } |
| @@ -193,9 +197,18 @@ static int intel_dp_init_lttpr(struct intel_dp *intel_dp) · 18 unmodified lines | ||
| 193 | 197 | */ |
| 194 | 198 | int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp) |
| 195 | 199 | { |
| 196 | − int lttpr_count = intel_dp_init_lttpr(intel_dp); | |
| 200 | + u8 dpcd[DP_RECEIVER_CAP_SIZE]; | |
| 201 | + int lttpr_count; | |
| 202 | + | |
| 203 | + if (drm_dp_read_dpcd_caps(&intel_dp->aux, dpcd)) | |
| 204 | + return -EIO; | |
| 197 | 205 | |
| 198 | − /* The DPTX shall read the DPRX caps after LTTPR detection. */ | |
| 206 | + lttpr_count = intel_dp_init_lttpr(intel_dp, dpcd); | |
| 207 | + | |
| 208 | + /* | |
| 209 | + * The DPTX shall read the DPRX caps after LTTPR detection, so re-read | |
| 210 | + * it here. | |
| 211 | + */ | |
| 199 | 212 | if (drm_dp_read_dpcd_caps(&intel_dp->aux, intel_dp->dpcd)) { |
| 200 | 213 | intel_dp_reset_lttpr_common_caps(intel_dp); |
| 201 | 214 | return -EIO; |
| @@ -2150,8 +2150,10 @@ bool drm_dp_read_sink_count_cap(struct drm_connector *connector, · 2149 unmodified lines | ||
| 2150 | 2150 | int drm_dp_read_sink_count(struct drm_dp_aux *aux); |
| 2151 | 2151 | |
| 2152 | 2152 | int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux, |
| 2153 | + const u8 dpcd[DP_RECEIVER_CAP_SIZE], | |
| 2153 | 2154 | u8 caps[DP_LTTPR_COMMON_CAP_SIZE]); |
| 2154 | 2155 | int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux, |
| 2156 | + const u8 dpcd[DP_RECEIVER_CAP_SIZE], | |
| 2155 | 2157 | enum drm_dp_phy dp_phy, |
| 2156 | 2158 | u8 caps[DP_LTTPR_PHY_CAP_SIZE]); |
| 2157 | 2159 | int drm_dp_lttpr_count(const u8 cap[DP_LTTPR_COMMON_CAP_SIZE]); |