oleavr-rgl-a500-mini-linux-.../drivers/video/sunxi/disp2/hdmi/hdmi_edid.c
Ole André Vadla Ravnås 169c65d57e Initial commit
2022-05-07 01:01:45 +02:00

448 lines
10 KiB
C
Executable file

/*
* linux-3.10/drivers/video/sunxi/disp2/hdmi/hdmi_edid.c
*
* Copyright (c) 2007-2017 Allwinnertech Co., Ltd.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "hdmi_core.h"
static s32 is_hdmi;
static s32 is_yuv;
u32 is_exp = 0;
u32 rgb_only = 0;
static u8 EDID_Buf[HDMI_EDID_LEN];
u8 Device_Support_VIC[512];
static u8 exp0[16] =
{
0x36,0x74,0x4d,0x53,0x74,0x61,0x72,0x20,0x44,0x65,0x6d,0x6f,0x0a,0x20,0x20,0x38
};
static u8 exp1[16] =
{
0x2d,0xee,0x4b,0x4f,0x4e,0x41,0x4b,0x20,0x54,0x56,0x0a,0x20,0x20,0x20,0x20,0xa5
};
static void ddc_init(void)
{
}
static void edid_read_data(u8 block,u8 *buf)
{
u8 i;
u8 * pbuf = buf + 128*block;
u8 offset = (block&0x01)? 128:0;
bsp_hdmi_ddc_read(Explicit_Offset_Address_E_DDC_Read,block>>1,offset,128,pbuf);
////////////////////////////////////////////////////////////////////////////
__inf("Sink : EDID bank %d:\n",block);
__inf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
__inf(" ===============================================================================================\n");
for (i = 0; i < 8; i++) {
__inf(" %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
pbuf[i*16 + 0 ],pbuf[i*16 + 1 ],pbuf[i*16 + 2 ],pbuf[i*16 + 3 ],
pbuf[i*16 + 4 ],pbuf[i*16 + 5 ],pbuf[i*16 + 6 ],pbuf[i*16 + 7 ],
pbuf[i*16 + 8 ],pbuf[i*16 + 9 ],pbuf[i*16 + 10],pbuf[i*16 + 11],
pbuf[i*16 + 12],pbuf[i*16 + 13],pbuf[i*16 + 14],pbuf[i*16 + 15]
);
}
__inf(" ===============================================================================================\n");
return;
}
/////////////////////////////////////////////////////////////////////
// hdmi_edid_parse()
// Check EDID check sum and EDID 1.3 extended segment.
/////////////////////////////////////////////////////////////////////
static s32 edid_check_sum(u8 block,u8 *buf)
{
s32 i = 0, CheckSum = 0;
u8 *pbuf = buf + 128*block;
for ( i = 0, CheckSum = 0 ; i < 128 ; i++ ) {
CheckSum += pbuf[i] ;
CheckSum &= 0xFF ;
}
if ( CheckSum != 0 ) {
__inf("EDID block %d checksum error\n",block);
return -1 ;
}
return 0;
}
static s32 edid_check_header(u8 *pbuf)
{
if ( pbuf[0] != 0x00 ||
pbuf[1] != 0xFF ||
pbuf[2] != 0xFF ||
pbuf[3] != 0xFF ||
pbuf[4] != 0xFF ||
pbuf[5] != 0xFF ||
pbuf[6] != 0xFF ||
pbuf[7] != 0x00)
{
__inf("EDID block0 header error\n");
return -1 ;
}
return 0;
}
static s32 edid_check_version(u8 *pbuf)
{
__inf("EDID version: %d.%d ",pbuf[0x12],pbuf[0x13]) ;
if ( (pbuf[0x12]!= 0x01) || (pbuf[0x13]!=0x03)) {
__inf("Unsupport EDID format,EDID parsing exit\n");
return -1;
}
return 0;
}
static s32 edid_parse_dtd_block(u8 *pbuf)
{
u32 pclk,sizex,Hblanking,sizey,Vblanking,/*Hsync_offset,Hsync_plus,
Vsync_offset,Vsync_plus,H_image_size,V_image_size,H_Border,
V_Border,*/pixels_total,frame_rate;
pclk = ( (u32)pbuf[1] << 8) + pbuf[0];
sizex = (((u32)pbuf[4] << 4) & 0x0f00) + pbuf[2];
Hblanking = (((u32)pbuf[4] << 8) & 0x0f00) + pbuf[3];
sizey = (((u32)pbuf[7] << 4) & 0x0f00) + pbuf[5];
Vblanking = (((u32)pbuf[7] << 8) & 0x0f00) + pbuf[6];
// Hsync_offset= (((u32)pbuf[11] << 2) & 0x0300) + pbuf[8];
// Hsync_plus = (((u32)pbuf[11] << 4) & 0x0300) + pbuf[9];
// Vsync_offset= (((u32)pbuf[11] << 2) & 0x0030) + (pbuf[10] >> 4);
// Vsync_plus = (((u32)pbuf[11] << 4) & 0x0030) + (pbuf[8] & 0x0f);
// H_image_size= (((u32)pbuf[14] << 4) & 0x0f00) + pbuf[12];
// V_image_size= (((u32)pbuf[14] << 8) & 0x0f00) + pbuf[13];
// H_Border = pbuf[15];
// V_Border = pbuf[16];
pixels_total = (sizex + Hblanking) * (sizey + Vblanking);
if ( (pbuf[0] == 0) && (pbuf[1] == 0) && (pbuf[2] == 0)) {
return 0;
}
if (pixels_total == 0) {
return 0;
} else {
frame_rate = (pclk * 10000) /pixels_total;
}
if ((frame_rate == 59) || (frame_rate == 60)) {
if ((sizex== 720) && (sizey == 240)) {
Device_Support_VIC[HDMI1440_480I] = 1;
}
if ((sizex== 720) && (sizey == 480)) {
//Device_Support_VIC[HDMI480P] = 1;
}
if ((sizex== 1280) && (sizey == 720)) {
Device_Support_VIC[HDMI720P_60] = 1;
}
if ((sizex== 1920) && (sizey == 540)) {
Device_Support_VIC[HDMI1080I_60] = 1;
}
if ((sizex== 1920) && (sizey == 1080)) {
Device_Support_VIC[HDMI1080P_60] = 1;
}
}
else if ((frame_rate == 49) || (frame_rate == 50)) {
if ((sizex== 720) && (sizey == 288)) {
Device_Support_VIC[HDMI1440_576I] = 1;
}
if ((sizex== 720) && (sizey == 576)) {
Device_Support_VIC[HDMI576P] = 1;
}
if ((sizex== 1280) && (sizey == 720)) {
Device_Support_VIC[HDMI720P_50] = 1;
}
if ((sizex== 1920) && (sizey == 540)) {
Device_Support_VIC[HDMI1080I_50] = 1;
}
if ((sizex== 1920) && (sizey == 1080)) {
Device_Support_VIC[HDMI1080P_50] = 1;
}
}
else if ((frame_rate == 23) || (frame_rate == 24)) {
if ((sizex== 1920) && (sizey == 1080)) {
Device_Support_VIC[HDMI1080P_24] = 1;
}
}
__inf("PCLK=%d\tXsize=%d\tYsize=%d\tFrame_rate=%d\n",
pclk*10000,sizex,sizey,frame_rate);
return 0;
}
static s32 edid_parse_videodata_block(u8 *pbuf,u8 size)
{
int i=0;
u8 vic_data = 0;
while (i<size) {
vic_data = pbuf[i] & 0x7f;
if ((vic_data == 93) ||
(vic_data == 94) ||
(vic_data == 95))
Device_Support_VIC[96 - vic_data + 0x100] = 1;
else if (vic_data == 98)
Device_Support_VIC[0x104] = 1;
else
Device_Support_VIC[vic_data] = 1;
if (pbuf[i] & 0x80)
__inf("edid_parse_videodata_block: VIC %d(native) support\n", pbuf[i]&0x7f);
else
__inf("edid_parse_videodata_block: VIC %d support\n", pbuf[i]);
i++;
}
return 0;
}
static s32 edid_parse_audiodata_block(u8 *pbuf,u8 size)
{
u8 sum = 0;
while (sum < size) {
if ( (pbuf[sum]&0xf8) == 0x08) {
__inf("edid_parse_audiodata_block: max channel=%d\n",(pbuf[sum]&0x7)+1);
__inf("edid_parse_audiodata_block: SampleRate code=%x\n",pbuf[sum+1]);
__inf("edid_parse_audiodata_block: WordLen code=%x\n",pbuf[sum+2]);
}
sum += 3;
}
return 0;
}
static s32 edid_parse_vsdb(u8 * pbuf,u8 size)
{
u8 index = 8;
u8 vic_len = 0;
u8 vsbd_vic = 0;
u8 i;
/* check if it's HDMI VSDB */
if ((pbuf[0] ==0x03) && (pbuf[1] ==0x0c) && (pbuf[2] ==0x00)) {
is_hdmi = 1;
__inf("Find HDMI Vendor Specific DataBlock\n");
} else {
is_hdmi = 0;
is_yuv = 0;
return 0;
}
if (size <=8)
return 0;
if ((pbuf[7]&0x20) == 0 )
return 0;
if ((pbuf[7]&0x40) == 0x40 )
index = index +2;
if ((pbuf[7]&0x80) == 0x80 )
index = index +2;
/* mandatary format support */
if (pbuf[index]&0x80) {
Device_Support_VIC[HDMI1080P_24_3D_FP] = 1;
Device_Support_VIC[HDMI720P_50_3D_FP] = 1;
Device_Support_VIC[HDMI720P_60_3D_FP] = 1;
__inf("3D_present\n");
}
if ( ((pbuf[index]&0x60) ==1) || ((pbuf[index]&0x60) ==2) )
__inf("3D_multi_present\n");
vic_len = pbuf[index+1]>>5;
__inf("vsdb_vic_len = %d\n", vic_len);
for (i=0; i<vic_len; i++) {
/* HDMI_VIC for extended resolution transmission */
vsbd_vic = pbuf[index+2+i];
if ((0 < vsbd_vic) && (vsbd_vic < 0x05)) {
Device_Support_VIC[vsbd_vic + 0x100] = 1;
__inf("edid_parse_vsdb: VIC %d support\n", vsbd_vic);
} else {
__inf("edid_parse_vsdb: VIC %d is a reserved VIC\n",
vsbd_vic);
}
}
index += (pbuf[index+1]&0xe0) + 2;
if (index > (size+1) )
return 0;
__inf("3D_multi_present byte(%2.2x,%2.2x)\n",pbuf[index],pbuf[index+1]);
return 0;
}
static s32 edid_check_special(u8 *buf_src, u8*buf_dst)
{
u32 i;
for (i = 0; i < 2; i++)
{
if (buf_dst[i] != buf_src[8+i])
return -1;
}
for (i = 0; i < 13; i++)
{
if (buf_dst[2+i] != buf_src[0x5f+i])
return -1;
}
if (buf_dst[15] != buf_src[0x7f])
return -1;
return 0;
}
s32 hdmi_edid_parse(void)
{
//collect the EDID ucdata of segment 0
u8 BlockCount ;
u32 i,offset ;
__inf("hdmi_edid_parse\n");
memset(Device_Support_VIC,0,sizeof(Device_Support_VIC));
memset(EDID_Buf,0,sizeof(EDID_Buf));
is_hdmi = 1;
is_yuv = 1;
is_exp = 0;
ddc_init();
edid_read_data(0, EDID_Buf);
if (edid_check_sum(0, EDID_Buf) != 0)
return 0;
if (edid_check_header(EDID_Buf)!= 0)
return 0;
if (edid_check_version(EDID_Buf)!= 0)
return 0;
edid_parse_dtd_block(EDID_Buf + 0x36);
edid_parse_dtd_block(EDID_Buf + 0x48);
BlockCount = EDID_Buf[0x7E];
if ((edid_check_special(EDID_Buf,exp0) == 0)||
(edid_check_special(EDID_Buf,exp1) == 0))
{
printk("*****************is_exp*****************\n");
is_exp = 1;
}
if (BlockCount == 0) {
is_hdmi = 0;
is_yuv = 0;
return 0;
}
if ( BlockCount > 0 ) {
if ( BlockCount > 4 )
BlockCount = 4 ;
for ( i = 1 ; i <= BlockCount ; i++ ) {
edid_read_data(i, EDID_Buf) ;
if (edid_check_sum(i, EDID_Buf)!= 0)
return 0;
if ((EDID_Buf[0x80*i+0]==2)/*&&(EDID_Buf[0x80*i+1]==1)*/)
{
if ( (EDID_Buf[0x80*i+1]>=1)) {
if (EDID_Buf[0x80*i+3]&0x20)
{
is_yuv = 1;
__inf("device support YCbCr44 output\n");
if (rgb_only == 1) {
__inf("rgb only test!\n");
is_yuv = 0;
}
} else
is_yuv = 0;
}
offset = EDID_Buf[0x80*i+2];
/* deal with reserved data block */
if (offset > 4) {
u8 bsum = 4;
while (bsum < offset)
{
u8 tag = EDID_Buf[0x80*i+bsum]>>5;
u8 len = EDID_Buf[0x80*i+bsum]&0x1f;
if ( (len >0) && ((bsum + len + 1) > offset) ) {
__inf("len or bsum size error\n");
return 0;
} else {
if ( tag == 1) {
/* ADB */
edid_parse_audiodata_block(EDID_Buf+0x80*i+bsum+1,len);
} else if ( tag == 2) {
/* VDB */
edid_parse_videodata_block(EDID_Buf+0x80*i+bsum+1,len);
} else if ( tag == 3) {
/* vendor specific */
edid_parse_vsdb(EDID_Buf+0x80*i+bsum+1,len);
}
}
bsum += (len +1);
}
} else {
__inf("no data in reserved block%d\n",i);
}
/* deal with 18-byte timing block */
if (offset >= 4) {
while (offset < (0x80-18)) {
edid_parse_dtd_block(EDID_Buf + 0x80*i + offset);
offset += 18;
}
} else {
__inf("no datail timing in block%d\n",i);
}
}
}
}
return 0 ;
}
u32 hdmi_edid_is_hdmi(void)
{
return is_hdmi;
}
u32 hdmi_edid_is_yuv(void)
{
return is_yuv;
}
uintptr_t hdmi_edid_get_data(void)
{
return (uintptr_t)EDID_Buf;
}