#if WITH_HW_MCU

/*
 * Copyright (C) 2010, 2015 Apple Inc. All rights reserved.
 *
 * This document is the property of Apple Inc.
 * It is considered confidential and proprietary.
 *
 * This document may not be reproduced or transmitted in any form,
 * in whole or in part, without the express written permission of
 * Apple Inc.
 */

/*
 * This file is based on a manual C++-to-C translation of:
 * IODisplayPortFamily/IODPService.cpp
 * The scoring system and timing collection is simplified.
 */

#include <debug.h>

#include <drivers/display.h>
#include <drivers/displayport/displayport.h>
#include <drivers/displayport.h>
#include <drivers/process_edid.h>
#include <platform/memmap.h>
#include <platform/timer.h>
#include <sys/task.h>
#include "dpcd.h"
#include "edid.h"

// I2C address for the EDID rom.
#define kI2CEdidDeviceAddr	0x50

// H3 DisplayPipe has issues higher than this.
#define kMaxHorizontalActive	1280

// Some monitors are slow. Insert microseconds of dumb.
#define kEdidRetryTimeout	5000000
// Some monitors are suspiciously single-threaded. Don't retry too
// often or they make no forward progress.
#define kEdidRetryDelay		250000

/////////////////////////////////////////
////////// debug support

#define DP_DEBUG_MASK ( 		\
		DP_DEBUG_EDID |	        \
		DP_DEBUG_ERROR |	\
		DP_DEBUG_INFO |         \
		0)

#undef DP_DEBUG_MASK
#define DP_DEBUG_MASK DP_DEBUG_ERROR // (DP_DEBUG_EDID | DP_DEBUG_SCORE | DP_DEBUG_DS | DP_DEBUG_ERROR | DP_DEBUG_INFO)

#define DP_DEBUG_EDID           (1<<16)  // EDID parsing
#define DP_DEBUG_SCORE          (1<<17)  // Final validation/scoring
#define DP_DEBUG_DS             (1<<18)  // Downstream type detection
#define DP_DEBUG_INFO           (1<<19)  // info
#define DP_DEBUG_ERROR          (1<<20)  // error
#define DP_DEBUG_ALWAYS         (1<<31)  // unconditional output

