diff options
Diffstat (limited to 'deps/sysobj_early/src/util_edid.c')
-rw-r--r-- | deps/sysobj_early/src/util_edid.c | 379 |
1 files changed, 290 insertions, 89 deletions
diff --git a/deps/sysobj_early/src/util_edid.c b/deps/sysobj_early/src/util_edid.c index 2aa595fa..6d90f6ac 100644 --- a/deps/sysobj_early/src/util_edid.c +++ b/deps/sysobj_early/src/util_edid.c @@ -31,16 +31,19 @@ #include "util_edid_svd_table.c" -/* block must be 128 bytes */ -static int block_check(const void *edid_block_bytes) { +static int block_check_n(const void *edid_block_bytes, int len) { if (!edid_block_bytes) return 0; uint8_t sum = 0; uint8_t *data = (uint8_t*)edid_block_bytes; int i; - for(i=0; i<128; i++) + for(i=0; i<len; i++) sum += data[i]; return sum == 0 ? 1 : 0; } +static int block_check(const void *edid_block_bytes) { + /* block must be 128 bytes */ + return block_check_n(edid_block_bytes, 128); +} char *hex_bytes(uint8_t *bytes, int count) { char *buffer = malloc(count*3+1), *p = buffer; @@ -53,6 +56,51 @@ char *hex_bytes(uint8_t *bytes, int count) { return buffer; } +#define OUTPUT_CPY_SIZE(DEST, SRC) \ + (DEST).horiz_cm = (SRC).horiz_cm; \ + (DEST).vert_cm = (SRC).vert_cm; \ + (DEST).diag_cm = (SRC).diag_cm; \ + (DEST).diag_in = (SRC).diag_in; \ + edid_output_fill(&(DEST)); + +static void edid_output_fill(edid_output *out) { + out->diag_cm = + sqrt( (out->horiz_cm * out->horiz_cm) + + (out->vert_cm * out->vert_cm) ); + out->diag_in = out->diag_cm / 2.54; + + if (out->is_interlaced) { + if (out->vert_lines) + out->vert_pixels = out->vert_lines * 2; + else + out->vert_lines = out->vert_pixels / 2; + } else { + if (out->vert_lines) + out->vert_pixels = out->vert_lines; + else + out->vert_lines = out->vert_pixels; + } + + if (!out->vert_freq_hz && out->pixel_clock_khz) { + uint64_t h = out->horiz_pixels + out->horiz_blanking; + uint64_t v = out->vert_lines + out->vert_blanking; + if (h && v) { + uint64_t work = out->pixel_clock_khz * 1000; + work /= (h*v); + out->vert_freq_hz = work; + } + } + + out->pixels = out->horiz_pixels; + out->pixels *= out->vert_pixels; + + if (out->diag_in) { + static const char *inlbl = "\""; /* TODO: unicode */ + sprintf(out->class_inch, "%0.1f%s", out->diag_in, inlbl); + util_strchomp_float(out->class_inch); + } +} + static void cea_block_decode(struct edid_cea_block *blk, edid *e) { int i; if (blk) { @@ -90,45 +138,77 @@ static void cea_block_decode(struct edid_cea_block *blk, edid *e) { } } -static void edid_output_fill(edid_output *out) { - out->diag_cm = - sqrt( (out->horiz_cm * out->horiz_cm) - + (out->vert_cm * out->vert_cm) ); - out->diag_in = out->diag_cm / 2.54; - - if (out->is_interlaced) { - if (out->vert_lines) - out->vert_pixels = out->vert_lines * 2; - else - out->vert_lines = out->vert_pixels / 2; - } else { - if (out->vert_lines) - out->vert_pixels = out->vert_lines; - else - out->vert_lines = out->vert_pixels; - } - - if (!out->vert_freq_hz && out->pixel_clock_khz) { - uint64_t h = out->horiz_pixels + out->horiz_blanking; - uint64_t v = out->vert_lines + out->vert_blanking; - if (h && v) { - uint64_t work = out->pixel_clock_khz * 1000; - work /= (h*v); - out->vert_freq_hz = work; +static void did_block_decode(DisplayIDBlock *blk, edid *e) { + int b = 3; + uint8_t *u8 = blk->ptr; + edid_output out = {}; + if (blk) { + switch(blk->type) { + case 0x03: /* Type I Detailed timings */ + out.pixel_clock_khz = u8[b+2] << 16; + out.pixel_clock_khz += u8[b+1] << 8; + out.pixel_clock_khz += u8[b]; + out.pixel_clock_khz *= 10; + out.horiz_pixels = (u8[b+5] << 8) + u8[b+4]; + out.horiz_blanking = (u8[b+7] << 8) + u8[b+6]; + out.vert_lines = (u8[b+13] << 8) + u8[b+12]; + out.vert_blanking = (u8[b+15] << 8) + u8[b+14]; + out.is_interlaced = (u8[b+3] >> 4) & 0x1; + out.stereo_mode = (u8[b+3] >> 5) & 0x2; + out.is_preferred = (u8[b+3] >> 7) & 0x1; + out.src = OUTSRC_DID_TYPE_I; + edid_output_fill(&out); + e->didts[e->didt_count++] = out; + break; + case 0x13: /* Type VI Detailed timings (super 0x03) */ + /* UNTESTED */ + out.pixel_clock_khz = (u8[b+2] << 16) & 0xa0; + out.pixel_clock_khz += u8[b+1] << 8; + out.pixel_clock_khz += u8[b]; + out.horiz_pixels = ((u8[b+5] << 8) + u8[b+3]) & 0x7fff; + out.vert_lines = ((u8[b+6] << 8) + u8[b+5]) & 0x7fff; + // TODO: blanking... + out.is_interlaced = (u8[b+13] >> 7) & 0x1; + out.stereo_mode = (u8[b+13] >> 5) & 0x2; + out.src = OUTSRC_DID_TYPE_VI; + edid_output_fill(&out); + e->didts[e->didt_count++] = out; + break; + case 0x22: /* Type VII Detailed timings (super 0x13) */ + /* UNTESTED */ + out.pixel_clock_khz = u8[b+2] << 16; + out.pixel_clock_khz += u8[b+1] << 8; + out.pixel_clock_khz += u8[b]; + out.horiz_pixels = (u8[b+5] << 8) + u8[b+4]; + out.horiz_blanking = (u8[b+7] << 8) + u8[b+6]; + out.vert_lines = (u8[b+13] << 8) + u8[b+12]; + out.vert_blanking = (u8[b+15] << 8) + u8[b+14]; + out.is_interlaced = (u8[b+3] >> 4) & 0x1; + out.stereo_mode = (u8[b+3] >> 5) & 0x2; + out.is_preferred = (u8[b+3] >> 7) & 0x1; + out.src = OUTSRC_DID_TYPE_VII; + edid_output_fill(&out); + e->didts[e->didt_count++] = out; + break; + case 0x81: /* CTA DisplayID, ... Embedded CEA Blocks */ + while(b < blk->len) { + int db_type = (u8[b] & 0xe0) >> 5; + int db_size = u8[b] & 0x1f; + e->cea_blocks[e->cea_block_count].header.ptr = &u8[b]; + e->cea_blocks[e->cea_block_count].header.type = db_type; + e->cea_blocks[e->cea_block_count].header.len = db_size; + cea_block_decode(&e->cea_blocks[e->cea_block_count], e); + e->cea_block_count++; + b += db_size + 1; + } + break; + default: + break; } } - - out->pixels = out->horiz_pixels; - out->pixels *= out->vert_pixels; - - if (out->diag_in) { - static const char *inlbl = "\""; /* TODO: unicode */ - sprintf(out->class_inch, "%0.1f%s", out->diag_in, inlbl); - util_strchomp_float(out->class_inch); - } } -edid_output edid_output_from_svd(uint8_t index) { +static edid_output edid_output_from_svd(uint8_t index) { int i; if (index >= 128 && index <= 192) index &= 0x7f; /* "native" flag for 0-64 */ for(i = 0; i < (int)G_N_ELEMENTS(cea_standard_timings); i++) { @@ -140,7 +220,7 @@ edid_output edid_output_from_svd(uint8_t index) { out.is_interlaced = 1; out.pixel_clock_khz = cea_standard_timings[i].pixel_clock_mhz * 1000; out.vert_freq_hz = cea_standard_timings[i].vert_freq_hz; - out.src = 5; + out.src = OUTSRC_SVD; edid_output_fill(&out); return out; } @@ -160,14 +240,19 @@ edid *edid_new(const char *data, unsigned int len) { e->ver_major = e->u8[18]; e->ver_minor = e->u8[19]; - e->dtds = malloc(sizeof(struct edid_dtd) * 1000); - e->cea_blocks = malloc(sizeof(struct edid_cea_block) * 1000); - e->svds = malloc(sizeof(struct edid_svd) * 1000); - e->sads = malloc(sizeof(struct edid_sad) * 1000); - memset(e->dtds, 0, sizeof(struct edid_dtd) * 1000); - memset(e->cea_blocks, 0, sizeof(struct edid_cea_block) * 1000); - memset(e->svds, 0, sizeof(struct edid_svd) * 1000); - memset(e->sads, 0, sizeof(struct edid_sad) * 1000); +#define RESERVE_COUNT 300 + e->dtds = malloc(sizeof(struct edid_dtd) * RESERVE_COUNT); + e->cea_blocks = malloc(sizeof(struct edid_cea_block) * RESERVE_COUNT); + e->svds = malloc(sizeof(struct edid_svd) * RESERVE_COUNT); + e->sads = malloc(sizeof(struct edid_sad) * RESERVE_COUNT); + e->did_blocks = malloc(sizeof(DisplayIDBlock) * RESERVE_COUNT); + e->didts = malloc(sizeof(edid_output) * RESERVE_COUNT); + memset(e->dtds, 0, sizeof(struct edid_dtd) * RESERVE_COUNT); + memset(e->cea_blocks, 0, sizeof(struct edid_cea_block) * RESERVE_COUNT); + memset(e->svds, 0, sizeof(struct edid_svd) * RESERVE_COUNT); + memset(e->sads, 0, sizeof(struct edid_sad) * RESERVE_COUNT); + memset(e->did_blocks, 0, sizeof(DisplayIDBlock) * RESERVE_COUNT); + memset(e->didts, 0, sizeof(edid_output) * RESERVE_COUNT); uint16_t vid = be16toh(e->u16[4]); /* bytes 8-9 */ e->ven[2] = 64 + (vid & 0x1f); @@ -210,7 +295,7 @@ edid *edid_new(const char *data, unsigned int len) { e->etbs[e->etb_count].vert_pixels = v; \ e->etbs[e->etb_count].vert_freq_hz = f; \ e->etbs[e->etb_count].is_interlaced = i; \ - e->etbs[e->etb_count].src = 1; \ + e->etbs[e->etb_count].src = OUTSRC_ETB; \ edid_output_fill(&e->etbs[e->etb_count]);\ e->etb_count++; }; ETB_CHECK(35, 7, 720, 400, 70, 0); //(VGA) @@ -262,7 +347,7 @@ edid *edid_new(const char *data, unsigned int len) { e->stds[e->std_count].out.horiz_pixels = xres; e->stds[e->std_count].out.vert_pixels = yres; e->stds[e->std_count].out.vert_freq_hz = vf; - e->stds[e->std_count].out.src = 2; + e->stds[e->std_count].out.src = OUTSRC_STD; edid_output_fill(&e->stds[e->std_count].out); e->std_count++; } @@ -299,13 +384,41 @@ edid *edid_new(const char *data, unsigned int len) { else e->ext_blocks_fail++; if (u8[0] == 0x70) { - e->std = MAX(e->std, 2); /* DisplayID */ - // TODO: ... + e->did.version = u8[1]; + if (e->did.version >= 0x20) + e->std = MAX(e->std, STD_DISPLAYID20); + else e->std = MAX(e->std, STD_DISPLAYID); + e->did.extension_length = u8[2]; + e->did.primary_use_case = u8[3]; + e->did.extension_count = u8[4]; + int db_end = u8[2] + 5; + int b = 5; + while(b < db_end) { + int db_type = u8[b]; + int db_revision = u8[b+1] & 0x7; + int db_size = u8[b+2]; + e->did_blocks[e->did_block_count].ptr = &u8[b]; + e->did_blocks[e->did_block_count].type = db_type; + e->did_blocks[e->did_block_count].revision = db_revision; + e->did_blocks[e->did_block_count].len = db_size; + did_block_decode(&e->did_blocks[e->did_block_count], e); + e->did_block_count++; + e->did.blocks++; + b += db_size + 3; + } + if (b != db_end) { + // over or under-run ... + } + e->did.checksum_ok = block_check_n(u8, u8[2] + 5); + //printf("DID: v:%02x el:%d uc:%d ec:%d, blocks:%d ok:%d\n", + // e->did.version, e->did.extension_length, + // e->did.primary_use_case, e->did.extension_count, + // e->did.blocks, e->checksum_ok); } if (u8[0] == 0x02) { - e->std = MAX(e->std, 1); + e->std = MAX(e->std, STD_EIACEA861); /* CEA extension */ int db_end = u8[2]; if (db_end) { @@ -334,6 +447,9 @@ edid *edid_new(const char *data, unsigned int len) { } } } + if (e->ext_blocks_ok) { + e->std = MAX(e->std, STD_EEDID); + } /* strings */ for(i = 0; i < 4; i++) { @@ -389,8 +505,8 @@ edid *edid_new(const char *data, unsigned int len) { uint8_t *u8 = e->dtds[i].ptr; uint16_t *u16 = (uint16_t*)e->dtds[i].ptr; edid_output *out = &e->dtds[i].out; - if (e->dtds[i].cea_ext) out->src = 4; - else out->src = 3; + if (e->dtds[i].cea_ext) out->src = OUTSRC_CEA_DTD; + else out->src = OUTSRC_DTD; out->pixel_clock_khz = le16toh(u16[0]) * 10; out->horiz_pixels = ((u8[4] & 0xf0) << 4) + u8[2]; @@ -421,42 +537,51 @@ edid *edid_new(const char *data, unsigned int len) { /* svds */ for(i = 0; i < e->svd_count; i++) { + e->svds[i].out = edid_output_from_svd(e->svds[i].v); if (e->svds[i].v >= 128 && e->svds[i].v <= 192) { e->svds[i].is_native = 1; + + edid_output tmp = e->img_max; + /* native res is max real res, right? */ + e->img_max = e->svds[i].out; + e->img_max.is_preferred = 1; + OUTPUT_CPY_SIZE(e->img_max, tmp); } - e->svds[i].out = edid_output_from_svd(e->svds[i].v); } - /* squeeze lists */ - if (!e->dtd_count) { - free(e->dtds); - e->dtds = NULL; - } else { - e->dtds = realloc(e->dtds, sizeof(struct edid_dtd) * e->dtd_count); - } - if (!e->cea_block_count) { - if (e->cea_blocks) - free(e->cea_blocks); - e->cea_blocks = NULL; - } else { - e->cea_blocks = realloc(e->cea_blocks, sizeof(struct edid_cea_block) * e->cea_block_count); - } - if (!e->svd_count) { - if (e->svds) - free(e->svds); - e->svds = NULL; - } else { - e->svds = realloc(e->svds, sizeof(struct edid_svd) * e->svd_count); + /* didts */ + for(i = 0; i < e->didt_count; i++) { + int pref = e->didts[i].is_preferred; + int max_pref = e->img_max.is_preferred; + int bigger = (e->didts[i].pixels > e->img_max.pixels); + int better = (e->didts[i].src > e->img_max.src); + if (bigger && !max_pref + || pref && !max_pref + || pref && better) { + edid_output tmp = e->img_max; + e->img_max = e->didts[i]; + OUTPUT_CPY_SIZE(e->img_max, tmp); + } } - if (!e->sad_count) { - if (e->sads) - free(e->sads); - e->sads = NULL; - } else { - e->sads = realloc(e->sads, sizeof(struct edid_sad) * e->sad_count); + + if (!e->speaker_alloc_bits && e->sad_count) { + /* make an assumption */ + if (e->sads[0].channels == 2) + e->speaker_alloc_bits = 0x1; } + /* squeeze lists */ +#define SQUEEZE(C, L) \ + if (!e->C) { free(e->L); e->L = NULL; } \ + else { e->L = realloc(e->L, sizeof(e->L[0]) * (e->C)); } + + SQUEEZE(dtd_count, dtds); + SQUEEZE(cea_block_count, cea_blocks); + SQUEEZE(svd_count, svds); + SQUEEZE(sad_count, sads); + SQUEEZE(did_block_count, did_blocks); + SQUEEZE(didt_count, didts); return e; } @@ -528,20 +653,26 @@ edid_dump_hex_done: const char *edid_standard(int std) { switch(std) { - case 0: return N_("VESA EDID"); - case 1: return N_("EIA/CEA-861"); - case 2: return N_("VESA DisplayID"); + case STD_EDID: return N_("VESA EDID"); + case STD_EEDID: return N_("VESA E-EDID"); + case STD_EIACEA861: return N_("EIA/CEA-861"); + case STD_DISPLAYID: return N_("VESA DisplayID"); + case STD_DISPLAYID20: return N_("VESA DisplayID 2.0"); }; return N_("unknown"); } const char *edid_output_src(int src) { switch(src) { - case 0: return N_("VESA EDID"); - case 1: return N_("VESA EDID ETB"); - case 2: return N_("VESA EDID STD"); - case 3: return N_("VESA EDID DTD"); - case 4: return N_("EIA/CEA-861 DTD"); + case OUTSRC_EDID: return N_("VESA EDID"); + case OUTSRC_ETB: return N_("VESA EDID ETB"); + case OUTSRC_STD: return N_("VESA EDID STD"); + case OUTSRC_DTD: return N_("VESA EDID DTD"); + case OUTSRC_CEA_DTD: return N_("EIA/CEA-861 DTD"); + case OUTSRC_SVD: return N_("EIA/CEA-861 SVD"); + case OUTSRC_DID_TYPE_I: return N_("DisplayID Type I"); + case OUTSRC_DID_TYPE_VI: return N_("DisplayID Type VI"); + case OUTSRC_DID_TYPE_VII: return N_("DisplayID Type VII"); }; return N_("unknown"); } @@ -592,6 +723,46 @@ const char *edid_cea_block_type(int type) { return N_("unknown type"); } +const char *edid_did_block_type(int type) { + switch(type) { + /* 1.x */ + case 0x00: return N_("Product Identification (1.x)"); + case 0x01: return N_("Display Parameters (1.x)"); + case 0x02: return N_("Color Characteristics (1.x)"); + case 0x03: return N_("Type I Timing - Detailed (1.x)"); + case 0x04: return N_("Type II Timing - Detailed (1.x)"); + case 0x05: return N_("Type III Timing - Short (1.x)"); + case 0x06: return N_("Type IV Timing - DMT ID Code (1.x)"); + case 0x07: return N_("VESA Timing Standard (1.x)"); + case 0x08: return N_("CEA Timing Standard (1.x)"); + case 0x09: return N_("Video Timing Range (1.x)"); + case 0x0A: return N_("Product Serial Number (1.x)"); + case 0x0B: return N_("General Purpose ASCII String (1.x)"); + case 0x0C: return N_("Display Device Data (1.x)"); + case 0x0D: return N_("Interface Power Sequencing (1.x)"); + case 0x0E: return N_("Transfer Characteristics (1.x)"); + case 0x0F: return N_("Display Interface Data (1.x)"); + case 0x10: return N_("Stereo Display Interface (1.x)"); + case 0x11: return N_("Type V Timing - Short (1.x)"); + case 0x13: return N_("Type VI Timing - Detailed (1.x)"); + case 0x7F: return N_("Vendor specific (1.x)"); + /* 2.x */ + case 0x20: return N_("Product Identification"); + case 0x21: return N_("Display Parameters"); + case 0x22: return N_("Type VII - Detailed Timing"); + case 0x23: return N_("Type VIII - Enumerated Timing Code"); + case 0x24: return N_("Type IX - Formula-based Timing"); + case 0x25: return N_("Dynamic Video Timing Range Limits"); + case 0x26: return N_("Display Interface Features"); + case 0x27: return N_("Stereo Display Interface"); + case 0x28: return N_("Tiled Display Topology"); + case 0x29: return N_("ContainerID"); + case 0x7E: return N_("Vendor specific"); + case 0x81: return N_("CTA DisplayID"); + } + return N_("unknown type"); +} + const char *edid_ext_block_type(int type) { switch(type) { case 0x00: @@ -735,6 +906,23 @@ char *edid_cea_block_describe(struct edid_cea_block *blk) { return ret; } +char *edid_did_block_describe(DisplayIDBlock *blk) { + gchar *ret = NULL; + if (blk) { + char *hb = hex_bytes(blk->ptr, blk->len+3); + switch(blk->type) { + default: + ret = g_strdup_printf("([%02x:r%02x] %s) len:%d -- %s", + blk->type, blk->revision, _(edid_did_block_type(blk->type)), + blk->len, + hb); + break; + } + free(hb); + } + return ret; +} + char *edid_output_describe(edid_output *out) { gchar *ret = NULL; if (out) { @@ -837,6 +1025,7 @@ char *edid_dump2(edid *e) { for(i = 0; i < e->cea_block_count; i++) { char *desc = edid_cea_block_describe(&e->cea_blocks[i]); ret = appfnl(ret, "cea_block[%d] %s", i, desc); + free(desc); } for(i = 0; i < e->svd_count; i++) { @@ -853,6 +1042,18 @@ char *edid_dump2(edid *e) { free(desc); } + for(i = 0; i < e->did_block_count; i++) { + char *desc = edid_did_block_describe(&e->did_blocks[i]); + ret = appfnl(ret, "did_block[%d] %s", i, desc); + free(desc); + } + + for(i = 0; i < e->didt_count; i++) { + char *desc = edid_output_describe(&e->didts[i]); + ret = appfnl(ret, "did_timing[%d]: %s", i, desc); + g_free(desc); + } + return ret; } |