/* * Copyright 2010 Intel Corporation * Yi Sun * * 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. */ /* * This program is intended for testing EDID. It is used to parse the EDID. * Now, it is mainly parse all the information in the block 0 of the EDID. * * TODO: * -HDMI block parse * - */ #include #include /*dir */ #include #include #include #include #include #include /*open */ #include /*read */ #include #include "drm_edid.h" #define EDIDPATH "/sys/class/drm/" #define MAXCARD 10 #define MAXFILENAME 256 #define MAXEDID 256 struct mode_line { char mode[12]; int freq; }; union VendorProduct { struct { int c:5; int b:5; int a:5; } idchr; struct { int lb:8; int hb:8; } singlechr; }; union VideoInputDefinition { struct { char serrationvsync:1; char syncongreen:1; char compositesync:1; char separatesyncs:1; char blanktoblacksetup:1; char videolevel:2; char analogordigital:1; } inputdef_a; struct { char InterfaceStandardSupported:4; char colorbitdepth:3; char analogordigital:1; } inputdef_d; }; struct PowerManSupport { char GTFSupport:1; char perfertimemod:1; char stdcolorspace:1; char monchrome:2; char activeoff:1; char suspend:1; char standby:1; }; char edid_head[] = {0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0}; int bitsperprimarycolor[] = {0, 6, 8, 10, 12, 14, 16, 0}; char *interfacetpye[] = { "", "DVI", "HDMI-a", "HDMI-b", "MDDI", "DisplayPort" }; struct mode_line estmode[] = { {"1280x1024",75}, {"1024x768",75}, {"1024x768",70}, {"1024x768",60}, {"1024x768",87}, {"832x624",75}, {"800x600",75}, {"800x600",72}, {"800x600",60}, {"800x600",56}, {"640x480",75}, {"640x480",72}, {"640x480",67}, {"640x480",60}, {"720x400",88}, {"720x400",70} }; struct StdTiming { int vertfrequency:6; int aspectratio:2; }; union PixelClock { struct { int clkl:8; int clkh:8; int :16; } clkchr; int clk; }; union Disp { struct { int vdispl:8; int vdisph:4; int :0; } vdispchr; int value; }; union Sync { struct { int syncl:8; int synch:2; } syncchr; int value; }; union H4bit { struct { char blank:4; char disp:4; } h4bitchr; char value; }; union H2bit { struct { char vsync:2; char dsync:2; char hsync:2; } chr; char value; }; char *bppinfo[] = { "DVI_Dual", "", "", "DC_Y444", "DC_30bit", "DC_36bit", "DC_48bit", "Supports_AI" }; static char* get_edid(char *edid_file_name) { char *edid_block; int edid_fp; int read_count = 0; edid_fp = open(edid_file_name,O_RDONLY); if (edid_fp < 0) { printf("open edid file error:%s\n", edid_file_name); return NULL; } edid_block = (char *)malloc(MAXEDID); memset(edid_block, 0 , MAXEDID); read_count = read(edid_fp, edid_block, MAXEDID); if (read_count > 0) return edid_block; else return NULL; } static void print_edid(char *edid) { for (int j=0; j < MAXEDID; j++) { if (j % 16 == 0) printf("\t%04hhx: ", j); if (j % 16 == 8) printf(" "); printf("%02hhx ", *(edid+j)); if (j % 16 == 15) printf("\n"); } printf("\n"); } static int get_connected_card_list(char *** connected_card_list ) { struct dirent *dirent_ptr; DIR* drm_dir_ptr; int connected_count = 0; char edid_file[MAXFILENAME]; char *new_edid; drm_dir_ptr = opendir(EDIDPATH); if (drm_dir_ptr == NULL){ printf("Error: fail to open the directory %s\n", EDIDPATH); return 1; } (*connected_card_list)= malloc(MAXCARD * sizeof(char*)); dirent_ptr = readdir(drm_dir_ptr); while (dirent_ptr) { if (strstr(dirent_ptr->d_name, "card0-") >0 ) { sprintf(edid_file, "%s%s/edid", EDIDPATH, dirent_ptr->d_name); new_edid = get_edid(edid_file); if (new_edid != NULL){ (*connected_card_list)[connected_count] = malloc(MAXFILENAME); memcpy((*connected_card_list)[connected_count], edid_file, strlen(edid_file) ); connected_count++; } } dirent_ptr = readdir(drm_dir_ptr); } closedir(drm_dir_ptr); return connected_count; } static int get_connected_edid_list (char ***edid_list) { char **connected_card_list; int connected_count; connected_count = get_connected_card_list( & connected_card_list ); (*edid_list) = malloc (connected_count * sizeof(char)); for (int i = 0; i < connected_count; i++) { //sprintf(edid_file, "%s%s/edid", EDIDPATH, dirent_ptr->d_name); (*edid_list)[i] = get_edid(connected_card_list[i]); } return connected_count; } static char chrmap(char ch) { return 'A'+ch-1; } /* Get the whole mode line declared in DRM by mode and frequency. */ static void find_dmt_mode(struct mode_line* m, struct drm_display_mode* rmode) { for (int i=0; i < sizeof(dmt_modes) / sizeof(struct drm_display_mode); i++) { if (strcmp(m->mode, dmt_modes[i].name) == 0 && m->freq==dmt_modes[i].freq) { memcpy(rmode, &dmt_modes[i], sizeof(struct drm_display_mode)); } } } static void find_est_mode(struct mode_line* m, struct drm_display_mode* rmode) { for (int i = 0; i < 16; i++) { if (strcmp(m->mode, est_modes[i].name) == 0 && m->freq == est_modes[i].freq) { memcpy(rmode, &est_modes[i], sizeof(struct drm_display_mode)); } } } static int gethdmiblock(char edid[]) { for (int i = 0; i < MAXEDID; i++) { if (edid[i] == 0x03 && edid[i+1] == 0x0c && edid[i+2] == 0) { return i; } } return -1; } static int check_edid(char edid[]) { if( memcmp(edid, edid_head, 8) !=0 ) { puts("Bad edid file\n"); return 1; } return 0; } static int parse_edid_vendor_product(char edid[]) { /* * Vendor & Product ID: 10 Bytes * 08h, 09h 2 ID Manufacturer Name Section 3.4.1 * 0Ah, 0Bh 2 ID Product Code Section 3.4.2 * 0Ch → 0Fh 4 ID Serial Number Section 3.4.3 * 10h, 11h 2 Week of Manufacture or Model Year Flag, */ union VendorProduct mfid; mfid.singlechr.hb = edid[8]; mfid.singlechr.lb = edid[9]; printf("\tManufactureID: %c%c%c\t", chrmap(mfid.idchr.a), chrmap(mfid.idchr.b), chrmap(mfid.idchr.c)); printf("\tMonitorID: %hhx%hhx\t", edid[0xb], edid[0xa]); printf("\tSerialNumber: %c%c%c%c\n", edid[0xf], edid[0xe], edid[0xd], edid[0xc]); printf("\tProductWeek/year: %d/%d\n", edid[0x10], edid[0x11]+1990); printf("\tEdidVersion: %hhx.%hhx\n", edid[0x12], edid[0x13]); return 0; } static int parse_edid_video_input(char edid[]) { /* * Address Bit Definitions Description * 7 _ _ _ _ _ _ _ Video Signal Interface: Bit 7 * 0 _ _ _ _ _ _ _ Input is an Analog Video Signal Interface: * 7 6 5 _ _ _ _ _ Signal Level Standard: Video : Sync : Total Bits 6 & 5 * 0 0 0 _ _ _ _ _ 0.700 : 0.300 : 1.000 V p-p * 0 0 1 _ _ _ _ _ 0.714 : 0.286 : 1.000 V p-p * 0 1 0 _ _ _ _ _ 1.000 : 0.400 : 1.400 V p-p * 0 1 1 _ _ _ _ _ 0.700 : 0.000 : 0.700 V p-p * 7 _ _ 4 _ _ _ _ Video Setup: Bit 4 * 0 _ _ 0 _ _ _ _ Video Setup: Blank Level = Black Level * 0 _ _ 1 _ _ _ _ Video Setup: Blank-to-Black setup or pedestal (see Note 1) * 7 _ _ _ 3 2 1 _ Synchronization Types: Bits 3 → 1 * 0 _ _ _ 0 _ _ _ Separate Sync H & V Signals are not supported * 0 _ _ _ 1 _ _ _ Separate Sync H & V Signals are supported * 0 _ _ _ _ 0 _ _ Composite Sync Signal on Horizontal is not supported * 0 _ _ _ _ 1 _ _ Composite Sync Signal on Horizontal is supported * 0 _ _ _ _ _ 0 _ Composite Sync Signal on Green Video is not supported * 0 _ _ _ _ _ 1 _ Composite Sync Signal on Green Video is supported * 7 _ _ _ _ _ _ 0 Serrations: Bit 0 * 0 _ _ _ _ _ _ 0 Serration on the Vertical Sync is not supported * 14h * 0 _ _ _ _ _ _ 1 Serration on the Vertical Sync is supported (see Note 2) * 7 _ _ _ _ _ _ _ Video Signal Interface: Bit 7 * 1 _ _ _ _ _ _ _ Input is a Digital Video Signal Interface: (see Note 3) * 7 6 5 4 _ _ _ _ Color Bit Depth: Bits 6 → 4 * 1 0 0 0 _ _ _ _ Color Bit Depth is undefined * 1 0 0 1 _ _ _ _ 6 Bits per Primary Color * 1 0 1 0 _ _ _ _ 8 Bits per Primary Color * 1 0 1 1 _ _ _ _ 10 Bits per Primary Color * 1 1 0 0 _ _ _ _ 12 Bits per Primary Color * 1 1 0 1 _ _ _ _ 14 Bits per Primary Color * 1 1 1 0 _ _ _ _ 16 Bits per Primary Color * 1 1 1 1 _ _ _ _ Reserved (Do Not Use) * 7 _ _ _ 3 2 1 0 Digital Video Interface Standard Supported: Bits 3 → 0 * 1 _ _ _ 0 0 0 0 Digital Interface is not defined (see Note 4) * 1 _ _ _ 0 0 0 1 DVI is supported * 1 _ _ _ 0 0 1 0 HDMI-a is supported * 1 _ _ _ 0 0 1 1 HDMI-b is supported * 1 _ _ _ 0 1 0 0 MDDI is supported * 1 _ _ _ 0 1 0 1 DisplayPort is supported * 14h * 1 _ _ _ → → → → All remaining values for Bits 3 → 0 are Reserved: Do Not Use */ union VideoInputDefinition inputdef; memcpy(&inputdef, &edid[0x14], 1); if (inputdef.inputdef_a.analogordigital) { printf("\tMonitorType: %s\n", "Digital"); printf("\tColorBitDepth: %d Bits per Primary Color\n", bitsperprimarycolor[inputdef.inputdef_d.colorbitdepth]); printf("\tInterfaceStandardSupported: %s\n", interfacetpye[inputdef.inputdef_d.InterfaceStandardSupported]); } else { printf("\tMonitorType: %s\n","Analogor"); printf("\tMaxImagesize: %hhdcm x %hhdcm\n", edid[0x15], edid[0x16]); printf("\tMonitorGamma: %.2f\n", (edid[0x17] + 100) * 1.0 / 100.0); } return 0; } static int parse_edid_power_support(char edid[]) { /* * Address Bits Definitions Description * 7 6 5 _ _ _ _ _ Display Power Management: (See Note 1) Bits 7 → 5 * 1 _ _ _ _ _ _ _ Standby Mode is supported. Bit 7 * 0 _ _ _ _ _ _ _ Standby Mode is not supported. Bit 7 * _ 1 _ _ _ _ _ _ Suspend Mode is supported. Bit 6 * _ 0 _ _ _ _ _ _ Suspend Mode is not supported. Bit 6 * _ _ 1 _ _ _ _ _ Active Off = Very Low Power is supported. Bit 5 * _ _ 0 _ _ _ _ _ Active Off = Very Low Power is not supported. Bit 5 * 4 3 _ _ _ If bit 7 at address 14h = ‘0’ then bits 4 & 3 at address 18h defines the * Display Color Type: (See Note 2) Bits 4 & 3 * 0 0 _ _ _ Monochrome or Grayscale display * 0 1 _ _ _ RGB color display * 1 0 _ _ _ Non-RGB color display * 1 1 _ _ _ Display Color Type is Undefined * 4 3 _ _ _ If bit 7 at address 14h = ‘1’ then bits 4 & 3 at address 18h defines the * Supported Color Encoding Format/s: (See Note 2) Bits 4 & 3 * 0 0 _ _ _ RGB 4:4:4 * 0 1 _ _ _ RGB 4:4:4 & YCrCb 4:4:4 * 1 0 _ _ _ RGB 4:4:4 & YCrCb 4:2:2 * 1 1 _ _ _ RGB 4:4:4 & YCrCb 4:4:4 & YCrCb 4:2:2 * 2 1 0 Other Feature Support Flags: Bits 2 → 0 * 1 _ _ sRGB Standard is the default color space. (See Note 3) Bit 2 * 0 _ _ sRGB Standard is not the default color space. Bit 2 * _ 1 _ Preferred Timing Mode includes the native pixel format and preferred refresh rate of the display device. (See Note 4) Bit 1 * _ 0 _ Preferred Timing Mode does not include the native pixel format and preferred refresh rate of the display device. Bit 1 * _ _ 1 Display is continuous frequency. (See Note 5) Bit 0 * _ _ 0 Display is non-continuous frequency (multi-mode). Bit 0 */ struct PowerManSupport powman; memcpy(&powman, &edid[0x18], 1); char msg[256]; memset(msg, 0, sizeof(msg)); memset(msg, 0, 256); if(powman.standby) strcat(msg, "standby "); if(powman.suspend) strcat(msg, "suspend "); if(powman.activeoff) strcat(msg, "activeoff "); if(!(powman.monchrome & 3)) strcat(msg, "monochrome "); else if(powman.activeoff & 1) strcat(msg, "RGB "); else if(powman.activeoff & 2) strcat(msg, "Non RGB "); else if(powman.activeoff & 3) strcat(msg, "Undefineed "); printf("\tPowerSupport: %s\n", msg); printf("\tChromaInfo: %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx \n", edid[0x19], edid[0x1a], edid[0x1b], edid[0x1c], edid[0x1d], edid[0x1e], edid[0x1f], edid[0x20], edid[0x21], edid[0x22]); return 0; } static int parse_edid_established_timing(char edid[]) { /* * 23h 1 Established Timing I * 7 720 x 400 @ 70Hz IBM, VGA * 6 720 x 400 @ 88Hz IBM, XGA2 * 5 640 x 480 @ 60Hz IBM, VGA * 4 640 x 480 @ 67Hz Apple, Mac II * 3 640 x 480 @ 72Hz VESA * 2 640 x 480 @ 75Hz VESA * 1 800 x 600 @ 56Hz VESA * 0 800 x 600 @ 60Hz VESA * 24h 1 Established Timing II * 7 800 x 600 @ 72Hz VESA * 6 800 x 600 @ 75Hz VESA * 5 832 x 624 @ 75Hz Apple, Mac II * 4 1024 x 768 @ 87Hz(I) IBM - Interlaced * 3 1024 x 768 @ 60Hz VESA * 2 1024 x 768 @ 70Hz VESA * 1 1024 x 768 @ 75Hz VESA * 0 1280 x 1024 @ 75Hz VESA * 25h 1 Manufacturer's Timings * 7 1152 x 870 @ 75Hz Apple, Mac II */ struct mode_line curestmode[80]; printf("\tEstablishedTiming: \n"); char es = edid[0x24]; int escount=0; for (int i=0; i < 8; i++) { if (es & 0x01) { memcpy(&curestmode[escount], &estmode[i], sizeof(struct mode_line)); escount++; } es = es >> 1; } es = edid[0x23]; for (int i=0; i<8; i++) { if (es & 0x01) { memcpy(&curestmode[escount], &estmode[i+8], sizeof(struct mode_line)); escount++; } es = es >> 1; } for (int i = 0; i < escount; i++) { struct drm_display_mode rmodeline; find_est_mode(&curestmode[i], &rmodeline); printf("\t\t%s @ %d %d %d %d\n", rmodeline.name, rmodeline.freq, rmodeline.pclock, rmodeline.htot, rmodeline.vtot); } return 0; } static int parse_edid_standar_timing(char edid[]) { /* * 26h 1 256 pixels → 2288 pixels, in increments of 8 pixels * 00h Reserved: Do not use. * Bit Definitions Description * 7 6 _ _ _ _ _ _ Image Aspect Ratio: bits 7 & 6 * 0 0 _ _ _ _ _ _ 16 : 10 AR * 0 1 _ _ _ _ _ _ 4 : 3 AR * 1 0 _ _ _ _ _ _ 5 : 4 AR * 1 1 _ _ _ _ _ _ 16 : 9 AR * 5 4 3 2 1 0 Field Refresh Rate: bits 5 → 0 * Value Stored (in binary) = Field Refresh Rate (in Hz) – 60 * 27h 1 * n n n n n n * Range: 60 Hz → 123Hz */ struct StdTiming stdtiminginfo; struct mode_line curestmode[80]; struct mode_line tdmtmode; struct drm_display_mode rmodeline; char tmod[20]; char msg[512]; char ratio[2][6]; printf("\r\tStandarTiming:\n"); memset(curestmode, 0, 80 * sizeof(struct mode_line)); for (int i = 0; i < 8; i++) { int tem = 0; memcpy(&tem, &edid[38 + 2 * i], 1); memcpy(&stdtiminginfo, &edid[39 + 2 * i], 1); memset(msg, 0, 256); memset(ratio, 0, sizeof(ratio)); if(!(stdtiminginfo.aspectratio & 3)) strcat(msg, "16:10"); else if(stdtiminginfo.aspectratio & 1) strcat(msg, "4:3"); else if(stdtiminginfo.aspectratio & 2) strcat(msg, "5:4"); else if(stdtiminginfo.aspectratio & 3) strcat(msg, "16:9"); char *msgp = strstr(msg, ":"); memcpy(ratio[0], msg, msgp-msg); memcpy(ratio[1], msgp+1, strlen(msgp)); int hratio = atoi(ratio[0]); int vratio = atoi(ratio[1]); if (tem != 1) { memset(&rmodeline, 0, sizeof(struct drm_display_mode)); memset(&tdmtmode, 0, sizeof(struct mode_line)); memset(tmod, 0, 20); //sprintf(tmod,"%dx%d",tem*8+248, (int)(((tem*8+248)*1.0*vratio+0.5)/hratio),stdtiminginfo.vertfrequency+60); sprintf(tmod, "%dx%d", tem * 8 + 248, (int)(((tem * 8 + 248) * 1.0 * vratio + 0.5) / hratio)); strcat(tdmtmode.mode , tmod); tdmtmode.freq = stdtiminginfo.vertfrequency + 60; find_dmt_mode(&tdmtmode, &rmodeline); printf("\t\t%s @ %d %d %d %d (%s)\n", rmodeline.name, rmodeline.freq, rmodeline.pclock, rmodeline.htot, rmodeline.vtot, msg); } } return 0; } static int parse_edid_detail_timing(char edid[]) { union PixelClock pixclk; union Disp vdisp; union Sync sync; memset(&pixclk, 0, sizeof(union PixelClock)); for(int i = 0; i < 4; i++) { int Hdisp, Vdisp, Hblank, Vblank; float freq; pixclk.clkchr.clkl = edid[0x36 + i*18]; pixclk.clkchr.clkh = edid[0x37 + i*18]; if (pixclk.clk == 0) continue; union H4bit h4bit; vdisp.vdispchr.vdispl = edid[0x36 + i*18 + 2]; h4bit.value = edid[0x36 + i*18 + 4]; vdisp.vdispchr.vdispl = edid[0x36+ i*18 + 2]; vdisp.vdispchr.vdisph = h4bit.h4bitchr.disp; Hdisp = vdisp.value; memset(&h4bit, 0, 1); memset(&vdisp, 0, 4); vdisp.vdispchr.vdispl=edid[0x36+ i*18 + 5]; h4bit.value = edid[0x36+ i*18 + 7]; vdisp.vdispchr.vdispl = edid[0x36+ i*18 + 5]; vdisp.vdispchr.vdisph = h4bit.h4bitchr.disp; Vdisp = vdisp.value; memset(&h4bit,0,1); memset(&vdisp,0,4); vdisp.vdispchr.vdispl = edid[0x36+ i*18 + 3]; h4bit.value = edid[0x36+ i*18 + 4]; vdisp.vdispchr.vdispl = edid[0x36+ i*18 + 3]; vdisp.vdispchr.vdisph = h4bit.h4bitchr.blank; Hblank = vdisp.value; memset(&h4bit,0,1); memset(&vdisp,0,4); vdisp.vdispchr.vdispl = edid[0x36+ i*18 + 6]; h4bit.value = edid[0x36+ i*18 + 7]; vdisp.vdispchr.vdispl = edid[0x36+ i*18 + 6]; vdisp.vdispchr.vdisph = h4bit.h4bitchr.blank; Vblank = vdisp.value; freq = pixclk.clk * 10000.0 / (Hdisp+Hblank) / (Vdisp+Vblank); printf("\tDetailTiming:\n\t\t%dx%d @ %.2f %d %d %d\n", Hdisp, Vdisp, freq, pixclk.clk * 10, Hdisp + Hblank, Vdisp + Vblank); union H2bit h2bit; h2bit.value = edid[0x36 + i*18 + 11]; memset(&sync, 0, 4); sync.syncchr.syncl = edid[0x36 + i*18 + 8]; sync.syncchr.synch = h2bit.chr.vsync; } return 0; } static int parse_edid_hdmi_bpp(char edid[]) { /* * Extended block for HDMI */ int phdmiindex = gethdmiblock(edid); if (phdmiindex != -1) { printf("\tHaving a hdmi block, start position is:%2hhx\n", phdmiindex); char bppbyte = edid[phdmiindex + 5]; for (int i = 0; i < 8; i++) { if (bppbyte & 0x01) { printf("\tHDMI bpp supports: %s\n", bppinfo[i]); } bppbyte = bppbyte >> 1; } } return 0; } static int parse_edid(char edid[]) { if (check_edid(edid)) return 1; parse_edid_vendor_product(edid); parse_edid_video_input(edid); parse_edid_power_support(edid); parse_edid_established_timing(edid); parse_edid_standar_timing(edid); parse_edid_detail_timing(edid); parse_edid_hdmi_bpp(edid); return 0; } int main(void) { int connected_count; char **edid_list; char **connected_card_list; connected_count = get_connected_edid_list( & edid_list); connected_count = get_connected_card_list( & connected_card_list ); printf("\33[1;;32mThe connected monitor number: %d\n\33[0m", connected_count); for (int i = 0; i < connected_count; i++) { printf("\33[1;;32m%d. Connected monitor card: %s\n\33[0m", i + 1, connected_card_list[i]); print_edid(edid_list[i]); parse_edid(edid_list[i]); } return 0; }