Home | History | Annotate | Line # | Download | only in videomode
edid.c revision 1.3
      1  1.3  gdamore /* $NetBSD: edid.c,v 1.3 2006/05/13 00:39:19 gdamore Exp $ */
      2  1.1  gdamore 
      3  1.1  gdamore /*-
      4  1.1  gdamore  * Copyright (c) 2006 Itronix Inc.
      5  1.1  gdamore  * All rights reserved.
      6  1.1  gdamore  *
      7  1.1  gdamore  * Written by Garrett D'Amore for Itronix Inc.
      8  1.1  gdamore  *
      9  1.1  gdamore  * Redistribution and use in source and binary forms, with or without
     10  1.1  gdamore  * modification, are permitted provided that the following conditions
     11  1.1  gdamore  * are met:
     12  1.1  gdamore  * 1. Redistributions of source code must retain the above copyright
     13  1.1  gdamore  *    notice, this list of conditions and the following disclaimer.
     14  1.1  gdamore  * 2. Redistributions in binary form must reproduce the above copyright
     15  1.1  gdamore  *    notice, this list of conditions and the following disclaimer in the
     16  1.1  gdamore  *    documentation and/or other materials provided with the distribution.
     17  1.1  gdamore  * 3. The name of Itronix Inc. may not be used to endorse
     18  1.1  gdamore  *    or promote products derived from this software without specific
     19  1.1  gdamore  *    prior written permission.
     20  1.1  gdamore  *
     21  1.1  gdamore  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND ANY EXPRESS
     22  1.1  gdamore  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     23  1.1  gdamore  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  1.1  gdamore  * ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
     25  1.1  gdamore  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  1.1  gdamore  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     27  1.1  gdamore  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     28  1.1  gdamore  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     29  1.1  gdamore  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     30  1.1  gdamore  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     31  1.1  gdamore  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     32  1.1  gdamore  */
     33  1.1  gdamore 
     34  1.1  gdamore #include <sys/cdefs.h>
     35  1.3  gdamore __KERNEL_RCSID(0, "$NetBSD: edid.c,v 1.3 2006/05/13 00:39:19 gdamore Exp $");
     36  1.1  gdamore 
     37  1.1  gdamore #include <sys/param.h>
     38  1.1  gdamore #include <sys/systm.h>
     39  1.1  gdamore #include <sys/device.h>
     40  1.1  gdamore #include <sys/kernel.h>
     41  1.1  gdamore #include <sys/malloc.h>
     42  1.1  gdamore #include <dev/videomode/videomode.h>
     43  1.1  gdamore #include <dev/videomode/ediddevs.h>
     44  1.1  gdamore #include <dev/videomode/edidreg.h>
     45  1.1  gdamore #include <dev/videomode/edidvar.h>
     46  1.1  gdamore #include <dev/videomode/vesagtf.h>
     47  1.1  gdamore 
     48  1.1  gdamore #define	EDIDVERBOSE	1
     49  1.1  gdamore #define	DIVIDE(x,y)	(((x) + ((y) / 2)) / (y))
     50  1.1  gdamore 
     51  1.1  gdamore static const char *_edid_modes[] =  {
     52  1.3  gdamore 	"1280x1024x75",
     53  1.3  gdamore 	"1024x768x75",
     54  1.3  gdamore 	"1024x768x70",
     55  1.3  gdamore 	"1024x768x60",
     56  1.3  gdamore 	"1024x768x87i",
     57  1.3  gdamore 	"832x768x74",	/* rounding error, 74.55 Hz aka "832x624x75" */
     58  1.3  gdamore 	"800x600x75",
     59  1.3  gdamore 	"800x600x72",
     60  1.3  gdamore 	"800x600x60",
     61  1.3  gdamore 	"800x600x56",
     62  1.3  gdamore 	"640x480x75",
     63  1.3  gdamore 	"640x480x72",
     64  1.3  gdamore 	"640x480x67",
     65  1.3  gdamore 	"640x480x60",
     66  1.3  gdamore 	"720x400x85",	/* should this really be "720x400x88" ? */
     67  1.1  gdamore 	"720x400x70",	/* hmm... videmode.c doesn't have this one */
     68  1.1  gdamore };
     69  1.1  gdamore 
     70  1.1  gdamore #ifdef	EDIDVERBOSE
     71  1.1  gdamore struct edid_vendor {
     72  1.1  gdamore 	const char	*vendor;
     73  1.1  gdamore 	const char	*name;
     74  1.1  gdamore };
     75  1.1  gdamore 
     76  1.1  gdamore struct edid_product {
     77  1.1  gdamore 	const char	*vendor;
     78  1.1  gdamore 	uint16_t	product;
     79  1.1  gdamore 	const char	*name;
     80  1.1  gdamore };
     81  1.1  gdamore 
     82  1.1  gdamore #include <dev/videomode/ediddevs_data.h>
     83  1.1  gdamore #endif	/* EDIDVERBOSE */
     84  1.1  gdamore 
     85  1.1  gdamore static const char *
     86  1.1  gdamore edid_findvendor(const char *vendor)
     87  1.1  gdamore {
     88  1.1  gdamore #ifdef	EDIDVERBOSE
     89  1.1  gdamore 	int	n;
     90  1.1  gdamore 
     91  1.1  gdamore 	for (n = 0; n < edid_nvendors; n++)
     92  1.1  gdamore 		if (memcmp(edid_vendors[n].vendor, vendor, 3) == 0)
     93  1.1  gdamore 			return (edid_vendors[n].name);
     94  1.1  gdamore #endif
     95  1.1  gdamore 	return NULL;
     96  1.1  gdamore }
     97  1.1  gdamore 
     98  1.1  gdamore static const char *
     99  1.1  gdamore edid_findproduct(const char *vendor, uint16_t product)
    100  1.1  gdamore {
    101  1.1  gdamore #ifdef	EDIDVERBOSE
    102  1.1  gdamore 	int	n;
    103  1.1  gdamore 
    104  1.1  gdamore 	for (n = 0; n < edid_nvendors; n++)
    105  1.1  gdamore 		if ((edid_products[n].product == product) &&
    106  1.1  gdamore 		    (memcmp(edid_products[n].vendor, vendor, 3) == 0))
    107  1.1  gdamore 			return (edid_products[n].name);
    108  1.1  gdamore #endif	/* EDIDVERBOSE */
    109  1.1  gdamore 	return NULL;
    110  1.1  gdamore 
    111  1.1  gdamore }
    112  1.1  gdamore 
    113  1.1  gdamore static void
    114  1.1  gdamore edid_strchomp(char *ptr)
    115  1.1  gdamore {
    116  1.1  gdamore 	for (;;) {
    117  1.1  gdamore 		switch (*ptr) {
    118  1.1  gdamore 		case 0:
    119  1.1  gdamore 			return;
    120  1.1  gdamore 		case '\r':
    121  1.1  gdamore 		case '\n':
    122  1.1  gdamore 			*ptr = 0;
    123  1.1  gdamore 			return;
    124  1.1  gdamore 		}
    125  1.1  gdamore 		ptr++;
    126  1.1  gdamore 	}
    127  1.1  gdamore }
    128  1.1  gdamore 
    129  1.1  gdamore int
    130  1.1  gdamore edid_is_valid(uint8_t *d)
    131  1.1  gdamore {
    132  1.1  gdamore 	int sum = 0, i;
    133  1.1  gdamore 	uint8_t sig[8] = EDID_SIGNATURE;
    134  1.1  gdamore 
    135  1.1  gdamore 	if (memcmp(d, sig, 8) != 0)
    136  1.1  gdamore 		return EINVAL;
    137  1.1  gdamore 
    138  1.1  gdamore 	for (i = 0; i < 128; i++)
    139  1.1  gdamore 		sum += d[i];
    140  1.1  gdamore 	if ((sum & 0xff) != 0)
    141  1.1  gdamore 		return EINVAL;
    142  1.1  gdamore 
    143  1.1  gdamore 	return 0;
    144  1.1  gdamore }
    145  1.1  gdamore 
    146  1.1  gdamore void
    147  1.1  gdamore edid_print(struct edid_info *edid)
    148  1.1  gdamore {
    149  1.1  gdamore 	int	i;
    150  1.1  gdamore 
    151  1.1  gdamore 	if (edid == NULL)
    152  1.1  gdamore 		return;
    153  1.1  gdamore 	printf("Vendor: [%s] %s\n", edid->edid_vendor, edid->edid_vendorname);
    154  1.1  gdamore 	printf("Product: [%04X] %s\n", edid->edid_product,
    155  1.1  gdamore 	    edid->edid_productname);
    156  1.1  gdamore 	printf("Serial number: %s\n", edid->edid_serial);
    157  1.1  gdamore 	printf("Manufactured %d Week %d\n",
    158  1.1  gdamore 	    edid->edid_year, edid->edid_week);
    159  1.1  gdamore 	printf("EDID Version %d.%d\n", edid->edid_version,
    160  1.1  gdamore 	    edid->edid_revision);
    161  1.1  gdamore 	printf("EDID Comment: %s\n", edid->edid_comment);
    162  1.1  gdamore 
    163  1.1  gdamore 	printf("Video Input: %x\n", edid->edid_video_input);
    164  1.1  gdamore 	if (edid->edid_video_input & EDID_VIDEO_INPUT_DIGITAL) {
    165  1.1  gdamore 		printf("\tDigital");
    166  1.1  gdamore 		if (edid->edid_video_input & EDID_VIDEO_INPUT_DFP1_COMPAT)
    167  1.1  gdamore 			printf(" (DFP 1.x compatible)");
    168  1.1  gdamore 		printf("\n");
    169  1.1  gdamore 	} else {
    170  1.1  gdamore 		printf("\tAnalog\n");
    171  1.1  gdamore 		switch (EDID_VIDEO_INPUT_LEVEL(edid->edid_video_input)) {
    172  1.1  gdamore 		case 0:
    173  1.1  gdamore 			printf("\t-0.7, 0.3V\n");
    174  1.1  gdamore 			break;
    175  1.1  gdamore 		case 1:
    176  1.1  gdamore 			printf("\t-0.714, 0.286V\n");
    177  1.1  gdamore 			break;
    178  1.1  gdamore 		case 2:
    179  1.1  gdamore 			printf("\t-1.0, 0.4V\n");
    180  1.1  gdamore 			break;
    181  1.1  gdamore 		case 3:
    182  1.1  gdamore 			printf("\t-0.7, 0.0V\n");
    183  1.1  gdamore 			break;
    184  1.1  gdamore 		}
    185  1.1  gdamore 		if (edid->edid_video_input & EDID_VIDEO_INPUT_BLANK_TO_BLACK)
    186  1.1  gdamore 			printf("\tBlank-to-black setup\n");
    187  1.1  gdamore 		if (edid->edid_video_input & EDID_VIDEO_INPUT_SEPARATE_SYNCS)
    188  1.1  gdamore 			printf("\tSeperate syncs\n");
    189  1.1  gdamore 		if (edid->edid_video_input & EDID_VIDEO_INPUT_COMPOSITE_SYNC)
    190  1.1  gdamore 			printf("\tComposite sync\n");
    191  1.1  gdamore 		if (edid->edid_video_input & EDID_VIDEO_INPUT_SYNC_ON_GRN)
    192  1.1  gdamore 			printf("\tSync on green\n");
    193  1.1  gdamore 		if (edid->edid_video_input & EDID_VIDEO_INPUT_SERRATION)
    194  1.1  gdamore 			printf("\tSerration vsync\n");
    195  1.1  gdamore 	}
    196  1.1  gdamore 
    197  1.1  gdamore 	printf("Gamma: %d.%02d\n",
    198  1.1  gdamore 	    edid->edid_gamma / 100, edid->edid_gamma % 100);
    199  1.1  gdamore 
    200  1.1  gdamore 	printf("Max Size: %d cm x %d cm\n",
    201  1.1  gdamore 	    edid->edid_max_hsize, edid->edid_max_vsize);
    202  1.1  gdamore 
    203  1.1  gdamore 	printf("Features: %x\n", edid->edid_features);
    204  1.1  gdamore 	if (edid->edid_features & EDID_FEATURES_STANDBY)
    205  1.1  gdamore 		printf("\tDPMS standby\n");
    206  1.1  gdamore 	if (edid->edid_features & EDID_FEATURES_SUSPEND)
    207  1.1  gdamore 		printf("\tDPMS suspend\n");
    208  1.1  gdamore 	if (edid->edid_features & EDID_FEATURES_ACTIVE_OFF)
    209  1.1  gdamore 		printf("\tDPMS active-off\n");
    210  1.1  gdamore 	switch (EDID_FEATURES_DISP_TYPE(edid->edid_features)) {
    211  1.1  gdamore 	case EDID_FEATURES_DISP_TYPE_MONO:
    212  1.1  gdamore 		printf("\tMonochrome\n");
    213  1.1  gdamore 		break;
    214  1.1  gdamore 	case EDID_FEATURES_DISP_TYPE_RGB:
    215  1.1  gdamore 		printf("\tRGB\n");
    216  1.1  gdamore 		break;
    217  1.1  gdamore 	case EDID_FEATURES_DISP_TYPE_NON_RGB:
    218  1.1  gdamore 		printf("\tMulticolor\n");
    219  1.1  gdamore 		break;
    220  1.1  gdamore 	case EDID_FEATURES_DISP_TYPE_UNDEFINED:
    221  1.1  gdamore 		printf("\tUndefined monitor type\n");
    222  1.1  gdamore 		break;
    223  1.1  gdamore 	}
    224  1.1  gdamore 	if (edid->edid_features & EDID_FEATURES_STD_COLOR)
    225  1.1  gdamore 		printf("\tStandard color space\n");
    226  1.1  gdamore 	if (edid->edid_features & EDID_FEATURES_PREFERRED_TIMING)
    227  1.1  gdamore 		printf("\tPreferred timing\n");
    228  1.1  gdamore 	if (edid->edid_features & EDID_FEATURES_DEFAULT_GTF)
    229  1.1  gdamore 		printf("\tDefault GTF supported\n");
    230  1.1  gdamore 
    231  1.1  gdamore 	printf("Chroma Info:\n");
    232  1.1  gdamore 	printf("\tRed X: 0.%03d\n", edid->edid_chroma.ec_redx);
    233  1.1  gdamore 	printf("\tRed Y: 0.%03d\n", edid->edid_chroma.ec_redy);
    234  1.1  gdamore 	printf("\tGrn X: 0.%03d\n", edid->edid_chroma.ec_greenx);
    235  1.1  gdamore 	printf("\tGrn Y: 0.%03d\n", edid->edid_chroma.ec_greeny);
    236  1.1  gdamore 	printf("\tBlu X: 0.%03d\n", edid->edid_chroma.ec_bluex);
    237  1.1  gdamore 	printf("\tBlu Y: 0.%03d\n", edid->edid_chroma.ec_bluey);
    238  1.1  gdamore 	printf("\tWht X: 0.%03d\n", edid->edid_chroma.ec_whitex);
    239  1.1  gdamore 	printf("\tWht Y: 0.%03d\n", edid->edid_chroma.ec_whitey);
    240  1.1  gdamore 
    241  1.1  gdamore 	if (edid->edid_have_range) {
    242  1.1  gdamore 		printf("Range:\n");
    243  1.1  gdamore 		printf("\tHorizontal: %d - %d kHz\n",
    244  1.1  gdamore 		    edid->edid_range.er_min_hfreq,
    245  1.1  gdamore 		    edid->edid_range.er_max_hfreq);
    246  1.1  gdamore 		printf("\tVertical: %d - %d Hz\n",
    247  1.1  gdamore 		    edid->edid_range.er_min_vfreq,
    248  1.1  gdamore 		    edid->edid_range.er_max_vfreq);
    249  1.1  gdamore 		printf("\tMax Dot Clock: %d MHz\n",
    250  1.1  gdamore 		    edid->edid_range.er_max_clock);
    251  1.1  gdamore 		if (edid->edid_range.er_have_gtf2) {
    252  1.1  gdamore 			printf("\tGTF2 hfreq: %d\n",
    253  1.1  gdamore 			    edid->edid_range.er_gtf2_hfreq);
    254  1.1  gdamore 			printf("\tGTF2 C: %d\n", edid->edid_range.er_gtf2_c);
    255  1.1  gdamore 			printf("\tGTF2 M: %d\n", edid->edid_range.er_gtf2_m);
    256  1.1  gdamore 			printf("\tGTF2 J: %d\n", edid->edid_range.er_gtf2_j);
    257  1.1  gdamore 			printf("\tGTF2 K: %d\n", edid->edid_range.er_gtf2_k);
    258  1.1  gdamore 		}
    259  1.1  gdamore 	}
    260  1.1  gdamore 	printf("Video modes:\n");
    261  1.1  gdamore 	for (i = 0; i < edid->edid_nmodes; i++) {
    262  1.1  gdamore 		printf("\t%dx%d @ %dHz\n",
    263  1.1  gdamore 		    edid->edid_modes[i].hdisplay,
    264  1.1  gdamore 		    edid->edid_modes[i].vdisplay,
    265  1.1  gdamore 		    DIVIDE(DIVIDE(edid->edid_modes[i].dot_clock * 1000,
    266  1.1  gdamore 			       edid->edid_modes[i].htotal),
    267  1.1  gdamore 			edid->edid_modes[i].vtotal));
    268  1.1  gdamore 	}
    269  1.1  gdamore 	if (edid->edid_preferred_mode)
    270  1.1  gdamore 		printf("Preferred mode: %dx%d @ %dHz\n",
    271  1.1  gdamore 		    edid->edid_preferred_mode->hdisplay,
    272  1.1  gdamore 		    edid->edid_preferred_mode->vdisplay,
    273  1.1  gdamore 		    DIVIDE(DIVIDE(edid->edid_preferred_mode->dot_clock * 1000,
    274  1.1  gdamore 			       edid->edid_preferred_mode->htotal),
    275  1.1  gdamore 			edid->edid_preferred_mode->vtotal));
    276  1.1  gdamore }
    277  1.1  gdamore 
    278  1.1  gdamore static const struct videomode *
    279  1.1  gdamore edid_mode_lookup_list(const char *name)
    280  1.1  gdamore {
    281  1.1  gdamore 	int	i;
    282  1.1  gdamore 
    283  1.1  gdamore 	for (i = 0; i < videomode_count; i++)
    284  1.1  gdamore 		if (strcmp(name, videomode_list[i].name) == 0)
    285  1.1  gdamore 			return &videomode_list[i];
    286  1.1  gdamore 	return NULL;
    287  1.1  gdamore }
    288  1.1  gdamore 
    289  1.1  gdamore static int
    290  1.1  gdamore edid_std_timing(uint8_t *data, struct videomode *vmp)
    291  1.1  gdamore {
    292  1.1  gdamore 	unsigned			x, y, f;
    293  1.1  gdamore 	const struct videomode		*lookup;
    294  1.1  gdamore 	char				name[80];
    295  1.1  gdamore 
    296  1.1  gdamore 	if ((data[0] == 1 && data[1] == 1) ||
    297  1.1  gdamore 	    (data[0] == 0 && data[1] == 0) ||
    298  1.1  gdamore 	    (data[0] == 0x20 && data[1] == 0x20))
    299  1.1  gdamore 		return 0;
    300  1.1  gdamore 
    301  1.1  gdamore 	x = EDID_STD_TIMING_HRES(data);
    302  1.1  gdamore 	switch (EDID_STD_TIMING_RATIO(data)) {
    303  1.1  gdamore 	case EDID_STD_TIMING_RATIO_16_10:
    304  1.1  gdamore 		y = x * 10 / 16;
    305  1.1  gdamore 		break;
    306  1.1  gdamore 	case EDID_STD_TIMING_RATIO_4_3:
    307  1.1  gdamore 		y = x * 3 / 4;
    308  1.1  gdamore 		break;
    309  1.1  gdamore 	case EDID_STD_TIMING_RATIO_5_4:
    310  1.1  gdamore 		y = x * 4 / 5;
    311  1.1  gdamore 		break;
    312  1.1  gdamore 	case EDID_STD_TIMING_RATIO_16_9:
    313  1.1  gdamore 	default:
    314  1.1  gdamore 		y = x * 9 / 16;
    315  1.1  gdamore 		break;
    316  1.1  gdamore 	}
    317  1.1  gdamore 	f = EDID_STD_TIMING_VFREQ(data);
    318  1.1  gdamore 
    319  1.1  gdamore 	/* first try to lookup the mode as a DMT timing */
    320  1.1  gdamore 	snprintf(name, sizeof (name), "%dx%dx%d", x, y, f);
    321  1.1  gdamore 	if ((lookup = edid_mode_lookup_list(name)) != NULL) {
    322  1.1  gdamore 		*vmp = *lookup;
    323  1.1  gdamore 	}
    324  1.1  gdamore 
    325  1.1  gdamore 	/* failing that, calculate it using gtf */
    326  1.1  gdamore 	else {
    327  1.1  gdamore 		/*
    328  1.1  gdamore 		 * Hmm. I'm not using alternate GTF timings, which
    329  1.1  gdamore 		 * could, in theory, be present.
    330  1.1  gdamore 		 */
    331  1.1  gdamore 		vesagtf_mode(x, y, f, vmp);
    332  1.1  gdamore 	}
    333  1.1  gdamore 	return 1;
    334  1.1  gdamore }
    335  1.1  gdamore 
    336  1.1  gdamore static int
    337  1.1  gdamore edid_det_timing(uint8_t *data, struct videomode *vmp)
    338  1.1  gdamore {
    339  1.1  gdamore 	unsigned	hactive, hblank, hsyncwid, hsyncoff;
    340  1.1  gdamore 	unsigned	vactive, vblank, vsyncwid, vsyncoff;
    341  1.1  gdamore 	uint8_t		flags;
    342  1.1  gdamore 
    343  1.1  gdamore 	flags = EDID_DET_TIMING_FLAGS(data);
    344  1.1  gdamore 
    345  1.1  gdamore 	/* we don't support stereo modes (for now) */
    346  1.1  gdamore 	if (flags & (EDID_DET_TIMING_FLAG_STEREO |
    347  1.1  gdamore 		EDID_DET_TIMING_FLAG_STEREO1))
    348  1.1  gdamore 		return 0;
    349  1.1  gdamore 
    350  1.1  gdamore 	vmp->dot_clock = EDID_DET_TIMING_DOT_CLOCK(data) / 1000;
    351  1.1  gdamore 
    352  1.1  gdamore 	hactive = EDID_DET_TIMING_HACTIVE(data);
    353  1.1  gdamore 	hblank = EDID_DET_TIMING_HBLANK(data);
    354  1.1  gdamore 	hsyncwid = EDID_DET_TIMING_HSYNC_WIDTH(data);
    355  1.1  gdamore 	hsyncoff = EDID_DET_TIMING_HSYNC_OFFSET(data);
    356  1.1  gdamore 
    357  1.1  gdamore 	vactive = EDID_DET_TIMING_VACTIVE(data);
    358  1.1  gdamore 	vblank = EDID_DET_TIMING_VBLANK(data);
    359  1.1  gdamore 	vsyncwid = EDID_DET_TIMING_VSYNC_WIDTH(data);
    360  1.1  gdamore 	vsyncoff = EDID_DET_TIMING_VSYNC_OFFSET(data);
    361  1.1  gdamore 
    362  1.1  gdamore 	/* XXX: I'm not doing anything with the borders, should I? */
    363  1.1  gdamore 
    364  1.1  gdamore 	vmp->hdisplay = hactive;
    365  1.1  gdamore 	vmp->htotal = hactive + hblank;
    366  1.1  gdamore 	vmp->hsync_start = hactive + hsyncoff;
    367  1.1  gdamore 	vmp->hsync_end = vmp->hsync_start + hsyncwid;
    368  1.1  gdamore 
    369  1.1  gdamore 	vmp->vdisplay = vactive;
    370  1.1  gdamore 	vmp->vtotal = vactive + vblank;
    371  1.1  gdamore 	vmp->vsync_start = vactive + vsyncoff;
    372  1.1  gdamore 	vmp->vsync_end = vmp->vsync_start + vsyncwid;
    373  1.1  gdamore 
    374  1.1  gdamore 	vmp->flags = 0;
    375  1.1  gdamore 
    376  1.1  gdamore 	if (flags & EDID_DET_TIMING_FLAG_INTERLACE)
    377  1.1  gdamore 		vmp->flags |= VID_INTERLACE;
    378  1.1  gdamore 	if (flags & EDID_DET_TIMING_FLAG_HSYNC_POSITIVE)
    379  1.1  gdamore 		vmp->flags |= VID_PHSYNC;
    380  1.1  gdamore 	else
    381  1.1  gdamore 		vmp->flags |= VID_NHSYNC;
    382  1.1  gdamore 
    383  1.1  gdamore 	if (flags & EDID_DET_TIMING_FLAG_VSYNC_POSITIVE)
    384  1.1  gdamore 		vmp->flags |= VID_PVSYNC;
    385  1.1  gdamore 	else
    386  1.1  gdamore 		vmp->flags |= VID_NVSYNC;
    387  1.1  gdamore 
    388  1.1  gdamore 	return 1;
    389  1.1  gdamore }
    390  1.1  gdamore 
    391  1.1  gdamore static void
    392  1.1  gdamore edid_block(struct edid_info *edid, uint8_t *data)
    393  1.1  gdamore {
    394  1.1  gdamore 	int			i;
    395  1.1  gdamore 	struct videomode	mode;
    396  1.1  gdamore 
    397  1.1  gdamore 	if (EDID_BLOCK_IS_DET_TIMING(data)) {
    398  1.1  gdamore 		if (edid_det_timing(data, &mode)) {
    399  1.1  gdamore 			edid->edid_modes[edid->edid_nmodes] = mode;
    400  1.1  gdamore 			if (edid->edid_preferred_mode == NULL) {
    401  1.1  gdamore 				edid->edid_preferred_mode =
    402  1.1  gdamore 				    &edid->edid_modes[edid->edid_nmodes];
    403  1.1  gdamore 			}
    404  1.1  gdamore 			edid->edid_nmodes++;
    405  1.1  gdamore 		}
    406  1.1  gdamore 		return;
    407  1.1  gdamore 	}
    408  1.1  gdamore 
    409  1.1  gdamore 	switch (EDID_BLOCK_TYPE(data)) {
    410  1.1  gdamore 	case EDID_DESC_BLOCK_TYPE_SERIAL:
    411  1.1  gdamore 		memcpy(edid->edid_serial,
    412  1.1  gdamore 		    data + EDID_DESC_ASCII_DATA_OFFSET,
    413  1.1  gdamore 		    EDID_DESC_ASCII_DATA_LEN);
    414  1.1  gdamore 		edid->edid_serial[sizeof (edid->edid_serial) - 1] = 0;
    415  1.1  gdamore 		break;
    416  1.1  gdamore 
    417  1.1  gdamore 	case EDID_DESC_BLOCK_TYPE_ASCII:
    418  1.1  gdamore 		memcpy(edid->edid_comment,
    419  1.1  gdamore 		    data + EDID_DESC_ASCII_DATA_OFFSET,
    420  1.1  gdamore 		    EDID_DESC_ASCII_DATA_LEN);
    421  1.1  gdamore 		edid->edid_comment[sizeof (edid->edid_comment) - 1] = 0;
    422  1.1  gdamore 		break;
    423  1.1  gdamore 
    424  1.1  gdamore 	case EDID_DESC_BLOCK_TYPE_RANGE:
    425  1.1  gdamore 		edid->edid_have_range = 1;
    426  1.1  gdamore 		edid->edid_range.er_min_vfreq =
    427  1.1  gdamore 		    EDID_DESC_RANGE_MIN_VFREQ(data);
    428  1.1  gdamore 		edid->edid_range.er_max_vfreq =
    429  1.1  gdamore 		    EDID_DESC_RANGE_MAX_VFREQ(data);
    430  1.1  gdamore 		edid->edid_range.er_min_hfreq =
    431  1.1  gdamore 		    EDID_DESC_RANGE_MIN_HFREQ(data);
    432  1.1  gdamore 		edid->edid_range.er_max_hfreq =
    433  1.1  gdamore 		    EDID_DESC_RANGE_MAX_HFREQ(data);
    434  1.1  gdamore 		edid->edid_range.er_max_clock =
    435  1.1  gdamore 		    EDID_DESC_RANGE_MAX_CLOCK(data);
    436  1.1  gdamore 		if (EDID_DESC_RANGE_HAVE_GTF2(data)) {
    437  1.1  gdamore 			edid->edid_range.er_have_gtf2 = 1;
    438  1.1  gdamore 			edid->edid_range.er_gtf2_hfreq =
    439  1.1  gdamore 			    EDID_DESC_RANGE_GTF2_HFREQ(data);
    440  1.1  gdamore 			edid->edid_range.er_gtf2_c =
    441  1.1  gdamore 			    EDID_DESC_RANGE_GTF2_C(data);
    442  1.1  gdamore 			edid->edid_range.er_gtf2_m =
    443  1.1  gdamore 			    EDID_DESC_RANGE_GTF2_M(data);
    444  1.1  gdamore 			edid->edid_range.er_gtf2_j =
    445  1.1  gdamore 			    EDID_DESC_RANGE_GTF2_J(data);
    446  1.1  gdamore 			edid->edid_range.er_gtf2_k =
    447  1.1  gdamore 			    EDID_DESC_RANGE_GTF2_K(data);
    448  1.1  gdamore 		}
    449  1.1  gdamore 		break;
    450  1.1  gdamore 
    451  1.1  gdamore 	case EDID_DESC_BLOCK_TYPE_NAME:
    452  1.1  gdamore 		/* copy the product name into place */
    453  1.1  gdamore 		memcpy(edid->edid_productname,
    454  1.1  gdamore 		    data + EDID_DESC_ASCII_DATA_OFFSET,
    455  1.1  gdamore 		    EDID_DESC_ASCII_DATA_LEN);
    456  1.1  gdamore 		break;
    457  1.1  gdamore 
    458  1.1  gdamore 	case EDID_DESC_BLOCK_TYPE_STD_TIMING:
    459  1.1  gdamore 		data += EDID_DESC_STD_TIMING_START;
    460  1.1  gdamore 		for (i = 0; i < EDID_DESC_STD_TIMING_COUNT; i++) {
    461  1.1  gdamore 			if (edid_std_timing(data, &mode)) {
    462  1.1  gdamore 				edid->edid_modes[edid->edid_nmodes] = mode;
    463  1.1  gdamore 				edid->edid_nmodes++;
    464  1.1  gdamore 			}
    465  1.1  gdamore 			data += 2;
    466  1.1  gdamore 		}
    467  1.1  gdamore 		break;
    468  1.1  gdamore 
    469  1.1  gdamore 	case EDID_DESC_BLOCK_TYPE_COLOR_POINT:
    470  1.1  gdamore 		/* XXX: not implemented yet */
    471  1.1  gdamore 		break;
    472  1.1  gdamore 	}
    473  1.1  gdamore }
    474  1.1  gdamore 
    475  1.1  gdamore /*
    476  1.1  gdamore  * Gets EDID version in BCD, e.g. EDID v1.3  returned as 0x0103
    477  1.1  gdamore  */
    478  1.2  gdamore int
    479  1.2  gdamore edid_parse(uint8_t *data, struct edid_info *edid)
    480  1.1  gdamore {
    481  1.1  gdamore 	uint16_t		manfid, estmodes;
    482  1.1  gdamore 	const struct videomode	*vmp;
    483  1.1  gdamore 	int			i;
    484  1.1  gdamore 	const char		*name;
    485  1.1  gdamore 
    486  1.1  gdamore 	if (edid_is_valid(data) != 0)
    487  1.2  gdamore 		return -1;
    488  1.1  gdamore 
    489  1.1  gdamore 	/* get product identification */
    490  1.1  gdamore 	manfid = EDID_VENDOR_ID(data);
    491  1.1  gdamore 	edid->edid_vendor[0] = EDID_MANFID_0(manfid);
    492  1.1  gdamore 	edid->edid_vendor[1] = EDID_MANFID_1(manfid);
    493  1.1  gdamore 	edid->edid_vendor[2] = EDID_MANFID_2(manfid);
    494  1.1  gdamore 	edid->edid_vendor[3] = 0;	/* null terminate for convenience */
    495  1.1  gdamore 
    496  1.1  gdamore 	edid->edid_product = data[EDID_OFFSET_PRODUCT_ID] +
    497  1.1  gdamore 	    (data[EDID_OFFSET_PRODUCT_ID + 1] << 8);
    498  1.1  gdamore 
    499  1.1  gdamore 	name = edid_findvendor(edid->edid_vendor);
    500  1.1  gdamore 	if (name != NULL) {
    501  1.1  gdamore 		snprintf(edid->edid_vendorname,
    502  1.1  gdamore 		    sizeof (edid->edid_vendorname), "%s", name);
    503  1.1  gdamore 	}
    504  1.1  gdamore 	edid->edid_vendorname[sizeof (edid->edid_vendorname) - 1] = 0;
    505  1.1  gdamore 
    506  1.1  gdamore 	name = edid_findproduct(edid->edid_vendor, edid->edid_product);
    507  1.1  gdamore 	if (name != NULL) {
    508  1.1  gdamore 		snprintf(edid->edid_productname,
    509  1.1  gdamore 		    sizeof (edid->edid_productname), "%s", name);
    510  1.1  gdamore 	}
    511  1.1  gdamore 	edid->edid_productname[sizeof (edid->edid_productname) - 1] = 0;
    512  1.1  gdamore 
    513  1.1  gdamore 	snprintf(edid->edid_serial, sizeof (edid->edid_serial), "%08x",
    514  1.1  gdamore 	    EDID_SERIAL_NUMBER(data));
    515  1.1  gdamore 
    516  1.1  gdamore 	edid->edid_week = EDID_WEEK(data);
    517  1.1  gdamore 	edid->edid_year = EDID_YEAR(data);
    518  1.1  gdamore 
    519  1.1  gdamore 	/* get edid revision */
    520  1.1  gdamore 	edid->edid_version = EDID_VERSION(data);
    521  1.1  gdamore 	edid->edid_revision = EDID_REVISION(data);
    522  1.1  gdamore 
    523  1.1  gdamore 	edid->edid_video_input = EDID_VIDEO_INPUT(data);
    524  1.1  gdamore 	edid->edid_max_hsize = EDID_MAX_HSIZE(data);
    525  1.1  gdamore 	edid->edid_max_vsize = EDID_MAX_VSIZE(data);
    526  1.1  gdamore 
    527  1.1  gdamore 	edid->edid_gamma = EDID_GAMMA(data);
    528  1.1  gdamore 	edid->edid_features = EDID_FEATURES(data);
    529  1.1  gdamore 
    530  1.1  gdamore 	edid->edid_chroma.ec_redx = EDID_CHROMA_REDX(data);
    531  1.1  gdamore 	edid->edid_chroma.ec_redy = EDID_CHROMA_REDX(data);
    532  1.1  gdamore 	edid->edid_chroma.ec_greenx = EDID_CHROMA_GREENX(data);
    533  1.1  gdamore 	edid->edid_chroma.ec_greeny = EDID_CHROMA_GREENY(data);
    534  1.1  gdamore 	edid->edid_chroma.ec_bluex = EDID_CHROMA_BLUEX(data);
    535  1.1  gdamore 	edid->edid_chroma.ec_bluey = EDID_CHROMA_BLUEY(data);
    536  1.1  gdamore 	edid->edid_chroma.ec_whitex = EDID_CHROMA_WHITEX(data);
    537  1.1  gdamore 	edid->edid_chroma.ec_whitey = EDID_CHROMA_WHITEY(data);
    538  1.1  gdamore 
    539  1.1  gdamore 	/* lookup established modes */
    540  1.1  gdamore 	estmodes = EDID_EST_TIMING(data);
    541  1.1  gdamore 	for (i = 0; i < 16; i++) {
    542  1.1  gdamore 		if (estmodes & (1 << i)) {
    543  1.1  gdamore 			vmp = edid_mode_lookup_list(_edid_modes[i]);
    544  1.1  gdamore 			if (vmp != NULL) {
    545  1.1  gdamore 				edid->edid_modes[edid->edid_nmodes] = *vmp;
    546  1.1  gdamore 				edid->edid_nmodes++;
    547  1.1  gdamore 			}
    548  1.1  gdamore 		}
    549  1.1  gdamore 	}
    550  1.1  gdamore 
    551  1.1  gdamore 	/* do standard timing section */
    552  1.1  gdamore 	for (i = 0; i < EDID_STD_TIMING_COUNT; i++) {
    553  1.1  gdamore 		struct videomode	mode;
    554  1.1  gdamore 		if (edid_std_timing(data + EDID_OFFSET_STD_TIMING + i * 2,
    555  1.1  gdamore 			&mode)) {
    556  1.1  gdamore 			edid->edid_modes[edid->edid_nmodes] = mode;
    557  1.1  gdamore 			edid->edid_nmodes++;
    558  1.1  gdamore 		}
    559  1.1  gdamore 	}
    560  1.1  gdamore 
    561  1.1  gdamore 	/* do detailed timings and descriptors */
    562  1.1  gdamore 	for (i = 0; i < EDID_BLOCK_COUNT; i++) {
    563  1.1  gdamore 		edid_block(edid, data + EDID_OFFSET_DESC_BLOCK +
    564  1.1  gdamore 		    i * EDID_BLOCK_SIZE);
    565  1.1  gdamore 	}
    566  1.1  gdamore 
    567  1.1  gdamore 	edid_strchomp(edid->edid_vendorname);
    568  1.1  gdamore 	edid_strchomp(edid->edid_productname);
    569  1.1  gdamore 	edid_strchomp(edid->edid_serial);
    570  1.1  gdamore 	edid_strchomp(edid->edid_comment);
    571  1.1  gdamore 
    572  1.2  gdamore 	return 0;
    573  1.1  gdamore }
    574  1.1  gdamore 
    575