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

drm/edid: add HF-EEODB support to EDID read and allocation

HDMI 2.1 section 10.3.6 defines an HDMI Forum EDID Extension Override
Data Block, which may contain a different extension count than the base
block claims. Add support for reading more EDID data if available. The
extra blocks aren't parsed yet, though.

Hard-coding the EEODB parsing instead of using the iterators we have is
a bit of a bummer, but we have to be able to do this on a partially
allocated EDID while reading it.

v2:
- Check for CEA Data Block Collection size (Ville)
- Amend commit message and comment about hard-coded parsing

Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/57b57a355d62eb91ad1e3cf555978576f2bd9fdd.1656494768.git.jani.nikula@intel.com

+86 -3
+86 -3
drivers/gpu/drm/drm_edid.c
··· 1581 1581 (edid->version == version && edid->revision > revision); 1582 1582 } 1583 1583 1584 + static int edid_hfeeodb_extension_block_count(const struct edid *edid); 1585 + 1586 + static int edid_hfeeodb_block_count(const struct edid *edid) 1587 + { 1588 + int eeodb = edid_hfeeodb_extension_block_count(edid); 1589 + 1590 + return eeodb ? eeodb + 1 : 0; 1591 + } 1592 + 1584 1593 static int edid_extension_block_count(const struct edid *edid) 1585 1594 { 1586 1595 return edid->extensions; ··· 2035 2026 struct edid *new; 2036 2027 int i, valid_blocks = 0; 2037 2028 2029 + /* 2030 + * Note: If the EDID uses HF-EEODB, but has invalid blocks, we'll revert 2031 + * back to regular extension count here. We don't want to start 2032 + * modifying the HF-EEODB extension too. 2033 + */ 2038 2034 for (i = 0; i < edid_block_count(edid); i++) { 2039 2035 const void *src_block = edid_block_data(edid, i); 2040 2036 ··· 2275 2261 size_t *size) 2276 2262 { 2277 2263 enum edid_block_status status; 2278 - int i, invalid_blocks = 0; 2264 + int i, num_blocks, invalid_blocks = 0; 2279 2265 struct edid *edid, *new; 2280 2266 size_t alloc_size = EDID_LENGTH; 2281 2267 ··· 2317 2303 goto fail; 2318 2304 edid = new; 2319 2305 2320 - for (i = 1; i < edid_block_count(edid); i++) { 2306 + num_blocks = edid_block_count(edid); 2307 + for (i = 1; i < num_blocks; i++) { 2321 2308 void *block = (void *)edid_block_data(edid, i); 2322 2309 2323 2310 status = edid_block_read(block, i, read_block, context); ··· 2329 2314 if (status == EDID_BLOCK_READ_FAIL) 2330 2315 goto fail; 2331 2316 invalid_blocks++; 2317 + } else if (i == 1) { 2318 + /* 2319 + * If the first EDID extension is a CTA extension, and 2320 + * the first Data Block is HF-EEODB, override the 2321 + * extension block count. 2322 + * 2323 + * Note: HF-EEODB could specify a smaller extension 2324 + * count too, but we can't risk allocating a smaller 2325 + * amount. 2326 + */ 2327 + int eeodb = edid_hfeeodb_block_count(edid); 2328 + 2329 + if (eeodb > num_blocks) { 2330 + num_blocks = eeodb; 2331 + alloc_size = edid_size_by_blocks(num_blocks); 2332 + new = krealloc(edid, alloc_size, GFP_KERNEL); 2333 + if (!new) 2334 + goto fail; 2335 + edid = new; 2336 + } 2332 2337 } 2333 2338 } 2334 2339 2335 2340 if (invalid_blocks) { 2336 - connector_bad_edid(connector, edid, edid_block_count(edid)); 2341 + connector_bad_edid(connector, edid, num_blocks); 2337 2342 2338 2343 edid = edid_filter_invalid_blocks(edid, &alloc_size); 2339 2344 } ··· 3886 3851 #define CTA_EXT_DB_HDR_STATIC_METADATA 6 3887 3852 #define CTA_EXT_DB_420_VIDEO_DATA 14 3888 3853 #define CTA_EXT_DB_420_VIDEO_CAP_MAP 15 3854 + #define CTA_EXT_DB_HF_EEODB 0x78 3889 3855 #define CTA_EXT_DB_HF_SCDB 0x79 3890 3856 3891 3857 #define EDID_BASIC_AUDIO (1 << 6) ··· 4946 4910 cea_db_payload_len(db) >= 7; 4947 4911 } 4948 4912 4913 + static bool cea_db_is_hdmi_forum_eeodb(const void *db) 4914 + { 4915 + return cea_db_is_extended_tag(db, CTA_EXT_DB_HF_EEODB) && 4916 + cea_db_payload_len(db) >= 2; 4917 + } 4918 + 4949 4919 static bool cea_db_is_microsoft_vsdb(const struct cea_db *db) 4950 4920 { 4951 4921 return cea_db_is_vendor(db, MICROSOFT_IEEE_OUI) && ··· 4984 4942 { 4985 4943 return cea_db_is_extended_tag(db, CTA_EXT_DB_HDR_STATIC_METADATA) && 4986 4944 cea_db_payload_len(db) >= 3; 4945 + } 4946 + 4947 + /* 4948 + * Get the HF-EEODB override extension block count from EDID. 4949 + * 4950 + * The passed in EDID may be partially read, as long as it has at least two 4951 + * blocks (base block and one extension block) if EDID extension count is > 0. 4952 + * 4953 + * Note that this is *not* how you should parse CTA Data Blocks in general; this 4954 + * is only to handle partially read EDIDs. Normally, use the CTA Data Block 4955 + * iterators instead. 4956 + * 4957 + * References: 4958 + * - HDMI 2.1 section 10.3.6 HDMI Forum EDID Extension Override Data Block 4959 + */ 4960 + static int edid_hfeeodb_extension_block_count(const struct edid *edid) 4961 + { 4962 + const u8 *cta; 4963 + 4964 + /* No extensions according to base block, no HF-EEODB. */ 4965 + if (!edid_extension_block_count(edid)) 4966 + return 0; 4967 + 4968 + /* HF-EEODB is always in the first EDID extension block only */ 4969 + cta = edid_extension_block_data(edid, 0); 4970 + if (edid_block_tag(cta) != CEA_EXT || cea_revision(cta) < 3) 4971 + return 0; 4972 + 4973 + /* Need to have the data block collection, and at least 3 bytes. */ 4974 + if (cea_db_collection_size(cta) < 3) 4975 + return 0; 4976 + 4977 + /* 4978 + * Sinks that include the HF-EEODB in their E-EDID shall include one and 4979 + * only one instance of the HF-EEODB in the E-EDID, occupying bytes 4 4980 + * through 6 of Block 1 of the E-EDID. 4981 + */ 4982 + if (!cea_db_is_hdmi_forum_eeodb(&cta[4])) 4983 + return 0; 4984 + 4985 + return cta[4 + 2]; 4987 4986 } 4988 4987 4989 4988 static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector,