Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: Add edid_corrupt flag for Displayport Link CTS 4.2.2.6

Displayport compliance test 4.2.2.6 requires that a source device be capable of
detecting a corrupt EDID. The test specification states that the sink device
sets up the EDID with an invalid checksum. To do this, the sink sets up an
invalid EDID header, expecting the source device to generate the checksum and
compare it to the value stored in the last byte of the block data.

Unfortunately, the DRM EDID reading and parsing functions are actually too good
in this case; the header is fixed before the checksum is computed and thus the
test never sees the invalid checksum. This results in a failure to pass the
compliance test.

To correct this issue, when the EDID code detects that the header is invalid,
a flag is set to indicate that the EDID is corrupted. In this case, it sets
edid_corrupt flag and continues with its fix-up code. This flag is also set in
the case of a more seriously damaged header (fixup score less than the
threshold). For consistency, the edid_corrupt flag is also set when the
checksum is invalid as well.

V2:
- Removed the static bool global
- Added a bool to the drm_connector struct to reaplce the static one for
holding the status of raw edid header corruption detection
- Modified the function signature of the is_valid function to take an
additional parameter to store the corruption detected value
- Fixed the other callers of the above is_valid function
V3:
- Updated the commit message to be more clear about what and why this
patch does what it does.
- Added comment in code to clarify the operations there
- Removed compliance variable and check_link_status update; those
have been moved to a later patch
- Removed variable assignment from the bottom of the test handler
V4:
- Removed i915 tag from subject line as the patch is not i915-specific
V5:
- Moved code causing a compilation error to this patch where the variable
is actually declared
- Maintained blank lines / spacing so as to not contaminate the patch
V6:
- Removed extra debug messages
- Added documentation to for the added parameter on drm_edid_block_valid
- Fixed more whitespace issues in check_link_status
- Added a clear of the header_corrupt flag to the end of the test handler
in intel_dp.c
- Changed the usage of the new function prototype in several places to use
NULL where it is not needed by compliance testing
V7:
- Updated to account for long_pulse flag propagation
V8:
- Removed clearing of header_corrupt flag from the test handler in intel_dp.c
- Added clearing of header_corrupt flag in the drm_edid_block_valid function
V9:
- Renamed header_corrupt flag to edid_corrupt to more accurately reflect its
value and purpose
- Updated commit message
V10:
- Updated for versioning and patch swizzle
- Revised the title to more accurately reflect the nature and contents of
the patch
- Fixed formatting/whitespace problems
- Added set flag when computed checksum is invalid

Signed-off-by: Todd Previte <tprevite@gmail.com>
Cc: dri-devel@lists.freedesktop.org
Acked-by: Dave Airlie <airlied@redhat.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>

authored by

Todd Previte and committed by
Daniel Vetter
6ba2bd3d 83a24979

