mirror of
https://github.com/NVIDIA/open-gpu-kernel-modules.git
synced 2026-01-30 04:59:46 +00:00
3215 lines
134 KiB
C
3215 lines
134 KiB
C
//*****************************************************************************
|
|
//
|
|
// SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
//
|
|
// File: nvt_edid.c
|
|
//
|
|
// Purpose: the provide edid related services
|
|
//
|
|
//*****************************************************************************
|
|
|
|
#include "nvBinSegment.h"
|
|
#include "nvmisc.h"
|
|
|
|
#include "edid.h"
|
|
|
|
|
|
|
|
PUSH_SEGMENTS
|
|
|
|
// Macro to declare a TIMING initializer for given parameters without border
|
|
#define EST_TIMING(hv,hfp,hsw,ht,hsp,vv,vfp,vsw,vt,vsp,rr,pclk,format) \
|
|
{hv,0,hfp,hsw,ht,(hsp)=='-',vv,0,vfp,vsw,vt,(vsp)=='-',NVT_PROGRESSIVE,pclk,((pclk<<3)+(pclk<<1)),{0,rr,set_rrx1k(pclk,ht,vt),0,1,{0},{0},{0},{0},format,"VESA Established"}}
|
|
|
|
DATA_SEGMENT(PAGE_DATA)
|
|
#if !defined(NV_WSA)
|
|
CONS_SEGMENT(PAGE_CONS)
|
|
#endif // wsa
|
|
|
|
// There is a large table of strings that translate 3-character PNP vendor IDs to a more user-friendly name in the following header.
|
|
// Mark this constant table as pageable.
|
|
#include "nvPNPVendorIds.h"
|
|
|
|
static const NVT_TIMING EDID_EST[] =
|
|
{
|
|
EST_TIMING( 720, 0, 0, 720,'-', 400, 0,0, 400,'-',70, 0,NVT_STATUS_EDID_EST), // 720x400x70Hz (IBM, VGA)
|
|
EST_TIMING( 720, 0, 0, 720,'-', 400, 0,0, 400,'-',88, 0,NVT_STATUS_EDID_EST), // 720x400x88Hz (IBM, XGA2)
|
|
{640,0,16,96,800,NVT_H_SYNC_NEGATIVE,480,0,10,2,525,NVT_V_SYNC_NEGATIVE,NVT_PROGRESSIVE,2518,25175,{0,60,60000,0,1,{0},{0},{0},{0},NVT_STATUS_EDID_EST,"EDID_Established"}},
|
|
|
|
EST_TIMING( 640, 0, 0, 640,'-', 480, 0,0, 480,'-',67, 0,NVT_STATUS_EDID_EST), // 640x480x67Hz (Apple, Mac II)
|
|
|
|
// 640x480x72Hz (VESA) - this entry have borders
|
|
{640,8,16,40,832,NVT_H_SYNC_NEGATIVE,480,8,1,3,520,NVT_V_SYNC_NEGATIVE,NVT_PROGRESSIVE,3150,31500,{0,72,72000,0,1,{0},{0},{0},{0},NVT_STATUS_EDID_EST,"EDID_Established"}},
|
|
EST_TIMING( 640,16, 64, 840,'-', 480, 1,3, 500,'-',75, 3150,NVT_STATUS_EDID_EST), // 640x480x75Hz (VESA)
|
|
EST_TIMING( 800,24, 72,1024,'+', 600, 1,2, 625,'+',56, 3600,NVT_STATUS_EDID_EST), // 800x600x56Hz (VESA)
|
|
EST_TIMING( 800,40,128,1056,'+', 600, 1,4, 628,'+',60, 4000,NVT_STATUS_EDID_EST), // 800x600x60Hz (VESA)
|
|
|
|
EST_TIMING( 800,56,120,1040,'+', 600,37,6, 666,'+',72, 5000,NVT_STATUS_EDID_EST), // 800x600x72Hz (VESA)
|
|
EST_TIMING( 800,16, 80,1056,'+', 600, 1,3, 625,'+',75, 4950,NVT_STATUS_EDID_EST), // 800x600x75Hz (VESA)
|
|
EST_TIMING( 832, 0, 0, 832,'-', 624, 0,0, 624,'-',75, 0,NVT_STATUS_EDID_EST), // 832x624x75Hz (Apple, Mac II)
|
|
EST_TIMING(1024, 0, 0,1024,'-', 768, 0,0, 768,'-',87, 0,NVT_STATUS_EDID_EST), // 1024x768x87Hz (IBM, Interlaced)
|
|
|
|
EST_TIMING(1024,24,136,1344,'-', 768, 3,6, 806,'-',60, 6500,NVT_STATUS_EDID_EST), // 1024x768x60Hz (VESA)
|
|
EST_TIMING(1024,24,136,1328,'-', 768, 3,6, 806,'-',70, 7500,NVT_STATUS_EDID_EST), // 1024x768x70Hz (VESA)
|
|
EST_TIMING(1024,16, 96,1312,'+', 768, 1,3, 800,'+',75, 7875,NVT_STATUS_EDID_EST), // 1024x768x75Hz (VESA)
|
|
EST_TIMING(1280,16,144,1688,'+',1024, 1,3,1066,'+',75,13500,NVT_STATUS_EDID_EST), // 1280x1024x75Hz (VESA)
|
|
|
|
// the end
|
|
NVT_TIMING_SENTINEL
|
|
};
|
|
|
|
static NvU32 MAX_EST_FORMAT = sizeof(EDID_EST)/sizeof(EDID_EST[0]) - 1;
|
|
|
|
static const NVT_TIMING EDID_ESTIII[] =
|
|
{
|
|
EST_TIMING( 640, 32, 64, 832,'+', 350,32,3, 445,'-',85, 3150,NVT_STATUS_EDID_EST), // 640x350x85Hz
|
|
EST_TIMING( 640, 32, 64, 832,'-', 400, 1,3, 445,'+',85, 3150,NVT_STATUS_EDID_EST), // 640x400x85Hz
|
|
EST_TIMING( 720, 36, 72, 936,'-', 400, 1,3, 446,'+',85, 3550,NVT_STATUS_EDID_EST), // 720x400x85Hz
|
|
EST_TIMING( 640, 56, 56, 832,'-', 480, 1,3, 509,'-',85, 3600,NVT_STATUS_EDID_EST), // 640x480x85Hz
|
|
EST_TIMING( 848, 16,112,1088,'+', 480, 6,8, 517,'+',60, 3375,NVT_STATUS_EDID_EST), // 848x480x60HZ
|
|
EST_TIMING( 800, 32, 64,1048,'+', 600, 1,3, 631,'+',85, 5625,NVT_STATUS_EDID_EST), // 800x600x85Hz
|
|
EST_TIMING(1024, 48, 96,1376,'+', 768, 1,3, 808,'+',85, 9450,NVT_STATUS_EDID_EST), // 1024x768x85Hz
|
|
EST_TIMING(1152, 64,128,1600,'+', 864, 1,3, 900,'+',75,10800,NVT_STATUS_EDID_EST), // 1152x864x75Hz
|
|
|
|
EST_TIMING(1280, 48, 32,1440,'+', 768, 3,7, 790,'-',60, 6825,NVT_STATUS_EDID_EST), // 1280x768x60Hz (RB)
|
|
EST_TIMING(1280, 64,128,1664,'-', 768, 3,7, 798,'+',60, 7950,NVT_STATUS_EDID_EST), // 1280x768x60Hz
|
|
EST_TIMING(1280, 80,128,1696,'-', 768, 3,7, 805,'+',75,10225,NVT_STATUS_EDID_EST), // 1280x768x75Hz
|
|
EST_TIMING(1280, 80,136,1712,'-', 768, 3,7, 809,'+',85,11750,NVT_STATUS_EDID_EST), // 1280x768x85Hz
|
|
EST_TIMING(1280, 96,112,1800,'+', 960, 1,3,1000,'+',60,10800,NVT_STATUS_EDID_EST), // 1280x960x60Hz
|
|
EST_TIMING(1280, 64,160,1728,'+', 960, 1,3,1011,'+',85,14850,NVT_STATUS_EDID_EST), // 1280x960x85Hz
|
|
EST_TIMING(1280, 48,112,1688,'+',1024, 1,3,1066,'+',60,10800,NVT_STATUS_EDID_EST), // 1280x1024x60Hz
|
|
EST_TIMING(1280, 64,160,1728,'+',1024, 1,3,1072,'+',85,15750,NVT_STATUS_EDID_EST), // 1280x1024x85Hz
|
|
|
|
EST_TIMING(1360, 64,112,1792,'+', 768, 3,6, 795,'+',60, 8550,NVT_STATUS_EDID_EST), // 1360x768x60Hz
|
|
EST_TIMING(1440, 48, 32,1600,'+', 900, 3,6, 926,'-',60, 8875,NVT_STATUS_EDID_EST), // 1440x900x60Hz (RB)
|
|
EST_TIMING(1440, 80,152,1904,'-', 900, 3,6, 934,'+',60,10650,NVT_STATUS_EDID_EST), // 1440x900x60Hz
|
|
EST_TIMING(1440, 96,152,1936,'-', 900, 3,6, 942,'+',75,13675,NVT_STATUS_EDID_EST), // 1440x900x75Hz
|
|
EST_TIMING(1440,104,152,1952,'-', 900, 3,6, 948,'+',85,15700,NVT_STATUS_EDID_EST), // 1440x900x85Hz
|
|
EST_TIMING(1400, 48, 32,1560,'+',1050, 3,4,1080,'-',60,10100,NVT_STATUS_EDID_EST), // 1440x1050x60Hz (RB)
|
|
EST_TIMING(1400, 88,144,1864,'-',1050, 3,4,1089,'+',60,12175,NVT_STATUS_EDID_EST), // 1440x1050x60Hz
|
|
EST_TIMING(1400,104,144,1896,'-',1050, 3,4,1099,'+',75,15600,NVT_STATUS_EDID_EST), // 1440x1050x75Hz
|
|
|
|
EST_TIMING(1400,104,152,1912,'-',1050, 3,4,1105,'+',85,17950,NVT_STATUS_EDID_EST), // 1440x1050x85Hz
|
|
EST_TIMING(1680, 48, 32,1840,'+',1050, 3,6,1080,'-',60,11900,NVT_STATUS_EDID_EST), // 1680x1050x60Hz (RB)
|
|
EST_TIMING(1680,104,176,2240,'-',1050, 3,6,1089,'+',60,14625,NVT_STATUS_EDID_EST), // 1680x1050x60Hz
|
|
EST_TIMING(1680,120,176,2272,'-',1050, 3,6,1099,'+',75,18700,NVT_STATUS_EDID_EST), // 1680x1050x75Hz
|
|
EST_TIMING(1680,128,176,2288,'-',1050, 3,6,1105,'+',85,21475,NVT_STATUS_EDID_EST), // 1680x1050x85Hz
|
|
EST_TIMING(1600, 64,192,2160,'+',1200, 1,3,1250,'+',60,16200,NVT_STATUS_EDID_EST), // 1600x1200x60Hz
|
|
EST_TIMING(1600, 64,192,2160,'+',1200, 1,3,1250,'+',65,17550,NVT_STATUS_EDID_EST), // 1600x1200x65Hz
|
|
EST_TIMING(1600, 64,192,2160,'+',1200, 1,3,1250,'+',70,18900,NVT_STATUS_EDID_EST), // 1600x1200x70Hz
|
|
|
|
EST_TIMING(1600, 64,192,2160,'+',1200, 1,3,1250,'+',75,20250,NVT_STATUS_EDID_EST), // 1600x1200x75Hz
|
|
EST_TIMING(1600, 64,192,2160,'+',1200, 1,3,1250,'+',85,22950,NVT_STATUS_EDID_EST), // 1600x1200x85Hz
|
|
EST_TIMING(1792,128,200,2448,'-',1344, 1,3,1394,'+',60,20475,NVT_STATUS_EDID_EST), // 1792x1344x60Hz
|
|
EST_TIMING(1792, 96,216,2456,'-',1344, 1,3,1417,'+',75,26100,NVT_STATUS_EDID_EST), // 1792x1344x75Hz
|
|
EST_TIMING(1856, 96,224,2528,'-',1392, 1,3,1439,'+',60,21825,NVT_STATUS_EDID_EST), // 1856x1392x60Hz
|
|
EST_TIMING(1856,128,224,2560,'-',1392, 1,3,1500,'+',75,28800,NVT_STATUS_EDID_EST), // 1856x1392x75Hz
|
|
EST_TIMING(1920, 48, 32,2080,'+',1200, 3,6,1235,'-',60,15400,NVT_STATUS_EDID_EST), // 1920x1200x60Hz (RB)
|
|
EST_TIMING(1920,136,200,2592,'-',1200, 3,6,1245,'+',60,19325,NVT_STATUS_EDID_EST), // 1920x1200x60Hz
|
|
|
|
EST_TIMING(1920,136,208,2608,'-',1200, 3,6,1255,'+',75,24525,NVT_STATUS_EDID_EST), // 1920x1200x75Hz
|
|
EST_TIMING(1920,144,208,2624,'-',1200, 3,6,1262,'+',85,28125,NVT_STATUS_EDID_EST), // 1920x1200x85Hz
|
|
EST_TIMING(1920,128,208,2600,'-',1440, 1,3,1500,'+',60,23400,NVT_STATUS_EDID_EST), // 1920x1440x60Hz
|
|
EST_TIMING(1920,144,224,2640,'-',1440, 1,3,1500,'+',75,29700,NVT_STATUS_EDID_EST), // 1920x1440x75Hz
|
|
|
|
NVT_TIMING_SENTINEL,
|
|
NVT_TIMING_SENTINEL,
|
|
NVT_TIMING_SENTINEL,
|
|
NVT_TIMING_SENTINEL
|
|
};
|
|
|
|
static NvU32 MAX_ESTIII_FORMAT = sizeof(EDID_ESTIII)/sizeof(EDID_ESTIII[0]) - 1;
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NvTiming_EnumEST(NvU32 index, NVT_TIMING *pT)
|
|
{
|
|
if ((pT == NULL) || (index > MAX_EST_FORMAT))
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
*pT = EDID_EST[index];
|
|
|
|
if (pT->HTotal == 0 || pT->VTotal == 0)
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
pT->etc.rrx1k = axb_div_c((NvU32)pT->pclk1khz,
|
|
(NvU32)1000*(NvU32)1000,
|
|
(NvU32)pT->HTotal*(NvU32)pT->VTotal);
|
|
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NvTiming_EnumESTIII(NvU32 index, NVT_TIMING *pT)
|
|
{
|
|
if ((pT == NULL) || (index > MAX_ESTIII_FORMAT))
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
*pT = EDID_ESTIII[index];
|
|
|
|
if (pT->HTotal == 0 || pT->VTotal == 0)
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
pT->etc.rrx1k = axb_div_c((NvU32)pT->pclk1khz,
|
|
(NvU32)1000*(NvU32)1000,
|
|
(NvU32)pT->HTotal*(NvU32)pT->VTotal);
|
|
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NvU32 isHdmi3DStereoType(NvU8 StereoStructureType)
|
|
{
|
|
return ((NVT_HDMI_VS_BYTE5_HDMI_3DS_FRAMEPACK == StereoStructureType) ||
|
|
(NVT_HDMI_VS_BYTE5_HDMI_3DS_FIELD_ALT == StereoStructureType) ||
|
|
(NVT_HDMI_VS_BYTE5_HDMI_3DS_LINE_ALT == StereoStructureType) ||
|
|
(NVT_HDMI_VS_BYTE5_HDMI_3DS_SIDEBYSIDEFULL == StereoStructureType) ||
|
|
(NVT_HDMI_VS_BYTE5_HDMI_3DS_LDEPTH == StereoStructureType) ||
|
|
(NVT_HDMI_VS_BYTE5_HDMI_3DS_LDEPTHGFX == StereoStructureType) ||
|
|
(NVT_HDMI_VS_BYTE5_HDMI_3DS_TOPBOTTOM == StereoStructureType) ||
|
|
(NVT_HDMI_VS_BYTE5_HDMI_3DS_SIDEBYSIDEHALF == StereoStructureType));
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NvU32 NvTiming_GetVESADisplayDescriptorVersion(NvU8 *rawData, NvU32 *pVer)
|
|
{
|
|
return getEdidVersion(rawData, pVer);
|
|
}
|
|
|
|
// get the EDID version
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NvU32 getEdidVersion(NvU8 *pEdid, NvU32 *pVer)
|
|
{
|
|
EDIDV1STRUC *p = (EDIDV1STRUC *) pEdid;
|
|
|
|
if (pEdid[0] == 0x00)
|
|
{
|
|
// For Version 1.x, first 8 bytes of EDID must be 00h, FFh, FFh, FFh, FFh, FFh, FFh, 00h.
|
|
// Beware of Endian-ness and signed-ness.
|
|
if (p->bHeader[1] != 0xFF || p->bHeader[2] != 0xFF || p->bHeader[3] != 0xFF ||
|
|
p->bHeader[4] != 0xFF || p->bHeader[5] != 0xFF || p->bHeader[6] != 0xFF ||
|
|
p->bHeader[7] != 0x00)
|
|
return NVT_STATUS_ERR;
|
|
|
|
*pVer = (((NvU32) p->bVersionNumber) << 8) + ((NvU32) p->bRevisionNumber);
|
|
}
|
|
else if ((pEdid[0] & 0xF0) == 0x20)
|
|
*pVer = (((NvU32) (pEdid[0] & 0XF0) << 4) + (NvU32) (pEdid[0] & 0X0F)) ; // DisplayID version 2.x
|
|
else
|
|
return NVT_STATUS_ERR; // un-recongnized EDID version
|
|
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
void parseEdidCvt3ByteDescriptor(NvU8 *p, NVT_EDID_INFO *pInfo, NvU32 *vtbCount)
|
|
{
|
|
NvU32 k;
|
|
NvU32 width, height, aspect, rr = 0;
|
|
NVT_EDID_DD_CVT_3BYTE_BLOCK *pTiming = (NVT_EDID_DD_CVT_3BYTE_BLOCK *)p;
|
|
NVT_TIMING newTiming;
|
|
NVT_STATUS status;
|
|
|
|
|
|
if (pTiming->addressable_lines == 0)
|
|
return;
|
|
|
|
height = pTiming->addressable_lines;
|
|
aspect = pTiming->aspect_ratio;
|
|
|
|
if (aspect == NVT_EDID_CVT3_ASPECT_4X3)
|
|
width = height * 4 / 3;
|
|
else if (aspect == NVT_EDID_CVT3_ASPECT_16X9)
|
|
width = height * 16 / 9;
|
|
else if (aspect == NVT_EDID_CVT3_ASPECT_16X10)
|
|
width = height * 16 / 10;
|
|
else //15:9
|
|
width = height * 15 / 9;
|
|
|
|
width &= 0xFFFFFFF8; // round down to nearest 8
|
|
|
|
// loop through bits4:0 of supported_vert_rate so we can add a timing
|
|
// for each supported rate
|
|
for (k=1; k<=0x10; k<<=1)
|
|
{
|
|
// skip if this bit indicate no support for the rate;
|
|
if ( (pTiming->supported_vert_rates & (k)) == 0)
|
|
continue;
|
|
|
|
// find the correct refresh rate for this bit
|
|
switch (k)
|
|
{
|
|
case NVT_EDID_CVT3_SUPPORTED_RATE_60HZ_REDUCED_BLANKING :
|
|
case NVT_EDID_CVT3_SUPPORTED_RATE_60HZ :
|
|
rr = 60;
|
|
break;
|
|
case NVT_EDID_CVT3_SUPPORTED_RATE_85HZ :
|
|
rr = 85;
|
|
break;
|
|
case NVT_EDID_CVT3_SUPPORTED_RATE_75HZ :
|
|
rr = 75;
|
|
break;
|
|
case NVT_EDID_CVT3_SUPPORTED_RATE_50HZ :
|
|
rr = 50;
|
|
break;
|
|
}
|
|
|
|
NVMISC_MEMSET(&newTiming, 0, sizeof(newTiming));
|
|
|
|
if ( (k) != NVT_EDID_CVT3_SUPPORTED_RATE_60HZ_REDUCED_BLANKING) // standard blanking
|
|
{
|
|
status = NvTiming_CalcCVT(width, height, rr,
|
|
NVT_PROGRESSIVE,
|
|
&newTiming);
|
|
}
|
|
else // reduced blanking
|
|
{
|
|
status = NvTiming_CalcCVT_RB(width, height, rr,
|
|
NVT_PROGRESSIVE,
|
|
&newTiming);
|
|
}
|
|
|
|
if (status == NVT_STATUS_SUCCESS)
|
|
{
|
|
// For VTB timings, add additional information
|
|
if (vtbCount)
|
|
{
|
|
(*vtbCount)++;
|
|
newTiming.etc.status = NVT_STATUS_EDID_VTB_EXT_CVTn(*vtbCount);
|
|
newTiming.etc.name[39] = '\0';
|
|
}
|
|
|
|
if (!assignNextAvailableTiming(pInfo, &newTiming))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
} // for (k=1; k<=0x10; k<<=1)
|
|
|
|
}
|
|
|
|
// parse the EDID 1.x based cvt timing info
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
void parseEdidCvtTiming(NVT_EDID_INFO *pInfo)
|
|
{
|
|
NvU32 i, j;
|
|
|
|
// find display range limit with cvt, or cvt 3-byte LDDs
|
|
for (i=0; i<NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++)
|
|
{
|
|
if ( pInfo->ldd[i].tag == NVT_EDID_DISPLAY_DESCRIPTOR_CVT )
|
|
{
|
|
NVT_EDID_DD_CVT_3BYTE *pCVT = (NVT_EDID_DD_CVT_3BYTE *)&pInfo->ldd[i].u.cvt;
|
|
|
|
// loop through cvt 3-byte blocks
|
|
for (j=0; j<NVT_EDID_DD_MAX_CVT3_PER_DESCRITPOR; j++)
|
|
{
|
|
parseEdidCvt3ByteDescriptor((NvU8 *)(pCVT->block + j),
|
|
pInfo, NULL);
|
|
} // for(j=0; j<NVT_EDID_DD_CVT3_BLOCK_NUM; j++)
|
|
} // else if CVT 3-byte block
|
|
} // for (i=0; i<NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++)
|
|
}
|
|
|
|
// parse the EDID 1.x based established timing info
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
void parseEdidEstablishedTiming(NVT_EDID_INFO *pInfo)
|
|
{
|
|
NvU32 i, j, k;
|
|
NvU32 count = 0;
|
|
NVT_TIMING newTiming;
|
|
|
|
for (i = 1 << (sizeof(pInfo->established_timings_1_2) * 8 - 1), j = 0; i != 0; i >>= 1, j ++)
|
|
{
|
|
if ((pInfo->established_timings_1_2 & i) != 0 && EDID_EST[j].pclk1khz != 0)
|
|
{
|
|
// count the timing
|
|
newTiming = EDID_EST[j];
|
|
newTiming.etc.status = NVT_STATUS_EDID_ESTn(++count);
|
|
NVT_SNPRINTF((char *)newTiming.etc.name, 40,
|
|
"EDID-EST(VESA):%dx%dx%dHz",
|
|
(int)newTiming.HVisible,
|
|
(int)newTiming.VVisible,
|
|
(int)newTiming.etc.rr);
|
|
newTiming.etc.name[39] = '\0';
|
|
|
|
if (!assignNextAvailableTiming(pInfo, &newTiming))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ESTIII block in ldd only supported in EDID1.4 and above
|
|
if (pInfo->version < NVT_EDID_VER_1_4)
|
|
return;
|
|
|
|
for (i=0; i<NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++)
|
|
{
|
|
if ( pInfo->ldd[i].tag == NVT_EDID_DISPLAY_DESCRIPTOR_ESTIII )
|
|
{
|
|
NVT_EDID_DD_EST_TIMING3* pEST = &pInfo->ldd[i].u.est3;
|
|
|
|
for (j=0; j<NVT_EDID_DD_EST_TIMING3_NUM; j++) // loop through each byte
|
|
{
|
|
// loop through each bit in the byte
|
|
for (k=7; k<=7; k--)
|
|
{
|
|
// find if the bit is 1 and we have a valid associated timing
|
|
if (pEST->data[j] & (1<<k) && EDID_ESTIII[(j*8)+(7-k)].pclk1khz != 0)
|
|
{
|
|
newTiming = EDID_ESTIII[(j*8)+(7-k)];
|
|
newTiming.etc.status = NVT_STATUS_EDID_ESTn(++count);
|
|
NVT_SNPRINTF((char *)newTiming.etc.name, 40,
|
|
"EDID-EST(III):%dx%dx%dHz",
|
|
(int)newTiming.HVisible,
|
|
(int)newTiming.VVisible,
|
|
(int)newTiming.etc.rr);
|
|
newTiming.etc.name[39] = '\0';
|
|
|
|
if (!assignNextAvailableTiming(pInfo, &newTiming))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
void parseEdidStandardTimingDescriptor(NvU16 timing, NVT_EDID_INFO *pInfo, NvU32 count, NVT_TIMING * pT)
|
|
{
|
|
NvU32 aspect, width, height, rr;
|
|
count++;
|
|
|
|
// The value in the EDID = (Horizontal active pixels/8) - 31
|
|
width = (timing & 0x0FF) + 31;
|
|
width <<= 3;
|
|
rr = ((timing >> 8) & 0x3F) + 60; // bits 5->0
|
|
|
|
// get the height
|
|
aspect = ((timing >> 8) & 0xC0); // aspect ratio at bit 7:6
|
|
if (aspect == 0x00)
|
|
height = (pInfo->version < 0x103) ? width : (width * 5 / 8); //16:10 per EDID1.3 and 1:1 with earlier EDID
|
|
else if (aspect == 0x40)
|
|
height = width * 3 / 4; //4:3
|
|
else if (aspect == 0x80)
|
|
height = width * 4 / 5; //5:4
|
|
else
|
|
height = width * 9 / 16; //16:9
|
|
|
|
// try to get the timing from DMT first
|
|
if (NvTiming_CalcDMT(width, height, rr, 0, pT) == NVT_STATUS_SUCCESS)
|
|
{
|
|
pT->etc.status = NVT_STATUS_EDID_STDn(count);
|
|
NVT_SNPRINTF((char *)pT->etc.name, 40, "EDID-STD(DMT):%dx%dx%dHz", (int)width, (int)height, (int)rr);
|
|
pT->etc.name[39] = '\0';
|
|
}
|
|
// EDID1.4 and above defaults to CVT, instead of GTF. GTF is deprecated as of 1.4.
|
|
else if ((pInfo->version >= NVT_EDID_VER_1_4) && (NvTiming_CalcCVT(width, height, rr, NVT_PROGRESSIVE, pT) == NVT_STATUS_SUCCESS))
|
|
{
|
|
pT->etc.status = NVT_STATUS_EDID_STDn(count);
|
|
NVT_SNPRINTF((char *)pT->etc.name, 40, "EDID-STD(CVT):%dx%dx%dHz", (int)width, (int)height, (int)rr);
|
|
pT->etc.name[39] = '\0';
|
|
}
|
|
else
|
|
{
|
|
// if the mode is not found in DMT, use GTF timing
|
|
if (NvTiming_CalcGTF(width, height, rr, NVT_PROGRESSIVE, pT) == NVT_STATUS_SUCCESS)
|
|
{
|
|
NVT_SNPRINTF((char *)pT->etc.name, 40, "EDID-STD(GTF):%dx%dx%dHz", (int)width, (int)height, (int)rr);
|
|
pT->etc.name[39] = '\0';
|
|
}
|
|
pT->etc.status = NVT_STATUS_EDID_STDn(count);
|
|
}
|
|
}
|
|
|
|
// parse the EDID 1.x based standard timing info
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
void parseEdidStandardTiming(NVT_EDID_INFO *pInfo)
|
|
{
|
|
NvU32 i, j;
|
|
NVT_TIMING newTiming;
|
|
NvU32 count = 0;
|
|
|
|
// now check for standard timings
|
|
for (i=0; i<NVT_EDID_MAX_STANDARD_TIMINGS; i++)
|
|
{
|
|
if (((pInfo->standard_timings[i] & 0x0FF) != 0x1) && //proper indication of unused field
|
|
(pInfo->standard_timings[i] != 0x0)) //improper indication (bad edid)
|
|
{
|
|
NVMISC_MEMSET(&newTiming, 0, sizeof(newTiming));
|
|
|
|
parseEdidStandardTimingDescriptor(pInfo->standard_timings[i],
|
|
pInfo, count, &newTiming);
|
|
|
|
if (!assignNextAvailableTiming(pInfo, &newTiming))
|
|
{
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
}//if ((pInfo->standard_timings[i] & 0x0FF) != 0x1)
|
|
} //for (i=0; i<NVT_EDID_MAX_STANDARD_TIMINGS; i++)
|
|
|
|
// STI block in ldd only supported in EDID1.4 and above
|
|
if (pInfo->version < NVT_EDID_VER_1_4)
|
|
return;
|
|
|
|
// now check for standard timings in long display descriptors
|
|
for (i=0; i<NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++)
|
|
{
|
|
if ( pInfo->ldd[i].tag == NVT_EDID_DISPLAY_DESCRIPTOR_STI )
|
|
{
|
|
NVT_EDID_DD_STD_TIMING* pSTI = &pInfo->ldd[i].u.std_timing;
|
|
for (j=0; j<NVT_EDID_DD_STI_NUM; j++)
|
|
{
|
|
if ((pSTI->descriptor[j] & 0x0FF) != 0x00)
|
|
{
|
|
NVMISC_MEMSET(&newTiming, 0, sizeof(newTiming));
|
|
|
|
parseEdidStandardTimingDescriptor(pSTI->descriptor[j],
|
|
pInfo, count, &newTiming);
|
|
|
|
if (!assignNextAvailableTiming(pInfo, &newTiming))
|
|
{
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
} // if ((pSTI->std_timing[i] & 0x0FF) != 0x1)
|
|
} //for (j=0; j<NVT_EDID_DD_STI_NUM; j++)
|
|
} //if ( ((EDID_LONG_DISPLAY_DESCRIPTOR)p->DetailedTimingDesc[i]).tag = NVT_EDID_DISPLAY_DESCRIPTOR_STI )
|
|
} //for (i=0; i<NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++)
|
|
}
|
|
|
|
// parse the signal detailed timing descriptor
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS parseEdidDetailedTimingDescriptor(NvU8 *p, NVT_TIMING *pT)
|
|
{
|
|
DETAILEDTIMINGDESCRIPTOR *pDTD = (DETAILEDTIMINGDESCRIPTOR *)p;
|
|
NvU32 dwTotalPixels;
|
|
NvS32 hvisible, vvisible;
|
|
|
|
// bypass input parameter check in the private function
|
|
|
|
|
|
// For EDID ver 1.1 & 1.2:
|
|
// All bytes filled with 1 are considered as an empty block and should be skipped.
|
|
// Here we just check first 5 bytes.
|
|
// Release 5th byte zero check. Now we only check first 4 bytes. Some buggy EDID has
|
|
// data in 5th byte.
|
|
if(pDTD && (pDTD->wDTPixelClock !=0 || pDTD->bDTHorizontalActive !=0)
|
|
&& (pDTD->wDTPixelClock != 0x0101 || pDTD->bDTHorizontalActive != 1 ||
|
|
pDTD->bDTHorizontalBlanking != 1 || pDTD->bDTHorizActiveBlank != 1))
|
|
{
|
|
// Note that hvisible and vvisible here correspond to the "Addressable Video" portion of the
|
|
// "Active Video" defined in the EDID spec (see section 3.12: Note Regarding Borders)
|
|
hvisible = (pDTD->bDTHorizontalActive + ((pDTD->bDTHorizActiveBlank & 0xF0) << 4)) - 2 * pDTD->bDTHorizontalBorder;
|
|
vvisible = (pDTD->bDTVerticalActive + ((pDTD->bDTVertActiveBlank & 0xF0) << 4)) - 2 * pDTD->bDTVerticalBorder;
|
|
|
|
// Sanity check since we are getting values from the monitor
|
|
if (hvisible <= 0 || vvisible <= 0 || pDTD->wDTPixelClock == 0)
|
|
{
|
|
if (pT)
|
|
pT->HVisible = 0;
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
// if the output timing buffer is not provide, simply return here to indicate a legal descriptor
|
|
if (pT == NULL)
|
|
return NVT_STATUS_SUCCESS;
|
|
|
|
// horizontal timing parameters
|
|
pT->HVisible = (NvU16)hvisible;
|
|
pT->HBorder = (NvU16)pDTD->bDTHorizontalBorder;
|
|
pT->HTotal = (NvU16)hvisible + (NvU16)(pDTD->bDTHorizontalBlanking + ((pDTD->bDTHorizActiveBlank & 0x0F) << 8)) + pT->HBorder * 2;
|
|
pT->HFrontPorch = (NvU16)(pDTD->bDTHorizontalSync + ((pDTD->bDTHorizVertSyncOverFlow & 0xC0) << 2));
|
|
pT->HSyncWidth = (NvU16)(pDTD->bDTHorizontalSyncWidth + ((pDTD->bDTHorizVertSyncOverFlow & 0x30) << 4));
|
|
|
|
// vertical timing parameters
|
|
pT->VVisible = (NvU16)vvisible;
|
|
pT->VBorder = (NvU16)pDTD->bDTVerticalBorder;
|
|
pT->VTotal = (NvU16)vvisible + (NvU16)(pDTD->bDTVerticalBlanking + ((pDTD->bDTVertActiveBlank & 0x0F) << 8)) + pT->VBorder * 2;
|
|
pT->VFrontPorch = (NvU16)(((pDTD->bDTVerticalSync & 0xF0) >> 4) + ((pDTD->bDTHorizVertSyncOverFlow & 0x0C) << 2));
|
|
pT->VSyncWidth = (NvU16)((pDTD->bDTVerticalSync & 0x0F) + ((pDTD->bDTHorizVertSyncOverFlow & 0x03) << 4));
|
|
|
|
// pixel clock
|
|
pT->pclk = (NvU32)pDTD->wDTPixelClock;
|
|
pT->pclk1khz = (NvU32)((pDTD->wDTPixelClock << 3) + (pDTD->wDTPixelClock << 1));
|
|
|
|
// sync polarities
|
|
if ((pDTD->bDTFlags & 0x18) == 0x18)
|
|
{
|
|
pT->HSyncPol = ((pDTD->bDTFlags & 0x2) != 0) ? NVT_H_SYNC_POSITIVE : NVT_H_SYNC_NEGATIVE;
|
|
pT->VSyncPol = ((pDTD->bDTFlags & 0x4) != 0) ? NVT_V_SYNC_POSITIVE : NVT_V_SYNC_NEGATIVE;
|
|
}
|
|
else if ((pDTD->bDTFlags & 0x18) == 0x10)
|
|
{
|
|
pT->HSyncPol = ((pDTD->bDTFlags & 0x2) != 0) ? NVT_H_SYNC_POSITIVE : NVT_H_SYNC_NEGATIVE;
|
|
pT->VSyncPol = NVT_V_SYNC_POSITIVE;
|
|
}
|
|
else
|
|
{
|
|
pT->HSyncPol = NVT_H_SYNC_NEGATIVE;
|
|
pT->VSyncPol = NVT_V_SYNC_POSITIVE;
|
|
}
|
|
|
|
// interlaced
|
|
if ((pDTD->bDTFlags & 0x80) == 0x80)
|
|
pT->interlaced = 1;
|
|
else
|
|
pT->interlaced = 0;
|
|
|
|
// Eizo split EDID case, using 0th bit to indicate split display capability
|
|
if (((pDTD->bDTFlags & 1) == 1) && !(((pDTD->bDTFlags & 0x20) == 0x20) || ((pDTD->bDTFlags & 0x40) == 0x40)))
|
|
{
|
|
pT->etc.flag |= NVT_FLAG_EDID_DTD_EIZO_SPLIT;
|
|
}
|
|
if (pT->interlaced)
|
|
{
|
|
// Adjust for one extra blank line in every other frame.
|
|
dwTotalPixels = (((NvU32)pT->HTotal * pT->VTotal) +
|
|
((NvU32)pT->HTotal * (pT->VTotal + 1))) / 2;
|
|
}
|
|
else
|
|
{
|
|
dwTotalPixels = (NvU32)pT->HTotal * pT->VTotal;
|
|
}
|
|
|
|
pT->etc.rr = (NvU16)(((NvU32)pDTD->wDTPixelClock*10000+dwTotalPixels/2)/dwTotalPixels);
|
|
// Using utility call to multiply and divide to take care of overflow and truncation of large values
|
|
// How did we arrive at 10000000? It comes from the fact that Pixel clock mentioned in EDID is in mulitples of 10KHz = 10000
|
|
// and the refresh rate is mentioned in 0.001Hz, that is 60Hz will be represented as 60000, which brings in the factor of 1000.
|
|
// And hence 10000 * 1000 = 10000000
|
|
pT->etc.rrx1k = axb_div_c(pDTD->wDTPixelClock, 10000000, dwTotalPixels);
|
|
pT->etc.status = NVT_STATUS_EDID_DTD;
|
|
NVT_SNPRINTF((char *)pT->etc.name, sizeof(pT->etc.name), "EDID-Detailed:%dx%dx%d.%03dHz%s", (int)pT->HVisible, (int)(pT->interlaced ? 2 : 1)*pT->VVisible , (int)pT->etc.rrx1k/1000, (int)pT->etc.rrx1k%1000, (pT->interlaced ? "/i" : ""));
|
|
pT->etc.name[sizeof(pT->etc.name) - 1] = '\0';
|
|
|
|
// aspect ratio
|
|
pT->etc.aspect = (pDTD->bDTHorizVertImage & 0xF0) << 20 | pDTD->bDTHorizontalImage << 16 |
|
|
(pDTD->bDTHorizVertImage & 0x0F) << 8 | pDTD->bDTVerticalImage;
|
|
|
|
pT->etc.rep = 0x1; // Bit mask for no pixel repetition.
|
|
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
// parse the EDID 1.x based standard timing info
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
void parseEdidDetailedTiming(NvU8 *pEdid, NVT_EDID_INFO *pInfo)
|
|
{
|
|
EDIDV1STRUC *p = (EDIDV1STRUC *) pEdid;
|
|
NVT_TIMING newTiming;
|
|
NvU32 i;
|
|
NvBool found = NV_FALSE;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
NVMISC_MEMSET(&newTiming, 0, sizeof(newTiming));
|
|
|
|
if (parseEdidDetailedTimingDescriptor((NvU8 *)&p->DetailedTimingDesc[i],
|
|
&newTiming) == NVT_STATUS_SUCCESS)
|
|
{
|
|
newTiming.etc.status = NVT_STATUS_EDID_DTDn(i+1);
|
|
|
|
if (!assignNextAvailableTiming(pInfo, &newTiming))
|
|
{
|
|
break;
|
|
}
|
|
|
|
found = NV_TRUE;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
// if edid_ver 1.3, PTM flag should be set
|
|
//nvt_assert(pInfo->version > 0x103 || (pInfo->u.feature &
|
|
// NVT_EDID_OTHER_FEATURES_PTM_INCLUDE_NATIVE));
|
|
|
|
if (pInfo->u.feature & NVT_EDID_OTHER_FEATURES_PTM_INCLUDE_NATIVE)
|
|
{
|
|
pInfo->timing[0].etc.flag |= NVT_FLAG_DTD1_PREFERRED_TIMING;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// parse the EDID 1.x 18-byte long display descriptor
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
static void parseEdidLongDisplayDescriptor(EDID_LONG_DISPLAY_DESCRIPTOR *descriptor, NVT_EDID_18BYTE_DESCRIPTOR *p, NvU32 version)
|
|
{
|
|
NvU32 i;
|
|
|
|
// bypass the input pointer check in this private function
|
|
|
|
// return if it's a detailed timing descriptor
|
|
if (descriptor->prefix[0] != 0 || descriptor->prefix[1] != 0)
|
|
return;
|
|
|
|
// other sanity check for the input data
|
|
if (descriptor->rsvd != 0)
|
|
return;
|
|
|
|
p->tag = descriptor->tag;
|
|
|
|
// now translate the descriptor
|
|
switch (descriptor->tag)
|
|
{
|
|
case NVT_EDID_DISPLAY_DESCRIPTOR_DPSN: // display product serial number
|
|
case NVT_EDID_DISPLAY_DESCRITPOR_DPN: // display product name
|
|
case NVT_EDID_DISPLAY_DESCRIPTOR_ADS: // alphanumeric data string (ASCII)
|
|
|
|
// copy the 13 characters payload from the 18-byte descriptor
|
|
for (i = 0; i < NVT_PVT_EDID_LDD_PAYLOAD_SIZE; i++)
|
|
{
|
|
if (descriptor->data[i] == 0x0A)
|
|
p->u.serial_number.str[i] = '\0';
|
|
else
|
|
p->u.serial_number.str[i] = descriptor->data[i];
|
|
}
|
|
break;
|
|
|
|
case NVT_EDID_DISPLAY_DESCRIPTOR_DRL: // display range limit
|
|
{
|
|
EDID_MONITOR_RANGE_LIMIT *pRangeLimit = (EDID_MONITOR_RANGE_LIMIT *)&descriptor->data[0];
|
|
|
|
p->u.range_limit.min_v_rate = pRangeLimit->minVRate;
|
|
p->u.range_limit.max_v_rate = pRangeLimit->maxVRate;
|
|
p->u.range_limit.min_h_rate = pRangeLimit->minHRate;
|
|
p->u.range_limit.max_h_rate = pRangeLimit->maxHRate;
|
|
p->u.range_limit.max_pclk_MHz = pRangeLimit->maxPClock10M * 10;
|
|
p->u.range_limit.timing_support = pRangeLimit->timing_support;
|
|
|
|
// add 255Hz offsets if needed, use descriptor->rsvd2
|
|
// to offset the min values their max MUST be offset as well
|
|
if (version >= NVT_EDID_VER_1_4)
|
|
{
|
|
if (descriptor->rsvd2 & NVT_PVT_EDID_RANGE_OFFSET_VER_MAX)
|
|
{
|
|
p->u.range_limit.max_v_rate += NVT_PVT_EDID_RANGE_OFFSET_AMOUNT;
|
|
if (descriptor->rsvd2 & NVT_PVT_EDID_RANGE_OFFSET_VER_MIN)
|
|
{
|
|
p->u.range_limit.min_v_rate += NVT_PVT_EDID_RANGE_OFFSET_AMOUNT;
|
|
}
|
|
}
|
|
if (descriptor->rsvd2 & NVT_PVT_EDID_RANGE_OFFSET_HOR_MAX)
|
|
{
|
|
p->u.range_limit.max_h_rate += NVT_PVT_EDID_RANGE_OFFSET_AMOUNT;
|
|
if (descriptor->rsvd2 & NVT_PVT_EDID_RANGE_OFFSET_HOR_MIN)
|
|
{
|
|
p->u.range_limit.min_h_rate += NVT_PVT_EDID_RANGE_OFFSET_AMOUNT;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p->u.range_limit.timing_support == NVT_EDID_RANGE_SUPPORT_GTF2)
|
|
{
|
|
// descriptor->data[7]
|
|
// Start frequency for secondary curve, hor freq./2[kHz]
|
|
p->u.range_limit.u.gtf2.C = pRangeLimit->u.gtf2.C / 2; // 0 <= C <= 127
|
|
p->u.range_limit.u.gtf2.K = pRangeLimit->u.gtf2.K; // 0 <= K <= 255
|
|
p->u.range_limit.u.gtf2.J = pRangeLimit->u.gtf2.J / 2; // 0 <= J <= 127
|
|
p->u.range_limit.u.gtf2.M = (pRangeLimit->u.gtf2.M_MSB << 8)
|
|
+ pRangeLimit->u.gtf2.M_LSB; // 0 <= M <= 65535
|
|
}
|
|
else if (p->u.range_limit.timing_support == NVT_EDID_RANGE_SUPPORT_CVT)
|
|
{
|
|
// the pixel clock adjustment is in cvt.pixel_clock @ bits7:2
|
|
// that number is in 0.25MHz, ie actual max clock is max_pclk_MHz - (0.25 x cvt_pixel_clock)
|
|
// subtract the whole number part from max_pclk_MHz, save the remainder
|
|
p->u.range_limit.max_pclk_MHz -= (pRangeLimit->u.cvt.pixel_clock & NVT_PVT_EDID_CVT_PIXEL_CLOCK_MASK) >> NVT_PVT_EDID_CVT_PIXEL_CLOCK_SHIFT >> 2; // ie divide by 4 to get whole number
|
|
p->u.range_limit.u.cvt.pixel_clock_adjustment = ((pRangeLimit->u.cvt.pixel_clock & NVT_PVT_EDID_CVT_PIXEL_CLOCK_MASK) >> NVT_PVT_EDID_CVT_PIXEL_CLOCK_SHIFT) & 0x03; // ie modulus 4
|
|
|
|
p->u.range_limit.u.cvt.max_active_pixels_per_line = (pRangeLimit->u.cvt.pixel_clock & NVT_PVT_EDID_CVT_ACTIVE_MSB_MASK) << NVT_PVT_EDID_CVT_ACTIVE_MSB_SHIFT;
|
|
p->u.range_limit.u.cvt.max_active_pixels_per_line |= pRangeLimit->u.cvt.max_active;
|
|
p->u.range_limit.u.cvt.max_active_pixels_per_line <<= 3; // ie multiply 8
|
|
|
|
p->u.range_limit.u.cvt.aspect_supported = (pRangeLimit->u.cvt.aspect_supported & NVT_PVT_EDID_CVT_ASPECT_SUPPORTED_MASK) >> NVT_PVT_EDID_CVT_ASPECT_SUPPORTED_SHIFT;
|
|
|
|
p->u.range_limit.u.cvt.aspect_preferred = ( pRangeLimit->u.cvt.aspect_preferred_blanking & NVT_PVT_EDID_CVT_ASPECT_PREFERRED_MASK) >> NVT_PVT_EDID_CVT_ASPECT_PREFERRED_SHIFT;
|
|
p->u.range_limit.u.cvt.blanking_support = ( pRangeLimit->u.cvt.aspect_preferred_blanking & NVT_PVT_EDID_CVT_BLANKING_MASK) >> NVT_PVT_EDID_CVT_BLANKING_SHIFT;
|
|
|
|
p->u.range_limit.u.cvt.scaling_support = (pRangeLimit->u.cvt.scaling_support & NVT_PVT_EDID_CVT_SCALING_MASK) >> NVT_PVT_EDID_CVT_SCALING_SHIFT;
|
|
|
|
p->u.range_limit.u.cvt.preferred_refresh_rate = pRangeLimit->u.cvt.preferred_refresh_rate;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case NVT_EDID_DISPLAY_DESCRIPTOR_CPD: // color point data
|
|
{
|
|
EDID_COLOR_POINT_DATA *pColorPoint = (EDID_COLOR_POINT_DATA *)&descriptor->data[0];
|
|
|
|
p->u.color_point.wp1_index = pColorPoint->wp1_index;
|
|
p->u.color_point.wp1_x = pColorPoint->wp1_x << 2;
|
|
p->u.color_point.wp1_x |= (pColorPoint->wp1_x_y & NVT_PVT_EDID_CPD_WP_X_MASK) >> NVT_PVT_EDID_CPD_WP_X_SHIFT;
|
|
p->u.color_point.wp1_y = pColorPoint->wp1_y << 2;
|
|
p->u.color_point.wp1_y |= (pColorPoint->wp1_x_y & NVT_PVT_EDID_CPD_WP_Y_MASK) >> NVT_PVT_EDID_CPD_WP_Y_SHIFT;
|
|
p->u.color_point.wp1_gamma = pColorPoint->wp1_gamma + 100;
|
|
|
|
p->u.color_point.wp2_index = pColorPoint->wp2_index;
|
|
p->u.color_point.wp2_x = pColorPoint->wp2_x << 2;
|
|
p->u.color_point.wp2_x |= (pColorPoint->wp2_x_y & NVT_PVT_EDID_CPD_WP_X_MASK) >> NVT_PVT_EDID_CPD_WP_X_SHIFT;
|
|
p->u.color_point.wp2_y = pColorPoint->wp2_y << 2;
|
|
p->u.color_point.wp2_y |= (pColorPoint->wp2_x_y & NVT_PVT_EDID_CPD_WP_Y_MASK) >> NVT_PVT_EDID_CPD_WP_Y_SHIFT;
|
|
p->u.color_point.wp2_gamma = pColorPoint->wp2_gamma + 100;
|
|
}
|
|
break;
|
|
|
|
case NVT_EDID_DISPLAY_DESCRIPTOR_STI: // standard timing identification
|
|
{
|
|
EDID_STANDARD_TIMING_ID *pStdTiming = (EDID_STANDARD_TIMING_ID *)&descriptor->data[0];
|
|
|
|
for(i=0; i<NVT_EDID_DD_STI_NUM; i++)
|
|
{
|
|
p->u.std_timing.descriptor[i] = pStdTiming->std_timing[i];
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NVT_EDID_DISPLAY_DESCRIPTOR_DCM: // display color management
|
|
{
|
|
EDID_COLOR_MANAGEMENT_DATA *pColorMan = (EDID_COLOR_MANAGEMENT_DATA *)&descriptor->data[0];
|
|
|
|
p->u.color_man.red_a3 = pColorMan->red_a3_lsb | (pColorMan->red_a3_msb << 8);
|
|
p->u.color_man.red_a2 = pColorMan->red_a2_lsb | (pColorMan->red_a2_msb << 8);
|
|
|
|
p->u.color_man.green_a3 = pColorMan->green_a3_lsb | (pColorMan->green_a3_msb << 8);
|
|
p->u.color_man.green_a2 = pColorMan->green_a2_lsb | (pColorMan->green_a2_msb << 8);
|
|
|
|
p->u.color_man.blue_a3 = pColorMan->blue_a3_lsb | (pColorMan->blue_a3_msb << 8);
|
|
p->u.color_man.blue_a2 = pColorMan->blue_a2_lsb | (pColorMan->blue_a2_msb << 8);
|
|
}
|
|
break;
|
|
|
|
case NVT_EDID_DISPLAY_DESCRIPTOR_CVT: // CVT 3-byte timing code
|
|
{
|
|
EDID_CVT_3BYTE *pCVT_3byte = (EDID_CVT_3BYTE *)&descriptor->data[0];
|
|
|
|
for (i=0; i<NVT_EDID_DD_MAX_CVT3_PER_DESCRITPOR; i++)
|
|
{
|
|
if (pCVT_3byte->block[i].addressable_lines != 0)
|
|
{
|
|
p->u.cvt.block[i].addressable_lines = pCVT_3byte->block[i].addressable_lines;
|
|
p->u.cvt.block[i].addressable_lines |= (pCVT_3byte->block[i].lines_ratio & NVT_PVT_EDID_CVT3_LINES_MSB_MASK) << NVT_PVT_EDID_CVT3_LINES_MSB_SHIFT;
|
|
p->u.cvt.block[i].addressable_lines +=1;
|
|
p->u.cvt.block[i].addressable_lines <<= 1;
|
|
|
|
p->u.cvt.block[i].aspect_ratio = (pCVT_3byte->block[i].lines_ratio & NVT_PVT_EDID_CVT3_ASPECT_MASK) >> NVT_PVT_EDID_CVT3_ASPECT_SHIFT;
|
|
|
|
p->u.cvt.block[i].preferred_vert_rates = (pCVT_3byte->block[i].refresh_rates & NVT_PVT_EDID_CVT3_PREFERRED_RATE_MASK) >> NVT_PVT_EDID_CVT3_PREFERRED_RATE_SHIFT;
|
|
p->u.cvt.block[i].supported_vert_rates = (pCVT_3byte->block[i].refresh_rates & NVT_PVT_EDID_CVT3_SUPPORTED_RATE_MASK) >> NVT_PVT_EDID_CVT3_SUPPORTED_RATE_SHIFT;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NVT_EDID_DISPLAY_DESCRIPTOR_ESTIII: // establishied timing III
|
|
{
|
|
EDID_EST_TIMINGS_III *pEstTiming = (EDID_EST_TIMINGS_III *)&descriptor->data[0];
|
|
|
|
for(i=0; i<NVT_EDID_DD_EST_TIMING3_NUM; i++)
|
|
{
|
|
p->u.est3.data[i] = pEstTiming->timing_byte[i];
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NVT_EDID_DISPLAY_DESCRIPTOR_DUMMY: // dummy descriptor
|
|
default:
|
|
// unresolved descriptor yet
|
|
for (i = 0; i < NVT_PVT_EDID_LDD_PAYLOAD_SIZE; i++)
|
|
{
|
|
p->u.dummy.data[i] = descriptor->data[i];
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// get generic EDID info
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NV_STDCALL NvTiming_ParseEDIDInfo(NvU8 *pEdid, NvU32 length, NVT_EDID_INFO *pInfo)
|
|
{
|
|
NvU32 i, j, k, data;
|
|
EDIDV1STRUC *p;
|
|
NvU8 *pExt;
|
|
NVT_EDID_CEA861_INFO *p861Info;
|
|
|
|
// parameter check
|
|
if (pEdid == NULL || length < 128 || pInfo == NULL)
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
NVMISC_MEMSET(pInfo, 0, sizeof(NVT_EDID_INFO));
|
|
|
|
// get the EDID version
|
|
if (getEdidVersion(pEdid, &pInfo->version) == NVT_STATUS_ERR)
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
p = (EDIDV1STRUC *) pEdid;
|
|
|
|
// get the IDs
|
|
pInfo->manuf_id = p->wIDManufName;
|
|
pInfo->product_id = p->wIDProductCode;
|
|
|
|
// translate the ID into manufacturer's name
|
|
pInfo->manuf_name[0] = 'A' + (NvU8)((pInfo->manuf_id & 0x007c) >> 2) - 1;
|
|
pInfo->manuf_name[1] = 'A' + (NvU8)((pInfo->manuf_id & 0x0003) << 3 | (pInfo->manuf_id & 0xe000) >> 13) - 1;
|
|
pInfo->manuf_name[2] = 'A' + (NvU8)((pInfo->manuf_id & 0x1f00) >> 8) - 1;
|
|
pInfo->manuf_name[3] = '\0';
|
|
|
|
// get serial number
|
|
pInfo->serial_number = p->dwIDSerialNumber;
|
|
|
|
// get the week and year
|
|
pInfo->week = p->bWeekManuf;
|
|
pInfo->year = p->bYearManuf + 1990;
|
|
|
|
// get the interface info
|
|
pInfo->input.isDigital = (p->bVideoInputDef & NVT_PVT_EDID_INPUT_ISDIGITAL_MASK) >> NVT_PVT_EDID_INPUT_ISDIGITAL_SHIFT;
|
|
|
|
if (pInfo->input.isDigital && pInfo->version > 0x103) // must be at least EDID1.4 to support the following fields
|
|
{
|
|
switch ( (p->bVideoInputDef & NVT_PVT_EDID_INPUT_BPC_MASK) >> NVT_PVT_EDID_INPUT_BPC_SHIFT)
|
|
{
|
|
case NVT_PVT_EDID_INPUT_BPC_6 :
|
|
pInfo->input.u.digital.bpc = 6;
|
|
break;
|
|
case NVT_PVT_EDID_INPUT_BPC_8 :
|
|
pInfo->input.u.digital.bpc = 8;
|
|
break;
|
|
case NVT_PVT_EDID_INPUT_BPC_10 :
|
|
pInfo->input.u.digital.bpc = 10;
|
|
break;
|
|
case NVT_PVT_EDID_INPUT_BPC_12 :
|
|
pInfo->input.u.digital.bpc = 12;
|
|
break;
|
|
case NVT_PVT_EDID_INPUT_BPC_14 :
|
|
pInfo->input.u.digital.bpc = 14;
|
|
break;
|
|
case NVT_PVT_EDID_INPUT_BPC_16 :
|
|
pInfo->input.u.digital.bpc = 16;
|
|
break;
|
|
default :
|
|
pInfo->input.u.digital.bpc = 0;
|
|
break;
|
|
}
|
|
pInfo->input.u.digital.video_interface = (p->bVideoInputDef & NVT_PVT_EDID_INPUT_INTERFACE_MASK) >> NVT_PVT_EDID_INPUT_INTERFACE_SHIFT;
|
|
}
|
|
else if (!pInfo->input.isDigital)
|
|
{
|
|
pInfo->input.u.analog_data = (p->bVideoInputDef & NVT_PVT_EDID_INPUT_ANALOG_ETC_MASK) >> NVT_PVT_EDID_INPUT_ANALOG_ETC_SHIFT;
|
|
}
|
|
|
|
// get the max image size and aspect ratio
|
|
if (p->bMaxHorizImageSize != 0 && p->bMaxVertImageSize != 0)
|
|
{
|
|
pInfo->screen_size_x = p->bMaxHorizImageSize;
|
|
pInfo->screen_size_y = p->bMaxVertImageSize;
|
|
pInfo->screen_aspect_x = 0;
|
|
pInfo->screen_aspect_y = 0;
|
|
}
|
|
else if (p->bMaxHorizImageSize != 0 && p->bMaxVertImageSize == 0)
|
|
{
|
|
pInfo->screen_size_x = 0;
|
|
pInfo->screen_size_y = 0;
|
|
pInfo->screen_aspect_x = 99 + p->bMaxHorizImageSize;
|
|
pInfo->screen_aspect_y = 100;
|
|
}
|
|
else if (p->bMaxHorizImageSize == 0 && p->bMaxVertImageSize != 0)
|
|
{
|
|
pInfo->screen_size_x = 0;
|
|
pInfo->screen_size_y = 0;
|
|
pInfo->screen_aspect_x = 100;
|
|
pInfo->screen_aspect_y = 99 + p->bMaxVertImageSize;
|
|
}
|
|
|
|
// get the gamma
|
|
pInfo->gamma = p->bDisplayXferChar + 100;
|
|
|
|
// get the features
|
|
pInfo->u.feature = p->bFeatureSupport;
|
|
|
|
// get chromaticity coordinates
|
|
pInfo->cc_red_x = p->Chromaticity[2] << 2;
|
|
pInfo->cc_red_x |= (p->Chromaticity[0] & NVT_PVT_EDID_CC_RED_X1_X0_MASK) >> NVT_PVT_EDID_CC_RED_X1_X0_SHIFT;
|
|
pInfo->cc_red_y = p->Chromaticity[3] << 2;
|
|
pInfo->cc_red_y |= (p->Chromaticity[0] & NVT_PVT_EDID_CC_RED_Y1_Y0_MASK) >> NVT_PVT_EDID_CC_RED_Y1_Y0_SHIFT;
|
|
|
|
pInfo->cc_green_x = p->Chromaticity[4] << 2;
|
|
pInfo->cc_green_x |= (p->Chromaticity[0] & NVT_PVT_EDID_CC_GREEN_X1_X0_MASK) >> NVT_PVT_EDID_CC_GREEN_X1_X0_SHIFT;
|
|
pInfo->cc_green_y = p->Chromaticity[5] << 2;
|
|
pInfo->cc_green_y |= (p->Chromaticity[0] & NVT_PVT_EDID_CC_GREEN_Y1_Y0_MASK) >> NVT_PVT_EDID_CC_GREEN_Y1_Y0_SHIFT;
|
|
|
|
pInfo->cc_blue_x = p->Chromaticity[6] << 2;
|
|
pInfo->cc_blue_x |= (p->Chromaticity[1] & NVT_PVT_EDID_CC_BLUE_X1_X0_MASK) >> NVT_PVT_EDID_CC_BLUE_X1_X0_SHIFT;
|
|
pInfo->cc_blue_y = p->Chromaticity[7] << 2;
|
|
pInfo->cc_blue_y |= (p->Chromaticity[1] & NVT_PVT_EDID_CC_BLUE_Y1_Y0_MASK) >> NVT_PVT_EDID_CC_BLUE_Y1_Y0_SHIFT;
|
|
|
|
pInfo->cc_white_x = p->Chromaticity[8] << 2;
|
|
pInfo->cc_white_x |= (p->Chromaticity[1] & NVT_PVT_EDID_CC_WHITE_X1_X0_MASK) >> NVT_PVT_EDID_CC_WHITE_X1_X0_SHIFT;
|
|
pInfo->cc_white_y = p->Chromaticity[9] << 2;
|
|
pInfo->cc_white_y |= (p->Chromaticity[1] & NVT_PVT_EDID_CC_WHITE_Y1_Y0_MASK) >> NVT_PVT_EDID_CC_WHITE_Y1_Y0_SHIFT;
|
|
|
|
// copy established timings
|
|
pInfo->established_timings_1_2 = (NvU16)p->bEstablishedTimings1 << 8;
|
|
pInfo->established_timings_1_2 |= (NvU16)p->bEstablishedTimings2;
|
|
|
|
// copy manuf reserved timings
|
|
pInfo->manufReservedTimings = p->bManufReservedTimings;
|
|
|
|
// copy standard timings
|
|
for (i = 0; i < NVT_EDID_MAX_STANDARD_TIMINGS; i++)
|
|
{
|
|
pInfo->standard_timings[i] = p->wStandardTimingID[i];
|
|
}
|
|
|
|
// get the number of extensions
|
|
pInfo->total_extensions = p->bExtensionFlag;
|
|
|
|
// check_sum
|
|
for (i = 0, data = 0; i < length; i++)
|
|
{
|
|
data += pEdid[i];
|
|
}
|
|
pInfo->checksum_ok = !(data & 0xFF);
|
|
pInfo->checksum = p->bChecksum;
|
|
|
|
|
|
// now find out the total number of all of the timings in the EDID
|
|
pInfo->total_timings = 0;
|
|
|
|
// now find out the detailed timings
|
|
parseEdidDetailedTiming(pEdid, pInfo);
|
|
|
|
// now parse all 18-byte long display descriptors (not detailed timing)
|
|
for (i = 0; i < NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++)
|
|
{
|
|
parseEdidLongDisplayDescriptor((EDID_LONG_DISPLAY_DESCRIPTOR *)&p->DetailedTimingDesc[i], &pInfo->ldd[i], pInfo->version);
|
|
}
|
|
|
|
// now check the number of timings in the extension
|
|
for (k = 0, j = 1; j <= pInfo->total_extensions && (j + 1) * sizeof(EDIDV1STRUC) <= length; j++)
|
|
{
|
|
pExt = pEdid + sizeof(EDIDV1STRUC) * j;
|
|
|
|
// check for 861 extension first
|
|
switch (*pExt)
|
|
{
|
|
case NVT_EDID_EXTENSION_CTA:
|
|
p861Info = (k == 0) ? &pInfo->ext861 : &pInfo->ext861_2;
|
|
|
|
get861ExtInfo(pExt, sizeof(EDIDV1STRUC), p861Info);
|
|
|
|
// HF EEODB is present in edid v1.3 and v1.4 does not need this.Also, it is always present in the 1st CTA extension block.
|
|
if (j == 1 && pInfo->version == NVT_EDID_VER_1_3)
|
|
{
|
|
parseCta861HfEeodb(p861Info, &pInfo->total_extensions);
|
|
}
|
|
|
|
// update pInfo with basic hdmi info
|
|
// assumes each edid will only have one such block across multiple cta861 blocks (otherwise may create declaration conflict)
|
|
// In case of multiple such blocks, the last one takes precedence, except for SCDB
|
|
|
|
// parseCta861VsdbBlocks() uses hfScdb info so need to be parsed first
|
|
parseCta861HfScdb(p861Info, pInfo, FROM_CTA861_EXTENSION);
|
|
parseCta861VsdbBlocks(p861Info, pInfo, FROM_CTA861_EXTENSION);
|
|
parseCta861VsvdbBlocks(p861Info, pInfo, FROM_CTA861_EXTENSION);
|
|
|
|
// parse HDR related information from the HDR static metadata data block
|
|
if (p861Info->valid.hdr_static_metadata != 0)
|
|
{
|
|
parseCta861HdrStaticMetadataDataBlock(p861Info, pInfo, FROM_CTA861_EXTENSION);
|
|
}
|
|
|
|
// Timings are listed (or shall) be listed in priority order
|
|
// So read SVD, yuv420 SVDs first before reading detailed timings
|
|
|
|
// add the 861B short video timing descriptor
|
|
if (p861Info->revision >= NVT_CEA861_REV_B)
|
|
{
|
|
// base video
|
|
parse861bShortTiming(p861Info, pInfo, FROM_CTA861_EXTENSION);
|
|
|
|
// yuv420-only video
|
|
parse861bShortYuv420Timing(p861Info, pInfo, FROM_CTA861_EXTENSION);
|
|
}
|
|
|
|
// add the detailed timings in 18-byte long display descriptor
|
|
parse861ExtDetailedTiming(pExt, p861Info->basic_caps, pInfo);
|
|
|
|
if (p861Info->revision >= NVT_CTA861_REV_H)
|
|
{
|
|
if (p861Info->total_vfdb != 0) parseCta861VideoFormatDataBlock(p861Info, pInfo);
|
|
if (p861Info->total_did_type7db != 0) parseCta861DIDType7VideoTimingDataBlock(p861Info, pInfo);
|
|
if (p861Info->total_did_type8db != 0) parseCta861DIDType8VideoTimingDataBlock(p861Info, pInfo);
|
|
if (p861Info->total_did_type10db != 0) parseCta861DIDType10VideoTimingDataBlock(p861Info, pInfo);
|
|
}
|
|
|
|
// CEA861-F at 7.5.12 section about VFPDB block.
|
|
if (p861Info->revision >= NVT_CEA861_REV_F && (p861Info->total_svr != 0 || p861Info->valid.NVRDB == 1))
|
|
{
|
|
parseCta861NativeOrPreferredTiming(p861Info, pInfo, FROM_CTA861_EXTENSION);
|
|
}
|
|
|
|
k++;
|
|
break;
|
|
|
|
case NVT_EDID_EXTENSION_VTB:
|
|
parseVTBExtension(pExt, pInfo);
|
|
break;
|
|
|
|
case NVT_EDID_EXTENSION_DISPLAYID:
|
|
if ((pExt[1] & 0xF0) == 0x20) // displayID2.x as EDID extension
|
|
{
|
|
if(getDisplayId20EDIDExtInfo(pExt, sizeof(EDIDV1STRUC),
|
|
pInfo) == NVT_STATUS_SUCCESS)
|
|
{
|
|
if (pInfo->ext861.total_y420vdb != 0 || pInfo->ext861.total_y420cmdb != 0)
|
|
{
|
|
pInfo->ext_displayid20.interface_features.yuv420_min_pclk = 0;
|
|
}
|
|
|
|
if (pInfo->ext_displayid20.valid_data_blocks.interface_feature_present)
|
|
{
|
|
pInfo->ext861.basic_caps |= pInfo->ext_displayid20.basic_caps;
|
|
}
|
|
}
|
|
}
|
|
else // displayID13 as EDID extension
|
|
{
|
|
//do not fail function based on return value of getDisplayIdEDIDExtInfo refer bug 3247180 where some rogue monitors don't provide correct DID13 raw data.
|
|
if (getDisplayIdEDIDExtInfo(pExt, sizeof(EDIDV1STRUC),
|
|
pInfo) == NVT_STATUS_SUCCESS)
|
|
{
|
|
// Check if YCbCr is supported in base block
|
|
// since it is mandatory if YCbCr is supported on any other display interface as per 5.1.1.1 Video Colorimetry
|
|
if(pInfo->u.feature_ver_1_4_digital.support_ycrcb_444)
|
|
{
|
|
if (!pInfo->ext_displayid.supported_displayId2_0)
|
|
{
|
|
pInfo->ext_displayid.u4.display_interface.ycbcr444_depth.support_8b = 1;
|
|
}
|
|
else
|
|
{
|
|
pInfo->ext_displayid.u4.display_interface_features.ycbcr444_depth.support_8b = 1;
|
|
}
|
|
}
|
|
|
|
if(pInfo->u.feature_ver_1_4_digital.support_ycrcb_422)
|
|
{
|
|
if (!pInfo->ext_displayid.supported_displayId2_0)
|
|
{
|
|
pInfo->ext_displayid.u4.display_interface.ycbcr422_depth.support_8b = 1;
|
|
}
|
|
else
|
|
{
|
|
pInfo->ext_displayid.u4.display_interface_features.ycbcr422_depth.support_8b = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Copy all the timings(could include type 7/8/9/10) from displayid20->timings[] to pEdidInfo->timings[]
|
|
for (i = 0; i < pInfo->ext_displayid20.total_timings; i++)
|
|
{
|
|
if (!assignNextAvailableTiming(pInfo, &(pInfo->ext_displayid20.timing[i])))
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
}
|
|
|
|
// check for cvt timings - in display range limits or cvt 3-byte LDD, only for EDID1.4 and above
|
|
if (pInfo->version > NVT_EDID_VER_1_3)
|
|
{
|
|
parseEdidCvtTiming(pInfo);
|
|
}
|
|
|
|
// now check for standard timings - base EDID and then the LDDs
|
|
parseEdidStandardTiming(pInfo);
|
|
|
|
// find out the total established timings - base EDID and then the LDDs
|
|
parseEdidEstablishedTiming(pInfo);
|
|
|
|
// remove the T8VTDB timing if it co-existed in standard or established timings
|
|
if (pInfo->ext861.revision >= NVT_CTA861_REV_H && pInfo->ext861.total_did_type8db != 0 && pInfo->total_timings > 1)
|
|
{
|
|
for (i = 0; i < pInfo->total_timings; i++)
|
|
{
|
|
if (NVT_GET_TIMING_STATUS_TYPE(pInfo->timing[i].etc.status) == NVT_TYPE_CTA861_DID_T8)
|
|
{
|
|
if (isMatchedStandardTiming(pInfo, &pInfo->timing[i]) || isMatchedEstablishedTiming(pInfo, &pInfo->timing[i]))
|
|
{
|
|
for (j = i; j < pInfo->total_timings - 1; j++)
|
|
{
|
|
// remove the entry by moving the next entry up.
|
|
pInfo->timing[j] = pInfo->timing[j+1];
|
|
}
|
|
NVMISC_MEMSET(&pInfo->timing[pInfo->total_timings-1], 0, sizeof(NVT_TIMING));
|
|
pInfo->total_timings--; i--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
getEdidHDM1_4bVsdbTiming(pInfo);
|
|
|
|
// Assert if no timings were found (due to a bad EDID) or if we mistakenly
|
|
// assigned more timings than we allocated space for (due to bad logic above)
|
|
nvt_assert(pInfo->total_timings &&
|
|
(pInfo->total_timings <= COUNT(pInfo->timing)));
|
|
|
|
// go through all timings and update supported color formats
|
|
// consider the supported bpc per color format from parsed EDID / CTA861 / DisplayId
|
|
updateColorFormatAndBpcTiming(pInfo);
|
|
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
void updateColorFormatAndBpcTiming(NVT_EDID_INFO *pInfo)
|
|
{
|
|
NvU32 i, j, data;
|
|
|
|
for (i = 0; i < pInfo->total_timings; i++)
|
|
{
|
|
data = NVT_GET_TIMING_STATUS_TYPE(pInfo->timing[i].etc.status);
|
|
switch (data)
|
|
{
|
|
case NVT_TYPE_HDMI_STEREO:
|
|
case NVT_TYPE_HDMI_EXT:
|
|
// VTB timing use the base EDID (block 0) to determine the color format support
|
|
case NVT_TYPE_EDID_VTB_EXT:
|
|
case NVT_TYPE_EDID_VTB_EXT_STD:
|
|
case NVT_TYPE_EDID_VTB_EXT_DTD:
|
|
case NVT_TYPE_EDID_VTB_EXT_CVT:
|
|
// pInfo->u.feature_ver_1_3.color_type provides mono, rgb, rgy, undefined
|
|
// assume RGB 8-bpc support only (VTB is pretty old edid standard)
|
|
pInfo->timing[i].etc.rgb444.bpc.bpc8 = 1;
|
|
break;
|
|
// These are from the CTA block, and relies on
|
|
// Since there could be multiple CEA blocks, these are adjusted when the blocks are parsed
|
|
case NVT_TYPE_EDID_861ST:
|
|
case NVT_TYPE_EDID_EXT_DTD:
|
|
if (pInfo->ext_displayid20.as_edid_extension &&
|
|
pInfo->ext_displayid20.valid_data_blocks.cta_data_present)
|
|
{
|
|
updateColorFormatForDisplayId20ExtnTimings(pInfo, i);
|
|
}
|
|
updateBpcForTiming(pInfo, i);
|
|
break;
|
|
default:
|
|
// * the displayID_v1.3/v2.0 EDID extension need to follow the EDID bpc definition.
|
|
// * all other default to base edid
|
|
updateBpcForTiming(pInfo, i);
|
|
}
|
|
|
|
// The timings[i] entries need to update the bpc values where are based on the different color format again
|
|
// if displayId extension existed it's interface feature data block
|
|
if (pInfo->ext_displayid.version == 0x12 || pInfo->ext_displayid.version == 0x13)
|
|
{
|
|
updateColorFormatForDisplayIdExtnTimings(pInfo, i);
|
|
}
|
|
else if (pInfo->ext_displayid20.valid_data_blocks.interface_feature_present)
|
|
{
|
|
// DisplayId2.0 spec has its own way of determining color format support which includes bpc + color format
|
|
updateColorFormatForDisplayId20ExtnTimings(pInfo, i);
|
|
}
|
|
}
|
|
|
|
// Go through all the timings and set CTA format accordingly. If a timing is a CTA 861b timing, store the
|
|
// index of this CTA 861b standard in NVT_TIMING.etc.status field.
|
|
// However parser needs to exclude the DTD timing in EDID base block where is shared same detailed timing in VIC/DTD_ext in CTA861
|
|
for (i = 0; i < pInfo->total_timings; i++)
|
|
{
|
|
data = NvTiming_GetCEA861TimingIndex(&pInfo->timing[i]);
|
|
// DisplayID block did not belong to CTA timing and it owned the deep color block itself
|
|
if (data && !((NVT_GET_TIMING_STATUS_TYPE(pInfo->timing[i].etc.status) == NVT_TYPE_DISPLAYID_1) ||
|
|
(NVT_GET_TIMING_STATUS_TYPE(pInfo->timing[i].etc.status) == NVT_TYPE_DISPLAYID_2) ||
|
|
(NVT_GET_TIMING_STATUS_TYPE(pInfo->timing[i].etc.status) == NVT_TYPE_DISPLAYID_7) ||
|
|
(NVT_GET_TIMING_STATUS_TYPE(pInfo->timing[i].etc.status) == NVT_TYPE_DISPLAYID_8) ||
|
|
(NVT_GET_TIMING_STATUS_TYPE(pInfo->timing[i].etc.status) == NVT_TYPE_DISPLAYID_9) ||
|
|
(NVT_GET_TIMING_STATUS_TYPE(pInfo->timing[i].etc.status) == NVT_TYPE_DISPLAYID_10)))
|
|
{
|
|
// CEA timings may be enumerated outside of SVD blocks -- the formats of these timings don't have CEA FORMAT (vic) set
|
|
// before marking them CEA, make sure their color formats are updated too
|
|
if (NVT_GET_CEA_FORMAT(pInfo->timing[i].etc.status) == 0 &&
|
|
(!NVT_IS_DTD(pInfo->timing[i].etc.status) ||
|
|
isMatchedCTA861Timing(pInfo, &pInfo->timing[i])))
|
|
{
|
|
for (j = 0; j < pInfo->total_timings; j++)
|
|
{
|
|
// It is assumed CTA timings that are repeated by the CTA block or different CTA blocks will
|
|
// announce the same color format for the same CTA timings
|
|
if (NVT_GET_CEA_FORMAT(pInfo->timing[j].etc.status) == data)
|
|
{
|
|
// There could be anomalies between EDID 1.4 base block color format vs CEA861 basic caps
|
|
// In this case we assume the union is supported
|
|
pInfo->timing[i].etc.rgb444.bpcs |= pInfo->timing[j].etc.rgb444.bpcs;
|
|
pInfo->timing[i].etc.yuv444.bpcs |= pInfo->timing[j].etc.yuv444.bpcs;
|
|
pInfo->timing[i].etc.yuv422.bpcs |= pInfo->timing[j].etc.yuv422.bpcs;
|
|
pInfo->timing[i].etc.yuv420.bpcs |= pInfo->timing[j].etc.yuv420.bpcs;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// now update the VIC of this timing
|
|
NVT_SET_CEA_FORMAT(pInfo->timing[i].etc.status, data);
|
|
}
|
|
// see the aspect ratio info if needed
|
|
if (pInfo->timing[i].etc.aspect == 0)
|
|
{
|
|
pInfo->timing[i].etc.aspect = getCEA861TimingAspectRatio(data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NvBool isMatchedStandardTiming(NVT_EDID_INFO *pInfo, NVT_TIMING *pT)
|
|
{
|
|
NvU32 j;
|
|
|
|
for (j = 0; j < pInfo->total_timings; j++)
|
|
{
|
|
if (NVT_GET_TIMING_STATUS_TYPE(pInfo->timing[j].etc.status) == NVT_TYPE_EDID_STD &&
|
|
NvTiming_IsTimingRelaxedEqual(&pInfo->timing[j], pT))
|
|
{
|
|
return NV_TRUE;
|
|
}
|
|
}
|
|
return NV_FALSE;
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NvBool isMatchedEstablishedTiming(NVT_EDID_INFO *pInfo, NVT_TIMING *pT)
|
|
{
|
|
NvU32 j;
|
|
|
|
for (j = 0; j < pInfo->total_timings; j++)
|
|
{
|
|
if (NVT_GET_TIMING_STATUS_TYPE(pInfo->timing[j].etc.status) == NVT_TYPE_EDID_EST &&
|
|
NvTiming_IsTimingRelaxedEqual(&pInfo->timing[j], pT))
|
|
{
|
|
return NV_TRUE;
|
|
}
|
|
}
|
|
return NV_FALSE;
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NvBool isMatchedCTA861Timing(NVT_EDID_INFO *pInfo, NVT_TIMING *pT)
|
|
{
|
|
NvU32 j;
|
|
|
|
for (j = 0; j < pInfo->total_timings; j++)
|
|
{
|
|
if (NVT_GET_CEA_FORMAT(pInfo->timing[j].etc.status) && NvTiming_IsTimingExactEqual(&pInfo->timing[j], pT))
|
|
{
|
|
return NV_TRUE;
|
|
}
|
|
}
|
|
return NV_FALSE;
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
void updateBpcForTiming(NVT_EDID_INFO *pInfo, NvU32 index)
|
|
{
|
|
NVT_EDID_CEA861_INFO *p861Info;
|
|
|
|
// assume/prefer data from 1st CEA block if multiple exist
|
|
p861Info = &pInfo->ext861;
|
|
|
|
pInfo->timing[index].etc.rgb444.bpc.bpc8 = 1;
|
|
|
|
if (pInfo->version >= NVT_EDID_VER_1_4 && pInfo->input.isDigital)
|
|
{
|
|
if (pInfo->u.feature_ver_1_4_digital.support_ycrcb_444)
|
|
{
|
|
pInfo->timing[index].etc.yuv444.bpc.bpc8 = 1;
|
|
}
|
|
if (pInfo->u.feature_ver_1_4_digital.support_ycrcb_422)
|
|
{
|
|
pInfo->timing[index].etc.yuv422.bpc.bpc8 = 1;
|
|
}
|
|
if (pInfo->input.u.digital.video_interface == NVT_EDID_DIGITAL_VIDEO_INTERFACE_STANDARD_DISPLAYPORT_SUPPORTED ||
|
|
pInfo->input.u.digital.video_interface == NVT_EDID_DIGITAL_VIDEO_INTERFACE_STANDARD_UNDEFINED)
|
|
{
|
|
pInfo->timing[index].etc.rgb444.bpc.bpc6 = 1;
|
|
|
|
// trust bpc claim in edid base block for DP only
|
|
if (pInfo->input.u.digital.bpc >= NVT_EDID_VIDEOSIGNAL_BPC_10)
|
|
{
|
|
pInfo->timing[index].etc.rgb444.bpc.bpc10 = 1;
|
|
pInfo->timing[index].etc.yuv444.bpc.bpc10 = pInfo->u.feature_ver_1_4_digital.support_ycrcb_444 || (p861Info->basic_caps & NVT_CEA861_CAP_YCbCr_444);
|
|
pInfo->timing[index].etc.yuv422.bpc.bpc10 = pInfo->u.feature_ver_1_4_digital.support_ycrcb_422 || (p861Info->basic_caps & NVT_CEA861_CAP_YCbCr_422);
|
|
}
|
|
if (pInfo->input.u.digital.bpc >= NVT_EDID_VIDEOSIGNAL_BPC_12)
|
|
{
|
|
pInfo->timing[index].etc.rgb444.bpc.bpc12 = 1;
|
|
pInfo->timing[index].etc.yuv444.bpc.bpc12 = pInfo->u.feature_ver_1_4_digital.support_ycrcb_444 || (p861Info->basic_caps & NVT_CEA861_CAP_YCbCr_444);
|
|
pInfo->timing[index].etc.yuv422.bpc.bpc12 = pInfo->u.feature_ver_1_4_digital.support_ycrcb_422 || (p861Info->basic_caps & NVT_CEA861_CAP_YCbCr_422);
|
|
}
|
|
if (pInfo->input.u.digital.bpc >= NVT_EDID_VIDEOSIGNAL_BPC_16)
|
|
{
|
|
pInfo->timing[index].etc.rgb444.bpc.bpc16 = 1;
|
|
pInfo->timing[index].etc.yuv444.bpc.bpc16 = pInfo->u.feature_ver_1_4_digital.support_ycrcb_444 || (p861Info->basic_caps & NVT_CEA861_CAP_YCbCr_444);
|
|
pInfo->timing[index].etc.yuv422.bpc.bpc16 = pInfo->u.feature_ver_1_4_digital.support_ycrcb_422 || (p861Info->basic_caps & NVT_CEA861_CAP_YCbCr_422);
|
|
}
|
|
}
|
|
else if ((pInfo->input.u.digital.video_interface == NVT_EDID_DIGITAL_VIDEO_INTERFACE_STANDARD_HDMI_A_SUPPORTED ||
|
|
pInfo->input.u.digital.video_interface == NVT_EDID_DIGITAL_VIDEO_INTERFACE_STANDARD_HDMI_B_SUPPORTED ||
|
|
pInfo->input.u.digital.video_interface == NVT_EDID_DIGITAL_VIDEO_INTERFACE_STANDARD_UNDEFINED) &&
|
|
p861Info->revision >= NVT_CEA861_REV_A)
|
|
{
|
|
updateHDMILLCDeepColorForTiming(pInfo, index);
|
|
}
|
|
}
|
|
else if (p861Info->revision >= NVT_CEA861_REV_A)
|
|
{
|
|
updateHDMILLCDeepColorForTiming(pInfo, index);
|
|
}
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NvTiming_Get18ByteLongDescriptorIndex(NVT_EDID_INFO *pEdidInfo, NvU8 tag, NvU32 *pDtdIndex)
|
|
{
|
|
NvU32 dtdIndex;
|
|
|
|
if (!pEdidInfo || !pDtdIndex)
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
for (dtdIndex = *pDtdIndex; dtdIndex < NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; dtdIndex++)
|
|
{
|
|
if (pEdidInfo->ldd[dtdIndex].tag == tag)
|
|
{
|
|
*pDtdIndex = dtdIndex;
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
// get the edid timing
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NvTiming_GetEdidTimingExWithPclk(NvU32 width, NvU32 height, NvU32 rr, NvU32 flag, NVT_EDID_INFO *pEdidInfo, NVT_TIMING *pT, NvU32 rrx1k, NvU32 pclk)
|
|
{
|
|
NvU8 kth = 0;
|
|
NvU32 i, j;
|
|
NvU32 native_cta, preferred_cta, preferred_displayid_dtd, preferred_dtd1, dtd1, map0, map1, map2, map3, map4, ceaIndex, max, cvt;
|
|
NVT_TIMING *pEdidTiming;
|
|
NVT_EDID_DD_RANGE_CVT *pCVT = NULL;
|
|
NVT_TIMING cvtTiming;
|
|
|
|
// input check
|
|
if (pEdidInfo == NULL || pEdidInfo->total_timings == 0 || pT == 0)
|
|
return NVT_STATUS_ERR;
|
|
|
|
if (width == 0 || height == 0 || rr == 0 ) // rrx1k and pclk are optional, can be 0.
|
|
return NVT_STATUS_ERR;
|
|
|
|
pEdidTiming = pEdidInfo->timing;
|
|
|
|
// the timing mapping index :
|
|
//
|
|
// native_cta - the "native resoluiotn of the sink" in the CTA861.6 A Source shall override any other native video resolution indicators
|
|
// if the Source supports NVRDB and the NVRDB was found in the E-EDID
|
|
// preferred_cta - the "prefer SVD" in CTA-861-F (i.e. A Sink that prefers a Video Format that is not listed as an SVD in Video Data Block, but instead listed in YCBCR 4:2:0 VDB)
|
|
// preferred_displayid_dtd - the "prefer detailed timing of DispalyID" extension
|
|
// preferred_dtd1 - the first deatiled timing and PTM flag is enable
|
|
// dtd1 - the first detailed timing
|
|
// map0 - the "perfect" match (the timing's H/V-visible and pixel clock(refresh rate) are the same as the asking "width", "height" and "rr".
|
|
// map1 - the "closest" match with the honor of the interlaced flag
|
|
// map2 - the "closest" match without the honor of the interlaced flag
|
|
// map3 - the "closest" match to the panel's native timing (i.e. the first DTD timing or the short 861B/C/D timings with "native" flag).
|
|
// map4 - the "closest" match with the same refresh rate
|
|
// max - the timing with the max visible area
|
|
native_cta = preferred_cta = preferred_displayid_dtd = preferred_dtd1 = dtd1 = map0 = map1 = map2 = map3 = map4 = ceaIndex = pEdidInfo->total_timings;
|
|
max = cvt = 0;
|
|
|
|
if (pEdidInfo->ext861.total_svr > 1)
|
|
{
|
|
kth = getHighestPrioritySVRIdx(&pEdidInfo->ext861);
|
|
}
|
|
|
|
for (i = 0; i < pEdidInfo->total_timings; i++)
|
|
{
|
|
// if the client prefers _NATIVE timing, then don't select custom timing
|
|
if ((flag & (NVT_FLAG_NATIVE_TIMING | NVT_FLAG_EDID_TIMING)) != 0 && NVT_IS_CUST_ENTRY(pEdidTiming[i].etc.status) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// find the perfect match is possible
|
|
if ((flag & NVT_FLAG_MAX_EDID_TIMING) == 0 &&
|
|
width == pEdidTiming[i].HVisible &&
|
|
height == frame_height(pEdidTiming[i]) &&
|
|
rr == pEdidTiming[i].etc.rr &&
|
|
((rrx1k == 0) || (rrx1k == pEdidTiming[i].etc.rrx1k)) &&
|
|
!!(flag & NVT_PVT_INTERLACED_MASK) == !!pEdidTiming[i].interlaced)
|
|
{
|
|
if (map0 >= pEdidInfo->total_timings || pEdidTiming[i].pclk == pclk)
|
|
{
|
|
// make sure we take the priority as "detailed>standard>established". (The array timing[] always have the detailed timings in the front and then the standard and established.)
|
|
map0 = i;
|
|
}
|
|
|
|
if ( (NVT_PREFERRED_TIMING_IS_CTA(pEdidTiming[i].etc.flag)) ||
|
|
((0 == (flag & NVT_FLAG_EDID_861_ST)) && NVT_PREFERRED_TIMING_IS_DTD1(pEdidTiming[i].etc.flag, pEdidTiming[i].etc.status)) ||
|
|
(NVT_PREFERRED_TIMING_IS_DISPLAYID(pEdidTiming[i].etc.flag)) ||
|
|
(NVT_IS_NATIVE_TIMING(pEdidTiming[i].etc.status)))
|
|
{
|
|
*pT = pEdidTiming[i];
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (NVT_GET_TIMING_STATUS_TYPE(pEdidTiming[i].etc.status) == NVT_TYPE_EDID_861ST)
|
|
{
|
|
if (ceaIndex == pEdidInfo->total_timings)
|
|
{
|
|
// Save the first entry found.
|
|
ceaIndex = i;
|
|
}
|
|
else
|
|
{
|
|
if (((flag & NVT_FLAG_CEA_4X3_TIMING) && (pEdidTiming[i].etc.aspect == 0x40003)) ||
|
|
((flag & NVT_FLAG_CEA_16X9_TIMING) && (pEdidTiming[i].etc.aspect == 0x160009)))
|
|
{
|
|
// Use preferred aspect ratio if specified.
|
|
ceaIndex = i;
|
|
}
|
|
}
|
|
}
|
|
} // if ((flag & NVT_FLAG_MAX_EDID_TIMING) == 0 &&
|
|
|
|
// bypass the custom timing to be select for the mismatch case
|
|
if (NVT_GET_TIMING_STATUS_TYPE(pEdidTiming[i].etc.status) == NVT_TYPE_CUST ||
|
|
NVT_IS_CUST_ENTRY(pEdidTiming[i].etc.status) != 0)
|
|
{
|
|
if (width != pEdidTiming[i].HVisible || height != frame_height(pEdidTiming[i]) || rr != pEdidTiming[i].etc.rr)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// find out the preferred timing just in case of cea_vfpdb is existed
|
|
if (native_cta == pEdidInfo->total_timings && NVT_NATIVE_TIMING_IS_CTA(pEdidTiming[i].etc.flag))
|
|
{
|
|
native_cta = i;
|
|
}
|
|
|
|
if (preferred_cta == pEdidInfo->total_timings && NVT_PREFERRED_TIMING_IS_CTA(pEdidTiming[i].etc.flag))
|
|
{
|
|
if (pEdidInfo->ext861.total_svr > 1)
|
|
{
|
|
if (kth != 0)
|
|
{
|
|
// svr == vic
|
|
if (NVT_IS_CTA861(pEdidTiming[i].etc.status) && (NVT_GET_CEA_FORMAT(pEdidTiming[i].etc.status) == kth))
|
|
{
|
|
preferred_cta = i;
|
|
}
|
|
else if (NVT_GET_TIMING_STATUS_SEQ(pEdidTiming[i].etc.status) == kth)
|
|
{
|
|
preferred_cta = i;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
preferred_cta = i;
|
|
}
|
|
}
|
|
|
|
// find out the preferred timing just in case
|
|
// Caller we will force rr value as 1 to select the DisplayID prefer timing in pEdidTiming if it existed
|
|
// however, we can't assign the correct refresh rate we want if we had two and above rr values which shared the same timing.
|
|
if (rr != 1)
|
|
{
|
|
if (pEdidTiming[i].etc.rr == rr && NVT_PREFERRED_TIMING_IS_DISPLAYID(pEdidTiming[i].etc.flag))
|
|
{
|
|
preferred_displayid_dtd = i;
|
|
}
|
|
}
|
|
else if (preferred_displayid_dtd == pEdidInfo->total_timings &&
|
|
NVT_PREFERRED_TIMING_IS_DISPLAYID(pEdidTiming[i].etc.flag))
|
|
{
|
|
preferred_displayid_dtd = i;
|
|
}
|
|
|
|
if (NVT_PREFERRED_TIMING_IS_DTD1(pEdidTiming[i].etc.flag, pEdidTiming[i].etc.status))
|
|
{
|
|
preferred_dtd1 = i;
|
|
}
|
|
|
|
if (NVT_IS_DTD1(pEdidTiming[i].etc.status))
|
|
{
|
|
dtd1 = i;
|
|
}
|
|
|
|
// find out the max mode just in case
|
|
if (pEdidTiming[i].HVisible * pEdidTiming[i].VVisible > pEdidTiming[max].HVisible * pEdidTiming[max].VVisible)
|
|
max = i;
|
|
|
|
// if the requested timing is not in the EDID, try to find out the EDID entry with the same progressive/interlaced setting
|
|
if (map1 >= pEdidInfo->total_timings)
|
|
{
|
|
if (!!(flag & NVT_PVT_INTERLACED_MASK) == !!pEdidTiming[i].interlaced &&
|
|
width <= pEdidTiming[i].HVisible &&
|
|
height <= frame_height(pEdidTiming[i]))
|
|
{
|
|
map1 = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!!(flag & NVT_PVT_INTERLACED_MASK) == !!pEdidTiming[i].interlaced &&
|
|
width <= pEdidTiming[i].HVisible &&
|
|
height <= frame_height(pEdidTiming[i]) &&
|
|
abs_delta(pEdidTiming[i].HVisible, width) <= abs_delta(pEdidTiming[map1].HVisible, width) &&
|
|
abs_delta(frame_height(pEdidTiming[i]), height) <= abs_delta(frame_height(pEdidTiming[map1]), height))
|
|
{
|
|
// if there're 2 timings with the same visible size, choose the one with closer refresh rate
|
|
if (pEdidTiming[i].HVisible == pEdidTiming[map1].HVisible &&
|
|
frame_height(pEdidTiming[i]) == frame_height(pEdidTiming[map1]))
|
|
{
|
|
if (abs_delta(pEdidTiming[i].etc.rr, rr) < abs_delta(pEdidTiming[map1].etc.rr, rr))
|
|
{
|
|
map1 = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
map1 = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the requested timing is not in the EDID, try to find out the EDID entry without the progressive/interlaced setting
|
|
if (map2 >= pEdidInfo->total_timings)
|
|
{
|
|
if (width <= pEdidTiming[i].HVisible &&
|
|
height <= frame_height(pEdidTiming[i]))
|
|
{
|
|
map2 = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (width <= pEdidTiming[i].HVisible &&
|
|
height <= frame_height(pEdidTiming[i]) &&
|
|
abs_delta(pEdidTiming[i].HVisible, width) <= abs_delta(pEdidTiming[map2].HVisible, width) &&
|
|
abs_delta(frame_height(pEdidTiming[i]), height) <= abs_delta(frame_height(pEdidTiming[map2]), height))
|
|
{
|
|
// if there're 2 timings with the same visible size, choose the one with closer refresh rate
|
|
if (pEdidTiming[i].HVisible == pEdidTiming[map2].HVisible &&
|
|
frame_height(pEdidTiming[i]) == frame_height(pEdidTiming[map2]))
|
|
{
|
|
if (abs_delta(pEdidTiming[i].etc.rr, rr) < abs_delta(pEdidTiming[map2].etc.rr, rr))
|
|
{
|
|
map2 = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
map2 = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// find out the native timing
|
|
if (NVT_IS_NATIVE_TIMING(pEdidTiming[i].etc.status) || NVT_IS_DTD1(pEdidTiming[i].etc.status))
|
|
{
|
|
if (map3 >= pEdidInfo->total_timings)
|
|
{
|
|
if (width <= pEdidTiming[i].HVisible &&
|
|
height <= frame_height(pEdidTiming[i]))
|
|
{
|
|
map3 = i;
|
|
}
|
|
}
|
|
else if(abs_delta(pEdidTiming[i].HVisible, width) <= abs_delta(pEdidTiming[map3].HVisible, width) &&
|
|
abs_delta(frame_height(pEdidTiming[i]), height) <= abs_delta(frame_height(pEdidTiming[map3]), height) &&
|
|
width <= pEdidTiming[i].HVisible &&
|
|
height <= frame_height(pEdidTiming[i]))
|
|
{
|
|
map3 = i;
|
|
}
|
|
}
|
|
|
|
// find the edid timing with refresh rate matching
|
|
if (map4 >= pEdidInfo->total_timings)
|
|
{
|
|
if (width <= pEdidTiming[i].HVisible &&
|
|
height <= pEdidTiming[i].VVisible &&
|
|
rr == pEdidTiming[i].etc.rr)
|
|
{
|
|
map4 = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (width <= pEdidTiming[i].HVisible &&
|
|
height <= pEdidTiming[i].HVisible &&
|
|
rr == pEdidTiming[i].etc.rr &&
|
|
abs_delta(pEdidTiming[i].HVisible, width) <= abs_delta(pEdidTiming[map4].HVisible, width) &&
|
|
abs_delta(pEdidTiming[i].VVisible, height) <= abs_delta(pEdidTiming[map4].VVisible, height))
|
|
{
|
|
map4 = i;
|
|
}
|
|
}
|
|
|
|
}//for (i = 0; i < pEdidInfo->total_timings; i++)
|
|
|
|
if ( (preferred_displayid_dtd == preferred_dtd1) && (preferred_dtd1 == dtd1) &&
|
|
(dtd1 == map0) &&
|
|
(map0 == map1) &&
|
|
(map1 == map2) &&
|
|
(map2 == map3) &&
|
|
(map3 == map4) &&
|
|
(map4 == pEdidInfo->total_timings) &&
|
|
pEdidInfo->version >= NVT_EDID_VER_1_4 &&
|
|
pEdidInfo->u.feature_ver_1_4_digital.continuous_frequency &&
|
|
!(flag & NVT_PVT_INTERLACED_MASK))
|
|
{
|
|
// try to find CVT timing that fits
|
|
NvU32 maxHeight, minHeight, tempHeight;
|
|
|
|
minHeight = ~0;
|
|
maxHeight = tempHeight= 0;
|
|
|
|
// looping through long display descriptors
|
|
for (i=0; i<NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++)
|
|
{
|
|
if (pEdidInfo->ldd[i].tag != NVT_EDID_DISPLAY_DESCRIPTOR_DRL || pEdidInfo->ldd[i].u.range_limit.timing_support != NVT_EDID_RANGE_SUPPORT_CVT)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pCVT = &pEdidInfo->ldd[i].u.range_limit.u.cvt;
|
|
|
|
if (width <= pCVT->max_active_pixels_per_line || (pCVT->scaling_support & NVT_EDID_CVT_SCALING_HOR_SHRINK))
|
|
{
|
|
for (j=0; j<NVT_EDID_CVT_ASPECT_SUPPORT_MAX && !cvt; j++)
|
|
{
|
|
if ( !(pCVT->aspect_supported & (1<<j)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch (1<<j)
|
|
{
|
|
case NVT_EDID_CVT_ASPECT_SUPPORT_4X3:
|
|
tempHeight = axb_div_c(width, 3, 4);
|
|
if (axb_div_c(width, 3, height) == 4) cvt = 1;
|
|
break;
|
|
case NVT_EDID_CVT_ASPECT_SUPPORT_16X9:
|
|
tempHeight = axb_div_c(width, 9, 16);
|
|
if (axb_div_c(width, 9, height) == 16) cvt = 1;
|
|
break;
|
|
case NVT_EDID_CVT_ASPECT_SUPPORT_16X10:
|
|
tempHeight = axb_div_c(width, 10, 16);
|
|
if (axb_div_c(width,10, height) == 16) cvt = 1;
|
|
break;
|
|
case NVT_EDID_CVT_ASPECT_SUPPORT_5X4:
|
|
tempHeight = axb_div_c(width, 4, 5);
|
|
if (axb_div_c(width, 4, height) == 5) cvt = 1;
|
|
break;
|
|
case NVT_EDID_CVT_ASPECT_SUPPORT_15X9:
|
|
tempHeight = axb_div_c(width, 9, 15);
|
|
if (axb_div_c(width, 9, height) == 15) cvt = 1;
|
|
break;
|
|
}
|
|
|
|
//keep track of max and min in case NVT_EDID_CVT_SCALING_VER_STRETCH/SHRINK are true
|
|
if (minHeight > tempHeight)
|
|
{
|
|
minHeight = tempHeight;
|
|
}
|
|
if (maxHeight < tempHeight)
|
|
{
|
|
maxHeight = tempHeight;
|
|
}
|
|
|
|
}//for (j=0; j<5; j++)
|
|
}//if (width <= pCVT->max_active_pixels_per_line || (pCVT->scaling_support & NVT_EDID_CVT_SCALING_HOR_STRETCH))
|
|
|
|
if ( ((minHeight < height) && (pCVT->scaling_support & NVT_EDID_CVT_SCALING_VER_SHRINK)) ||
|
|
((maxHeight > height) && (pCVT->scaling_support & NVT_EDID_CVT_SCALING_VER_STRETCH)) )
|
|
{
|
|
cvt = 1;
|
|
}
|
|
|
|
if (cvt)
|
|
{
|
|
break;
|
|
}
|
|
}//for (i=0; i<NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++)
|
|
|
|
if (cvt)
|
|
{
|
|
//calculate the CVT timing
|
|
// pclk is in 10KHz, max_pclk_MHz is in MHz, pixel_clock_adjustment is in .25MHz - make sure timing pclk is inside the max pclk declared in the EDID
|
|
if (pCVT->blanking_support & NVT_EDID_CVT_BLANKING_REDUCED && NvTiming_CalcCVT_RB(width, height, rr, NVT_PROGRESSIVE, &cvtTiming) == NVT_STATUS_SUCCESS)
|
|
{
|
|
if ( cvtTiming.pclk > (NvU32)((pEdidInfo->ldd[i].u.range_limit.max_pclk_MHz * 100) - (pCVT->pixel_clock_adjustment * 25)) )
|
|
{
|
|
cvt = 0;
|
|
}
|
|
}
|
|
else if (pCVT->blanking_support & NVT_EDID_CVT_BLANKING_STANDARD && NvTiming_CalcCVT(width, height, rr, NVT_PROGRESSIVE, &cvtTiming) == NVT_STATUS_SUCCESS)
|
|
{
|
|
if ( cvtTiming.pclk > (NvU32)((pEdidInfo->ldd[i].u.range_limit.max_pclk_MHz * 100) - (pCVT->pixel_clock_adjustment * 25)) )
|
|
{
|
|
cvt = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cvt = 0;
|
|
}
|
|
|
|
}
|
|
}//(dtd1 == map0 == map1 == map2 == map3 == pEdidInfo->total_timings) && pEdidInfo->version >= NVT_EDID_VER_1_4 &&
|
|
// pEdidInfo->feature_ver_1_4_digital.continuous_frequency && !(flag & NVT_PVT_INTERLACED_MASK))
|
|
|
|
// now return the mismatched EDID timing
|
|
if (flag & NVT_FLAG_NV_PREFERRED_TIMING)
|
|
{
|
|
*pT = (preferred_displayid_dtd != pEdidInfo->total_timings) ? pEdidTiming[preferred_displayid_dtd] :
|
|
(native_cta != pEdidInfo->total_timings) ? pEdidTiming[native_cta] :
|
|
(preferred_cta != pEdidInfo->total_timings) ? pEdidTiming[preferred_cta] :
|
|
(preferred_dtd1 != pEdidInfo->total_timings) ? pEdidTiming[preferred_dtd1] :
|
|
pEdidTiming[dtd1];
|
|
// what if DTD1 itself is filtered out, in such case dtd1 index points to an invalid timing[]?
|
|
// (dtd1 != pEdidInfo->total_timings) ? pEdidTiming[dtd1] : pEdidTiming[0];
|
|
}
|
|
else if (flag & NVT_FLAG_DTD1_TIMING)
|
|
{
|
|
*pT = pEdidTiming[dtd1];
|
|
}
|
|
else if ((flag & NVT_FLAG_MAX_EDID_TIMING) && (0 == (flag & NVT_FLAG_EDID_861_ST)))
|
|
{
|
|
*pT = pEdidTiming[max];
|
|
}
|
|
else if ((flag & (NVT_FLAG_CEA_4X3_TIMING | NVT_FLAG_CEA_16X9_TIMING | NVT_FLAG_EDID_861_ST)) && ceaIndex < (pEdidInfo->total_timings))
|
|
{
|
|
*pT = pEdidTiming[ceaIndex];
|
|
}
|
|
else if ((flag & NVT_FLAG_NATIVE_TIMING) != 0 && map3 < pEdidInfo->total_timings)
|
|
{
|
|
// Allow closest refresh rate match when EDID has detailed timing for different RR on native resolution.
|
|
if (map0 < pEdidInfo->total_timings &&
|
|
pEdidTiming[map0].HVisible == pEdidTiming[map3].HVisible &&
|
|
pEdidTiming[map0].VVisible == pEdidTiming[map3].VVisible)
|
|
{
|
|
*pT = pEdidTiming[map0];
|
|
}
|
|
else
|
|
{
|
|
*pT = pEdidTiming[map3];
|
|
}
|
|
}
|
|
else if (map0 < pEdidInfo->total_timings)
|
|
{
|
|
// use the exact mapped timing if possible
|
|
*pT = pEdidTiming[map0];
|
|
}
|
|
else if ((flag & NVT_FLAG_EDID_TIMING_RR_MATCH) && map4 < pEdidInfo->total_timings)
|
|
{
|
|
*pT = pEdidTiming[map4];
|
|
}
|
|
else if (map1 < pEdidInfo->total_timings)
|
|
{
|
|
// use the mapped timing if possible
|
|
*pT = pEdidTiming[map1];
|
|
}
|
|
else if (map2 < pEdidInfo->total_timings)
|
|
{
|
|
// use the 2nd mapped timing if possible
|
|
*pT = pEdidTiming[map2];
|
|
}
|
|
else if (dtd1 < pEdidInfo->total_timings && width <= pEdidTiming[dtd1].HVisible && height <= pEdidTiming[dtd1].VVisible)
|
|
{
|
|
// use the 1st detailed timing if possible
|
|
*pT = pEdidTiming[dtd1];
|
|
}
|
|
else if (cvt)
|
|
{
|
|
// use the cvt timing
|
|
*pT = cvtTiming;
|
|
}
|
|
else
|
|
{
|
|
// use the max timing for all other cases
|
|
*pT = pEdidTiming[max];
|
|
}
|
|
|
|
// set the mismatch status
|
|
if (pT->HVisible != width || frame_height(*pT) != height)
|
|
{
|
|
NVT_SET_TIMING_STATUS_MISMATCH(pT->etc.status, NVT_STATUS_TIMING_MISMATCH_SIZE);
|
|
}
|
|
if (!NvTiming_IsRoundedRREqual(pT->etc.rr, pT->etc.rrx1k, (NvU16)rr))
|
|
{
|
|
NVT_SET_TIMING_STATUS_MISMATCH(pT->etc.status, NVT_STATUS_TIMING_MISMATCH_RR);
|
|
}
|
|
if (!!pT->interlaced != !!(flag & NVT_PVT_INTERLACED_MASK))
|
|
{
|
|
NVT_SET_TIMING_STATUS_MISMATCH(pT->etc.status, NVT_STATUS_TIMING_MISMATCH_FORMAT);
|
|
}
|
|
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NvTiming_GetEdidTimingEx(NvU32 width, NvU32 height, NvU32 rr, NvU32 flag, NVT_EDID_INFO *pEdidInfo, NVT_TIMING *pT, NvU32 rrx1k)
|
|
{
|
|
return NvTiming_GetEdidTimingExWithPclk(width, height, rr, flag, pEdidInfo, pT, rrx1k, 0);
|
|
}
|
|
|
|
// get the edid timing
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NvTiming_GetEdidTiming(NvU32 width, NvU32 height, NvU32 rr, NvU32 flag, NVT_EDID_INFO *pEdidInfo, NVT_TIMING *pT)
|
|
{
|
|
return NvTiming_GetEdidTimingEx(width, height, rr, flag, pEdidInfo, pT, 0);
|
|
}
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NvTiming_GetHDMIStereoExtTimingFromEDID(NvU32 width, NvU32 height, NvU32 rr, NvU8 StereoStructureType, NvU8 SideBySideHalfDetail, NvU32 flag, NVT_EDID_INFO *pEdidInfo, NVT_EXT_TIMING *pT)
|
|
{
|
|
NVT_STATUS status = NVT_STATUS_ERR;
|
|
NvU8 Vic;
|
|
NvU32 i;
|
|
NVT_TIMING Timing;
|
|
|
|
NVMISC_MEMSET(pT, 0, sizeof(NVT_EXT_TIMING));
|
|
|
|
// adjust the flags --
|
|
// need EDID timing with RR match,
|
|
// not max timing,
|
|
flag = flag | NVT_FLAG_EDID_TIMING | NVT_FLAG_EDID_TIMING_RR_MATCH | NVT_FLAG_EDID_861_ST;
|
|
flag = flag & ~(NVT_FLAG_MAX_EDID_TIMING);
|
|
|
|
status = NvTiming_GetEdidTiming(width, height, rr, flag, pEdidInfo, &Timing);
|
|
if (NVT_STATUS_SUCCESS == status)
|
|
{
|
|
status = NVT_STATUS_ERR;
|
|
|
|
// is this an exact match?
|
|
if (0 == NVT_GET_TIMING_STATUS_MATCH(Timing.etc.status))
|
|
{
|
|
if (NVT_TYPE_EDID_861ST == NVT_GET_TIMING_STATUS_TYPE(Timing.etc.status))
|
|
{
|
|
// lookup the vic for this timing in the support map.
|
|
Vic = (NvU8) NVT_GET_CEA_FORMAT(Timing.etc.status);
|
|
for (i = 0; i < pEdidInfo->Hdmi3Dsupport.total; ++i)
|
|
{
|
|
if (Vic == pEdidInfo->Hdmi3Dsupport.map[i].Vic)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i < pEdidInfo->Hdmi3Dsupport.total)
|
|
{
|
|
// does this vic support the requested structure type?
|
|
if (0 != (NVT_HDMI_3D_SUPPORTED_STRUCT_MASK(StereoStructureType) & pEdidInfo->Hdmi3Dsupport.map[i].StereoStructureMask))
|
|
{
|
|
// if this is side-by-side(half) the detail needs to match also.
|
|
if ((NVT_HDMI_VS_BYTE5_HDMI_3DS_SIDEBYSIDEHALF != StereoStructureType) || (SideBySideHalfDetail == pEdidInfo->Hdmi3Dsupport.map[i].SideBySideHalfDetail))
|
|
{
|
|
// convert the 2D timing to 3D.
|
|
NvTiming_GetHDMIStereoTimingFrom2DTiming(&Timing, StereoStructureType, SideBySideHalfDetail, pT);
|
|
status = NVT_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
// EDID based AspectRatio Timing
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NvTiming_GetEDIDBasedASPRTiming( NvU16 width, NvU16 height, NvU16 rr, NVT_EDID_INFO *pEI, NVT_TIMING *pT)
|
|
{
|
|
NvU32 i, dwStatus;
|
|
NvU32 dwNativeIndex;
|
|
NvU32 flag;
|
|
NvU32 ret;
|
|
|
|
// sanity check
|
|
if( pEI == NULL || pEI->total_timings == 0 || pT == NULL )
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
if( width == 0 || height == 0 )
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
// get an EDID timing. Return err if it fails as we don't have any timing to tweak.
|
|
flag = 0;
|
|
ret = NvTiming_GetEdidTiming(width, height, rr, flag, pEI, pT);
|
|
if( NVT_STATUS_SUCCESS != ret )
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
// in case we have an exact match from EDID (in terms of Size), we return Success.
|
|
else if ((NVT_GET_TIMING_STATUS_MATCH(pT->etc.status) & NVT_STATUS_TIMING_MISMATCH_SIZE) == 0)
|
|
{
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
|
|
// find the Native timing
|
|
for (i = 0, dwNativeIndex = pEI->total_timings + 1; i < pEI->total_timings; i++)
|
|
{
|
|
dwStatus = pEI->timing[i].etc.status;
|
|
|
|
if ((NVT_IS_NATIVE_TIMING(dwStatus)) || NVT_IS_DTD1(dwStatus))
|
|
{
|
|
dwNativeIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// we don't want to apply LogicScaling(Letterboxing) to Wide Mode on Wide Panel (or non-Wide Mode on non-Wide Panel)
|
|
if( nvt_is_wideaspect(width, height) == nvt_is_wideaspect(pEI->timing[dwNativeIndex].HVisible, pEI->timing[dwNativeIndex].VVisible) )
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
// Letterbox mode enabled by regkey LogicScalingMode
|
|
// When we try to set modes not supported in EDID (eg. DFP over DSub) the display may not fit the screen.
|
|
// If Logic Scaling is enabled (ie why we are here), we need to tweak the timing (for CRT) provided:
|
|
// 1) the aspect ratio of native mode and requested mode differ
|
|
// eg. Native AR = 5:4, 1280x1024
|
|
// Requested AR = 16:10, 1280x800
|
|
// 2) Both Width and Height do not mismatch together; If they do we shall go in for DMT/GTF timing
|
|
// by failing this call.
|
|
if( pT->interlaced == 0 &&
|
|
dwNativeIndex < pEI->total_timings &&
|
|
(pEI->timing[dwNativeIndex].HVisible*height != pEI->timing[dwNativeIndex].VVisible*width) &&
|
|
(width == pT->HVisible || height == pT->VVisible))
|
|
{
|
|
pT->HFrontPorch += (pT->HVisible - width) / 2;
|
|
pT->VFrontPorch += (pT->VVisible - height) / 2;
|
|
pT->HVisible = width;
|
|
pT->VVisible = height;
|
|
if(rr != pT->etc.rr)
|
|
{
|
|
pT->etc.rrx1k = rr * 1000;
|
|
pT->pclk = RRx1kToPclk (pT);
|
|
pT->pclk1khz = RRx1kToPclk1khz(pT);
|
|
}
|
|
|
|
pT->etc.status = NVT_STATUS_ASPR;
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief check EDID raw data is valid or not, and it will return the err flags if it existed
|
|
* @param pEdid : this is a pointer to EDID data
|
|
* @param length : read length of EDID
|
|
* @param bIsTrongValidation : true - added more check
|
|
* false- only header and checksum and size check
|
|
*
|
|
*/
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NvU32 NvTiming_EDIDValidationMask(NvU8 *pEdid, NvU32 length, NvBool bIsStrongValidation)
|
|
{
|
|
NvU32 i, j, version, checkSum;
|
|
EDIDV1STRUC *p = (EDIDV1STRUC *)pEdid;
|
|
EDID_LONG_DISPLAY_DESCRIPTOR *pLdd;
|
|
NvU8 *pExt;
|
|
DETAILEDTIMINGDESCRIPTOR *pDTD;
|
|
NvU32 ret = 0;
|
|
|
|
// check the EDID base size to avoid accessing beyond the EDID buffer, do not proceed with
|
|
// further validation.
|
|
if (length < sizeof(EDIDV1STRUC))
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_SIZE);
|
|
return ret;
|
|
}
|
|
|
|
// check the EDID version and signature
|
|
if (getEdidVersion(pEdid, &version) != NVT_STATUS_SUCCESS)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_VERSION);
|
|
return ret;
|
|
}
|
|
|
|
// check block 0 checksum value
|
|
if (!isChecksumValid(pEdid))
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_CHECKSUM);
|
|
return ret;
|
|
}
|
|
|
|
// Strong validation to follow
|
|
if (bIsStrongValidation == NV_TRUE)
|
|
{
|
|
// range limit check
|
|
for (i = 0; i < NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++)
|
|
{
|
|
pLdd = (EDID_LONG_DISPLAY_DESCRIPTOR *)&p->DetailedTimingDesc[i];
|
|
if (pLdd->tag == NVT_EDID_DISPLAY_DESCRIPTOR_DRL && (version == 0x103 || (version == 0x104 && (p->bFeatureSupport & 1))))
|
|
{
|
|
EDID_MONITOR_RANGE_LIMIT *pRangeLimit = (EDID_MONITOR_RANGE_LIMIT *)pLdd->data;
|
|
NvU8 max_v_rate_offset, min_v_rate_offset, max_h_rate_offset, min_h_rate_offset;
|
|
|
|
// add 255Hz offsets as needed before doing the check, use descriptor->rsvd2
|
|
nvt_assert(!(pLdd->rsvd2 & 0xF0));
|
|
|
|
max_v_rate_offset = pLdd->rsvd2 & NVT_PVT_EDID_RANGE_OFFSET_VER_MAX ? NVT_PVT_EDID_RANGE_OFFSET_AMOUNT : 0;
|
|
min_v_rate_offset = pLdd->rsvd2 & NVT_PVT_EDID_RANGE_OFFSET_VER_MIN ? NVT_PVT_EDID_RANGE_OFFSET_AMOUNT : 0;
|
|
max_h_rate_offset = pLdd->rsvd2 & NVT_PVT_EDID_RANGE_OFFSET_HOR_MAX ? NVT_PVT_EDID_RANGE_OFFSET_AMOUNT : 0;
|
|
min_h_rate_offset = pLdd->rsvd2 & NVT_PVT_EDID_RANGE_OFFSET_HOR_MIN ? NVT_PVT_EDID_RANGE_OFFSET_AMOUNT : 0;
|
|
|
|
if ((pRangeLimit->minVRate + min_v_rate_offset) > (pRangeLimit->maxVRate + max_v_rate_offset) ||
|
|
(pRangeLimit->minHRate + min_h_rate_offset) > (pRangeLimit->maxHRate + max_h_rate_offset) ||
|
|
pRangeLimit->maxVRate == 0 ||
|
|
pRangeLimit->maxHRate == 0)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_RANGE_LIMIT);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// extension and size check
|
|
if ((NvU32)(p->bExtensionFlag + 1) * sizeof(EDIDV1STRUC) > length)
|
|
{
|
|
// Do not proceed with further validation if the size is invalid.
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_SIZE);
|
|
return ret;
|
|
}
|
|
|
|
// validate Detailed Timing Descriptors, 4 blocks
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (*((NvU16 *)&p->DetailedTimingDesc[i]) != 0)
|
|
{
|
|
// This block is not a Display Descriptor.
|
|
// It must be a valid timing definition
|
|
// validate the block by passing NULL as the NVTIMING parameter to parseEdidDetailedTimingDescriptor
|
|
if (parseEdidDetailedTimingDescriptor((NvU8 *)&p->DetailedTimingDesc[i], NULL) != NVT_STATUS_SUCCESS)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_DTD);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This block is a display descriptor, validate
|
|
if (((EDID_LONG_DISPLAY_DESCRIPTOR *)&p->DetailedTimingDesc[i])->rsvd != 0)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_DTD);
|
|
}
|
|
}
|
|
}
|
|
|
|
// validate extension blocks
|
|
for (j = 1; j <= p->bExtensionFlag; j++)
|
|
{
|
|
pExt = pEdid + sizeof(EDIDV1STRUC) * j;
|
|
|
|
// check for 861 extension
|
|
switch (*pExt)
|
|
{
|
|
case NVT_EDID_EXTENSION_CTA:
|
|
// first sanity check on the extension block
|
|
if (get861ExtInfo(pExt, sizeof(EIA861EXTENSION), NULL) != NVT_STATUS_SUCCESS)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT);
|
|
}
|
|
|
|
// check sum on CEA extension block
|
|
for (i = 0, checkSum = 0; i < sizeof(EIA861EXTENSION); i ++)
|
|
{
|
|
checkSum += pExt[i];
|
|
}
|
|
|
|
if ((checkSum & 0xFF) != 0)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_CHECKSUM);
|
|
}
|
|
|
|
// 0 indicates no DTD in this block
|
|
if (((EIA861EXTENSION*)pExt)->offset == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// validate DTD blocks
|
|
pDTD = (DETAILEDTIMINGDESCRIPTOR *)&pExt[((EIA861EXTENSION *)pExt)->offset];
|
|
while ((pDTD->wDTPixelClock != 0) &&
|
|
(((NvU8 *)pDTD - pExt + sizeof(DETAILEDTIMINGDESCRIPTOR)) < ((NvU8)sizeof(EIA861EXTENSION))))
|
|
{
|
|
if (parseEdidDetailedTimingDescriptor((NvU8 *)pDTD, NULL) != NVT_STATUS_SUCCESS)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DTD);
|
|
}
|
|
pDTD++;
|
|
}
|
|
break;
|
|
case NVT_EDID_EXTENSION_VTB:
|
|
// perform a checksum on the VTB block
|
|
for (i = 0, checkSum = 0; i < sizeof(VTBEXTENSION); i++)
|
|
{
|
|
checkSum += pExt[i];
|
|
}
|
|
if ((checkSum & 0xFF) != 0)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_CHECKSUM);
|
|
}
|
|
break;
|
|
case NVT_EDID_EXTENSION_DISPLAYID:
|
|
// perform a checksum on the VTB block
|
|
for (i = 0, checkSum = 0; i < sizeof(EIA861EXTENSION); i++)
|
|
{
|
|
checkSum += pExt[i];
|
|
}
|
|
if ((checkSum & 0xFF) != 0)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_CHECKSUM);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @brief sanity check EDID binary frequently used data block is valid or not,
|
|
* and it will return error checkpoint flag if it existed
|
|
* @param pEdid : this is a pointer to EDID raw data
|
|
* @param length : read length of EDID
|
|
*
|
|
*/
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NvU32 NvTiming_EDIDStrongValidationMask(NvU8 *pEdid, NvU32 length)
|
|
{
|
|
NvU32 i, j, version, extnCount;
|
|
EDIDV1STRUC *p = (EDIDV1STRUC *)pEdid;
|
|
EDID_LONG_DISPLAY_DESCRIPTOR *pLdd;
|
|
NvU8 *pExt;
|
|
DETAILEDTIMINGDESCRIPTOR *pDTD;
|
|
// For CTA861
|
|
NvU8 ctaDTD_Offset;
|
|
NvU8 *pData_collection;
|
|
NvU32 ctaBlockTag, ctaPayload, vic;
|
|
// For DisplayID
|
|
DIDEXTENSION *pDisplayid;
|
|
NvU8 did_section_length = 0x79;
|
|
NvU8 did2ExtCount = 0;
|
|
DISPLAYID_2_0_DATA_BLOCK_HEADER *pDID2Header;
|
|
DISPLAYID_DATA_BLOCK_HEADER *pHeader;
|
|
NvU8 block_length = 0;
|
|
NvBool bAllZero = NV_TRUE;
|
|
NvU32 ret = 0;
|
|
|
|
// check the EDID base size to avoid accessing beyond the EDID buffer
|
|
if (length < sizeof(EDIDV1STRUC) || (length > sizeof(EDIDV1STRUC) && (length % sizeof(EDIDV1STRUC) != 0)))
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_SIZE);
|
|
|
|
// check the EDID version and signature
|
|
if (getEdidVersion(pEdid, &version) != NVT_STATUS_SUCCESS)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_HEADER);
|
|
|
|
// check block 0 checksum value
|
|
if (!isChecksumValid(pEdid))
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_CHECKSUM);
|
|
|
|
if (p->bVersionNumber != 0x01 || p->bRevisionNumber > 0x04)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_VERSION);
|
|
}
|
|
|
|
// 18bytes in DTD or Display Descriptor check
|
|
for (i = 0; i < NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++)
|
|
{
|
|
if (*((NvU16 *)&p->DetailedTimingDesc[i]) != 0)
|
|
{
|
|
// This block is not a Display Descriptor.
|
|
// It must be a valid timing definition
|
|
// validate the block by passing NULL as the NVTIMING parameter to parseEdidDetailedTimingDescriptor
|
|
if (parseEdidDetailedTimingDescriptor((NvU8 *)&p->DetailedTimingDesc[i], NULL) != NVT_STATUS_SUCCESS)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_DTD);
|
|
}
|
|
else
|
|
{
|
|
// check the max image size in monitor and its DTD defines value
|
|
if (p->bMaxHorizImageSize != 0 && p->bMaxVertImageSize != 0)
|
|
{
|
|
DETAILEDTIMINGDESCRIPTOR *pDTD = (DETAILEDTIMINGDESCRIPTOR *)&p->DetailedTimingDesc[i];
|
|
NvU16 hDTDImageSize = (pDTD->bDTHorizVertImage & 0xF0) << 4 | pDTD->bDTHorizontalImage;
|
|
NvU16 vDTDImageSize = (pDTD->bDTHorizVertImage & 0x0F) << 8 | pDTD->bDTVerticalImage;
|
|
|
|
if ((hDTDImageSize/10) > p->bMaxHorizImageSize || (vDTDImageSize/10) > p->bMaxVertImageSize)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_DTD);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pLdd = (EDID_LONG_DISPLAY_DESCRIPTOR *)&p->DetailedTimingDesc[i];
|
|
|
|
// This block is a display descriptor, validate
|
|
if (((EDID_LONG_DISPLAY_DESCRIPTOR *)&p->DetailedTimingDesc[i])->rsvd != 0 || // (00 00 00)h indicates Display Descriptor
|
|
(pLdd->tag >= 0x11 && pLdd->tag <= 0xF6)) // Reserved : Do Not Use
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_DESCRIPTOR);
|
|
|
|
if (pLdd->tag == NVT_EDID_DISPLAY_DESCRIPTOR_DRL && (version == 0x103 || (version == 0x104 && (p->bFeatureSupport & 1))))
|
|
{
|
|
EDID_MONITOR_RANGE_LIMIT *pRangeLimit = (EDID_MONITOR_RANGE_LIMIT *)pLdd->data;
|
|
NvU8 max_v_rate_offset, min_v_rate_offset, max_h_rate_offset, min_h_rate_offset;
|
|
|
|
// add 255Hz offsets as needed before doing the check, use descriptor->rsvd2
|
|
nvt_assert(!(pLdd->rsvd2 & 0xF0));
|
|
|
|
max_v_rate_offset = pLdd->rsvd2 & NVT_PVT_EDID_RANGE_OFFSET_VER_MAX ? NVT_PVT_EDID_RANGE_OFFSET_AMOUNT : 0;
|
|
min_v_rate_offset = pLdd->rsvd2 & NVT_PVT_EDID_RANGE_OFFSET_VER_MIN ? NVT_PVT_EDID_RANGE_OFFSET_AMOUNT : 0;
|
|
max_h_rate_offset = pLdd->rsvd2 & NVT_PVT_EDID_RANGE_OFFSET_HOR_MAX ? NVT_PVT_EDID_RANGE_OFFSET_AMOUNT : 0;
|
|
min_h_rate_offset = pLdd->rsvd2 & NVT_PVT_EDID_RANGE_OFFSET_HOR_MIN ? NVT_PVT_EDID_RANGE_OFFSET_AMOUNT : 0;
|
|
|
|
if ((pRangeLimit->minVRate + min_v_rate_offset) > (pRangeLimit->maxVRate + max_v_rate_offset) ||
|
|
(pRangeLimit->minHRate + min_h_rate_offset) > (pRangeLimit->maxHRate + max_h_rate_offset) ||
|
|
pRangeLimit->maxVRate == 0 ||
|
|
pRangeLimit->maxHRate == 0)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_RANGE_LIMIT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// extension and size check
|
|
if ((NvU32)(p->bExtensionFlag + 1) * sizeof(EDIDV1STRUC) > length)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXTENSION_COUNT);
|
|
}
|
|
|
|
// we shall not trust any extension blocks with wrong input EDID size
|
|
if (NVT_IS_EDID_VALIDATION_FLAGS(ret, NVT_EDID_VALIDATION_ERR_SIZE) ||
|
|
NVT_IS_EDID_VALIDATION_FLAGS(ret, NVT_EDID_VALIDATION_ERR_EXTENSION_COUNT))
|
|
return ret;
|
|
|
|
// validate extension blocks
|
|
for (j = 1; j <= p->bExtensionFlag; j++)
|
|
{
|
|
pExt = pEdid + sizeof(EDIDV1STRUC) * j;
|
|
|
|
// check for 861 extension
|
|
switch (*pExt)
|
|
{
|
|
case NVT_EDID_EXTENSION_CTA:
|
|
ctaDTD_Offset = ((EIA861EXTENSION *)pExt)->offset;
|
|
// first sanity check on the extension block
|
|
if (get861ExtInfo(pExt, sizeof(EIA861EXTENSION), NULL) != NVT_STATUS_SUCCESS ||
|
|
((EIA861EXTENSION *)pExt)->revision < NVT_CEA861_REV_B)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_CTA_BASIC);
|
|
}
|
|
|
|
// 0 indicated there is no DTD and data collection in this block
|
|
if (ctaDTD_Offset == 0)
|
|
{
|
|
if(!isChecksumValid(pExt))
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_CTA_CHECKSUM);
|
|
continue;
|
|
}
|
|
|
|
// validate SVD block
|
|
ctaBlockTag = NVT_CEA861_GET_SHORT_DESCRIPTOR_TAG(((EIA861EXTENSION *)pExt)->data[0]);
|
|
pData_collection = ((EIA861EXTENSION *)pExt)->data;
|
|
|
|
while ((ctaDTD_Offset - 4) > 0 && pData_collection != &pExt[ctaDTD_Offset] &&
|
|
ctaBlockTag > NVT_CEA861_TAG_RSVD && ctaBlockTag <= NVT_CEA861_TAG_EXTENDED_FLAG)
|
|
{
|
|
ctaBlockTag = NVT_CEA861_GET_SHORT_DESCRIPTOR_TAG(*pData_collection);
|
|
ctaPayload = NVT_CEA861_GET_SHORT_DESCRIPTOR_SIZE(*pData_collection);
|
|
|
|
if (parseCta861DataBlockInfo(pData_collection, (NvU32)ctaDTD_Offset - 4, NULL) == NVT_STATUS_SUCCESS)
|
|
{
|
|
pData_collection++; // go to the next byte. skip Tag+Length byte
|
|
|
|
if (ctaBlockTag == NVT_CEA861_TAG_VIDEO)
|
|
{
|
|
for (i=0; i < ctaPayload; i++)
|
|
{
|
|
vic = NVT_GET_CTA_8BIT_VIC(*pData_collection);
|
|
if (vic == 0 || vic > 255 || (vic >= 128 && vic <=192))
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_CTA_SVD);
|
|
pData_collection++;
|
|
}
|
|
}
|
|
else if (ctaBlockTag == NVT_CEA861_TAG_EXTENDED_FLAG)
|
|
{
|
|
if (*pData_collection == NVT_CTA861_EXT_TAG_HF_EEODB)
|
|
{
|
|
if ((p->bVersionNumber != 0x01) || (p->bRevisionNumber != 0x03))
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_CTA_INVALID_DATA_BLOCK);
|
|
pData_collection += ctaPayload;
|
|
}
|
|
else
|
|
{
|
|
ret &= ~NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXTENSION_COUNT);
|
|
extnCount = *(++pData_collection);
|
|
// check the EDID extension count value again because EDID extension block count
|
|
// value in EEODB override it and source shall ignore extension flag > 1 value
|
|
if ((extnCount + 1) != (length / (sizeof(EDIDV1STRUC))))
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXTENSION_COUNT);
|
|
pData_collection++;
|
|
}
|
|
}
|
|
else
|
|
pData_collection += ctaPayload;
|
|
}
|
|
else if (ctaBlockTag == NVT_CEA861_TAG_RSVD)
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_CTA_TAG);
|
|
pData_collection += ctaPayload;
|
|
}
|
|
else
|
|
pData_collection += ctaPayload;
|
|
}
|
|
else
|
|
{
|
|
pData_collection++; // go to the next byte. skip Tag+Length byte
|
|
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_CTA_INVALID_DATA_BLOCK);
|
|
pData_collection += ctaPayload;
|
|
}
|
|
}
|
|
|
|
// validate DTD blocks
|
|
pDTD = (DETAILEDTIMINGDESCRIPTOR *)&pExt[((EIA861EXTENSION *)pExt)->offset];
|
|
while ((pDTD->wDTPixelClock != 0) &&
|
|
(((NvU8 *)pDTD - pExt + sizeof(DETAILEDTIMINGDESCRIPTOR)) < ((NvU8)sizeof(EIA861EXTENSION))))
|
|
{
|
|
if (parseEdidDetailedTimingDescriptor((NvU8 *)pDTD, NULL) != NVT_STATUS_SUCCESS)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DTD);
|
|
else
|
|
{
|
|
// check the max image size and
|
|
if (p->bMaxHorizImageSize != 0 && p->bMaxVertImageSize != 0)
|
|
{
|
|
NvU16 hDTDImageSize = (pDTD->bDTHorizVertImage & 0xF0) << 4 | pDTD->bDTHorizontalImage;
|
|
NvU16 vDTDImageSize = (pDTD->bDTHorizVertImage & 0x0F) << 8 | pDTD->bDTVerticalImage;
|
|
|
|
if ((hDTDImageSize/10) > (p->bMaxHorizImageSize) || (vDTDImageSize/10) > p->bMaxVertImageSize)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_CTA_DTD);
|
|
}
|
|
}
|
|
pDTD++;
|
|
}
|
|
|
|
if(!isChecksumValid(pExt))
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_CTA_CHECKSUM);
|
|
break;
|
|
case NVT_EDID_EXTENSION_DISPLAYID:
|
|
pDisplayid = ((DIDEXTENSION *)pExt);
|
|
if (pDisplayid->ext_count != 0)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DID_EXTCOUNT);
|
|
|
|
if (pDisplayid->length != 0x79)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DID_SEC_SIZE);
|
|
|
|
if (!isChecksumValid(pExt))
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DID_CHECKSUM);
|
|
|
|
// check the DID2 data blocks
|
|
if ((pDisplayid->struct_version & 0xF0) >> 4 == 2)
|
|
{
|
|
if ((pDisplayid->struct_version & 0xFF) == 0x21)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DID_VERSION);
|
|
|
|
did2ExtCount++;
|
|
|
|
if (pDisplayid->use_case == 0 && did2ExtCount == 1)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DID2_USE_CASE);
|
|
|
|
// check the DisplayId2 valid timing
|
|
pDID2Header = (DISPLAYID_2_0_DATA_BLOCK_HEADER*)pDisplayid->data;
|
|
pData_collection = pDisplayid->data;
|
|
|
|
// Sanity check every data blocks
|
|
while (((pDID2Header->type >= DISPLAYID_2_0_BLOCK_TYPE_PRODUCT_IDENTITY &&
|
|
pDID2Header->type <= DISPLAYID_2_0_BLOCK_TYPE_BRIGHTNESS_LUMINANCE_RANGE) ||
|
|
pDID2Header->type == DISPLAYID_2_0_BLOCK_TYPE_VENDOR_SPEC ||
|
|
pDID2Header->type == DISPLAYID_2_0_BLOCK_TYPE_CTA_DATA) && pDID2Header->data_bytes != 0 &&
|
|
(pData_collection - pExt < (int)sizeof(DIDEXTENSION)))
|
|
{
|
|
if (parseDisplayId20EDIDExtDataBlocks(pData_collection, did_section_length, &block_length, NULL) == NVT_STATUS_ERR)
|
|
{
|
|
if (pDID2Header->type == DISPLAYID_2_0_BLOCK_TYPE_TIMING_7)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DID2_TYPE7);
|
|
|
|
if (pDID2Header->type == DISPLAYID_2_0_BLOCK_TYPE_RANGE_LIMITS)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_RANGE_LIMIT);
|
|
|
|
if (pDID2Header->type == DISPLAYID_2_0_BLOCK_TYPE_ADAPTIVE_SYNC)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DID2_ADAPTIVE_SYNC);
|
|
// add more data blocks tag here to evaluate
|
|
}
|
|
pData_collection += block_length;
|
|
pDID2Header = (DISPLAYID_2_0_DATA_BLOCK_HEADER*)pData_collection;
|
|
}
|
|
|
|
// compare the remain 0 value are correct or not before meet checksum byte
|
|
for (i = 0; i < (NvU32)(&pDisplayid->data[NVT_DID_MAX_EXT_PAYLOAD-1] - pData_collection); i++)
|
|
{
|
|
if (pData_collection[i] != 0)
|
|
{
|
|
bAllZero = NV_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if the first tag failed, ignore all the tags afterward then
|
|
if (!bAllZero &&
|
|
(pDID2Header->type < DISPLAYID_2_0_BLOCK_TYPE_PRODUCT_IDENTITY ||
|
|
(pDID2Header->type > DISPLAYID_2_0_BLOCK_TYPE_BRIGHTNESS_LUMINANCE_RANGE &&
|
|
pDID2Header->type != DISPLAYID_2_0_BLOCK_TYPE_VENDOR_SPEC &&
|
|
pDID2Header->type != DISPLAYID_2_0_BLOCK_TYPE_CTA_DATA)) &&
|
|
(pData_collection - pExt < (int)sizeof(DIDEXTENSION)))
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DID2_TAG);
|
|
continue;
|
|
}
|
|
}
|
|
else if ((pDisplayid->struct_version & 0xFF) == 0x12 || (pDisplayid->struct_version & 0xFF) == 0x13)
|
|
{
|
|
if ((pDisplayid->struct_version & 0xFF) == 0x13)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DID_VERSION);
|
|
|
|
pHeader = (DISPLAYID_DATA_BLOCK_HEADER*)pDisplayid->data;
|
|
pData_collection = pDisplayid->data;
|
|
|
|
// Sanity check every data blocks
|
|
while ((pHeader->type <= NVT_DISPLAYID_BLOCK_TYPE_TILEDDISPLAY ||
|
|
pHeader->type == NVT_DISPLAYID_BLOCK_TYPE_CTA_DATA ||
|
|
pHeader->type == NVT_DISPLAYID_BLOCK_TYPE_VENDOR_SPEC) && pHeader->data_bytes != 0 &&
|
|
(pData_collection - pExt < (int)sizeof(DIDEXTENSION)))
|
|
{
|
|
if (parseDisplayIdBlock(pData_collection, did_section_length, &block_length, NULL) == NVT_STATUS_ERR)
|
|
{
|
|
if (pHeader->type == NVT_DISPLAYID_BLOCK_TYPE_TIMING_1)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DID13_TYPE1);
|
|
|
|
if (pHeader->type == NVT_DISPLAYID_BLOCK_TYPE_RANGE_LIMITS)
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_RANGE_LIMIT);
|
|
|
|
// add more data blocks tag here to evaluate
|
|
}
|
|
pData_collection += block_length;
|
|
pHeader = (DISPLAYID_DATA_BLOCK_HEADER*)pData_collection;
|
|
}
|
|
|
|
// compare the remain 0 value are correct or not before meet checksum byte
|
|
for (i = 0; i < (NvU32)(&pDisplayid->data[NVT_DID_MAX_EXT_PAYLOAD-1] - pData_collection); i++)
|
|
{
|
|
if (pData_collection[i] != 0)
|
|
{
|
|
bAllZero = NV_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if the first tag failed, ignore all the tags afterward then
|
|
if (!bAllZero &&
|
|
pHeader->type > NVT_DISPLAYID_BLOCK_TYPE_TILEDDISPLAY &&
|
|
pHeader->type != NVT_DISPLAYID_BLOCK_TYPE_CTA_DATA &&
|
|
pHeader->type != NVT_DISPLAYID_BLOCK_TYPE_VENDOR_SPEC &&
|
|
(pData_collection - pExt < (int)sizeof(DIDEXTENSION)))
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DID13_TAG);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXT_DID_VERSION);
|
|
break;
|
|
default:
|
|
// the useful extension only CTA (0x02) and DisplayID (0x70)
|
|
if ( *pExt != NVT_EDID_EXTENSION_VTB && *pExt != NVT_EDID_EXTENSION_DI &&
|
|
*pExt != NVT_EDID_EXTENSION_LS && *pExt != NVT_EDID_EXTENSION_DPVL &&
|
|
*pExt != NVT_EDID_EXTENSION_BM && *pExt != NVT_EDID_EXTENSION_OEM )
|
|
{
|
|
ret |= NVT_EDID_VALIDATION_ERR_MASK(NVT_EDID_VALIDATION_ERR_EXTENSION_TAG);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NvTiming_EDIDValidation (NvU8 *pEdid, NvU32 length, NvBool bIsStrongValidation)
|
|
{
|
|
if (NvTiming_EDIDValidationMask(pEdid, length, bIsStrongValidation) != 0) {
|
|
return NVT_STATUS_ERR;
|
|
} else {
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// Function Description: Get the first Detailed Timing Descriptor
|
|
//
|
|
// Parameters:
|
|
// pEdidInfo: IN - pointer to parsed EDID
|
|
// pT: OUT - pointer to where the DTD1 timing will be stored
|
|
//
|
|
// Return:
|
|
// NVT_STATUS_SUCCESS: DTD1 was found in parsed EDID, pT is a valid result
|
|
// NVT_STATUS_INVALID_PARAMETER: one or more parameter was invalid
|
|
// NVT_STATUS_ERR: DTD1 was not found in parsed EDID, pT is invalid
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NvTiming_GetDTD1Timing (NVT_EDID_INFO * pEdidInfo, NVT_TIMING * pT)
|
|
{
|
|
NvU32 j;
|
|
|
|
// check param
|
|
if (pEdidInfo == NULL || pT == NULL)
|
|
{
|
|
return NVT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// find the PTM mode
|
|
for (j = 0; j < pEdidInfo->total_timings; j++)
|
|
{
|
|
if (NVT_PREFERRED_TIMING_IS_DTD1(pEdidInfo->timing[j].etc.flag, pEdidInfo->timing[j].etc.status))
|
|
{
|
|
*pT = pEdidInfo->timing[j];
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// find DisplayID preferred
|
|
for (j = 1; j < pEdidInfo->total_timings; j++)
|
|
{
|
|
if (NVT_PREFERRED_TIMING_IS_DISPLAYID(pEdidInfo->timing[j].etc.flag))
|
|
{
|
|
*pT = pEdidInfo->timing[j];
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// DTD1 should exist, but if it doesn't, return not found
|
|
for (j = 0; j < pEdidInfo->total_timings; j++)
|
|
{
|
|
NvU32 data = pEdidInfo->timing[j].etc.status;
|
|
if (NVT_IS_DTD1(data))
|
|
{
|
|
*pT = pEdidInfo->timing[j];
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// DTD1 should exist, but if it doesn't, return not found
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
// Description: Parses a VTB extension block into its associated timings
|
|
//
|
|
// Parameters:
|
|
// pEdidExt: IN - pointer to the beginning of the extension block
|
|
// pInfo: IN - The original block information, including the
|
|
// array of timings.
|
|
//
|
|
// NOTE: this function *really* should be in its own separate file, but a certain DVS test
|
|
// uses cross build makefiles which do not allow the specification of a new file.
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
void parseVTBExtension(NvU8 *pEdidExt, NVT_EDID_INFO *pInfo)
|
|
{
|
|
NvU32 i;
|
|
VTBEXTENSION *pExt = (VTBEXTENSION *)pEdidExt;
|
|
NvU32 count;
|
|
NvU32 bytes;
|
|
NVT_TIMING newTiming;
|
|
|
|
// Null = bad idea
|
|
if (pEdidExt == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Sanity check for VTB extension block
|
|
if (pExt->tag != NVT_EDID_EXTENSION_VTB ||
|
|
pExt->revision == NVT_VTB_REV_NONE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Sanity check - ensure that the # of descriptor does not exceed
|
|
// byte size
|
|
count = (NvU32)sizeof(EDID_LONG_DISPLAY_DESCRIPTOR) * pExt->num_detailed
|
|
+ (NvU32)sizeof(EDID_CVT_3BYTE_BLOCK) * pExt->num_cvt
|
|
+ (NvU32)sizeof(NvU16) * pExt->num_standard;
|
|
if (count > NVT_VTB_MAX_PAYLOAD)
|
|
{
|
|
return;
|
|
}
|
|
|
|
count = 0;
|
|
bytes = 0;
|
|
|
|
// Process Detailed Timings
|
|
for (i = 0; i < pExt->num_detailed; i++)
|
|
{
|
|
NVMISC_MEMSET(&newTiming, 0, sizeof(newTiming));
|
|
|
|
if (parseEdidDetailedTimingDescriptor((NvU8 *)(pExt->data + bytes),
|
|
&newTiming) == NVT_STATUS_SUCCESS)
|
|
{
|
|
newTiming.etc.name[39] = '\0';
|
|
newTiming.etc.status = NVT_STATUS_EDID_VTB_EXT_DTDn(++count);
|
|
|
|
if (!assignNextAvailableTiming(pInfo, &newTiming))
|
|
{
|
|
break;
|
|
}
|
|
|
|
bytes += (NvU32)(sizeof(EDID_LONG_DISPLAY_DESCRIPTOR));
|
|
}
|
|
}
|
|
|
|
// Process CVT Timings
|
|
for (i = 0; i < pExt->num_cvt; i++)
|
|
{
|
|
parseEdidCvt3ByteDescriptor((NvU8 *)(pExt->data + bytes), pInfo, &count);
|
|
|
|
bytes += (NvU32)sizeof(EDID_CVT_3BYTE_BLOCK);
|
|
}
|
|
|
|
// Process Standard Timings
|
|
for (i = 0; i < pExt->num_standard; i++)
|
|
{
|
|
NVMISC_MEMSET(&newTiming, 0, sizeof(newTiming));
|
|
|
|
parseEdidStandardTimingDescriptor(*(NvU16 *)(pExt->data + bytes),
|
|
pInfo, count, &newTiming);
|
|
newTiming.etc.name[39] = '\0';
|
|
newTiming.etc.status = NVT_STATUS_EDID_VTB_EXT_STDn(++count);
|
|
|
|
if (!assignNextAvailableTiming(pInfo, &newTiming))
|
|
{
|
|
break;
|
|
}
|
|
|
|
bytes += (NvU32)sizeof(NvU16);
|
|
}
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
static int IsPrintable(NvU8 c)
|
|
{
|
|
return ((c >= ' ') && (c <= '~'));
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
static int IsWhiteSpace(NvU8 c)
|
|
{
|
|
// consider anything unprintable or single space (ASCII 32)
|
|
// to be whitespace
|
|
return (!IsPrintable(c) || (c == ' '));
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
static void RemoveTrailingWhiteSpace(NvU8 *str, int len)
|
|
{
|
|
int i;
|
|
|
|
for (i = len; (i >= 0) && IsWhiteSpace(str[i]); i--)
|
|
{
|
|
str[i] = '\0';
|
|
}
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
static void RemoveNonPrintableCharacters(NvU8 *str)
|
|
{
|
|
int i;
|
|
|
|
// Check that all characters are printable.
|
|
// If not, replace them with '?'
|
|
for (i = 0; str[i] != '\0'; i++)
|
|
{
|
|
if (!IsPrintable(str[i]))
|
|
{
|
|
str[i] = '?';
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Assigns this timing to the next available slot in pInfo->timing[] if
|
|
* possible.
|
|
* @param pInfo EDID struct containing the parsed timings
|
|
* @param pTiming New timing to be copied into pInfo->timing[]
|
|
*/
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NvBool assignNextAvailableTiming(NVT_EDID_INFO *pInfo,
|
|
const NVT_TIMING *pTiming)
|
|
{
|
|
if (pInfo == NULL) return NV_TRUE;
|
|
|
|
// Don't write past the end of
|
|
// pInfo->timing[NVT_EDID_MAX_TOTAL_TIMING]
|
|
if (pInfo->total_timings >= COUNT(pInfo->timing)) {
|
|
return NV_FALSE;
|
|
}
|
|
|
|
pInfo->timing[pInfo->total_timings++] = *pTiming;
|
|
return NV_TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Return the nth highest priority index based on the different SVR
|
|
* @param svr Short Video Reference
|
|
*/
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NvU8 getHighestPrioritySVRIdx(const NVT_EDID_CEA861_INFO *pExt861)
|
|
{
|
|
// In general, sink shall define the first one timing sequence
|
|
NvU8 kth = 1;
|
|
NvU8 i = 0;
|
|
|
|
for (i = 0; i < pExt861->total_svr; i++)
|
|
{
|
|
NvU8 svr = pExt861->svr_vfpdb[i];
|
|
|
|
// Reserved
|
|
if (svr == 0 || svr == 128 || (svr >= 176 && svr <= 192) || svr == 255)
|
|
continue;
|
|
|
|
if (svr >= 129 && svr <= 144) return svr - 128; // Interpret as the Kth 18-byte DTD in both base0 and CTA block (for N = 1 to 16)
|
|
else if (svr >= 145 && svr <= 160) return svr - 144; // Interpret as the Nth 20-byte DTD or 6- or 7-byte CVT-based descriptor. (for N = 1 to 16)
|
|
else if (svr >= 161 && svr <= 175) return svr - 160; // Interpret as the video format indicated by the first VFD of the first VFDB with Frame Rates of Rate Index N (for N = 1 to 15)
|
|
else if (svr == 254) return kth; // Interpret as the timing format indicated by the first code of the first T8VTDB (for N = 1)
|
|
else // assign corresponding CTA format's timing from pre-defined CE timing table, EIA861B
|
|
{
|
|
// ( SVR >= 1 and SVR <= 127) and (SVR >= 193 and SVR <= 253) needs to handle it by client
|
|
return svr;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NvTiming_GetProductName(const NVT_EDID_INFO *pEdidInfo,
|
|
NvU8 *pProductName,
|
|
const NvU32 productNameLength)
|
|
{
|
|
NvU32 i = 0, m = 0, n = 0;
|
|
|
|
if( pEdidInfo == NULL || pProductName == NULL )
|
|
{
|
|
return NVT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
for ( i = 0; i < NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++)
|
|
{
|
|
if (pEdidInfo->ldd[i].tag == NVT_EDID_DISPLAY_DESCRITPOR_DPN)
|
|
{
|
|
for(n = 0; n < NVT_EDID_LDD_PAYLOAD_SIZE && pEdidInfo->ldd[i].u.product_name.str[n] != 0x0; n++)
|
|
{
|
|
pProductName[m++] = pEdidInfo->ldd[i].u.product_name.str[n];
|
|
if ((m + 1) >= productNameLength)
|
|
{
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
done:
|
|
pProductName[m] = '\0'; //Ensure a null termination at the end.
|
|
|
|
RemoveTrailingWhiteSpace(pProductName, m);
|
|
RemoveNonPrintableCharacters(pProductName);
|
|
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NvU32 NvTiming_CalculateEDIDCRC32(NvU8* pEDIDBuffer, NvU32 edidsize)
|
|
{
|
|
return calculateCRC32(pEDIDBuffer, edidsize);
|
|
}
|
|
|
|
//Calculates EDID/DisplayID2 CRC after purging 'Week of Manufacture', 'Year of Manufacture',
|
|
//'Product ID String' & 'Serial Number' from EDID
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NvU32 NvTiming_CalculateCommonEDIDCRC32(NvU8* pEDIDBuffer, NvU32 edidVersion)
|
|
{
|
|
NvU32 commonEDIDBufferSize = 0;
|
|
NvU8 CommonEDIDBuffer[256];
|
|
NvU32 edidBufferIndex = 0;
|
|
|
|
if(pEDIDBuffer==NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Transfer over the original EDID buffer
|
|
NVMISC_MEMCPY(CommonEDIDBuffer, pEDIDBuffer, 256);
|
|
|
|
if ((pEDIDBuffer[0] & 0xF0) == 0x20)
|
|
{
|
|
/*
|
|
typedef struct DisplayId2Struct
|
|
{
|
|
NvU8 bVersion; // 0x00
|
|
NvU8 bSectionBytes; // 0x01 - section length, exclusive the five mandatory bytes.
|
|
NvU8 bwPrimaryUseCase; // 0x02
|
|
NvU8 bExtensionCount; // 0x03
|
|
// 0x20 DisplayId2 Standalone always exists Product Identification data block
|
|
NvU8 bProductIdtag; // 0x04
|
|
NvU8 bPIDRevision; // 0x05
|
|
NvU8 bPayloadByte; // 0x06
|
|
NvU8 bManuId[3]; // 0x07-0x09
|
|
NvU16 wProductId; // 0x0A-0x0B
|
|
NvU32 dwSerialNum; // 0x0C-0x0F
|
|
NvU16 wWeekandYear; // 0x10-0x11
|
|
NvU8 SizeOfProductNameString; // 0x12
|
|
} DISPLAY_ID2_FIXED_FORMAT;
|
|
*/
|
|
|
|
// Wipe out the Serial Number, Week of Manufacture, and Year of Manufacture or Model Year
|
|
NVMISC_MEMSET(CommonEDIDBuffer + 0x0C, 0, 6);
|
|
|
|
// Wipe out the checksums
|
|
CommonEDIDBuffer[CommonEDIDBuffer[1]+5/*mandatory bytes*/-1] = 0;
|
|
CommonEDIDBuffer[0xFF] = 0;
|
|
|
|
// zero out any Produc Name in Prodcut Identification data block
|
|
if (CommonEDIDBuffer[0x12] != 0)
|
|
{
|
|
NVMISC_MEMSET(CommonEDIDBuffer + 0x13, 0, CommonEDIDBuffer[0x12]);
|
|
CommonEDIDBuffer[0x12] = 0;
|
|
}
|
|
|
|
// displayId2 standalone uses 256 length sections
|
|
commonEDIDBufferSize = 256;
|
|
}
|
|
else
|
|
{
|
|
// Wipe out the Serial Number, Week of Manufacture, and Year of Manufacture or Model Year
|
|
NVMISC_MEMSET(CommonEDIDBuffer + 0x0C, 0, 6);
|
|
|
|
// Wipe out the checksums
|
|
CommonEDIDBuffer[0x7F] = 0;
|
|
CommonEDIDBuffer[0xFF] = 0;
|
|
|
|
// We also need to zero out any "EDID Other Monitor Descriptors" (https://en.wikipedia.org/wiki/Extended_display_identification_data)
|
|
for (edidBufferIndex = 54; edidBufferIndex <= 108; edidBufferIndex += 18)
|
|
{
|
|
if (CommonEDIDBuffer[edidBufferIndex] == 0 && CommonEDIDBuffer[edidBufferIndex+1] == 0)
|
|
{
|
|
// Wipe this block out. It contains OEM-specific details that contain things like serial numbers
|
|
NVMISC_MEMSET(CommonEDIDBuffer + edidBufferIndex, 0, 18);
|
|
}
|
|
}
|
|
|
|
// Check what size we should do the compare against
|
|
commonEDIDBufferSize = 128;
|
|
}
|
|
|
|
return NvTiming_CalculateEDIDCRC32(CommonEDIDBuffer, commonEDIDBufferSize);
|
|
} // NvTiming_CalculateCommonEDIDCRC32
|
|
|
|
// Calculate the minimum and maximum v_rate and h_rate, as well as
|
|
// maximum pclk; initialize with the range of values in the EDID mode
|
|
// list, but override with what is in the range limit descriptor section.
|
|
//
|
|
// based on drivers/modeset.nxt/CODE/edid.c:EdidGetMonitorLimits() and
|
|
// EdidBuildRangeLimits()
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
NVT_STATUS NvTiming_CalculateEDIDLimits(NVT_EDID_INFO *pEdidInfo, NVT_EDID_RANGE_LIMIT *pLimit)
|
|
{
|
|
NvU32 i, pclk10khz;
|
|
|
|
NVMISC_MEMSET(pLimit, 0, sizeof(NVT_EDID_RANGE_LIMIT));
|
|
|
|
// the below currently only supports 1.x EDIDs
|
|
if ((pEdidInfo->version & 0xFF00) != 0x100)
|
|
{
|
|
return NVT_STATUS_ERR;
|
|
}
|
|
|
|
pLimit->min_v_rate_hzx1k = ~0;
|
|
pLimit->max_v_rate_hzx1k = 0;
|
|
pLimit->min_h_rate_hz = ~0;
|
|
pLimit->max_h_rate_hz = 0;
|
|
pLimit->max_pclk_10khz = 0;
|
|
|
|
// find the ranges in the EDID mode list
|
|
for (i = 0; i < pEdidInfo->total_timings; i++)
|
|
{
|
|
NVT_TIMING *pTiming = &pEdidInfo->timing[i];
|
|
NvU32 h_rate_hz;
|
|
|
|
if (pLimit->min_v_rate_hzx1k > pTiming->etc.rrx1k)
|
|
{
|
|
pLimit->min_v_rate_hzx1k = pTiming->etc.rrx1k;
|
|
}
|
|
if (pLimit->max_v_rate_hzx1k < pTiming->etc.rrx1k)
|
|
{
|
|
pLimit->max_v_rate_hzx1k = pTiming->etc.rrx1k;
|
|
}
|
|
|
|
h_rate_hz = axb_div_c(pTiming->pclk1khz, 1000, (NvU32)pTiming->HTotal);
|
|
|
|
if (pLimit->min_h_rate_hz > h_rate_hz)
|
|
{
|
|
pLimit->min_h_rate_hz = h_rate_hz;
|
|
}
|
|
if (pLimit->max_h_rate_hz < h_rate_hz)
|
|
{
|
|
pLimit->max_h_rate_hz = h_rate_hz;
|
|
}
|
|
|
|
pclk10khz = (pTiming->pclk1khz + 5 ) / 10;
|
|
|
|
if (pLimit->max_pclk_10khz < pclk10khz)
|
|
{
|
|
pLimit->max_pclk_10khz = pclk10khz;
|
|
}
|
|
}
|
|
|
|
// use the range limit display descriptor, if available: these
|
|
// override anything we found in the EDID mode list
|
|
for (i = 0; i < NVT_EDID_MAX_LONG_DISPLAY_DESCRIPTOR; i++)
|
|
{
|
|
if (pEdidInfo->ldd[i].tag == NVT_EDID_DISPLAY_DESCRIPTOR_DRL)
|
|
{
|
|
NVT_EDID_DD_RANGE_LIMIT *pRangeLimit = &pEdidInfo->ldd[i].u.range_limit;
|
|
NvU32 max_pclk_10khz;
|
|
|
|
// {min,max}_v_rate is in hz
|
|
if (pRangeLimit->min_v_rate != 0) {
|
|
pLimit->min_v_rate_hzx1k = pRangeLimit->min_v_rate * 1000;
|
|
}
|
|
if (pRangeLimit->max_v_rate != 0) {
|
|
pLimit->max_v_rate_hzx1k = pRangeLimit->max_v_rate * 1000;
|
|
}
|
|
|
|
// {min,max}_h_rate is in khz
|
|
if (pRangeLimit->min_h_rate != 0) {
|
|
pLimit->min_h_rate_hz = pRangeLimit->min_h_rate * 1000;
|
|
}
|
|
if (pRangeLimit->max_h_rate != 0) {
|
|
pLimit->max_h_rate_hz = pRangeLimit->max_h_rate * 1000;
|
|
}
|
|
|
|
// EdidGetMonitorLimits() honored the pclk from the
|
|
// modelist over what it found in the range limit
|
|
// descriptor, so do the same here
|
|
max_pclk_10khz = pRangeLimit->max_pclk_MHz * 100;
|
|
if (pLimit->max_pclk_10khz < max_pclk_10khz) {
|
|
pLimit->max_pclk_10khz = max_pclk_10khz;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NVT_STATUS_SUCCESS;
|
|
}
|
|
|
|
// Build a user-friendly name:
|
|
//
|
|
// * get the vendor name:
|
|
// * use the 3 character PNP ID from the EDID's manufacturer ID field
|
|
// * expand, if possible, the PNP ID using the PNPVendorIds[] table
|
|
// * get the product name from the descriptor block(s)
|
|
// * prepend the vendor name and the product name, unless the product
|
|
// name already contains the vendor name
|
|
// * if any characters in the string are outside the printable ASCII
|
|
// range, replace them with '?'
|
|
|
|
#define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c) + ('a'-'A') : (c))
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
void NvTiming_GetMonitorName(NVT_EDID_INFO *pEdidInfo,
|
|
NvU8 monitor_name[NVT_EDID_MONITOR_NAME_STRING_LENGTH])
|
|
{
|
|
NvU8 product_name[NVT_EDID_MONITOR_NAME_STRING_LENGTH];
|
|
const NvU8 *vendor_name;
|
|
NVT_STATUS status;
|
|
NvU32 i, j;
|
|
NvBool prepend_vendor;
|
|
|
|
NVMISC_MEMSET(monitor_name, 0, NVT_EDID_MONITOR_NAME_STRING_LENGTH);
|
|
|
|
// get vendor_name: it is either the manufacturer ID or the PNP vendor name
|
|
vendor_name = pEdidInfo->manuf_name;
|
|
|
|
for (i = 0; i < (sizeof(PNPVendorIds)/sizeof(PNPVendorIds[0])); i++)
|
|
{
|
|
if ((vendor_name[0] == PNPVendorIds[i].vendorId[0]) &&
|
|
(vendor_name[1] == PNPVendorIds[i].vendorId[1]) &&
|
|
(vendor_name[2] == PNPVendorIds[i].vendorId[2]))
|
|
{
|
|
vendor_name = (const NvU8 *) PNPVendorIds[i].vendorName;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// get the product name from the descriptor blocks
|
|
status = NvTiming_GetProductName(pEdidInfo, product_name, sizeof(product_name));
|
|
|
|
if (status != NVT_STATUS_SUCCESS)
|
|
{
|
|
product_name[0] = '\0';
|
|
}
|
|
|
|
// determine if the product name already includes the vendor name;
|
|
// if so, do not prepend the vendor name to the monitor name
|
|
prepend_vendor = NV_TRUE;
|
|
|
|
for (i = 0; i < NVT_EDID_MONITOR_NAME_STRING_LENGTH; i++)
|
|
{
|
|
if (vendor_name[i] == '\0')
|
|
{
|
|
prepend_vendor = NV_FALSE;
|
|
break;
|
|
}
|
|
|
|
if (tolower(product_name[i]) != tolower(vendor_name[i]))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
j = 0;
|
|
|
|
// prepend the vendor name to the monitor name
|
|
if (prepend_vendor)
|
|
{
|
|
for (i = 0; (i < NVT_EDID_MONITOR_NAME_STRING_LENGTH) && (vendor_name[i] != '\0'); i++)
|
|
{
|
|
monitor_name[j++] = vendor_name[i];
|
|
}
|
|
}
|
|
|
|
// if we added the vendor name above, add a space between the
|
|
// vendor name and the product name
|
|
if ((j > 0) && (j < (NVT_EDID_MONITOR_NAME_STRING_LENGTH - 1)))
|
|
{
|
|
monitor_name[j++] = ' ';
|
|
}
|
|
|
|
// append the product name to the monitor string
|
|
for (i = 0; (i < NVT_EDID_MONITOR_NAME_STRING_LENGTH) && (product_name[i] != '\0'); i++)
|
|
{
|
|
if (j >= (NVT_EDID_MONITOR_NAME_STRING_LENGTH - 1))
|
|
{
|
|
break;
|
|
}
|
|
monitor_name[j++] = product_name[i];
|
|
}
|
|
monitor_name[j] = '\0';
|
|
|
|
RemoveTrailingWhiteSpace(monitor_name, j);
|
|
RemoveNonPrintableCharacters(monitor_name);
|
|
}
|
|
|
|
CODE_SEGMENT(PAGE_DD_CODE)
|
|
void updateHDMILLCDeepColorForTiming(NVT_EDID_INFO *pInfo, NvU32 index)
|
|
{
|
|
NVT_EDID_CEA861_INFO *p861Info = &pInfo->ext861;
|
|
// NOTE: EDID and CEA861 does not have clear statement regarding this.
|
|
// To be backward compatible with current Nvidia implementation, if not edid >= 1.4 and CEA block exists, follow color format declaration from CEA block.
|
|
// update supported color space within each bpc
|
|
// rgb 8bpc always supported
|
|
|
|
UPDATE_BPC_FOR_COLORFORMAT(pInfo->timing[index].etc.rgb444, 0, 1,
|
|
pInfo->hdmiLlcInfo.dc_30_bit,
|
|
pInfo->hdmiLlcInfo.dc_36_bit,
|
|
0, pInfo->hdmiLlcInfo.dc_48_bit);
|
|
|
|
if (p861Info->basic_caps & NVT_CEA861_CAP_YCbCr_444)
|
|
{
|
|
// pHdmiLlc->dc_y444 assumed basic cap is set; when base cap is set, 8bpc yuv444 always supported
|
|
UPDATE_BPC_FOR_COLORFORMAT(pInfo->timing[index].etc.yuv444, 0, 1,
|
|
pInfo->hdmiLlcInfo.dc_y444 && pInfo->hdmiLlcInfo.dc_30_bit,
|
|
pInfo->hdmiLlcInfo.dc_y444 && pInfo->hdmiLlcInfo.dc_36_bit,
|
|
0, pInfo->hdmiLlcInfo.dc_y444 && pInfo->hdmiLlcInfo.dc_48_bit);
|
|
}
|
|
if (p861Info->basic_caps & NVT_CEA861_CAP_YCbCr_422)
|
|
{
|
|
// pHdmiLlc->dc_y444 assumed basic cap is set; when base cap is set, 8bpc yuv422 always supported
|
|
// newer CEA861/HDMI specs suggest the base cap should support both or neither (Nvidia puts no limitations here)
|
|
// HDMI1.4b spec Section 6.2.4 Color Depth Requirements states that YCbCr 4:2:2 format is 36-bit mode, which means 8, 10 and 12bpc output is supported as soon as there is enough bandwidth
|
|
UPDATE_BPC_FOR_COLORFORMAT(pInfo->timing[index].etc.yuv422, 0, 1, 1, 1, 0, 0);
|
|
}
|
|
}
|
|
|
|
POP_SEGMENTS
|