cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

edid-parse.c (19643B)


      1/*
      2 * Copyright 2007 Red Hat, Inc.
      3 *
      4 * Permission is hereby granted, free of charge, to any person obtaining a
      5 * copy of this software and associated documentation files (the "Software"),
      6 * to deal in the Software without restriction, including without limitation
      7 * on the rights to use, copy, modify, merge, publish, distribute, sub
      8 * license, and/or sell copies of the Software, and to permit persons to whom
      9 * the Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice (including the next
     12 * paragraph) shall be included in all copies or substantial portions of the
     13 * Software.
     14 *
     15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
     18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
     19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     21 */
     22
     23/* Author: Soren Sandmann <sandmann@redhat.com> */
     24
     25#include "edid.h"
     26#include <stdlib.h>
     27#include <string.h>
     28#include <math.h>
     29#include <stdio.h>
     30
     31#define TRUE 1
     32#define FALSE 0
     33
     34static int
     35get_bit (int in, int bit)
     36{
     37    return (in & (1 << bit)) >> bit;
     38}
     39
     40static int
     41get_bits (int in, int begin, int end)
     42{
     43    int mask = (1 << (end - begin + 1)) - 1;
     44    
     45    return (in >> begin) & mask;
     46}
     47
     48static int
     49decode_header (const uchar *edid)
     50{
     51    if (memcmp (edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0)
     52	return TRUE;
     53    return FALSE;
     54}
     55
     56static int
     57decode_vendor_and_product_identification (const uchar *edid, MonitorInfo *info)
     58{
     59    int is_model_year;
     60    
     61    /* Manufacturer Code */
     62    info->manufacturer_code[0]  = get_bits (edid[0x08], 2, 6);
     63    info->manufacturer_code[1]  = get_bits (edid[0x08], 0, 1) << 3;
     64    info->manufacturer_code[1] |= get_bits (edid[0x09], 5, 7);
     65    info->manufacturer_code[2]  = get_bits (edid[0x09], 0, 4);
     66    info->manufacturer_code[3]  = '\0';
     67    
     68    info->manufacturer_code[0] += 'A' - 1;
     69    info->manufacturer_code[1] += 'A' - 1;
     70    info->manufacturer_code[2] += 'A' - 1;
     71
     72    /* Product Code */
     73    info->product_code = edid[0x0b] << 8 | edid[0x0a];
     74
     75    /* Serial Number */
     76    info->serial_number =
     77	edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | edid[0x0f] << 24;
     78
     79    /* Week and Year */
     80    is_model_year = FALSE;
     81    switch (edid[0x10])
     82    {
     83    case 0x00:
     84	info->production_week = -1;
     85	break;
     86
     87    case 0xff:
     88	info->production_week = -1;
     89	is_model_year = TRUE;
     90	break;
     91
     92    default:
     93	info->production_week = edid[0x10];
     94	break;
     95    }
     96
     97    if (is_model_year)
     98    {
     99	info->production_year = -1;
    100	info->model_year = 1990 + edid[0x11];
    101    }
    102    else
    103    {
    104	info->production_year = 1990 + edid[0x11];
    105	info->model_year = -1;
    106    }
    107
    108    return TRUE;
    109}
    110
    111static int
    112decode_edid_version (const uchar *edid, MonitorInfo *info)
    113{
    114    info->major_version = edid[0x12];
    115    info->minor_version = edid[0x13];
    116
    117    return TRUE;
    118}
    119
    120static int
    121decode_display_parameters (const uchar *edid, MonitorInfo *info)
    122{
    123    /* Digital vs Analog */
    124    info->is_digital = get_bit (edid[0x14], 7);
    125
    126    if (info->is_digital)
    127    {
    128	int bits;
    129	
    130	static const int bit_depth[8] =
    131	{
    132	    -1, 6, 8, 10, 12, 14, 16, -1
    133	};
    134
    135	static const Interface interfaces[6] =
    136	{
    137	    UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT
    138	};
    139
    140	bits = get_bits (edid[0x14], 4, 6);
    141	info->digital.bits_per_primary = bit_depth[bits];
    142
    143	bits = get_bits (edid[0x14], 0, 3);
    144	
    145	if (bits <= 5)
    146	    info->digital.interface = interfaces[bits];
    147	else
    148	    info->digital.interface = UNDEFINED;
    149    }
    150    else
    151    {
    152	int bits = get_bits (edid[0x14], 5, 6);
    153	
    154	static const double levels[][3] =
    155	{
    156	    { 0.7,   0.3,    1.0 },
    157	    { 0.714, 0.286,  1.0 },
    158	    { 1.0,   0.4,    1.4 },
    159	    { 0.7,   0.0,    0.7 },
    160	};
    161
    162	info->analog.video_signal_level = levels[bits][0];
    163	info->analog.sync_signal_level = levels[bits][1];
    164	info->analog.total_signal_level = levels[bits][2];
    165
    166	info->analog.blank_to_black = get_bit (edid[0x14], 4);
    167
    168	info->analog.separate_hv_sync = get_bit (edid[0x14], 3);
    169	info->analog.composite_sync_on_h = get_bit (edid[0x14], 2);
    170	info->analog.composite_sync_on_green = get_bit (edid[0x14], 1);
    171
    172	info->analog.serration_on_vsync = get_bit (edid[0x14], 0);
    173    }
    174
    175    /* Screen Size / Aspect Ratio */
    176    if (edid[0x15] == 0 && edid[0x16] == 0)
    177    {
    178	info->width_mm = -1;
    179	info->height_mm = -1;
    180	info->aspect_ratio = -1.0;
    181    }
    182    else if (edid[0x16] == 0)
    183    {
    184	info->width_mm = -1;
    185	info->height_mm = -1; 
    186	info->aspect_ratio = 100.0 / (edid[0x15] + 99);
    187    }
    188    else if (edid[0x15] == 0)
    189    {
    190	info->width_mm = -1;
    191	info->height_mm = -1;
    192	info->aspect_ratio = 100.0 / (edid[0x16] + 99);
    193	info->aspect_ratio = 1/info->aspect_ratio; /* portrait */
    194    }
    195    else
    196    {
    197	info->width_mm = 10 * edid[0x15];
    198	info->height_mm = 10 * edid[0x16];
    199    }
    200
    201    /* Gamma */
    202    if (edid[0x17] == 0xFF)
    203	info->gamma = -1.0;
    204    else
    205	info->gamma = (edid[0x17] + 100.0) / 100.0;
    206
    207    /* Features */
    208    info->standby = get_bit (edid[0x18], 7);
    209    info->suspend = get_bit (edid[0x18], 6);
    210    info->active_off = get_bit (edid[0x18], 5);
    211
    212    if (info->is_digital)
    213    {
    214	info->digital.rgb444 = TRUE;
    215	if (get_bit (edid[0x18], 3))
    216	    info->digital.ycrcb444 = 1;
    217	if (get_bit (edid[0x18], 4))
    218	    info->digital.ycrcb422 = 1;
    219    }
    220    else
    221    {
    222	int bits = get_bits (edid[0x18], 3, 4);
    223	ColorType color_type[4] =
    224	{
    225	    MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR
    226	};
    227
    228	info->analog.color_type = color_type[bits];
    229    }
    230
    231    info->srgb_is_standard = get_bit (edid[0x18], 2);
    232
    233    /* In 1.3 this is called "has preferred timing" */
    234    info->preferred_timing_includes_native = get_bit (edid[0x18], 1);
    235
    236    /* FIXME: In 1.3 this indicates whether the monitor accepts GTF */
    237    info->continuous_frequency = get_bit (edid[0x18], 0);
    238    return TRUE;
    239}
    240
    241static double
    242decode_fraction (int high, int low)
    243{
    244    double result = 0.0;
    245    int i;
    246
    247    high = (high << 2) | low;
    248
    249    for (i = 0; i < 10; ++i)
    250	result += get_bit (high, i) * pow (2, i - 10);
    251
    252    return result;
    253}
    254
    255static int
    256decode_color_characteristics (const uchar *edid, MonitorInfo *info)
    257{
    258    info->red_x = decode_fraction (edid[0x1b], get_bits (edid[0x19], 6, 7));
    259    info->red_y = decode_fraction (edid[0x1c], get_bits (edid[0x19], 5, 4));
    260    info->green_x = decode_fraction (edid[0x1d], get_bits (edid[0x19], 2, 3));
    261    info->green_y = decode_fraction (edid[0x1e], get_bits (edid[0x19], 0, 1));
    262    info->blue_x = decode_fraction (edid[0x1f], get_bits (edid[0x1a], 6, 7));
    263    info->blue_y = decode_fraction (edid[0x20], get_bits (edid[0x1a], 4, 5));
    264    info->white_x = decode_fraction (edid[0x21], get_bits (edid[0x1a], 2, 3));
    265    info->white_y = decode_fraction (edid[0x22], get_bits (edid[0x1a], 0, 1));
    266
    267    return TRUE;
    268}
    269
    270static int
    271decode_established_timings (const uchar *edid, MonitorInfo *info)
    272{
    273    static const Timing established[][8] = 
    274    {
    275	{
    276	    { 800, 600, 60 },
    277	    { 800, 600, 56 },
    278	    { 640, 480, 75 },
    279	    { 640, 480, 72 },
    280	    { 640, 480, 67 },
    281	    { 640, 480, 60 },
    282	    { 720, 400, 88 },
    283	    { 720, 400, 70 }
    284	},
    285	{
    286	    { 1280, 1024, 75 },
    287	    { 1024, 768, 75 },
    288	    { 1024, 768, 70 },
    289	    { 1024, 768, 60 },
    290	    { 1024, 768, 87 },
    291	    { 832, 624, 75 },
    292	    { 800, 600, 75 },
    293	    { 800, 600, 72 }
    294	},
    295	{
    296	    { 0, 0, 0 },
    297	    { 0, 0, 0 },
    298	    { 0, 0, 0 },
    299	    { 0, 0, 0 },
    300	    { 0, 0, 0 },
    301	    { 0, 0, 0 },
    302	    { 0, 0, 0 },
    303	    { 1152, 870, 75 }
    304	},
    305    };
    306
    307    int i, j, idx;
    308
    309    idx = 0;
    310    for (i = 0; i < 3; ++i)
    311    {
    312	for (j = 0; j < 8; ++j)
    313	{
    314	    int byte = edid[0x23 + i];
    315
    316	    if (get_bit (byte, j) && established[i][j].frequency != 0)
    317		info->established[idx++] = established[i][j];
    318	}
    319    }
    320    return TRUE;
    321}
    322
    323static int
    324decode_standard_timings (const uchar *edid, MonitorInfo *info)
    325{
    326    int i;
    327    
    328    for (i = 0; i < 8; i++)
    329    {
    330	int first = edid[0x26 + 2 * i];
    331	int second = edid[0x27 + 2 * i];
    332
    333	if (first != 0x01 && second != 0x01)
    334	{
    335	    int w = 8 * (first + 31);
    336	    int h = 0;
    337
    338	    switch (get_bits (second, 6, 7))
    339	    {
    340	    case 0x00: h = (w / 16) * 10; break;
    341	    case 0x01: h = (w / 4) * 3; break;
    342	    case 0x02: h = (w / 5) * 4; break;
    343	    case 0x03: h = (w / 16) * 9; break;
    344	    }
    345
    346	    info->standard[i].width = w;
    347	    info->standard[i].height = h;
    348	    info->standard[i].frequency = get_bits (second, 0, 5) + 60;
    349	}
    350    }
    351    
    352    return TRUE;
    353}
    354
    355static void
    356decode_lf_string (const uchar *s, int n_chars, char *result)
    357{
    358    int i;
    359    for (i = 0; i < n_chars; ++i)
    360    {
    361	if (s[i] == 0x0a)
    362	{
    363	    *result++ = '\0';
    364	    break;
    365	}
    366	else if (s[i] == 0x00)
    367	{
    368	    /* Convert embedded 0's to spaces */
    369	    *result++ = ' ';
    370	}
    371	else
    372	{
    373	    *result++ = s[i];
    374	}
    375    }
    376}
    377
    378static void
    379decode_display_descriptor (const uchar *desc,
    380			   MonitorInfo *info)
    381{
    382    switch (desc[0x03])
    383    {
    384    case 0xFC:
    385	decode_lf_string (desc + 5, 13, info->dsc_product_name);
    386	break;
    387    case 0xFF:
    388	decode_lf_string (desc + 5, 13, info->dsc_serial_number);
    389	break;
    390    case 0xFE:
    391	decode_lf_string (desc + 5, 13, info->dsc_string);
    392	break;
    393    case 0xFD:
    394	/* Range Limits */
    395	break;
    396    case 0xFB:
    397	/* Color Point */
    398	break;
    399    case 0xFA:
    400	/* Timing Identifications */
    401	break;
    402    case 0xF9:
    403	/* Color Management */
    404	break;
    405    case 0xF8:
    406	/* Timing Codes */
    407	break;
    408    case 0xF7:
    409	/* Established Timings */
    410	break;
    411    case 0x10:
    412	break;
    413    }
    414}
    415
    416static void
    417decode_detailed_timing (const uchar *timing,
    418			DetailedTiming *detailed)
    419{
    420    int bits;
    421    StereoType stereo[] =
    422    {
    423	NO_STEREO, NO_STEREO, FIELD_RIGHT, FIELD_LEFT,
    424	TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN,
    425	FOUR_WAY_INTERLEAVED, SIDE_BY_SIDE
    426    };
    427    
    428    detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000;
    429    detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4);
    430    detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8);
    431    detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4);
    432    detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8);
    433    detailed->h_front_porch = timing[0x08] | get_bits (timing[0x0b], 6, 7) << 8;
    434    detailed->h_sync = timing[0x09] | get_bits (timing[0x0b], 4, 5) << 8;
    435    detailed->v_front_porch =
    436	get_bits (timing[0x0a], 4, 7) | get_bits (timing[0x0b], 2, 3) << 4;
    437    detailed->v_sync =
    438	get_bits (timing[0x0a], 0, 3) | get_bits (timing[0x0b], 0, 1) << 4;
    439    detailed->width_mm =  timing[0x0c] | get_bits (timing[0x0e], 4, 7) << 8;
    440    detailed->height_mm = timing[0x0d] | get_bits (timing[0x0e], 0, 3) << 8;
    441    detailed->right_border = timing[0x0f];
    442    detailed->top_border = timing[0x10];
    443
    444    detailed->interlaced = get_bit (timing[0x11], 7);
    445
    446    /* Stereo */
    447    bits = get_bits (timing[0x11], 5, 6) << 1 | get_bit (timing[0x11], 0);
    448    detailed->stereo = stereo[bits];
    449
    450    /* Sync */
    451    bits = timing[0x11];
    452
    453    detailed->digital_sync = get_bit (bits, 4);
    454    if (detailed->digital_sync)
    455    {
    456	detailed->digital.composite = !get_bit (bits, 3);
    457
    458	if (detailed->digital.composite)
    459	{
    460	    detailed->digital.serrations = get_bit (bits, 2);
    461	    detailed->digital.negative_vsync = FALSE;
    462	}
    463	else
    464	{
    465	    detailed->digital.serrations = FALSE;
    466	    detailed->digital.negative_vsync = !get_bit (bits, 2);
    467	}
    468
    469	detailed->digital.negative_hsync = !get_bit (bits, 0);
    470    }
    471    else
    472    {
    473	detailed->analog.bipolar = get_bit (bits, 3);
    474	detailed->analog.serrations = get_bit (bits, 2);
    475	detailed->analog.sync_on_green = !get_bit (bits, 1);
    476    }
    477}
    478
    479static int
    480decode_descriptors (const uchar *edid, MonitorInfo *info)
    481{
    482    int i;
    483    int timing_idx;
    484    
    485    timing_idx = 0;
    486    
    487    for (i = 0; i < 4; ++i)
    488    {
    489	int index = 0x36 + i * 18;
    490
    491	if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00)
    492	{
    493	    decode_display_descriptor (edid + index, info);
    494	}
    495	else
    496	{
    497	    decode_detailed_timing (
    498		edid + index, &(info->detailed_timings[timing_idx++]));
    499	}
    500    }
    501
    502    info->n_detailed_timings = timing_idx;
    503
    504    return TRUE;
    505}
    506
    507static void
    508decode_check_sum (const uchar *edid,
    509		  MonitorInfo *info)
    510{
    511    int i;
    512    uchar check = 0;
    513
    514    for (i = 0; i < 128; ++i)
    515	check += edid[i];
    516
    517    info->checksum = check;
    518}
    519
    520MonitorInfo *
    521decode_edid (const uchar *edid)
    522{
    523    MonitorInfo *info = calloc (1, sizeof (MonitorInfo));
    524
    525    decode_check_sum (edid, info);
    526    
    527    if (!decode_header (edid) ||
    528        !decode_vendor_and_product_identification (edid, info) ||
    529        !decode_edid_version (edid, info) ||
    530        !decode_display_parameters (edid, info) ||
    531        !decode_color_characteristics (edid, info) ||
    532        !decode_established_timings (edid, info) ||
    533        !decode_standard_timings (edid, info) ||
    534        !decode_descriptors (edid, info)) {
    535        free(info);
    536	return NULL;
    537    }
    538    
    539    return info;
    540}
    541
    542static const char *
    543yesno (int v)
    544{
    545    return v? "yes" : "no";
    546}
    547
    548void
    549dump_monitor_info (MonitorInfo *info)
    550{
    551    int i;
    552    
    553    printf ("Checksum: %d (%s)\n",
    554	    info->checksum, info->checksum? "incorrect" : "correct");
    555    printf ("Manufacturer Code: %s\n", info->manufacturer_code);
    556    printf ("Product Code: 0x%x\n", info->product_code);
    557    printf ("Serial Number: %u\n", info->serial_number);
    558    
    559    if (info->production_week != -1)
    560	printf ("Production Week: %d\n", info->production_week);
    561    else
    562	printf ("Production Week: unspecified\n");
    563    
    564    if (info->production_year != -1)
    565	printf ("Production Year: %d\n", info->production_year);
    566    else
    567	printf ("Production Year: unspecified\n");
    568    
    569    if (info->model_year != -1)
    570	printf ("Model Year: %d\n", info->model_year);
    571    else
    572	printf ("Model Year: unspecified\n");
    573    
    574    printf ("EDID revision: %d.%d\n", info->major_version, info->minor_version);
    575    
    576    printf ("Display is %s\n", info->is_digital? "digital" : "analog");
    577    if (info->is_digital)
    578    {
    579	const char *interface;
    580	if (info->digital.bits_per_primary != -1)
    581	    printf ("Bits Per Primary: %d\n", info->digital.bits_per_primary);
    582	else
    583	    printf ("Bits Per Primary: undefined\n");
    584	
    585	switch (info->digital.interface)
    586	{
    587	case DVI: interface = "DVI"; break;
    588	case HDMI_A: interface = "HDMI-a"; break;
    589	case HDMI_B: interface = "HDMI-b"; break;
    590	case MDDI: interface = "MDDI"; break;
    591	case DISPLAY_PORT: interface = "DisplayPort"; break;
    592	case UNDEFINED: interface = "undefined"; break;
    593	default: interface = "unknown"; break;
    594	}
    595	printf ("Interface: %s\n", interface);
    596	
    597	printf ("RGB 4:4:4: %s\n", yesno (info->digital.rgb444));
    598	printf ("YCrCb 4:4:4: %s\n", yesno (info->digital.ycrcb444));
    599	printf ("YCrCb 4:2:2: %s\n", yesno (info->digital.ycrcb422));
    600    }
    601    else
    602    {
    603       const char *s;
    604	printf ("Video Signal Level: %f\n", info->analog.video_signal_level);
    605	printf ("Sync Signal Level: %f\n", info->analog.sync_signal_level);
    606	printf ("Total Signal Level: %f\n", info->analog.total_signal_level);
    607	
    608	printf ("Blank to Black: %s\n",
    609		yesno (info->analog.blank_to_black));
    610	printf ("Separate HV Sync: %s\n",
    611		yesno (info->analog.separate_hv_sync));
    612	printf ("Composite Sync on H: %s\n",
    613		yesno (info->analog.composite_sync_on_h));
    614	printf ("Serration on VSync: %s\n",
    615		yesno (info->analog.serration_on_vsync));
    616	
    617	switch (info->analog.color_type)
    618	{
    619	case UNDEFINED_COLOR: s = "undefined"; break;
    620	case MONOCHROME: s = "monochrome"; break;
    621	case RGB: s = "rgb"; break;
    622	case OTHER_COLOR: s = "other color"; break;
    623	default: s = "unknown"; break;
    624	};
    625	
    626	printf ("Color: %s\n", s);
    627    }
    628    
    629    if (info->width_mm == -1)
    630	printf ("Width: undefined\n");
    631    else
    632	printf ("Width: %d mm\n", info->width_mm);
    633    
    634    if (info->height_mm == -1)
    635	printf ("Height: undefined\n");
    636    else
    637	printf ("Height: %d mm\n", info->height_mm);
    638    
    639    if (info->aspect_ratio > 0)
    640	printf ("Aspect Ratio: %f\n", info->aspect_ratio);
    641    else
    642	printf ("Aspect Ratio: undefined\n");
    643    
    644    if (info->gamma >= 0)
    645	printf ("Gamma: %f\n", info->gamma);
    646    else
    647	printf ("Gamma: undefined\n");
    648    
    649    printf ("Standby: %s\n", yesno (info->standby));
    650    printf ("Suspend: %s\n", yesno (info->suspend));
    651    printf ("Active Off: %s\n", yesno (info->active_off));
    652    
    653    printf ("SRGB is Standard: %s\n", yesno (info->srgb_is_standard));
    654    printf ("Preferred Timing Includes Native: %s\n",
    655	    yesno (info->preferred_timing_includes_native));
    656    printf ("Continuous Frequency: %s\n", yesno (info->continuous_frequency));
    657    
    658    printf ("Red X: %f\n", info->red_x);
    659    printf ("Red Y: %f\n", info->red_y);
    660    printf ("Green X: %f\n", info->green_x);
    661    printf ("Green Y: %f\n", info->green_y);
    662    printf ("Blue X: %f\n", info->blue_x);
    663    printf ("Blue Y: %f\n", info->blue_y);
    664    printf ("White X: %f\n", info->white_x);
    665    printf ("White Y: %f\n", info->white_y);
    666    
    667    printf ("Established Timings:\n");
    668    
    669    for (i = 0; i < 24; ++i)
    670    {
    671	Timing *timing = &(info->established[i]);
    672	
    673	if (timing->frequency == 0)
    674	    break;
    675	
    676	printf ("  %d x %d @ %d Hz\n",
    677		timing->width, timing->height, timing->frequency);
    678	
    679    }
    680    
    681    printf ("Standard Timings:\n");
    682    for (i = 0; i < 8; ++i)
    683    {
    684	Timing *timing = &(info->standard[i]);
    685	
    686	if (timing->frequency == 0)
    687	    break;
    688	
    689	printf ("  %d x %d @ %d Hz\n",
    690		timing->width, timing->height, timing->frequency);
    691    }
    692    
    693    for (i = 0; i < info->n_detailed_timings; ++i)
    694    {
    695	DetailedTiming *timing = &(info->detailed_timings[i]);
    696	const char *s;
    697	
    698	printf ("Timing%s: \n",
    699		(i == 0 && info->preferred_timing_includes_native)?
    700		" (Preferred)" : "");
    701	printf ("  Pixel Clock: %d\n", timing->pixel_clock);
    702	printf ("  H Addressable: %d\n", timing->h_addr);
    703	printf ("  H Blank: %d\n", timing->h_blank);
    704	printf ("  H Front Porch: %d\n", timing->h_front_porch);
    705	printf ("  H Sync: %d\n", timing->h_sync);
    706	printf ("  V Addressable: %d\n", timing->v_addr);
    707	printf ("  V Blank: %d\n", timing->v_blank);
    708	printf ("  V Front Porch: %d\n", timing->v_front_porch);
    709	printf ("  V Sync: %d\n", timing->v_sync);
    710	printf ("  Width: %d mm\n", timing->width_mm);
    711	printf ("  Height: %d mm\n", timing->height_mm);
    712	printf ("  Right Border: %d\n", timing->right_border);
    713	printf ("  Top Border: %d\n", timing->top_border);
    714	switch (timing->stereo)
    715	{
    716	default:
    717	case NO_STEREO:   s = "No Stereo"; break;
    718	case FIELD_RIGHT: s = "Field Sequential, Right on Sync"; break;
    719	case FIELD_LEFT:  s = "Field Sequential, Left on Sync"; break;
    720	case TWO_WAY_RIGHT_ON_EVEN: s = "Two-way, Right on Even"; break;
    721	case TWO_WAY_LEFT_ON_EVEN:  s = "Two-way, Left on Even"; break;
    722	case FOUR_WAY_INTERLEAVED:  s = "Four-way Interleaved"; break;
    723	case SIDE_BY_SIDE:          s = "Side-by-Side"; break;
    724	}
    725	printf ("  Stereo: %s\n", s);
    726	
    727	if (timing->digital_sync)
    728	{
    729	    printf ("  Digital Sync:\n");
    730	    printf ("    composite: %s\n", yesno (timing->digital.composite));
    731	    printf ("    serrations: %s\n", yesno (timing->digital.serrations));
    732	    printf ("    negative vsync: %s\n",
    733		    yesno (timing->digital.negative_vsync));
    734	    printf ("    negative hsync: %s\n",
    735		    yesno (timing->digital.negative_hsync));
    736	}
    737	else
    738	{
    739	    printf ("  Analog Sync:\n");
    740	    printf ("    bipolar: %s\n", yesno (timing->analog.bipolar));
    741	    printf ("    serrations: %s\n", yesno (timing->analog.serrations));
    742	    printf ("    sync on green: %s\n", yesno (
    743			timing->analog.sync_on_green));
    744	}
    745    }
    746    
    747    printf ("Detailed Product information:\n");
    748    printf ("  Product Name: %s\n", info->dsc_product_name);
    749    printf ("  Serial Number: %s\n", info->dsc_serial_number);
    750    printf ("  Unspecified String: %s\n", info->dsc_string);
    751}
    752