1739 lines
80 KiB
C
1739 lines
80 KiB
C
/*
|
|
* Copyright (C) 2012 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/hdmi.h>
|
|
#include <drivers/process_edid.h>
|
|
#include <platform/memmap.h>
|
|
#include <platform/timer.h>
|
|
#include <sys/task.h>
|
|
#include "edid.h"
|
|
|
|
// I2C address for the EDID rom.
|
|
#define kI2CEdidReadDeviceAddr 0xA1
|
|
#define kI2CEdidWriteDeviceAddr 0xA0
|
|
|
|
// 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 HDMIDEBUG_MASK ( \
|
|
HDMIDEBUG_EDID | \
|
|
HDMIDEBUG_ERROR | \
|
|
HDMIDEBUG_INFO | \
|
|
0)
|
|
|
|
#undef HDMIDEBUG_MASK
|
|
#define HDMIDEBUG_MASK HDMIDEBUG_ERROR // (HDMIDEBUG_EDID | HDMIDEBUG_SCORE | HDMIDEBUG_DS | HDMIDEBUG_ERROR | HDMIDEBUG_INFO)
|
|
|
|
#define HDMIDEBUG_EDID (1<<16) // EDID parsing
|
|
#define HDMIDEBUG_SCORE (1<<17) // Final validation/scoring
|
|
#define HDMIDEBUG_DS (1<<18) // Downstream type detection
|
|
#define HDMIDEBUG_INFO (1<<19) // info
|
|
#define HDMIDEBUG_ERROR (1<<20) // error
|
|
#define HDMIDEBUG_ALWAYS (1<<31) // unconditional output
|
|
|
|
#define debug(_fac, _fmt, _args...) \
|
|
do { \
|
|
if ((HDMIDEBUG_ ## _fac) & (HDMIDEBUG_MASK | HDMIDEBUG_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(uint8_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;
|
|
uint8_t ext_block_count;
|
|
uint8_t index = 0;
|
|
|
|
s_have_timings = false;
|
|
bzero(&s_best_timings, sizeof(s_best_timings));
|
|
s_timing_support = kSupportFlagNone;
|
|
s_ds_type = kHDMI_tx_mode_DVI;
|
|
|
|
ret = get_edid(0, &edid);
|
|
debug(EDID, "Obtaining base EDID result=%d\n", ret);
|
|
if (ret) goto exit;
|
|
|
|
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, "tx mode %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 = kHDMI_tx_mode_HDMI;
|
|
}
|
|
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 != kHDMI_tx_mode_None) && (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 = kHDMI_tx_mode_DVI;
|
|
break;
|
|
case 5:
|
|
debug(DS, "EDID says downstream DP\n");
|
|
s_ds_type = kHDMI_tx_mode_HDMI;
|
|
break;
|
|
default:
|
|
debug(DS, "EDID says downstream HDMI\n");
|
|
s_ds_type = kHDMI_tx_mode_HDMI;
|
|
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 (hdmi_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 %u bytes\n",
|
|
size, (uint32_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(uint8_t offset, EDID *edid)
|
|
{
|
|
int ret;
|
|
uint32_t tries = 0;
|
|
uint64_t timer_start;
|
|
|
|
timer_start = system_time();
|
|
|
|
do {
|
|
uint8_t checksum;
|
|
++tries;
|
|
ret = hdmi_controller_read_bytes_i2c(kI2CEdidReadDeviceAddr, 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;
|
|
}
|