+38 -9
+26 -6
drivers/gpu/drm/drm_edid.c
··· 1041 1041 * @raw_edid: pointer to raw EDID block 1042 1042 * @block: type of block to validate (0 for base, extension otherwise) 1043 1043 * @print_bad_edid: if true, dump bad EDID blocks to the console 1044 + * @edid_corrupt: if true, the header or checksum is invalid 1044 1045 * 1045 1046 * Validate a base or extension EDID block and optionally dump bad blocks to 1046 1047 * the console. 1047 1048 * 1048 1049 * Return: True if the block is valid, false otherwise. 1049 1050 */ 1050 - bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid) 1051 + bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, 1052 + bool *edid_corrupt) 1051 1053 { 1052 1054 u8 csum; 1053 1055 struct edid *edid = (struct edid *)raw_edid; ··· 1062 1060 1063 1061 if (block == 0) { 1064 1062 int score = drm_edid_header_is_valid(raw_edid); 1065 - if (score == 8) ; 1066 - else if (score >= edid_fixup) { 1063 + if (score == 8) { 1064 + if (edid_corrupt) 1065 + *edid_corrupt = 0; 1066 + } else if (score >= edid_fixup) { 1067 + /* Displayport Link CTS Core 1.2 rev1.1 test 4.2.2.6 1068 + * The corrupt flag needs to be set here otherwise, the 1069 + * fix-up code here will correct the problem, the 1070 + * checksum is correct and the test fails 1071 + */ 1072 + if (edid_corrupt) 1073 + *edid_corrupt = 1; 1067 1074 DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); 1068 1075 memcpy(raw_edid, edid_header, sizeof(edid_header)); 1069 1076 } else { 1077 + if (edid_corrupt) 1078 + *edid_corrupt = 1; 1070 1079 goto bad; 1071 1080 } 1072 1081 } ··· 1087 1074 if (print_bad_edid) { 1088 1075 DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); 1089 1076 } 1077 + 1078 + if (edid_corrupt) 1079 + *edid_corrupt = 1; 1090 1080 1091 1081 /* allow CEA to slide through, switches mangle this */ 1092 1082 if (raw_edid[0] != 0x02) ··· 1145 1129 return false; 1146 1130 1147 1131 for (i = 0; i <= edid->extensions; i++) 1148 - if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true)) 1132 + if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true, NULL)) 1149 1133 return false; 1150 1134 1151 1135 return true; ··· 1248 1232 for (i = 0; i < 4; i++) { 1249 1233 if (get_edid_block(data, block, 0, EDID_LENGTH)) 1250 1234 goto out; 1251 - if (drm_edid_block_valid(block, 0, print_bad_edid)) 1235 + if (drm_edid_block_valid(block, 0, print_bad_edid, 1236 + &connector->edid_corrupt)) 1252 1237 break; 1253 1238 if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) { 1254 1239 connector->null_edid_counter++; ··· 1274 1257 block + (valid_extensions + 1) * EDID_LENGTH, 1275 1258 j, EDID_LENGTH)) 1276 1259 goto out; 1277 - if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH, j, print_bad_edid)) { 1260 + if (drm_edid_block_valid(block + (valid_extensions + 1) 1261 + * EDID_LENGTH, j, 1262 + print_bad_edid, 1263 + NULL)) { 1278 1264 valid_extensions++; 1279 1265 break; 1280 1266 }
+5 -2
drivers/gpu/drm/drm_edid_load.c
··· 216 216 goto out; 217 217 } 218 218 219 - if (!drm_edid_block_valid(edid, 0, print_bad_edid)) { 219 + if (!drm_edid_block_valid(edid, 0, print_bad_edid, 220 + &connector->edid_corrupt)) { 220 221 connector->bad_edid_counter++; 221 222 DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ", 222 223 name); ··· 230 229 if (i != valid_extensions + 1) 231 230 memcpy(edid + (valid_extensions + 1) * EDID_LENGTH, 232 231 edid + i * EDID_LENGTH, EDID_LENGTH); 233 - if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, print_bad_edid)) 232 + if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, 233 + print_bad_edid, 234 + NULL)) 234 235 valid_extensions++; 235 236 } 236 237
+7 -1
include/drm/drm_crtc.h
··· 719 719 int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */ 720 720 unsigned bad_edid_counter; 721 721 722 + /* Flag for raw EDID header corruption - used in Displayport 723 + * compliance testing - * Displayport Link CTS Core 1.2 rev1.1 4.2.2.6 724 + */ 725 + bool edid_corrupt; 726 + 722 727 struct dentry *debugfs_entry; 723 728 724 729 struct drm_connector_state *state; ··· 1448 1443 int hpref, int vpref); 1449 1444 1450 1445 extern int drm_edid_header_is_valid(const u8 *raw_edid); 1451 - extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid); 1446 + extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid, 1447 + bool *edid_corrupt); 1452 1448 extern bool drm_edid_is_valid(struct edid *edid); 1453 1449 1454 1450 extern struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev,