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