// Copyright 2017 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "edid.h" #include #include #include #include #include #include #include #include #include bool edid_has_extension(const uint8_t* edid_buf) { const edid_t* edid = (edid_t *) edid_buf; return (edid->ext_flag == 1); } zx_status_t edid_get_num_dtd(const uint8_t* edid_buf, uint8_t* num_dtd) { const uint8_t* start_ext; const uint8_t* start_dtd; *num_dtd = 0; int i; if (!edid_has_extension(edid_buf)) { *num_dtd = 0; return ZX_OK; } // It has extension. Read from start of DTD until you hit 00 00 start_ext = &edid_buf[128]; if (start_ext[0] != 0x2) { zxlogf(ERROR, "%s: Unknown tag! %d\n", __FUNCTION__, start_ext[0]); return ZX_ERR_WRONG_TYPE; } if (start_ext[2] == 0) { zxlogf(ERROR, "%s: Invalid DTD pointer! 0x%x\n", __FUNCTION__, start_ext[2]); return ZX_ERR_WRONG_TYPE; } start_dtd = &start_ext[0] + start_ext[2]; i = 0; while (start_dtd[i] != 0x0 && start_dtd[i + 1] != 0x0) { *num_dtd += 1; i += 18; } return ZX_OK; } void edid_dump_disp_timing(const disp_timing_t* d) { zxlogf(INFO, "%s\n", __FUNCTION__); zxlogf(INFO, "pixel_clk = 0x%x\n", d->pixel_clk); zxlogf(INFO, "HActive = 0x%x\n", d->HActive); zxlogf(INFO, "HBlanking = 0x%x\n", d->HBlanking); zxlogf(INFO, "VActive = 0x%x\n", d->VActive); zxlogf(INFO, "VBlanking = 0x%x\n", d->VBlanking); zxlogf(INFO, "HSyncOffset = 0x%x\n", d->HSyncOffset); zxlogf(INFO, "HSyncPulseWidth = 0x%x\n", d->HSyncPulseWidth); zxlogf(INFO, "VSyncOffset = 0x%x\n", d->VSyncOffset); zxlogf(INFO, "VSyncPulseWidth = 0x%x\n", d->VSyncPulseWidth); zxlogf(INFO, "HImageSize = 0x%x\n", d->HImageSize); zxlogf(INFO, "VImageSize = 0x%x\n", d->VImageSize); zxlogf(INFO, "HBorder = 0x%x\n", d->HBorder); zxlogf(INFO, "VBorder = 0x%x\n", d->VBorder); zxlogf(INFO, "Flags = 0x%x\n", d->Flags); } zx_status_t edid_parse_std_display_timing(const uint8_t* edid_buf, detailed_timing_t* raw_dtd, disp_timing_t* disp_timing) { const uint8_t* start_dtd; uint8_t* s_r_dtd = (uint8_t*) raw_dtd; start_dtd = &edid_buf[0x36]; // populate raw structure first memcpy(s_r_dtd, start_dtd, 18); disp_timing->pixel_clk = raw_dtd->raw_pixel_clk[1] << 8 | raw_dtd->raw_pixel_clk[0]; disp_timing->HActive = (((raw_dtd->raw_Hact_HBlank & 0xf0)>>4) << 8) | raw_dtd->raw_Hact; disp_timing->HBlanking = ((raw_dtd->raw_Hact_HBlank & 0x0f) << 8) | raw_dtd->raw_HBlank; disp_timing->VActive = (((raw_dtd->raw_Vact_VBlank & 0xf0)>>4) << 8) | raw_dtd->raw_Vact; disp_timing->VBlanking = ((raw_dtd->raw_Vact_VBlank & 0x0f) << 8) | raw_dtd->raw_VBlank; disp_timing->HSyncOffset = (((raw_dtd->raw_HSync_VSync_OFF_PW & 0xc0)>>6) << 8) | raw_dtd->raw_HSyncOff; disp_timing->HSyncPulseWidth = (((raw_dtd->raw_HSync_VSync_OFF_PW & 0x30)>>4) << 8) | raw_dtd->raw_HSyncPW; disp_timing->VSyncOffset = (((raw_dtd->raw_HSync_VSync_OFF_PW & 0x0c)>>2) << 4) | (raw_dtd->raw_VSyncOff_VSyncPW & 0xf0)>>4; disp_timing->VSyncPulseWidth = ((raw_dtd->raw_HSync_VSync_OFF_PW & 0x03) << 4) | (raw_dtd->raw_VSyncOff_VSyncPW & 0x0f); disp_timing->HImageSize = (((raw_dtd->raw_H_V_ImageSize & 0xf0)>>4)<<8) | raw_dtd->raw_HImageSize; disp_timing->VImageSize = ((raw_dtd->raw_H_V_ImageSize & 0x0f)<<8) | raw_dtd->raw_VImageSize; disp_timing->HBorder = raw_dtd->raw_HBorder; disp_timing->VBorder = raw_dtd->raw_VBorder; disp_timing->Flags = raw_dtd->raw_Flags; return ZX_OK; } zx_status_t edid_parse_display_timing(const uint8_t* edid_buf, detailed_timing_t* raw_dtd, disp_timing_t* disp_timing, uint8_t num_dtd) { const uint8_t* start_ext; const uint8_t* start_dtd; uint8_t* s_r_dtd = (uint8_t*) raw_dtd; if (!edid_has_extension(edid_buf)) { return ZX_ERR_INVALID_ARGS; } // It has extension. Read from start of DTD until you hit 00 00 start_ext = &edid_buf[128]; if (start_ext[0] != 0x2) { zxlogf(ERROR, "%s: Unknown tag! %d\n", __FUNCTION__, start_ext[0]); return ZX_ERR_WRONG_TYPE; } if (start_ext[2] == 0) { zxlogf(ERROR, "%s: Invalid DTD pointer! 0x%x\n", __FUNCTION__, start_ext[2]); return ZX_ERR_WRONG_TYPE; } start_dtd = &start_ext[0] + start_ext[2]; for (int i = 0; i < num_dtd; i++) { // populate raw structure first memcpy(s_r_dtd, start_dtd, 18); disp_timing[i].pixel_clk = raw_dtd[i].raw_pixel_clk[1] << 8 | raw_dtd[i].raw_pixel_clk[0]; disp_timing[i].HActive = (((raw_dtd[i].raw_Hact_HBlank & 0xf0)>>4) << 8) | raw_dtd[i].raw_Hact; disp_timing[i].HBlanking = ((raw_dtd[i].raw_Hact_HBlank & 0x0f) << 8) | raw_dtd[i].raw_HBlank; disp_timing[i].VActive = (((raw_dtd[i].raw_Vact_VBlank & 0xf0)>>4) << 8) | raw_dtd[i].raw_Vact; disp_timing[i].VBlanking = ((raw_dtd[i].raw_Vact_VBlank & 0x0f) << 8) | raw_dtd[i].raw_VBlank; disp_timing[i].HSyncOffset = (((raw_dtd[i].raw_HSync_VSync_OFF_PW & 0xc0)>>6) << 8) | raw_dtd[i].raw_HSyncOff; disp_timing[i].HSyncPulseWidth = (((raw_dtd[i].raw_HSync_VSync_OFF_PW & 0x30)>>4) << 8) | raw_dtd[i].raw_HSyncPW; disp_timing[i].VSyncOffset = (((raw_dtd[i].raw_HSync_VSync_OFF_PW & 0x0c)>>2) << 4) | (raw_dtd[i].raw_VSyncOff_VSyncPW & 0xf0)>>4; disp_timing[i].VSyncPulseWidth = ((raw_dtd[i].raw_HSync_VSync_OFF_PW & 0x03) << 4) | (raw_dtd[i].raw_VSyncOff_VSyncPW & 0x0f); disp_timing[i].HImageSize = (((raw_dtd[i].raw_H_V_ImageSize & 0xf0)>>4)<<8) | raw_dtd[i].raw_HImageSize; disp_timing[i].VImageSize = ((raw_dtd[i].raw_H_V_ImageSize & 0x0f)<<8) | raw_dtd[i].raw_VImageSize; disp_timing[i].HBorder = raw_dtd[i].raw_HBorder; disp_timing[i].VBorder = raw_dtd[i].raw_VBorder; disp_timing[i].Flags = raw_dtd[i].raw_Flags; s_r_dtd += 18; start_dtd += 18; } return ZX_OK; }