2018年7月11日 星期三

HDMI EDID Data Collection解析

最近研究下HDMI EDID內的resolution list,想要了解如何知道EDID如何存放4K解析度。

從手上現有的HDMI EDID(256 bytes)發現,block1(CEA EDID Timing Extension data format)的video data block會存放擴展用的resolution list。

因此寫一支小程式特別讀取resolution id並轉換成寬x高 (pixel)


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BLOCK0_SIZE 128

int getResolutionWidth(int index)
{
    switch(index)
    {
        case 1: return 640;
        case 2: case 3: case 6: case 17: case 18: return 720;
        case 4: case 19: case 60: case 61: case 62: case 65: case 66: case 67: case 68: case 69: return 1280;
        case 5: case 16: case 20: case 31: case 32: case 33: case 34: case 39: case 72: case 73: case 74: case 75: case 76: return 1920;
        case 86: case 87: case 88: case 89: case 90: case 91: case 92: case 113: return 2560;
        case 93: case 94: case 95: case 96: case 97: case 114: case 116: case 117: case 118: case 119: case 120: return 3840;
        case 98: case 99: case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: case 108: case 115: return 4096;
        case 121: case 122: case 123: case 124: case 125: case 126: case 127: return 5120;
    }
    return 0;
}

int getResolutionHeight(int index)
{
    switch(index)
    {
        case 1: case 2: case 3: case 6: return 480;
        case 17: case 18: return 720;
        case 4: case 19: case 60: case 61: case 62: case 65: case 66: case 67: case 68: case 69: return 720;
        case 5: case 16: case 20: case 31: case 32: case 33: case 34: case 39: case 72: case 73: case 74: case 75: case 76: case 86: case 87: case 88: case 89: case 90: case 91: case 92: case 113: return 1080;
        case 93: case 94: case 95: case 96: case 97: case 114: case 116: case 117: case 118: case 119: case 120: case 98: case 99: case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: case 108: case 115: case 121: case 122: case 123: case 124: case 125: case 126: case 127: return 2160;
    }
    return 0;
}

int main(int argc, char *argv[])
{
    int fd = -1;
    int ret = 0;
    int size = 0;
    char *edidBuf = NULL;

    if(argc <= 1) {
        printf("invalid argument, please set input parameter.\n");
        return -1;
    }

    fd = open(argv[1],O_RDONLY);
    if(fd == -1)
        return -1;

    size = lseek(fd,0,SEEK_END);
    lseek(fd,0,SEEK_SET);

    printf("edid size:%d \n",size);
    edidBuf = (char*)malloc(sizeof(char)*size);
    if(edidBuf == NULL)
    {
        printf("malloc failed\n");
        ret = -1;
        goto parse_exit;
    }

    if((size != read(fd,edidBuf,sizeof(char)*size)) && size < BLOCK0_SIZE)
    {
        printf("Get edid size not match, size should be %d \n",size);
        ret = -1;
        goto parse_exit;
    }

    int index = 0;
    int width = 0, height = 0;
    int block_num = 0, block_end_offset;

    //CEA EDID
    if(size > BLOCK0_SIZE && edidBuf[0x80] == 0x02)
    {
        if(edidBuf[0x82] == 0x04)  //No data block, jump out
        {
            printf("No data block, exit!\n");
            goto parse_exit;
        }
        else if(edidBuf[0x82] == 0)
        {
            block_end_offset=size;
        }
        else
        {
            block_end_offset = edidBuf[0x82] + 0x80;
        }

        printf("block_end_offset:%d \n",block_end_offset);

        index = 0;
        width = 0;
        height = 0;
        while((0x84 + index) < block_end_offset) //Start to parse from data block
        {
            printf("edid data:0x%02x \n",edidBuf[0x84+index]);
            if((edidBuf[0x84+index]&0xE0) == 0x40) //Video data block
            {
                block_num = edidBuf[0x84+index]&0x1F;
                printf("video block number:%d \n",block_num);
                int i=0,max=0;
                for(i=0;i<block_num;i++)
                {
                    if(getResolutionHeight((int)edidBuf[0x84+index+1+i])>height)
                    {
                        width = getResolutionWidth((int)edidBuf[0x84+index+1+i]);
                        height = getResolutionHeight((int)edidBuf[0x84+index+1+i]);
                    }
                    printf("id:%d width:%d height:%d\n",edidBuf[0x84+index+1+i],getResolutionWidth((int)edidBuf[0x84+index+1+i]),getResolutionHeight((int)edidBuf[0x84+index+1+i]));
                }
                break;
            }
            else //other data block(we ignore it)
            {
                block_num = edidBuf[0x84+index]&0x1F;
                index+=block_num;
            }
            index++;
        }
        printf("max resolution is (%d,%d)\n",width,height);
    }
parse_exit:
    if(edidBuf != NULL)
        free(edidBuf);

    close(fd);
    return ret;
}


簡單來說就是檢查block1是否是CEA block,非CEA就不做下一步。

檢查offset 0x02是否有DTD,如果值是0x04也代表不用檢查Data block,因為resolution會是由DTD block決定(不過暫時不支援DTD parser)

然後從offset 0x04開始過濾Data Block Collection ,因為只要過濾video data block,所以其它部分均跳過,Data Block Collection的類型可以參考EDID Wiki有說明。

最後根據VIC數值轉換到相應的寬x高並找出即可(我們先判斷高度然後再判斷寬度)。

運行結果如下:

edid size:256
block_end_offset:177
edid data:0x48
video block number:8
id:93 width:3840 height:2160
id:16 width:1920 height:1080
id:5 width:1920 height:1080
id:34 width:1920 height:1080
id:32 width:1920 height:1080
id:2 width:720 height:480
id:6 width:720 height:480
id:1 width:640 height:480
max resolution is (3840,2160)


參考:
1. https://en.wikipedia.org/wiki/Extended_Display_Identification_Data
2.. http://www.edidreader.com/

沒有留言:

張貼留言