History

drm/i915: Add a DP1.2 compatible way to read LTTPR capabilities

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.

Imre Deak committed 4 years ago

Commit 657586e1 parent 7e4e0e53 files · +60 −31Browse files

Unified · Split

3 files
  1. drivers/gpu/drm/dp/drm_dp.c +36 −22
  2. drivers/gpu/drm/i915/display/intel_dp_link_training.c +22 −9
  3. include/drm/dp/drm_dp_helper.h +2 −0
drivers/gpu/drm/dp/drm_dp.c+36−22
@@ -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}
2401EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs);2401EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs);
2402 2402
2403static 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 * drm_dp_read_lttpr_common_caps - read the LTTPR common capabilities2430 * drm_dp_read_lttpr_common_caps - read the LTTPR common capabilities
2405 * @aux: DisplayPort AUX channel2431 * @aux: DisplayPort AUX channel
2432 * @dpcd: DisplayPort configuration data
2406 * @caps: buffer to return the capability info in2433 * @caps: buffer to return the capability info in
2407 *2434 *
2408 * Read capabilities common to all LTTPRs.2435 * Read capabilities common to all LTTPRs.
@@ -2410,25 +2437,19 @@ EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs); · 1 unmodified line
2410 * Returns 0 on success or a negative error code on failure.2437 * Returns 0 on success or a negative error code on failure.
2411 */2438 */
2412int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux,2439int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux,
2440 const u8 dpcd[DP_RECEIVER_CAP_SIZE],
2413 u8 caps[DP_LTTPR_COMMON_CAP_SIZE])2441 u8 caps[DP_LTTPR_COMMON_CAP_SIZE])
2414{2442{
2415 int ret;2443 return drm_dp_read_lttpr_regs(aux, dpcd,
2416 2444 DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV,
2417 ret = drm_dp_dpcd_read(aux,2445 caps, DP_LTTPR_COMMON_CAP_SIZE);
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;
2426}2446}
2427EXPORT_SYMBOL(drm_dp_read_lttpr_common_caps);2447EXPORT_SYMBOL(drm_dp_read_lttpr_common_caps);
2428 2448
2429/**2449/**
2430 * drm_dp_read_lttpr_phy_caps - read the capabilities for a given LTTPR PHY2450 * drm_dp_read_lttpr_phy_caps - read the capabilities for a given LTTPR PHY
2431 * @aux: DisplayPort AUX channel2451 * @aux: DisplayPort AUX channel
2452 * @dpcd: DisplayPort configuration data
2432 * @dp_phy: LTTPR PHY to read the capabilities for2453 * @dp_phy: LTTPR PHY to read the capabilities for
2433 * @caps: buffer to return the capability info in2454 * @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 * Returns 0 on success or a negative error code on failure.2458 * Returns 0 on success or a negative error code on failure.
2438 */2459 */
2439int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux,2460int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux,
2461 const u8 dpcd[DP_RECEIVER_CAP_SIZE],
2440 enum drm_dp_phy dp_phy,2462 enum drm_dp_phy dp_phy,
2441 u8 caps[DP_LTTPR_PHY_CAP_SIZE])2463 u8 caps[DP_LTTPR_PHY_CAP_SIZE])
2442{2464{
2443 int ret;2465 return drm_dp_read_lttpr_regs(aux, dpcd,
2444 2466 DP_TRAINING_AUX_RD_INTERVAL_PHY_REPEATER(dp_phy),
2445 ret = drm_dp_dpcd_read(aux,2467 caps, DP_LTTPR_PHY_CAP_SIZE);
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;
2454}2468}
2455EXPORT_SYMBOL(drm_dp_read_lttpr_phy_caps);2469EXPORT_SYMBOL(drm_dp_read_lttpr_phy_caps);
2456 2470
drivers/gpu/drm/i915/display/intel_dp_link_training.c+22−9
@@ -55,6 +55,7 @@ static u8 *intel_dp_lttpr_phy_caps(struct intel_dp *intel_dp, · 54 unmodified lines
55}55}
56 56
57static void intel_dp_read_lttpr_phy_caps(struct intel_dp *intel_dp,57static void intel_dp_read_lttpr_phy_caps(struct intel_dp *intel_dp,
58 const u8 dpcd[DP_RECEIVER_CAP_SIZE],
58 enum drm_dp_phy dp_phy)59 enum drm_dp_phy dp_phy)
59{60{
60 struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;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 intel_dp_phy_name(dp_phy, phy_name, sizeof(phy_name));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 drm_dbg_kms(&dp_to_i915(intel_dp)->drm,68 drm_dbg_kms(&dp_to_i915(intel_dp)->drm,
68 "[ENCODER:%d:%s][%s] failed to read the PHY caps\n",69 "[ENCODER:%d:%s][%s] failed to read the PHY caps\n",
69 encoder->base.base.id, encoder->base.name, phy_name);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 phy_caps);78 phy_caps);
78}79}
79 80
80static bool intel_dp_read_lttpr_common_caps(struct intel_dp *intel_dp)81static bool intel_dp_read_lttpr_common_caps(struct intel_dp *intel_dp,
82 const u8 dpcd[DP_RECEIVER_CAP_SIZE])
81{83{
82 struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;84 struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
83 struct drm_i915_private *i915 = to_i915(encoder->base.dev);85 struct drm_i915_private *i915 = to_i915(encoder->base.dev);
86 int ret;
84 87
85 if (intel_dp_is_edp(intel_dp))88 if (intel_dp_is_edp(intel_dp))
86 return false;89 return false;
@@ -92,8 +95,9 @@ static bool intel_dp_read_lttpr_common_caps(struct intel_dp *intel_dp) · 5 unmodified lines
92 if (DISPLAY_VER(i915) < 10 || IS_GEMINILAKE(i915))95 if (DISPLAY_VER(i915) < 10 || IS_GEMINILAKE(i915))
93 return false;96 return false;
94 97
95 if (drm_dp_read_lttpr_common_caps(&intel_dp->aux,98 ret = drm_dp_read_lttpr_common_caps(&intel_dp->aux, dpcd,
96 intel_dp->lttpr_common_caps) < 0)99 intel_dp->lttpr_common_caps);
100 if (ret < 0)
97 goto reset_caps;101 goto reset_caps;
98 102
99 drm_dbg_kms(&dp_to_i915(intel_dp)->drm,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 return drm_dp_dpcd_write(&intel_dp->aux, DP_PHY_REPEATER_MODE, &val, 1) == 1;126 return drm_dp_dpcd_write(&intel_dp->aux, DP_PHY_REPEATER_MODE, &val, 1) == 1;
123}127}
124 128
125static int intel_dp_init_lttpr(struct intel_dp *intel_dp)129static int intel_dp_init_lttpr(struct intel_dp *intel_dp, const u8 dpcd[DP_RECEIVER_CAP_SIZE])
126{130{
127 struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;131 struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
128 struct drm_i915_private *i915 = to_i915(encoder->base.dev);132 struct drm_i915_private *i915 = to_i915(encoder->base.dev);
129 int lttpr_count;133 int lttpr_count;
130 int i;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 return 0;137 return 0;
134 138
135 lttpr_count = drm_dp_lttpr_count(intel_dp->lttpr_common_caps);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 for (i = 0; i < lttpr_count; i++)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 return lttpr_count;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 */
194int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp)198int 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 if (drm_dp_read_dpcd_caps(&intel_dp->aux, intel_dp->dpcd)) {212 if (drm_dp_read_dpcd_caps(&intel_dp->aux, intel_dp->dpcd)) {
200 intel_dp_reset_lttpr_common_caps(intel_dp);213 intel_dp_reset_lttpr_common_caps(intel_dp);
201 return -EIO;214 return -EIO;
include/drm/dp/drm_dp_helper.h+2−0
@@ -2150,8 +2150,10 @@ bool drm_dp_read_sink_count_cap(struct drm_connector *connector, · 2149 unmodified lines
2150int drm_dp_read_sink_count(struct drm_dp_aux *aux);2150int drm_dp_read_sink_count(struct drm_dp_aux *aux);
2151 2151
2152int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux,2152int drm_dp_read_lttpr_common_caps(struct drm_dp_aux *aux,
2153 const u8 dpcd[DP_RECEIVER_CAP_SIZE],
2153 u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);2154 u8 caps[DP_LTTPR_COMMON_CAP_SIZE]);
2154int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux,2155int drm_dp_read_lttpr_phy_caps(struct drm_dp_aux *aux,
2156 const u8 dpcd[DP_RECEIVER_CAP_SIZE],
2155 enum drm_dp_phy dp_phy,2157 enum drm_dp_phy dp_phy,
2156 u8 caps[DP_LTTPR_PHY_CAP_SIZE]);2158 u8 caps[DP_LTTPR_PHY_CAP_SIZE]);
2157int drm_dp_lttpr_count(const u8 cap[DP_LTTPR_COMMON_CAP_SIZE]);2159int drm_dp_lttpr_count(const u8 cap[DP_LTTPR_COMMON_CAP_SIZE]);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t