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

drm/displayid: add quirk to ignore DisplayID checksum errors

Add a mechanism for DisplayID specific quirks, and add the first quirk
to ignore DisplayID section checksum errors.

It would be quite inconvenient to pass existing EDID quirks from
drm_edid.c for DisplayID parsing. Not all places doing DisplayID
iteration have the quirks readily available, and would have to pass it
in all places. Simply add a separate array of DisplayID specific EDID
quirks. We do end up checking it every time we iterate DisplayID blocks,
but hopefully the number of quirks remains small.

There are a few laptop models with DisplayID checksum failures, leading
to higher refresh rates only present in the DisplayID blocks being
ignored. Add a quirk for the panel in the machines.

Reported-by: Tiago Martins Araújo <tiago.martins.araujo@gmail.com>
Closes: https://lore.kernel.org/r/CACRbrPGvLP5LANXuFi6z0S7XMbAG4X5y2YOLBDxfOVtfGGqiKQ@mail.gmail.com
Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/14703
Acked-by: Alex Deucher <alexander.deucher@amd.com>
Tested-by: Tiago Martins Araújo <tiago.martins.araujo@gmail.com>
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/c04d81ae648c5f21b3f5b7953f924718051f2798.1761681968.git.jani.nikula@intel.com
Signed-off-by: Jani Nikula <jani.nikula@intel.com>

+39 -4
+37 -4
drivers/gpu/drm/drm_displayid.c
··· 9 9 #include "drm_crtc_internal.h" 10 10 #include "drm_displayid_internal.h" 11 11 12 + enum { 13 + QUIRK_IGNORE_CHECKSUM, 14 + }; 15 + 16 + struct displayid_quirk { 17 + const struct drm_edid_ident ident; 18 + u8 quirks; 19 + }; 20 + 21 + static const struct displayid_quirk quirks[] = { 22 + { 23 + .ident = DRM_EDID_IDENT_INIT('C', 'S', 'O', 5142, "MNE007ZA1-5"), 24 + .quirks = BIT(QUIRK_IGNORE_CHECKSUM), 25 + }, 26 + }; 27 + 28 + static u8 get_quirks(const struct drm_edid *drm_edid) 29 + { 30 + int i; 31 + 32 + for (i = 0; i < ARRAY_SIZE(quirks); i++) { 33 + if (drm_edid_match(drm_edid, &quirks[i].ident)) 34 + return quirks[i].quirks; 35 + } 36 + 37 + return 0; 38 + } 39 + 12 40 static const struct displayid_header * 13 41 displayid_get_header(const u8 *displayid, int length, int index) 14 42 { ··· 51 23 } 52 24 53 25 static const struct displayid_header * 54 - validate_displayid(const u8 *displayid, int length, int idx) 26 + validate_displayid(const u8 *displayid, int length, int idx, bool ignore_checksum) 55 27 { 56 28 int i, dispid_length; 57 29 u8 csum = 0; ··· 69 41 for (i = 0; i < dispid_length; i++) 70 42 csum += displayid[idx + i]; 71 43 if (csum) { 72 - DRM_NOTE("DisplayID checksum invalid, remainder is %d\n", csum); 73 - return ERR_PTR(-EINVAL); 44 + DRM_NOTE("DisplayID checksum invalid, remainder is %d%s\n", csum, 45 + ignore_checksum ? " (ignoring)" : ""); 46 + 47 + if (!ignore_checksum) 48 + return ERR_PTR(-EINVAL); 74 49 } 75 50 76 51 return base; ··· 83 52 { 84 53 const struct displayid_header *base; 85 54 const u8 *displayid; 55 + bool ignore_checksum = iter->quirks & BIT(QUIRK_IGNORE_CHECKSUM); 86 56 87 57 displayid = drm_edid_find_extension(iter->drm_edid, DISPLAYID_EXT, &iter->ext_index); 88 58 if (!displayid) ··· 93 61 iter->length = EDID_LENGTH - 1; 94 62 iter->idx = 1; 95 63 96 - base = validate_displayid(displayid, iter->length, iter->idx); 64 + base = validate_displayid(displayid, iter->length, iter->idx, ignore_checksum); 97 65 if (IS_ERR(base)) 98 66 return NULL; 99 67 ··· 108 76 memset(iter, 0, sizeof(*iter)); 109 77 110 78 iter->drm_edid = drm_edid; 79 + iter->quirks = get_quirks(drm_edid); 111 80 } 112 81 113 82 static const struct displayid_block *
+2
drivers/gpu/drm/drm_displayid_internal.h
··· 167 167 168 168 u8 version; 169 169 u8 primary_use; 170 + 171 + u8 quirks; 170 172 }; 171 173 172 174 void displayid_iter_edid_begin(const struct drm_edid *drm_edid,