#define debug(_fac, _fmt, _args...)	        							\
	do {					        						\
		if ((DP_DEBUG_ ## _fac) & (DP_DEBUG_MASK | DP_DEBUG_ALWAYS))    			\
			dprintf(DEBUG_INFO, "EDID: %s, %d: " _fmt, __FUNCTION__, __LINE__, ##_args);	\
	} while(0)

typedef struct {
	// Slightly compacted vs IODPDisplayTimingElement.cpp
	uint16_t    horizontal;
	uint16_t    vertical;
	uint32_t    rate;
} __IODPTEEstablished;

typedef enum {
        kSupportFlagNone    = (0<<0),
        kSupportFlagGTF     = (1<<0),
        kSupportFlagCVT     = (1<<1)
} SupportFlags;

static int process_edid(EDID *edid);
static int verify_edid(EDID *edid, uint8_t *p_checksum);
static int get_edid(uint32_t offset, EDID *edid);
static int process_edid_vtb(EDIDVTB *vtb);
static int process_edid_cvt_timings(uint8_t *data, uint32_t length);
static int process_edid_cea(EDIDCEA *cea);
static int process_edid_cea1(EDIDCEA_1 *cea1);
static int process_edid_cea2(EDIDCEA_2 *cea2);
static int process_edid_cea3(EDIDCEA_3 *cea3);
static int process_edid_cea3_video(EDIDCEA3DataBlockVideo *video, uint32_t length);
static int process_edid_cea3_vendor_specific(EDIDCEA3DataBlockVendorSpecific *vendor, uint32_t length);
static int process_edid_standard(EDIDStandard *standard);
static int process_edid_standard_feature(EDIDStandard *standard);
static int process_edid_standard_timings(uint8_t *data, uint32_t length);
static int process_edid_standard_established_timings(EDIDStandard *standard);
static int process_edid_detailed(EDIDDetailed *detailed);
static int process_edid_detailed_descriptor(EDIDDetailedDescriptor *descriptor);
static int process_edid_detailed_established_timings_iii(EDIDDetailedEstablishedTimingsIII *established);
static int try_standard_timing_id(uint16_t timing_id);
static int try_established_timing_id(uint8_t index);
static int try_established_timing_iii_id(uint8_t establishedID);
static int try_detailed_timing(EDIDDetailedTiming *detailed);
static int try_cvt_timing_id(EDIDCVT *cvt);
static int try_cea_short_id(uint32_t shortVideoID);
static int get_timing_data_for_cea_short_id(uint32_t shortVideoID, struct video_timing_data *timingData);
static int use_dimensions(uint32_t horizontal, uint32_t vertical, uint32_t rate);
static int use_dimensions_dmt(uint32_t horizontal, uint32_t vertical, uint32_t rate);
static int use_dimensions_cvt(uint32_t horizontal, uint32_t vertical, uint32_t rate);
static int use_dimensions_gtf(uint32_t horizontal, uint32_t vertical, uint32_t rate);
static int use_dimensions_table(const struct video_timing_data * list, uint32_t count, uint32_t horizontal, uint32_t vertical, uint32_t rate);
static int use_timings(struct video_timing_data *data);
static uint16_t read_le16(const void *data, uint32_t offset);
static uint32_t read_le32(const void *data, uint32_t offset);

static bool s_have_timings;
static bool s_abort_edid;
static struct video_timing_data s_best_timings;
static SupportFlags s_timing_support;
static uint32_t s_restrict_h_active;
static uint32_t s_restrict_v_active;
static int s_ds_type = -1;


int obtain_edid(void)
{
	int ret = -1;
	EDID edid;
	uint32_t ext_block_count;
	uint32_t index = 0;

	s_have_timings = false;
	bzero(&s_best_timings, sizeof(s_best_timings));
	s_timing_support = kSupportFlagNone;
	s_ds_type = kDPDownstreamTypeOther;
	(void) dp_device_get_downstream_port_type(&s_ds_type);
	debug(DS, "Displayport immediate downstream type %d\n", s_ds_type);

	ret = get_edid(0, &edid);
	debug(EDID, "Obtaining base EDID result=%d\n", ret);
	if (ret) goto exit;
	if (s_ds_type == kDPDownstreamTypeHDMI) {
		// If we see any EDID activity, start by assuming it's
		// actually a DVI monitor with HDMI ports (or adaptor).
		// Revert to detecting as HDMI if we receive an appropriate
		// flag in one of the EDID packets.
		debug(DS, "Assuming HDMI port actually DVI\n");
		s_ds_type = kDPDownstreamTypeDVI;
	}
	
	ret = process_edid(&edid);
	debug(EDID, "Processing EDID result=%d\n", ret);
	if (ret) goto exit;
	
	ext_block_count = edid.data.standard.extensionFlag;
	debug(EDID, "EDID contains %d extensions\n", ext_block_count);

	for (index = 1; index <= ext_block_count; ++index) {
		ret = get_edid(index * sizeof(EDID), &edid);
		debug(EDID, "Obtaining EDID extension %d result=%d\n", index, ret);
		if (ret) goto exit;

		ret = process_edid(&edid);
		debug(EDID, "Processing EDID extension %d result=%d\n", index, ret);
		if (ret) goto exit;
	}
	debug(DS, "Downstream type %d\n", s_ds_type);

 exit:
	return ret;
}

void abort_edid(void)
{
	s_abort_edid = true;
}

void restrict_edid(uint32_t h_active, uint32_t v_active)
{
	s_restrict_h_active = h_active;
	s_restrict_v_active = v_active;
}

int get_edid_timings(struct video_timing_data *data)
{
	if (!s_have_timings) return -1;
	bcopy(&s_best_timings, data, sizeof(s_best_timings));
	debug(ALWAYS, "EDID: %ux%u%c/%u\n",
	      s_best_timings.axis[kDisplayAxisTypeHorizontal].active,
	      s_best_timings.axis[kDisplayAxisTypeVertical].active,
	      s_best_timings.interlaced ? 'i' : 'p',
	      s_best_timings.axis[kDisplayAxisTypeVertical].sync_rate >> 16);
	return 0;
}

int get_edid_downstream_type(void)
{
	return s_ds_type;
}

static int process_edid(EDID *edid)
{
	static const uint8_t edid_standard_header[] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
	
	debug(EDID, "Processing EDID\n");
	
	if (!memcmp(edid->data.header.header, edid_standard_header, sizeof(edid_standard_header))) {
		process_edid_standard(&edid->data.standard);
	} else {
		switch (edid->data.header.header[0]) {
		case kEDIDTypeCEA:
			process_edid_cea(&edid->data.cea);
			break;
		case KEDIDTypeVTB:
			process_edid_vtb(&edid->data.vtb);
			break;
		default:
			debug(EDID, "Unknown EDID type %d\n", edid->data.header.header[0]);
			break;
		}
	}
	return 0;
}

static int process_edid_vtb(EDIDVTB *vtb)
{
	uint32_t index, length;
	uint32_t dtbCount, cvtCount, stCount;

	debug(EDID, "Processing EDID VTB\n");
	
	if (1 != vtb->header.revision) goto exit;
    
	if (!(vtb->dtbCount && vtb->cvtCount && vtb->stCount)) goto exit;
    
	length      = sizeof(vtb->data);
	index       = 0;
	dtbCount    = vtb->dtbCount;
	cvtCount    = vtb->cvtCount;
	stCount     = vtb->stCount;
	while ( index<length ) {
		uint32_t remaining = length-index;
        
		if ( dtbCount ) {
			if (sizeof(EDIDDetailed)>remaining)
				break;
            
			process_edid_detailed((EDIDDetailed*)&vtb->data[index]);
			
			index += sizeof(EDIDDetailed);
			dtbCount--;
		}
		else if ( cvtCount ) {
			uint32_t cvtLength = cvtCount * sizeof(EDIDCVT);
			if (cvtLength>remaining)
				break;
			
			process_edid_cvt_timings(&vtb->data[index], cvtLength);
			
			index += cvtLength;
			cvtCount = 0;
		}
		else if ( stCount ) {
			uint32_t stLength = stCount * sizeof(uint16_t);
			if (stLength>remaining)
				break;
			
			process_edid_standard_timings(&vtb->data[index], stLength);
			
			index += stLength;
			stCount = 0;
		}
		else {
			break;
		}
	}
 exit:
	return 0;
}

static int process_edid_cvt_timings(uint8_t *data, uint32_t length)
{
	uint32_t index;
	debug(EDID, "Processing EDID CVT timings\n");
	for( index = 0; index < length; index+=sizeof(EDIDCVT) ) {		
		try_cvt_timing_id((EDIDCVT*)&data[index]);
	}
	return 0;
}

static int process_edid_cea(EDIDCEA *cea)
{
	debug(EDID, "Processing CEA EDID\n");

	switch (cea->data.header.revision) {
        case 1:
		process_edid_cea1(&cea->data.cea1);
		break;
        case 2:
		process_edid_cea2(&cea->data.cea2);
		break;
        case 3:
		process_edid_cea3(&cea->data.cea3);
		break;
        default:
		break;
	}
	
	return 0;
}

static int process_edid_cea1(EDIDCEA_1 * cea1)
{
	uint32_t relativeDataOffset;
	uint32_t index = 0;
	
	debug(EDID, "Processing CEA v1 EDID\n");
	if (cea1->header.dataOffset<=sizeof(cea1->header)) return -1;
	
	relativeDataOffset = cea1->header.dataOffset-sizeof(cea1->header);
	while ( relativeDataOffset < sizeof(cea1->data) && (sizeof(cea1->data)-relativeDataOffset) >= sizeof(EDIDDetailed)) {
		debug(EDID, "Processing Detailed Timing index %d\n", index++);
		process_edid_detailed((EDIDDetailed*)(cea1->data + relativeDataOffset));
		relativeDataOffset += sizeof(EDIDDetailed);
	}
	
	return 0;
}

static int process_edid_cea2(EDIDCEA_2 * cea2)
{
	debug(EDID, "Processing CEA v2 EDID\n");
	// Don't care about audio/color/overscan. Just pass it along.
	return process_edid_cea1((EDIDCEA_1*)cea2);
}

static int process_edid_cea3(EDIDCEA_3 * cea3)
{
	uint32_t startOffset    = 0;
	uint32_t length         = 0;
	uint32_t index          = 0;
	
	debug(EDID, "Processing CEA v3 EDID\n");
    
	if (cea3->header.dataLength <= sizeof(cea3->header) || cea3->header.dataLength>sizeof(EDIDCEA_3)) return -1;
    
	length = cea3->header.dataLength-sizeof(cea3->header);
	while ( startOffset < length ) {
		EDIDCEA3DataBlock *     block = (EDIDCEA3DataBlock*)(cea3->data + startOffset);
		EDIDCEA3DataBlockType   blockType;
		uint32_t                blockLength;
		uint32_t		sectionLength;
		
		debug(EDID, "Processing CEA v3 EDID Data Block index %d\n", index++);
		
		// Check for padding
		if ( !block->flags ) {
			debug(EDID, "Encountered zero padding.\n");
			break;
		}
		
		blockType   = (EDIDCEA3DataBlockType)((block->flags>>5) & 0x7);
		blockLength = (block->flags) & 0x1f;

		sectionLength = blockLength+sizeof(block->flags);
                if ( sectionLength>(length-startOffset) ) return -1;
		
		debug(EDID, "Processing CEA v3 EDID Data Block type %d of length %d\n",
		      blockType, blockLength);
        
		switch ( blockType ) {
		case kEDIDCEA3DataBlockTypeAudio:
			// Don't care.
			// process_edid_cea3_audio((EDIDCEA3DataBlockAudio*)&block->data, blockLength);
			break;
		case kEDIDCEA3DataBlockTypeVideo:
			process_edid_cea3_video((EDIDCEA3DataBlockVideo*)&block->data, blockLength);
			break;
		case kEDIDCEA3DataBlockTypeSpeakerAllocation:
			// Don't care.
			// process_edid_cea3_speaker_allocation((EDIDCEA3DataBlockSpeakerAllocation*)&block->data, blockLength);
			break;
		case kEDIDCEA3DataBlockTypeExtended:
			// Don't care.
			// process_edid_cea3_extended((EDIDCEA3DataBlockExtended*)&block->data, blockLength);
			break;
		case kEDIDCEA3DataBlockTypeVenderSpecific:
			process_edid_cea3_vendor_specific((EDIDCEA3DataBlockVendorSpecific*)&block->data, blockLength);
			break;
		default:
			break;
		}
		startOffset += sectionLength;
	}
	
	return process_edid_cea2((EDIDCEA_2*)cea3);
}

static int process_edid_cea3_video(EDIDCEA3DataBlockVideo * video, uint32_t length)
{
	uint32_t        index;
	
	debug(EDID, "Processing CEA v3 EDID Video\n");
    
	for ( index=0; index<length; index++ ) {
		uint32_t id = video->descriptor[index];
		debug(EDID, "Processing CEA ShortID %d\n", id);
		try_cea_short_id(video->descriptor[index]);
	}
    
	return 0;
}

static int process_edid_cea3_vendor_specific(EDIDCEA3DataBlockVendorSpecific *vendor, uint32_t length)
{
	// Only care that this might be a signal we're HDMI.
	static const uint8_t hdmi_out[] = { 0x03, 0x0c, 0x00 };
	if (!memcmp(hdmi_out, vendor->ieeeOui, sizeof(hdmi_out))) {
		debug(DS, "EDID says oui, we're HDMI\n");
		s_ds_type = kDPDownstreamTypeHDMI;
	}
	return 0;
}

static int process_edid_standard(EDIDStandard *standard)
{
	debug(EDID, "Processing Standard EDID\n");
	
	// Don't care about vendor/product IDs, color.
	process_edid_standard_feature(standard);
	process_edid_standard_established_timings(standard);
	process_edid_standard_timings(standard->standard, sizeof(standard->standard));
	process_edid_detailed(&standard->preferred);
	process_edid_detailed(&standard->detailed[0]);
	process_edid_detailed(&standard->detailed[1]);
	process_edid_detailed(&standard->detailed[2]);

	return 0;
}

static int process_edid_standard_feature(EDIDStandard *standard)
{
	// Don't care about color, just HDMI vs DVI and timings.
	if ((s_ds_type != kDPDownstreamTypeVGA) && (standard->videoInputDef & (1<<7)) ) {
		if ( standard->version == 1 && standard->revision >= 4 ) {
			switch (standard->videoInputDef & 0x0F) {
			case 0:
			case 1:
				debug(DS, "EDID says downstream DVI\n");
				s_ds_type = kDPDownstreamTypeDVI;
				break;
			case 5:
				debug(DS, "EDID says downstream DP\n");
				s_ds_type = kDPDownstreamTypeDP;
				break;
			default:
				debug(DS, "EDID says downstream HDMI\n");
				s_ds_type = kDPDownstreamTypeHDMI;
				break;
			}
		}
	}

	if ( standard->feature & kEDIDStandardFeatureFlagGTF ) {
		if ( standard->version == 1 && standard->revision >= 4 )
			s_timing_support = kSupportFlagCVT;
		else 
			s_timing_support = kSupportFlagGTF;
	}
	return 0;
}

static int process_edid_standard_timings(uint8_t *standard, uint32_t length)
{
	uint32_t index;
	for (index = 0; index < length; index += 2) {
		uint16_t timing_id = read_le16(standard, index);
		try_standard_timing_id(timing_id);
	}
	return 0;
}

static int process_edid_standard_established_timings(EDIDStandard *standard)
{
	uint32_t index;

	debug(EDID, "established=0x%02x established1=0x%02x established2=0x%02x\n",
	      standard->established[0],
	      standard->established[1],
	      standard->established[2]);
	
	for (index = 0; index < kEDIDEstablishedTimingsCount; ++index) {
		if (!(standard->established[index>>3] & (1 << (index % 8))))
			continue;
		debug(EDID, "index=%d\n", index);
		try_established_timing_id(index);
	}
	return 0;
}

static int process_edid_detailed(EDIDDetailed *detailed)
{
	if (detailed->data.header.header[0] == 0 && detailed->data.header.header[1] == 0) {
		return process_edid_detailed_descriptor(&(detailed->data.descriptor));
	} else {
		return try_detailed_timing(&(detailed->data.timing));
	}
}

static int process_edid_detailed_descriptor(EDIDDetailedDescriptor *descriptor)
{
	if (!descriptor->flag) return 0;
	
	switch (descriptor->type) {
        case 0xff: //Serial number
		// Don't care.
		break;
        case 0xfd: //Range limits
		// IODPService.cpp doesn't use this.
		// process_edid_detailed_range(&descriptor->data.range);
		break;
        case 0xfc: //Monitor name
		// Don't care.
		break;
        case 0xfb: // color point data
		// Don't care.
		// process_edid_detailed_color_point(&descriptor->data.color);
		break;
        case 0xfa: // standard timing
		process_edid_standard_timings(descriptor->data.standard, sizeof(descriptor->data.standard));
		break;
        case 0xf8: // CVT
		process_edid_cvt_timings(descriptor->data.cvt, sizeof(descriptor->data.cvt));
		break;
        case 0xf7: // established timings III
		process_edid_detailed_established_timings_iii(&descriptor->data.established);
        default: // DCM
		break;		
	}

	return 0;
}

static int process_edid_detailed_established_timings_iii(EDIDDetailedEstablishedTimingsIII *established)
{
	uint32_t index;
    
	debug(EDID, "established=%02x established1=%02x established2=%02x established3=%02x established4=%02x established5=%02x\n", established->data[0], established->data[1], established->data[2], established->data[3], established->data[4], established->data[5]);
    
	for (index = 0; index < kEDIDEstablishedTimingsCount; ++index) {
		debug(EDID, "index=%d mask=%08x\n", index, (1 << (index % 8)));
        
		if ( !(established->data[index>>3] & (1 << (index % 8))) )
			continue;
		
		try_established_timing_iii_id(index);
	}
	
	return 0;
}

static int try_standard_timing_id(uint16_t timing_id)
{
	uint32_t horizontal;
	uint32_t vertical = 0;
	uint32_t rate;
	
	debug(EDID, "timing_id 0x%04x\n", timing_id);

	if (timing_id == 0x0101)
		return -1;

	horizontal = ((timing_id & 0xff) + 31) * 8;
	switch ((timing_id >> 14) & 0x3) {
        case 0:
		vertical = horizontal * 10 / 16;
		break;
        case 1:
		vertical = horizontal * 3 /43;
		break;
        case 2:
		vertical = horizontal * 4 / 5;
		break;
        case 3:
		vertical = horizontal * 9 / 16;
		break;
	}
	
	rate = ((timing_id >> 8) & 0x3f) + 60;
	
	return use_dimensions(horizontal, vertical, rate << 16);
}

static int try_established_timing_id(uint8_t established_id)
{
	static const __IODPTEEstablished established_info[] = {
		{800, 600, 60<<16},
		{800, 600, 56<<16},
		{640, 480, 75<<16},
		{640, 480, 72<<16},
		{640, 480, 67<<16},
		{640, 480, 60<<16},
		{720, 400, 88<<16},
		{720, 400, 70<<16},
		
		{1280, 1024, 75<<16},
		{1024, 768, 75<<16},
		{1024, 768, 70<<16},
		{1024, 768, 60<<16},
		{0,0,0}, //interlaced {1024, 768, 87<<16},
		{832, 624, 75<<16},
		{800, 600, 75<<16},
		{800, 600, 72<<16},
		
		{0,0,0},
		{0,0,0},
		{0,0,0},
		{0,0,0},
		{0,0,0},
		{0,0,0},
		{0,0,0},
		{1152, 870, 75<<16}
	};
	
	if (established_id >= (sizeof(established_info) / sizeof(__IODPTEEstablished)))
		return 0;
        
	return use_dimensions(established_info[established_id].horizontal,
			      established_info[established_id].vertical,
			      established_info[established_id].rate);
}

static int try_established_timing_iii_id(uint8_t establishedID)
{
	static const __IODPTEEstablished sEstablishedInfo[] = {
		{1152, 864, 75<<16},
		{1024, 768, 85<<16},
		{800, 600, 85<<16},
		{848, 480, 60<<16},
		{640, 480, 85<<16},
		{720, 400, 85<<16},
		{640, 400, 85<<16},
		{640, 350, 85<<16},
		
		{1280, 1024, 85<<16},
		{1280, 1024, 60<<16},
		{1280, 960, 85<<16},
		{1280, 960, 60<<16},
		{1280, 768, 85<<16},
		{1280, 768, 75<<16},
		{1280, 768, 60<<16},
		{0,0,0},            // Reduced Blanking {1280, 768, 60<<16},
		
		{1400, 1050, 75<<16},
		{1400, 1050, 60<<16},
		{0,0,0},            // Reduced Blanking {1400, 1050, 60<<16},
		{1440, 900, 85<<16},
		{1440, 900, 75<<16},
		{1440, 900, 60<<16},
		{0,0,0},            // Reduced Blanking {1440, 900, 60<<16},
		{1360, 768, 60<<16},
		
		{1600, 1200, 70<<16},
		{1600, 1200, 65<<16},
		{1600, 1200, 60<<16},
		{1680, 1050, 85<<16},
		{1680, 1050, 75<<16},
		{1680, 1050, 60<<16},
		{0,0,0},            // Reduced Blanking {1680, 1050, 60<<16},
		{1400, 1050, 85<<16},
		
		{1920, 1200, 60<<16},
		{0,0,0},            // Reduced Blanking {1920, 1200, 60<<16},
		{1856, 1392, 75<<16},
		{1856, 1392, 60<<16},
		{1792, 1344, 75<<16},
		{1792, 1344, 60<<16},
		{1600, 1200, 85<<16},
		{1600, 1200, 75<<16},
        
		{0,0,0},
		{0,0,0},
		{0,0,0},
		{0,0,0},
		{1920, 1440, 75<<16},
		{1920, 1440, 60<<16},
		{1920, 1200, 85<<16},
		{1920, 1200, 75<<16},
	};
	
	if (establishedID >= (sizeof(sEstablishedInfo) / sizeof(__IODPTEEstablished)))
		return -1;
        
	return use_dimensions(sEstablishedInfo[establishedID].horizontal, 
			      sEstablishedInfo[establishedID].vertical, 
			      sEstablishedInfo[establishedID].rate);
}

static int try_detailed_timing(EDIDDetailedTiming *detailed)
{
	struct video_timing_data _data;
	uint32_t value;
	uint16_t pixelClock;
	int64_t rate, area;

	bzero(&_data, sizeof(_data));

	value = detailed->hor_addr_low;
	value |= (detailed->hor_addr_blank_high & 0xF0) << 4;
	_data.axis[kDisplayAxisTypeHorizontal].active = value;
    
	value = detailed->hor_fporch;
	value |= (detailed->hor_ver_porch_sync_high & (0x3<<6))<<2;
	_data.axis[kDisplayAxisTypeHorizontal].front_porch = value;
    
	value = detailed->hor_sync;
	value |= (detailed->hor_ver_porch_sync_high & (0x3<<4))<<4;
	_data.axis[kDisplayAxisTypeHorizontal].sync_width = value;
    
	value = detailed->hor_blank_low;
	value |= (detailed->hor_addr_blank_high & 0xF) << 8;
	if ( value < detailed->hor_border )
		return -1;
	value -= detailed->hor_border;
    
	_data.axis[kDisplayAxisTypeHorizontal].total = value + _data.axis[kDisplayAxisTypeHorizontal].active;
    
	if ( value < (_data.axis[kDisplayAxisTypeHorizontal].front_porch + _data.axis[kDisplayAxisTypeHorizontal].sync_width) ) {
		debug(EDID, "Invaid horizontal blanking\n");
		return -1;
	}
    
	_data.axis[kDisplayAxisTypeHorizontal].back_porch = value - _data.axis[kDisplayAxisTypeHorizontal].front_porch - _data.axis[kDisplayAxisTypeHorizontal].sync_width;
    
	value = detailed->ver_addr_low;
	value |= (detailed->ver_addr_blank_high & 0xF0) << 4;
	_data.axis[kDisplayAxisTypeVertical].active = value;
    
	value = (detailed->ver_fporch_sync & 0xF0) >> 4;
	value |= (detailed->hor_ver_porch_sync_high & (0x3<<2))<<2;
	_data.axis[kDisplayAxisTypeVertical].front_porch = value;
    
	value = detailed->ver_fporch_sync & 0x0F;
	value |= (detailed->hor_ver_porch_sync_high & (0x3<<0))<<4;
	_data.axis[kDisplayAxisTypeVertical].sync_width = value;
    
	value = detailed->ver_blank_low;
	value |= (detailed->ver_addr_blank_high & 0xF) << 8;
	if ( value < detailed->ver_border ) {
		debug(EDID, "Vertical blank < vertical border\n");
		return -1;
	}
        
	value -= detailed->ver_border;
    
	_data.axis[kDisplayAxisTypeVertical].total = value + _data.axis[kDisplayAxisTypeVertical].active;
	if ( value < (_data.axis[kDisplayAxisTypeVertical].front_porch + _data.axis[kDisplayAxisTypeVertical].sync_width) )
		return -1;
	_data.axis[kDisplayAxisTypeVertical].back_porch = value - _data.axis[kDisplayAxisTypeVertical].front_porch - _data.axis[kDisplayAxisTypeVertical].sync_width;
	
	pixelClock = (detailed->pixelClock[1]<<8) | detailed->pixelClock[0]; 
	area = _data.axis[kDisplayAxisTypeHorizontal].total * _data.axis[kDisplayAxisTypeVertical].total;

	if ( !area )
		return -1;

	rate = ((((uint64_t)pixelClock) * 10000) << 16) / area;
    
	// round up
	rate += 0x8000;
	rate &= 0xffff0000;
    
	_data.axis[kDisplayAxisTypeVertical].sync_rate = rate;
	_data.interlaced = ( detailed->detailed & (1<<7) ) != 0;
    
	// digital
	if ( ( detailed->detailed & (1<<4) ) != 0 ) {
    
		_data.axis[kDisplayAxisTypeHorizontal].sync_polarity = ( detailed->detailed & (1<<1) ) != 0;

		if ( ( detailed->detailed & (1<<3) ) != 0 ) {
			_data.axis[kDisplayAxisTypeVertical].sync_polarity = ( detailed->detailed & (1<<2) ) != 0;
		} else {
			_data.axis[kDisplayAxisTypeVertical].sync_polarity = _data.axis[kDisplayAxisTypeHorizontal].sync_polarity;

		}

	}

    
	if ( !_data.axis[kDisplayAxisTypeVertical].active || !_data.axis[kDisplayAxisTypeHorizontal].active || !_data.axis[kDisplayAxisTypeVertical].sync_rate)     
		return -1;

	return use_timings(&_data);
}

static int try_cvt_timing_id(EDIDCVT *cvt)
{   
	uint32_t horizontal = 0;
	uint32_t vertical;
	uint32_t rate;
	uint32_t index;
	
	vertical = (((uint32_t)(cvt->data[1]&0xf0))<<4) | cvt->data[0];
	vertical = ( vertical + 1 ) * 2;
	switch ((cvt->data[1]>>2) & 0x3) {
        case 0:
		horizontal = vertical * 4 / 3;
		break;
        case 1:
		horizontal = vertical * 16 / 9;
		break;
        case 2:
		horizontal = vertical * 16 / 10;
		break;
        default:
		return -1;
	}
	
	index = (cvt->data[2]>>5) & 0x3;
	if ( cvt->data[2] & (1<<(4-index)) ) {
		static const uint32_t sRates[] = { 50, 60, 75, 80 };
		rate = sRates[index];
	} else {
		return -1;
	}
	
	return use_dimensions(horizontal, vertical, rate<<16);
}

static int try_cea_short_id(uint32_t shortVideoID)
{
	struct video_timing_data data;
	debug(EDID, "cea_short_id 0x%08x\n", shortVideoID);

	if (get_timing_data_for_cea_short_id(shortVideoID, &data) != 0)
		return -1;
	
	return use_timings(&data);
}

static int get_timing_data_for_cea_short_id(uint32_t shortVideoID, struct video_timing_data *timingData)
{
    bzero(timingData, sizeof(struct video_timing_data));
    
    switch ( shortVideoID ) {
            // 1 640x480p @ 60
        case 1:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 800;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 640;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 96;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 48;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 16;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 525;
            timingData->axis[kDisplayAxisTypeVertical].active          = 480;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 2;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 33;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 10;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 60<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;
            break;        
            // 2,3 720x480p @ 60
        case 2:
        case 3:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 858;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 720;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 62;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 60;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 16;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 525;
            timingData->axis[kDisplayAxisTypeVertical].active          = 480;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 6;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 30;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 9;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 60<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;
            break; 
            // 4 1280x720p @ 60
        case 4:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 1650;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1280;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 40;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 220;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 110;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 1;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 750;
            timingData->axis[kDisplayAxisTypeVertical].active          = 720;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 20;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 5;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 60<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 1;
            break;        
            // 7, 8 720(1440)x240p @ 60
        case 8:
        case 9:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 1716;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1440;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 124;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 114;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 38;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 262;
            timingData->axis[kDisplayAxisTypeVertical].active          = 240;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 3;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 15;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 4;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 60<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;            
            break;        
            // 12, 13 (2880)x240p @ 60
        case 12:
        case 13:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 3432;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 2880;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 248;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 228;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 76;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 262;
            timingData->axis[kDisplayAxisTypeVertical].active          = 240;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 3;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 15;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 4;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 60<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;
            break;
            // 6,7 720(1440)x480i @ 60
        case 6:
        case 7:
            timingData->interlaced = true;        
            // 14,15 720(1440)x480p @ 60
        case 14:
        case 15:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 1716;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1440;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 124;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 120;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 32;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 525;
            timingData->axis[kDisplayAxisTypeVertical].active          = 480;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 6;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 30;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 9;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 60<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;            
            break;        
            // 5 1920x1080i @ 60
        case 5:
            timingData->interlaced = true;
            // 16 1920x1080p @ 60
        case 16:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 2200;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1920;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 44;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 148;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 88;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 1;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 1125;
            timingData->axis[kDisplayAxisTypeVertical].active          = 1080;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 36;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 4;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 60<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 1;            
            break;     
            // 21, 22 720(1440)x576i @ 50
        case 21:
        case 22:
            timingData->interlaced = true;
            // 17 18 720x576p @ 50
        case 17:
        case 18:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 864;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 720;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 64;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 68;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 12;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 625;
            timingData->axis[kDisplayAxisTypeVertical].active          = 576;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 39;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 5;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 50<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;    
            break;
            // 19 1280x720p @ 50
        case 19:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 1980;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1280;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 40;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 220;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 440;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 1;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 750;
            timingData->axis[kDisplayAxisTypeVertical].active          = 720;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 20;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 5;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 50<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 1;
            break;        
            // 23, 24 720(1440)x288p @ 50
        case 23:
        case 24:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 1728;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1440;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 126;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 138;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 24;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 312;
            timingData->axis[kDisplayAxisTypeVertical].active          = 288;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 3;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 19;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 2;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 50<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;                        
            break;  
            // 27, 28 (2880)x288p @ 50
        case 27:
        case 28:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 3456;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 2880;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 252;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 276;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 48;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 312;
            timingData->axis[kDisplayAxisTypeVertical].active          = 288;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 3;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 19;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 2;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 50<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;            
            break;        
            // 29, 30 1440x576p @ 50
        case 29:
        case 30:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 1728;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1440;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 128;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 136;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 24;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 625;
            timingData->axis[kDisplayAxisTypeVertical].active          = 576;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 39;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 5;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 50<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;            
            break;        
            // 20 1920x1080i @ 50
        case 20:
            timingData->interlaced = true;
            // 31 1920x1080p @ 50
        case 31:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 2640;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1920;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 44;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 148;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 528;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 1;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 1125;
            timingData->axis[kDisplayAxisTypeVertical].active          = 1080;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 36;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 4;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 50<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 1;            
            break;
            // 32 1920x1080p @ 24
        case 32:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 2750;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1920;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 44;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 148;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 638;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 1;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 1125;
            timingData->axis[kDisplayAxisTypeVertical].active          = 1080;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 36;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 4;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 24<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 1;            
            break;
            // 33 1920x1080p @ 25
        case 33:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 2640;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1920;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 44;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 148;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 528;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 1;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 1125;
            timingData->axis[kDisplayAxisTypeVertical].active          = 1080;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 36;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 4;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 25<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 1;            
            break;
            // 34 1920x1080p @ 30
        case 34:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 2200;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1920;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 44;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 148;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 88;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 1;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 1125;
            timingData->axis[kDisplayAxisTypeVertical].active          = 1080;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 36;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 4;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 30<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 1;            
            break;
            // 10, 11 (2880)x480i @ 60
        case 10:
        case 11:
            timingData->interlaced = true;
            // 35, 36 2880x480p @ 60
        case 35:
        case 36:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 3432;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1920;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 248;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 240;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 64;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 525;
            timingData->axis[kDisplayAxisTypeVertical].active          = 480;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 6;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 30;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 9;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 60<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;            
            break;
            // 25, 26 (2880)x576i @ 50
        case 25:
        case 26:
            timingData->interlaced = true;
            // 37, 38 2880x576p @ 50
        case 37:
        case 38:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 3456;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 2880;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 256;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 272;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 48;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 625;
            timingData->axis[kDisplayAxisTypeVertical].active          = 576;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 39;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 5;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 50<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;            
            break;
            // 39 1920x1080i @ 50
        case 39:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 2304;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1920;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 168;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 184;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 32;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 1;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 1250;
            timingData->axis[kDisplayAxisTypeVertical].active          = 1080;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 120;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 45;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 50<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;            

            timingData->interlaced = true;
            break;
            // 40 1920x1080i @ 100
        case 40:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 2640;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1920;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 44;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 148;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 528;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 1;        

            timingData->axis[kDisplayAxisTypeVertical].total           = 1250;
            timingData->axis[kDisplayAxisTypeVertical].active          = 1080;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 120;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 45;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 50<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 1;            
            
            timingData->interlaced = true;
            break;
            // 41 1280x720p @ 100
        case 41:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 1980;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1280;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 40;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 220;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 440;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 1;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 750;
            timingData->axis[kDisplayAxisTypeVertical].active          = 720;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 20;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 5;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 100<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 1;
            break;        
            // 44, 45 720(1440)x576i @ 100  
        case 44:
        case 45:
            timingData->interlaced = true;
            // 42, 43 720x576p @ 100
        case 42:
        case 43:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 864;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 720;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 64;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 68;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 12;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 625;
            timingData->axis[kDisplayAxisTypeVertical].active          = 576;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 39;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 5;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 100<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;    
            break;
            // 46 1920x1280i @ 120  
        case 46:
            return -1;
            // 47 1280x720p @ 120
        case 47:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 1650;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 1280;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 40;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 220;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 110;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 1;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 750;
            timingData->axis[kDisplayAxisTypeVertical].active          = 720;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 20;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 5;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 120<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 1;
            break;        
            // 48, 49 720x480p @ 120
        case 48:
        case 49:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 858;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 720;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 62;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 60;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 16;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 525;
            timingData->axis[kDisplayAxisTypeVertical].active          = 480;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 6;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 30;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 9;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 120<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;
            break; 
            // 50, 51 720(1440)x480i @ 120
        case 50:
        case 51:
            return -1;
            // 42, 43 720x576p @ 100
        case 52:
        case 53:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 864;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 720;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 64;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 68;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 12;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 625;
            timingData->axis[kDisplayAxisTypeVertical].active          = 576;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 5;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 39;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 5;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 200<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;    
            break;
            // 54, 55 720(1440)x576i @ 200
        case 54:
        case 55:
            return -1;
            // 56, 57 720x480p @ 240
        case 56:
        case 57:
            timingData->axis[kDisplayAxisTypeHorizontal].total         = 858;
            timingData->axis[kDisplayAxisTypeHorizontal].active        = 720;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_width     = 62;
            timingData->axis[kDisplayAxisTypeHorizontal].back_porch     = 60;
            timingData->axis[kDisplayAxisTypeHorizontal].front_porch    = 16;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_rate      = 0;
            timingData->axis[kDisplayAxisTypeHorizontal].sync_polarity  = 0;        
            
            timingData->axis[kDisplayAxisTypeVertical].total           = 525;
            timingData->axis[kDisplayAxisTypeVertical].active          = 480;
            timingData->axis[kDisplayAxisTypeVertical].sync_width       = 6;
            timingData->axis[kDisplayAxisTypeVertical].back_porch       = 30;
            timingData->axis[kDisplayAxisTypeVertical].front_porch      = 9;
            timingData->axis[kDisplayAxisTypeVertical].sync_rate        = 240<<16;
            timingData->axis[kDisplayAxisTypeVertical].sync_polarity    = 0;
            break; 
            // 58, 59 720(1440)x480i @ 240
        case 58:
        case 59:
            return -1;
            // more to come...too lazy right now
        default:
            return -1;
            break;
    }
        
    return 0;
}

static int use_dimensions(uint32_t horizontal, uint32_t vertical, uint32_t rate)
{
	int ret = -1;
	debug(EDID, "Use dimensions %ux%u %uHz\n", horizontal, vertical, rate >> 16);
	if (s_timing_support & kSupportFlagCVT) {
		ret = use_dimensions_cvt(horizontal, vertical, rate);
	} else if (s_timing_support & kSupportFlagGTF) {
		ret = use_dimensions_gtf(horizontal, vertical, rate);
	}
	if (ret != 0) {
		ret = use_dimensions_dmt(horizontal, vertical, rate);
	}
	return ret;
}

static int use_dimensions_dmt(uint32_t horizontal, uint32_t vertical, uint32_t rate)
{
    static const struct video_timing_data sDMTTimingInfo[] = { 
        {false, {{832,  640,    64,     96,     32,     0,  1}, {445,   350,    3,  60,     32, 85<<16,     0}} },
        {false, {{832,  640,    64,     96,     32,     0,  0}, {445,   400,    3,  41,     1,  85<<16,     1}} },
        {false, {{936,  720,    72,     108,    36,     0,  0}, {446,   400,    3,  42,     1,  85<<16,     1}} },
        {false, {{800,  640,    96,     48,     16,     0,  0}, {525,   480,    2,  33,     10, 60<<16,     0}} },
        {false, {{832,  640,    40,     128,    24,     0,  0}, {520,   480,    3,  28,     9,  72<<16,     0}} },
        {false, {{840,  640,    64,     120,    16,     0,  0}, {500,   480,    3,  16,     1,  75<<16,     0}} },
        {false, {{832,  640,    56,     80,     56,     0,  0}, {509,   480,    3,  25,     1,  85<<16,     0}} },
        {false, {{1024, 800,    72,     128,    24,     0,  1}, {625,   600,    2,  22,     1,  56<<16,     1}} },
        {false, {{1056, 800,    128,    88,     40,     0,  1}, {628,   600,    4,  23,     1,  60<<16,     1}} },
        {false, {{1040, 800,    120,    64,     56,     0,  1}, {666,   600,    6,  23,     37, 72<<16,     1}} },
        {false, {{1056, 800,    80,     160,    16,     0,  1}, {625,   600,    3,  21,     1,  75<<16,     1}} },
        {false, {{1048, 800,    64,     152,    32,     0,  1}, {631,   600,    3,  27,     1,  85<<16,     1}} },
        {false, {{960,  800,    32,     80,     48,     0,  1}, {636,   600,    4,  29,     3,  120<<16,    0}} },
        {false, {{1088, 848,    112,    112,    16,     0,  1}, {517,   480,    8,  23,     6,  60<<16,     1}} },
        {false, {{1344, 1024,   136,    160,    24,     0,  0}, {806,   768,    6,  29,     3,  60<<16,     0}} },
        {false, {{1328, 1024,   136,    144,    24,     0,  0}, {806,   768,    6,  29,     3,  70<<16,     0}} },
        {false, {{1312, 1024,   96,     176,    16,     0,  1}, {800,   768,    3,  28,     1,  75<<16,     1}} },
        {false, {{1376, 1024,   96,     208,    48,     0,  1}, {808,   768,    3,  36,     1,  85<<16,     1}} },
        {false, {{1600, 1152,   128,    256,    64,     0,  1}, {900,   864,    3,  32,     1,  75<<16,     1}} },
        {false, {{1650, 1280,   40,     220,    110,    0,  1}, {750,   720,    5,  20,     5,  60<<16,     1}} },
        {false, {{1664, 1280,   128,    192,    64,     0,  0}, {798,   768,    7,  20,     3,  60<<16,     1}} },
        {false, {{1696, 1280,   128,    208,    80,     0,  0}, {805,   768,    7,  27,     3,  75<<16,     1}} },
        {false, {{1712, 1280,   136,    216,    80,     0,  0}, {809,   768,    7,  31,     3,  85<<16,     1}} },
        {false, {{1680, 1280,   128,    200,    72,     0,  0}, {831,   800,    6,  22,     3,  60<<16,     1}} },
        {false, {{1696, 1280,   128,    208,    80,     0,  0}, {838,   800,    6,  29,     3,  75<<16,     1}} },
        {false, {{1712, 1280,   136,    216,    80,     0,  0}, {843,   800,    6,  34,     3,  85<<16,     1}} },
        {false, {{1800, 1280,   112,    312,    96,     0,  1}, {1000,  960,    3,  36,     1,  60<<16,     1}} },
        {false, {{1728, 1280,   160,    224,    64,     0,  1}, {1011,  960,    3,  47,     1,  85<<16,     1}} },
        {false, {{1688, 1280,   112,    248,    48,     0,  1}, {1066,  1024,   3,  38,     1,  60<<16,     1}} },
        {false, {{1688, 1280,   144,    248,    16,     0,  1}, {1066,  1024,   3,  38,     1,  75<<16,     1}} },
        {false, {{1728, 1280,   160,    224,    64,     0,  1}, {1072,  1024,   3,  44,     1,  85<<16,     1}} },
        {false, {{1792, 1360,   112,    256,    64,     0,  1}, {795,   768,    6,  18,     3,  60<<16,     1}} },
        {false, {{1792, 1366,   143,    213,    70,     0,  1}, {798,   768,    3,  24,     3,  60<<16,     1}} },
        {false, {{1864, 1400,   144,    232,    88,     0,  0}, {1089,  1050,   4,  32,     3,  60<<16,     1}} },
        {false, {{1896, 1400,   144,    248,    104,    0,  0}, {1099,  1050,   4,  42,     3,  75<<16,     1}} },
        {false, {{1912, 1400,   152,    256,    104,    0,  0}, {1105,  1050,   4,  48,     3,  85<<16,     1}} },
        {false, {{1904, 1440,   152,    232,    80,     0,  0}, {934,   900,    6,  25,     3,  60<<16,     1}} },
        {false, {{1936, 1440,   152,    248,    96,     0,  0}, {942,   900,    6,  33,     3,  75<<16,     1}} },
        {false, {{1952, 1440,   152,    256,    104,    0,  0}, {948,   900,    6,  39,     3,  85<<16,     1}} },
        {false, {{2160, 1600,   192,    304,    64,     0,  1}, {1250,  1200,   3,  46,     1,  60<<16,     1}} },
        {false, {{2160, 1600,   192,    304,    64,     0,  1}, {1250,  1200,   3,  46,     1,  65<<16,     1}} },
        {false, {{2160, 1600,   192,    304,    64,     0,  1}, {1250,  1200,   3,  46,     1,  70<<16,     1}} },
        {false, {{2160, 1600,   192,    304,    64,     0,  1}, {1250,  1200,   3,  46,     1,  75<<16,     1}} },
        {false, {{2160, 1600,   192,    304,    64,     0,  1}, {1250,  1200,   3,  46,     1,  85<<16,     1}} },
        {false, {{2240, 1680,   176,    280,    104,    0,  0}, {1089,  1050,   6,  30,     3,  60<<16,     1}} },
        {false, {{2272, 1680,   176,    296,    120,    0,  0}, {1099,  1050,   6,  40,     3,  75<<16,     1}} },
        {false, {{2288, 1680,   176,    304,    128,    0,  0}, {1105,  1050,   6,  46,     3,  85<<16,     1}} },
        {false, {{2448, 1792,   200,    328,    128,    0,  0}, {1394,  1344,   3,  46,     1,  60<<16,     1}} },
        {false, {{2456, 1792,   216,    352,    96,     0,  0}, {1417,  1344,   3,  69,     1,  75<<16,     1}} },
        {false, {{2528, 1856,   224,    352,    96,     0,  0}, {1439,  1392,   3,  43,     1,  60<<16,     1}} },
        {false, {{2560, 1856,   224,    352,    128,    0,  0}, {1500,  1392,   3,  104,    1,  75<<16,     1}} },
        {false, {{2200, 1920,   44,     148,    88,     0,  1}, {1125,  1080,   5,  36,     4,  60<<16,     1}} },
        {false, {{2592, 1920,   200,    336,    136,    0,  0}, {1245,  1200,   6,  36,     3,  60<<16,     1}} },
        {false, {{2608, 1920,   208,    344,    136,    0,  0}, {1255,  1200,   6,  46,     3,  75<<16,     1}} },
        {false, {{2624, 1920,   208,    352,    144,    0,  0}, {1262,  1200,   6,  53,     3,  85<<16,     1}} },
        {false, {{2600, 1920,   208,    344,    128,    0,  0}, {1500,  1440,   3,  56,     1,  60<<16,     1}} },
        {false, {{2640, 1920,   224,    352,    144,    0,  0}, {1500,  1440,   3,  56,     1,  75<<16,     1}} },
        {false, {{3504, 2560,   280,    472,    192,    0,  0}, {1658,  1600,   6,  49,     3,  60<<16,     1}} },
        {false, {{3536, 2560,   280,    488,    208,    0,  0}, {1672,  1600,   6,  63,     3,  75<<16,     1}} },
        {false, {{3536, 2560,   280,    488,    208,    0,  0}, {1682,  1600,   6,  73,     3,  85<<16,     1}} }
    };

    return use_dimensions_table(sDMTTimingInfo, (sizeof(sDMTTimingInfo) / sizeof(struct video_timing_data)), horizontal, vertical, rate);
}

static int use_dimensions_cvt(uint32_t horizontal, uint32_t vertical, uint32_t rate)
{
    static const struct video_timing_data sCVTTimingInfo[] = { 
        {false, {{816,  640,    64,     88,     24,     0,  0}, {423,   400,    6,  14,     3,  85<<16,     1}} },
        {false, {{800,  640,    64,     80,     16,     0,  0}, {500,   480,    4,  13,     3,  60<<16,     1}} },
        {false, {{816,  640,    64,     88,     24,     0,  0}, {504,   480,    4,  17,     3,  75<<16,     1}} },
        {false, {{816,  640,    64,     88,     24,     0,  0}, {507,   480,    4,  20,     3,  85<<16,     1}} },
        {false, {{1024, 800,    80,     112,    32,     0,  0}, {624,   600,    4,  17,     3,  60<<16,     1}} },
        {false, {{1040, 800,    80,     120,    40,     0,  0}, {625,   600,    4,  22,     3,  75<<16,     1}} },
        {false, {{1056, 800,    80,     128,    48,     0,  0}, {633,   600,    4,  26,     3,  85<<16,     1}} },
        {false, {{1328, 1024,   104,    152,    48,     0,  0}, {798,   768,    4,  23,     3,  60<<16,     1}} },
        {false, {{1360, 1024,   104,    168,    64,     0,  0}, {805,   768,    4,  30,     3,  75<<16,     1}} },
        {false, {{1376, 1024,   104,    176,    72,     0,  0}, {809,   768,    4,  34,     3,  85<<16,     1}} },
        {false, {{1536, 1152,   120,    192,    72,     0,  0}, {905,   864,    4,  34,     3,  75<<16,     1}} },
        {false, {{1664, 1280,   128,    192,    64,     0,  0}, {748,   720,    5,  20,     3,  60<<16,     1}} },
        {false, {{1664, 1280,   128,    192,    64,     0,  0}, {798,   768,    7,  20,     3,  60<<16,     1}} },
        {false, {{1696, 1280,   128,    208,    80,     0,  0}, {805,   768,    7,  27,     3,  75<<16,     1}} },
        {false, {{1712, 1280,   136,    216,    80,     0,  0}, {809,   768,    7,  31,     3,  85<<16,     1}} },
        {false, {{1680, 1280,   128,    200,    72,     0,  0}, {831,   800,    6,  22,     3,  60<<16,     1}} },
        {false, {{1696, 1280,   128,    208,    80,     0,  0}, {838,   800,    6,  29,     3,  75<<16,     1}} },
        {false, {{1712, 1280,   136,    216,    80,     0,  0}, {843,   800,    6,  34,     3,  85<<16,     1}} },
        {false, {{1696, 1280,   128,    208,    80,     0,  0}, {996,   960,    4,  29,     3,  60<<16,     1}} },
        {false, {{1728, 1280,   136,    224,    88,     0,  0}, {1011,  960,    4,  44,     3,  85<<16,     1}} },
        {false, {{1712, 1280,   136,    216,    80,     0,  0}, {1063,  1024,   7,  29,     3,  60<<16,     1}} },
        {false, {{1728, 1280,   136,    224,    88,     0,  0}, {1072,  1024,   7,  38,     3,  75<<16,     1}} },
        {false, {{1744, 1280,   136,    232,    96,     0,  0}, {1078,  1024,   7,  44,     3,  85<<16,     1}} },
        {false, {{1776, 1360,   136,    208,    72,     0,  0}, {798,   768,    5,  22,     3,  60<<16,     1}} },
        {false, {{1864, 1400,   144,    232,    88,     0,  0}, {1089,  1050,   4,  32,     3,  60<<16,     1}} },
        {false, {{1896, 1400,   144,    248,    104,    0,  0}, {1099,  1050,   4,  42,     3,  75<<16,     1}} },
        {false, {{1912, 1400,   152,    256,    104,    0,  0}, {1105,  1050,   4,  48,     3,  85<<16,     1}} },
        {false, {{1904, 1440,   152,    232,    80,     0,  0}, {934,   900,    6,  25,     3,  60<<16,     1}} },
        {false, {{1936, 1440,   152,    248,    96,     0,  0}, {942,   900,    6,  33,     3,  75<<16,     1}} },
        {false, {{1952, 1440,   152,    256,    104,    0,  0}, {948,   900,    6,  39,     3,  85<<16,     1}} },
        {false, {{2160, 1600,   168,    280,    112,    0,  0}, {1245,  1200,   4,  38,     3,  60<<16,     1}} },
        {false, {{2160, 1600,   168,    288,    120,    0,  0}, {1255,  1200,   4,  48,     3,  75<<16,     1}} },
        {false, {{2192, 1600,   168,    296,    128,    0,  0}, {1262,  1200,   4,  55,     3,  85<<16,     1}} },
        {false, {{2240, 1680,   176,    280,    104,    0,  0}, {1089,  1050,   6,  30,     3,  60<<16,     1}} },
        {false, {{2272, 1680,   176,    296,    120,    0,  0}, {1099,  1050,   6,  40,     3,  75<<16,     1}} },
        {false, {{2288, 1680,   176,    304,    128,    0,  0}, {1105,  1050,   6,  46,     3,  85<<16,     1}} },
        {false, {{2432, 1792,   192,    320,    128,    0,  0}, {1393,  1344,   4,  42,     3,  60<<16,     1}} },
        {false, {{2448, 1792,   192,    328,    136,    0,  0}, {1405,  1344,   4,  54,     3,  75<<16,     1}} },
        {false, {{2512, 1856,   200,    328,    128,    0,  0}, {1443,  1392,   4,  44,     3,  60<<16,     1}} },
        {false, {{2544, 1856,   200,    344,    144,    0,  0}, {1456,  1392,   4,  57,     3,  75<<16,     1}} },
        {false, {{2576, 1920,   200,    328,    128,    0,  0}, {1120,  1080,   5,  32,     3,  60<<16,     1}} },
        {false, {{2592, 1920,   200,    336,    136,    0,  0}, {1245,  1200,   6,  36,     3,  60<<16,     1}} },
        {false, {{2608, 1920,   208,    344,    136,    0,  0}, {1255,  1200,   6,  46,     3,  75<<16,     1}} },
        {false, {{2624, 1920,   208,    352,    144,    0,  0}, {1262,  1200,   6,  53,     3,  85<<16,     1}} },
        {false, {{2608, 1920,   208,    344,    136,    0,  0}, {1493,  1440,   4,  46,     3,  60<<16,     1}} },
        {false, {{2640, 1920,   208,    360,    152,    0,  0}, {1506,  1440,   4,  59,     3,  75<<16,     1}} },
        {false, {{3504, 2560,   280,    472,    192,    0,  0}, {1658,  1600,   6,  49,     3,  60<<16,     1}} },
        {false, {{3536, 2560,   280,    488,    208,    0,  0}, {1672,  1600,   6,  63,     3,  75<<16,     1}} },
        {false, {{3536, 2560,   280,    488,    208,    0,  0}, {1682,  1600,   6,  73,     3,  85<<16,     1}} }
    };

    return use_dimensions_table(sCVTTimingInfo, (sizeof(sCVTTimingInfo) / sizeof(struct video_timing_data)), horizontal, vertical, rate);
}

static int use_dimensions_gtf(uint32_t horizontal, uint32_t vertical, uint32_t rate)
{
    static const struct video_timing_data sGTFTimingInfo[] = { 
        {false, {{816,  640,    64,      88,    24,     0,  0}, {421,   400,    3,  17,     1,  85<<16,     1}} },
        {false, {{800,  640,    64,      80,    16,     0,  0}, {497,   480,    3,  13,     1,  60<<16,     1}} },
        {false, {{816,  640,    64,      88,    24,     0,  0}, {502,   480,    3,  18,     1,  75<<16,     1}} },
        {false, {{832,  640,    64,      96,    32,     0,  0}, {505,   480,    3,  21,     1,  85<<16,     1}} },
        {false, {{1024, 800,    80,     112,    32,     0,  0}, {622,   600,    3,  18,     1,  60<<16,     1}} },
        {false, {{1040, 800,    80,     120,    40,     0,  0}, {627,   600,    3,  23,     1,  75<<16,     1}} },
        {false, {{1056, 800,    88,     128,    40,     0,  0}, {630,   600,    3,  26,     1,  85<<16,     1}} },
        {false, {{1344, 1024,   104,    160,    56,     0,  0}, {795,   768,    3,  23,     1,  60<<16,     1}} },
        {false, {{1360, 1024,   112,    168,    56,     0,  0}, {802,   768,    3,  30,     1,  75<<16,     1}} },
        {false, {{1376, 1024,   112,    176,    64,     0,  0}, {807,   768,    3,  35,     1,  85<<16,     1}} },
        {false, {{1552, 1152,   128,    200,    72,     0,  0}, {902,   864,    3,  34,     1,  75<<16,     1}} },
        {false, {{1664, 1280,   136,    192,    56,     0,  0}, {746,   720,    3,  22,     1,  60<<16,     1}} },
        {false, {{1680, 1280,   136,    200,    64,     0,  0}, {795,   768,    3,  23,     1,  60<<16,     1}} },
        {false, {{1712, 1280,   136,    216,    80,     0,  0}, {802,   768,    3,  30,     1,  75<<16,     1}} },
        {false, {{1728, 1280,   136,    224,    88,     0,  0}, {807,   768,    3,  35,     1,  85<<16,     1}} },
        {false, {{1680, 1280,   136,    200,    64,     0,  0}, {828,   800,    3,  24,     1,  60<<16,     1}} },
        {false, {{1712, 1280,   136,    216,    80,     0,  0}, {835,   800,    3,  31,     1,  75<<16,     1}} },
        {false, {{1728, 1280,   136,    224,    88,     0,  0}, {840,   800,    3,  36,     1,  85<<16,     1}} },
        {false, {{1712, 1280,   136,    216,    80,     0,  0}, {994,   960,    3,  30,     1,  60<<16,     1}} },
        {false, {{1744, 1280,   136,    232,    96,     0,  0}, {1008,  960,    3,  44,     1,  85<<16,     1}} },
        {false, {{1712, 1280,   136,    216,    80,     0,  0}, {1060,  1024,   3,  32,     1,  60<<16,     1}} },
        {false, {{1728, 1280,   136,    224,    88,     0,  0}, {1069,  1024,   3,  44,     1,  75<<16,     1}} },
        {false, {{1744, 1280,   136,    232,    96,     0,  0}, {1075,  1024,   3,  47,     1,  85<<16,     1}} },
        {false, {{1776, 1360,   144,    208,    64,     0,  0}, {795,   768,    3,  23,     1,  60<<16,     1}} },
        {false, {{1800, 1366,   144,    216,    72,     0,  0}, {795,   768,    3,  23,     1,  60<<16,     1}} },
        {false, {{1880, 1400,   152,    240,    88,     0,  0}, {1087,  1050,   3,  33,     1,  60<<16,     1}} },
        {false, {{1896, 1400,   152,    248,    96,     0,  0}, {1096,  1050,   3,  42,     1,  75<<16,     1}} },
        {false, {{1912, 1400,   152,    256,    104,    0,  0}, {1103,  1050,   3,  49,     1,  85<<16,     1}} },
        {false, {{1904, 1440,   152,    232,    80,     0,  0}, {932,   900,    3,  28,     1,  60<<16,     1}} },
        {false, {{1936, 1440,   152,    248,    96,     0,  0}, {940,   900,    3,  36,     1,  75<<16,     1}} },
        {false, {{1952, 1440,   160,    256,    96,     0,  0}, {945,   900,    3,  41,     1,  85<<16,     1}} },
        {false, {{2160, 1600,   176,    280,    104,    0,  0}, {1242,  1200,   3,  38,     1,  60<<16,     1}} },
        {false, {{2192, 1600,   176,    296,    120,    0,  0}, {1253,  1200,   3,  49,     1,  75<<16,     1}} },
        {false, {{2192, 1600,   176,    296,    120,    0,  0}, {1260,  1200,   3,  56,     1,  85<<16,     1}} },
        {false, {{2256, 1680,   184,    288,    104,    0,  0}, {1087,  1050,   3,  33,     1,  60<<16,     1}} },
        {false, {{2288, 1680,   184,    304,    120,    0,  0}, {1096,  1050,   3,  42,     1,  75<<16,     1}} },
        {false, {{2288, 1680,   184,    304,    120,    0,  0}, {1103,  1050,   3,  49,     1,  85<<16,     1}} },
        {false, {{2432, 1792,   192,    320,    128,    0,  0}, {1391,  1344,   3,  43,     1,  60<<16,     1}} },
        {false, {{2464, 1792,   200,    336,    136,    0,  0}, {1403,  1344,   3,  55,     1,  75<<16,     1}} },
        {false, {{2528, 1856,   200,    336,    136,    0,  0}, {1441,  1392,   3,  45,     1,  60<<16,     1}} },
        {false, {{2544, 1856,   200,    344,    144,    0,  0}, {1453,  1392,   3,  57,     1,  75<<16,     1}} },
        {false, {{2576, 1920,   208,    328,    120,    0,  0}, {1118,  1080,   3,  34,     1,  60<<16,     1}} },
        {false, {{2592, 1920,   208,    336,    128,    0,  0}, {1242,  1200,   3,  38,     1,  60<<16,     1}} },
        {false, {{2624, 1920,   208,    352,    144,    0,  0}, {1253,  1200,   3,  49,     1,  75<<16,     1}} },
        {false, {{2640, 1920,   208,    360,    152,    0,  0}, {1260,  1200,   3,  56,     1,  85<<16,     1}} },
        {false, {{2624, 1920,   208,    352,    144,    0,  0}, {1490,  1440,   3,  46,     1,  60<<16,     1}} },
        {false, {{2640, 1920,   208,    360,    152,    0,  0}, {1503,  1440,   3,  59,     1,  75<<16,     1}} },
        {false, {{3504, 2560,   280,    472,    192,    0,  0}, {1656,  1600,   3,  52,     1,  60<<16,     1}} },
        {false, {{3536, 2560,   280,    488,    208,    0,  0}, {1670,  1600,   3,  66,     1,  75<<16,     1}} },
        {false, {{3552, 2560,   288,    496,    208,    0,  0}, {1680,  1600,   3,  76,     1,  85<<16,     1}} }
    };

    return use_dimensions_table(sGTFTimingInfo, (sizeof(sGTFTimingInfo) / sizeof(struct video_timing_data)), horizontal, vertical, rate);
}

static int use_dimensions_table(const struct video_timing_data * list, uint32_t count, uint32_t horizontal, uint32_t vertical, uint32_t rate)
{
	const struct video_timing_data * matching    = NULL;
	uint32_t                    roundRate   = (rate + 0x8000) & 0xffff0000;
	uint32_t                    index;
	struct video_timing_data data;
	
	if ( !list || !count || !horizontal || !vertical || !rate )
		return -1;
    
	for (index=0; index<count; index++) {
		const struct video_timing_data * timing = &list[index];
		
		if ( timing->axis[kDisplayAxisTypeHorizontal].active != horizontal )
			continue;
		
		if ( timing->axis[kDisplayAxisTypeVertical].active != vertical )
			continue;
		
		if ( (uint32_t) timing->axis[kDisplayAxisTypeVertical].sync_rate != roundRate )
			continue;
		
		matching = timing;
		break;
	}
    
	if ( !matching )
		return -1;
    
	bcopy(matching, &data, sizeof(data));
	data.axis[kDisplayAxisTypeVertical].sync_rate = rate;
	return use_timings(&data);
}

static int use_timings(struct video_timing_data *data)
{
	bool winner = false;
	uint32_t width, height, rate, size, old_rate;
	width = data->axis[kDisplayAxisTypeHorizontal].active;
	height = data->axis[kDisplayAxisTypeVertical].active;
	rate = data->axis[kDisplayAxisTypeVertical].sync_rate;
	size = width * height * 4;  // Assume RGBx8888

	debug(SCORE, "Try timings %ux%u%c/%u\n",
	      width, height, data->interlaced ? 'i' : 'p', rate >> 16);

	// Reject timings we can't support or don't like.
	if (dp_controller_validate_video(data) != 0) {
		debug(SCORE, "Rejecting: bad timings\n");
		return -1;
	} else if (s_restrict_h_active && s_restrict_v_active &&
		   (width != s_restrict_h_active || height != s_restrict_v_active)) {
		// Restricting to specific dimensions.
		debug(SCORE, "Rejecting dimensions not matching %ux%u\n",
		      s_restrict_h_active, s_restrict_v_active);
		return -1;
	} else if (width > kMaxHorizontalActive) {
		debug(SCORE, "Rejecting: horizontal active %u greater than %u\n",
		      width, kMaxHorizontalActive);
		return -1;
	} else if (size > DISPLAY_SIZE) {
		debug(SCORE, "Rejecting: total size %u too big for buffer %zu bytes\n",
		      size, (size_t)DISPLAY_SIZE);
		return -1;
	}

	debug(SCORE, "Accepted\n");

	// Score these new timings vs the best we have so far. Note than wider/taller
	// scoring will only occur if restrict_edid() was not used.
	if (s_have_timings) {
		old_rate = s_best_timings.axis[kDisplayAxisTypeVertical].sync_rate;
	} else {
		old_rate = 0;
	}

	if (!s_have_timings) {
		winner = true;
		debug(SCORE, "First candidate\n");
	} else if ((rate >= (59 << 16) && rate <= (61 << 16)) &&
		   (old_rate < (59 << 16) || old_rate > (61 << 16))) {
		winner = true;
		debug(SCORE, "Preferring 60Hz to non-60Hz\n");
	} else if (width > s_best_timings.axis[kDisplayAxisTypeHorizontal].active) {
		winner = true;
		debug(SCORE, "Preferring wider\n");
	} else if (height > s_best_timings.axis[kDisplayAxisTypeVertical].active) {
		winner = true;
		debug(SCORE, "Preferring taller\n");
	}

	if (winner) {
		s_have_timings = true;
		bcopy(data, &s_best_timings, sizeof(s_best_timings));
	}

	return 0;
}

static int verify_edid(EDID *edid, uint8_t *p_checksum)
{
	uint32_t index;
	uint8_t sum;

	debug(EDID, "Validating EDID\n");

	for (index = 0, sum = 0; index < sizeof(EDID); ++index) {
		sum += ((uint8_t *) edid)[index];
	}

	debug(EDID, "EDID checksum of 0x%02x\n", sum);

	if (p_checksum)
		*p_checksum = sum;

	return sum == 0 ? 0 : -1;
}

static int get_edid(uint32_t offset, EDID *edid)
{
	int ret;
	uint32_t tries = 0;
	uint64_t timer_start;

	timer_start = system_time();

	do {
		uint8_t checksum;
		++tries;
		ret = dp_controller_read_bytes_i2c(kI2CEdidDeviceAddr, offset,
						   (uint8_t *) edid, sizeof(EDID));
		if (ret) {
			debug(EDID, "Failed to read EDID offset=0x%08x ret=0x%08x\n", offset, ret);
			if (s_abort_edid) {
				// Display is trying to shut down - don't delay it.
				debug(ERROR, "EDID retry loop aborted\n");
				break;
			}
			task_sleep(kEdidRetryDelay);
		} else {
			ret = verify_edid(edid, &checksum);
			debug(EDID, "Verify EDID offset=0x%08x ret=0x%08x checksum=0x%02x\n", offset, ret, checksum);
		}
	} while (ret != 0 && !time_has_elapsed(timer_start, kEdidRetryTimeout));

	if (ret == 0) {
		debug(EDID, "Got EDID after %d tries, %llu usecs\n", tries, system_time() - timer_start);
	} else {
		debug(ERROR, "Failed to get EDID after %llu usecs\n", system_time() - timer_start);
	}

	return ret;
}

static uint16_t read_le16(const void *data, uint32_t offset)
{
	const uint8_t *p = (const uint8_t *) data;
	uint16_t result = p[offset];
	result |= ((uint16_t) p[offset + 1]) << 8;
	return result;
}

static uint32_t read_le32(const void *data, uint32_t offset)
{
	const uint8_t *p = (const uint8_t *) data;
	uint32_t result = p[offset];
	result |= ((uint32_t) p[offset + 1]) << 8;
	result |= ((uint32_t) p[offset + 2]) << 16;
	result |= ((uint32_t) p[offset + 3]) << 24;
	return result;
}

#endif // WITH_HW_MCU
