//------------------------------------------------------------------------ BOOLEAN verifyEDID(UINT8 *edid) { if (edid == NULL || !edid_checksum(edid) || !edid_check_header(edid)) { return FALSE; } return TRUE; }
u8 edid_simple_parsing(struct hdmi_info *hdmi) { u8 *edid_buf = hdmi->edid_buf; int i, index, ret = -EINVAL; struct edid_info_struct *edid_info = &hdmi->edid_info; unsigned v1, width, height, aspect; unsigned extensions; unsigned extension; EDID_DBG("%s\n", __func__); if (!edid_check_header(edid_buf)) { pr_err("%s: incorrect header\n", __func__); return INCORRECT_EDID_HEADER; } // Retrieve the number of extensions in this EDID extensions = edid_buf[126]; EDID_DBG("%s: extensions=%d\n", __func__, extensions); if (!extensions) { hdmi->edid_info.hdmi_sink = false; return NO_861_EXTENSIONS; } /* reset all supported */ for (i = 0 ; i < ARRAY_SIZE(additional_timing_db); i++) additional_timing_db[i].supported = false; for (i = 0 ; i < ARRAY_SIZE(established_timing_db); i++) established_timing_db[i].supported = false; /* Block 0: established timing */ pr_info("established timing: {%02x, %02x, %02x}\n", edid_buf[35], edid_buf[36], edid_buf[37]); v1 = edid_buf[35] | edid_buf[36] << 8; if (edid_buf[37] & 0x80) v1 |= 0x00010000; for (i = 0 ; i < 17; i++ ) // 17 bits defined in established timing established_timing_db[i].supported = ((v1 >>= 1) & 1) ; /* standard timing identification */ for (i = 0; i < 8; i++) { width = (edid_buf[38 + (i * 2)] * 8) + 248; v1 = edid_buf[38 + (i * 2) + 1]; switch (v1 >> 6) { case 0: height = width * 10 / 16; aspect = ASPECT(16, 10); break; case 1: height = width * 3 / 4; aspect = ASPECT(4, 3); break; case 2: height = width * 4 / 5; aspect = ASPECT(5, 4); break; case 3: height = width * 9 / 16; aspect = ASPECT(16, 9); break; } standard_timing_db[i].width = width; standard_timing_db[i].height = height; standard_timing_db[i].aspect = aspect; standard_timing_db[i].refresh_rate = (v1 & 0x3F) + 60; standard_timing_db[i].supported = true; } for (extension = 0; extension < extensions; extension++) { unsigned baseOffset = (extension + 1) * 0x80; unsigned dtdStart = 0; unsigned nativeFormats = 0; unsigned dbcOffset; EDID_DBG("Extension %d\n", extension +1); if (edid_buf[baseOffset] != 0x02) { EDID_DBG(" Extension is not CEA EDID format. Skipping.\n"); continue; } if (edid_buf[baseOffset+1] < 0x03) { EDID_DBG(" CEA EDID Extension is below version 3. Skipping.\n"); continue; } dtdStart = edid_buf[baseOffset+2]; edid_info->under_scan = edid_buf[baseOffset+3] & EDID_BIT(7); edid_info->basic_audio = edid_buf[baseOffset+3] & EDID_BIT(6); edid_info->ycbcr_4_4_4 = edid_buf[baseOffset+3] & EDID_BIT(5); edid_info->ycbcr_4_2_2 = edid_buf[baseOffset+3] & EDID_BIT(4); nativeFormats = edid_buf[baseOffset+3] & 0x04; dbcOffset = 4; while (dbcOffset < dtdStart) { unsigned blockType = edid_buf[baseOffset + dbcOffset] >> 5; unsigned blockLen = edid_buf[baseOffset + dbcOffset] & 0x1f; unsigned byte; EDID_DBG(" Block Type: %d Block Length: %d\n", blockType, blockLen); // Check for an audio data block if (blockType == AUDIO_D_BLOCK) { edid_info->basic_audio = true; EDID_DBG(" CEA3 Audio Data Block found.\n"); dbcOffset += blockLen + 1; continue; } // Check for a vendor data block if (blockType == VENDOR_SPEC_D_BLOCK) { EDID_DBG(" CEA3 Vendor Block found.\n"); // This may be an HDMI vendor block, and if so, we need to parse it if (edid_buf[baseOffset + dbcOffset + 1] == 0x03 && edid_buf[baseOffset + dbcOffset + 2] == 0x0C && edid_buf[baseOffset + dbcOffset + 3] == 0x00 ) { // We found the HDMI block EDID_DBG(" CEA3 HDMI Vendor Block found.\n"); hdmi->edid_info.hdmi_sink = true; } dbcOffset += blockLen + 1; continue; } if (blockType != VIDEO_D_BLOCK) { dbcOffset += blockLen + 1; continue; } // The block will be an array of indexes for (byte = 1; byte < blockLen; byte++) { index = edid_buf[baseOffset + dbcOffset + byte] & 0x7f; if (index > 63) { EDID_DBG("Invalid index in EDID Video block. Ignoring.\n"); } else { additional_timing_db[index-1].supported = true; EDID_DBG("%s\n", additional_timing_db[index-1].descrption); } } dbcOffset += blockLen + 1; } } // As a cheat, we're replacing the existing timings with our own "custom" // definition of these bytes. edid_buf[35] = 0; edid_buf[36] = 0; edid_buf[37] = 0; /* edid_buf[37] bit4: 480p, bit5: 576p, bit6: 720p */ if (additional_timing_db[CEA_MODE_720X480P_60HZ_4_3].supported || additional_timing_db[CEA_MODE_720X480P_60HZ_16_9].supported) { EDID_DBG("decide to support 480P\n"); edid_buf[37] |= (1<<4); } if (additional_timing_db[CEA_MODE_720X576P_50HZ_4_3].supported || additional_timing_db[CEA_MODE_720X576P_50HZ_16_9].supported) { EDID_DBG("decide to support 576P\n"); edid_buf[37] |= (1<<5); } if (additional_timing_db[CEA_MODE_1280X720P_60HZ_16_9].supported) { EDID_DBG("decide to support 720P\n"); edid_buf[37] |= (1<<6); } if (established_timing_db[ESTABLISHED_MODE_800X600_60HZ].supported) { EDID_DBG("decide to support 800x600\n"); edid_buf[36] |= (1<<6); } if (established_timing_db[ESTABLISHED_MODE_640X480_60HZ].supported) { EDID_DBG("decide to support 640x480\n"); edid_buf[35] |= (1<<5); } edid_fixup_compatibility_list(hdmi); return ret; }