Home | History | Annotate | Line # | Download | only in videomode
edid.c revision 1.16
      1  1.16   mlelstv /* $NetBSD: edid.c,v 1.16 2021/12/25 13:51:31 mlelstv 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.16   mlelstv __KERNEL_RCSID(0, "$NetBSD: edid.c,v 1.16 2021/12/25 13:51:31 mlelstv Exp $");
     36   1.1   gdamore 
     37  1.16   mlelstv #ifdef _KERNEL
     38   1.1   gdamore #include <sys/param.h>
     39   1.1   gdamore #include <sys/systm.h>
     40   1.1   gdamore #include <sys/device.h>
     41   1.1   gdamore #include <sys/kernel.h>
     42   1.1   gdamore #include <sys/malloc.h>
     43  1.16   mlelstv #else
     44  1.16   mlelstv #include <stdlib.h>
     45  1.16   mlelstv #include <inttypes.h>
     46  1.16   mlelstv #include <string.h>
     47  1.16   mlelstv #include <errno.h>
     48  1.16   mlelstv #include <stdio.h>
     49  1.16   mlelstv #endif
     50  1.16   mlelstv 
     51   1.1   gdamore #include <dev/videomode/videomode.h>
     52   1.1   gdamore #include <dev/videomode/ediddevs.h>
     53   1.1   gdamore #include <dev/videomode/edidreg.h>
     54   1.1   gdamore #include <dev/videomode/edidvar.h>
     55   1.1   gdamore #include <dev/videomode/vesagtf.h>
     56   1.1   gdamore 
     57   1.1   gdamore #define	EDIDVERBOSE	1
     58   1.1   gdamore #define	DIVIDE(x,y)	(((x) + ((y) / 2)) / (y))
     59   1.1   gdamore 
     60   1.7       jdc /* These are reversed established timing order */
     61   1.1   gdamore static const char *_edid_modes[] =  {
     62   1.3   gdamore 	"1280x1024x75",
     63   1.3   gdamore 	"1024x768x75",
     64   1.3   gdamore 	"1024x768x70",
     65   1.3   gdamore 	"1024x768x60",
     66   1.3   gdamore 	"1024x768x87i",
     67   1.7       jdc 	"832x624x74",	/* rounding error, 74.55 Hz aka "832x624x75" */
     68   1.3   gdamore 	"800x600x75",
     69   1.3   gdamore 	"800x600x72",
     70   1.3   gdamore 	"800x600x60",
     71   1.3   gdamore 	"800x600x56",
     72   1.3   gdamore 	"640x480x75",
     73   1.3   gdamore 	"640x480x72",
     74   1.3   gdamore 	"640x480x67",
     75   1.3   gdamore 	"640x480x60",
     76   1.7       jdc 	"720x400x87",	/* rounding error, 87.85 Hz aka "720x400x88" */
     77   1.7       jdc 	"720x400x70",
     78   1.1   gdamore };
     79   1.1   gdamore 
     80   1.1   gdamore #ifdef	EDIDVERBOSE
     81   1.1   gdamore struct edid_vendor {
     82   1.1   gdamore 	const char	*vendor;
     83   1.1   gdamore 	const char	*name;
     84   1.1   gdamore };
     85   1.1   gdamore 
     86   1.1   gdamore struct edid_product {
     87   1.1   gdamore 	const char	*vendor;
     88   1.1   gdamore 	uint16_t	product;
     89   1.1   gdamore 	const char	*name;
     90   1.1   gdamore };
     91   1.1   gdamore 
     92   1.1   gdamore #include <dev/videomode/ediddevs_data.h>
     93   1.1   gdamore #endif	/* EDIDVERBOSE */
     94   1.1   gdamore 
     95   1.1   gdamore static const char *
     96   1.1   gdamore edid_findvendor(const char *vendor)
     97   1.1   gdamore {
     98   1.1   gdamore #ifdef	EDIDVERBOSE
     99   1.1   gdamore 	int	n;
    100   1.1   gdamore 
    101   1.1   gdamore 	for (n = 0; n < edid_nvendors; n++)
    102   1.1   gdamore 		if (memcmp(edid_vendors[n].vendor, vendor, 3) == 0)
    103  1.10  christos 			return edid_vendors[n].name;
    104   1.1   gdamore #endif
    105   1.1   gdamore 	return NULL;
    106   1.1   gdamore }
    107   1.1   gdamore 
    108   1.1   gdamore static const char *
    109   1.1   gdamore edid_findproduct(const char *vendor, uint16_t product)
    110   1.1   gdamore {
    111   1.1   gdamore #ifdef	EDIDVERBOSE
    112   1.1   gdamore 	int	n;
    113   1.1   gdamore 
    114   1.6   tsutsui 	for (n = 0; n < edid_nproducts; n++)
    115  1.10  christos 		if (edid_products[n].product == product &&
    116  1.10  christos 		    memcmp(edid_products[n].vendor, vendor, 3) == 0)
    117  1.11  christos 			return edid_products[n].name;
    118   1.1   gdamore #endif	/* EDIDVERBOSE */
    119   1.1   gdamore 	return NULL;
    120   1.1   gdamore 
    121   1.1   gdamore }
    122   1.1   gdamore 
    123   1.1   gdamore static void
    124   1.1   gdamore edid_strchomp(char *ptr)
    125   1.1   gdamore {
    126   1.1   gdamore 	for (;;) {
    127   1.1   gdamore 		switch (*ptr) {
    128  1.10  christos 		case '\0':
    129   1.1   gdamore 			return;
    130   1.1   gdamore 		case '\r':
    131   1.1   gdamore 		case '\n':
    132  1.10  christos 			*ptr = '\0';
    133   1.1   gdamore 			return;
    134   1.1   gdamore 		}
    135   1.1   gdamore 		ptr++;
    136   1.1   gdamore 	}
    137   1.1   gdamore }
    138   1.1   gdamore 
    139   1.1   gdamore int
    140   1.1   gdamore edid_is_valid(uint8_t *d)
    141   1.1   gdamore {
    142   1.1   gdamore 	int sum = 0, i;
    143   1.1   gdamore 	uint8_t sig[8] = EDID_SIGNATURE;
    144  1.16   mlelstv 
    145   1.1   gdamore 	if (memcmp(d, sig, 8) != 0)
    146   1.1   gdamore 		return EINVAL;
    147   1.1   gdamore 
    148   1.1   gdamore 	for (i = 0; i < 128; i++)
    149   1.1   gdamore 		sum += d[i];
    150   1.1   gdamore 	if ((sum & 0xff) != 0)
    151   1.1   gdamore 		return EINVAL;
    152   1.1   gdamore 
    153   1.1   gdamore 	return 0;
    154   1.1   gdamore }
    155   1.1   gdamore 
    156   1.1   gdamore void
    157   1.1   gdamore edid_print(struct edid_info *edid)
    158   1.1   gdamore {
    159   1.1   gdamore 	int	i;
    160   1.1   gdamore 
    161   1.1   gdamore 	if (edid == NULL)
    162   1.1   gdamore 		return;
    163   1.1   gdamore 	printf("Vendor: [%s] %s\n", edid->edid_vendor, edid->edid_vendorname);
    164   1.1   gdamore 	printf("Product: [%04X] %s\n", edid->edid_product,
    165   1.1   gdamore 	    edid->edid_productname);
    166   1.1   gdamore 	printf("Serial number: %s\n", edid->edid_serial);
    167   1.1   gdamore 	printf("Manufactured %d Week %d\n",
    168   1.1   gdamore 	    edid->edid_year, edid->edid_week);
    169   1.1   gdamore 	printf("EDID Version %d.%d\n", edid->edid_version,
    170   1.1   gdamore 	    edid->edid_revision);
    171   1.1   gdamore 	printf("EDID Comment: %s\n", edid->edid_comment);
    172   1.1   gdamore 
    173   1.1   gdamore 	printf("Video Input: %x\n", edid->edid_video_input);
    174   1.1   gdamore 	if (edid->edid_video_input & EDID_VIDEO_INPUT_DIGITAL) {
    175   1.1   gdamore 		printf("\tDigital");
    176   1.1   gdamore 		if (edid->edid_video_input & EDID_VIDEO_INPUT_DFP1_COMPAT)
    177   1.1   gdamore 			printf(" (DFP 1.x compatible)");
    178   1.1   gdamore 		printf("\n");
    179   1.1   gdamore 	} else {
    180   1.1   gdamore 		printf("\tAnalog\n");
    181   1.1   gdamore 		switch (EDID_VIDEO_INPUT_LEVEL(edid->edid_video_input)) {
    182   1.1   gdamore 		case 0:
    183   1.1   gdamore 			printf("\t-0.7, 0.3V\n");
    184   1.1   gdamore 			break;
    185   1.1   gdamore 		case 1:
    186   1.1   gdamore 			printf("\t-0.714, 0.286V\n");
    187   1.1   gdamore 			break;
    188   1.1   gdamore 		case 2:
    189   1.1   gdamore 			printf("\t-1.0, 0.4V\n");
    190   1.1   gdamore 			break;
    191   1.1   gdamore 		case 3:
    192   1.1   gdamore 			printf("\t-0.7, 0.0V\n");
    193   1.1   gdamore 			break;
    194   1.1   gdamore 		}
    195   1.1   gdamore 		if (edid->edid_video_input & EDID_VIDEO_INPUT_BLANK_TO_BLACK)
    196   1.1   gdamore 			printf("\tBlank-to-black setup\n");
    197   1.1   gdamore 		if (edid->edid_video_input & EDID_VIDEO_INPUT_SEPARATE_SYNCS)
    198   1.1   gdamore 			printf("\tSeperate syncs\n");
    199   1.1   gdamore 		if (edid->edid_video_input & EDID_VIDEO_INPUT_COMPOSITE_SYNC)
    200   1.1   gdamore 			printf("\tComposite sync\n");
    201   1.1   gdamore 		if (edid->edid_video_input & EDID_VIDEO_INPUT_SYNC_ON_GRN)
    202   1.1   gdamore 			printf("\tSync on green\n");
    203   1.1   gdamore 		if (edid->edid_video_input & EDID_VIDEO_INPUT_SERRATION)
    204   1.1   gdamore 			printf("\tSerration vsync\n");
    205   1.1   gdamore 	}
    206   1.1   gdamore 
    207   1.1   gdamore 	printf("Gamma: %d.%02d\n",
    208   1.1   gdamore 	    edid->edid_gamma / 100, edid->edid_gamma % 100);
    209   1.1   gdamore 
    210   1.1   gdamore 	printf("Max Size: %d cm x %d cm\n",
    211   1.1   gdamore 	    edid->edid_max_hsize, edid->edid_max_vsize);
    212   1.1   gdamore 
    213   1.1   gdamore 	printf("Features: %x\n", edid->edid_features);
    214   1.1   gdamore 	if (edid->edid_features & EDID_FEATURES_STANDBY)
    215   1.1   gdamore 		printf("\tDPMS standby\n");
    216   1.1   gdamore 	if (edid->edid_features & EDID_FEATURES_SUSPEND)
    217   1.1   gdamore 		printf("\tDPMS suspend\n");
    218   1.1   gdamore 	if (edid->edid_features & EDID_FEATURES_ACTIVE_OFF)
    219   1.1   gdamore 		printf("\tDPMS active-off\n");
    220   1.1   gdamore 	switch (EDID_FEATURES_DISP_TYPE(edid->edid_features)) {
    221   1.1   gdamore 	case EDID_FEATURES_DISP_TYPE_MONO:
    222   1.1   gdamore 		printf("\tMonochrome\n");
    223   1.1   gdamore 		break;
    224   1.1   gdamore 	case EDID_FEATURES_DISP_TYPE_RGB:
    225   1.1   gdamore 		printf("\tRGB\n");
    226   1.1   gdamore 		break;
    227   1.1   gdamore 	case EDID_FEATURES_DISP_TYPE_NON_RGB:
    228   1.1   gdamore 		printf("\tMulticolor\n");
    229   1.1   gdamore 		break;
    230   1.1   gdamore 	case EDID_FEATURES_DISP_TYPE_UNDEFINED:
    231   1.1   gdamore 		printf("\tUndefined monitor type\n");
    232   1.1   gdamore 		break;
    233   1.1   gdamore 	}
    234   1.1   gdamore 	if (edid->edid_features & EDID_FEATURES_STD_COLOR)
    235   1.1   gdamore 		printf("\tStandard color space\n");
    236   1.1   gdamore 	if (edid->edid_features & EDID_FEATURES_PREFERRED_TIMING)
    237   1.1   gdamore 		printf("\tPreferred timing\n");
    238   1.1   gdamore 	if (edid->edid_features & EDID_FEATURES_DEFAULT_GTF)
    239   1.1   gdamore 		printf("\tDefault GTF supported\n");
    240   1.1   gdamore 
    241   1.1   gdamore 	printf("Chroma Info:\n");
    242   1.1   gdamore 	printf("\tRed X: 0.%03d\n", edid->edid_chroma.ec_redx);
    243   1.1   gdamore 	printf("\tRed Y: 0.%03d\n", edid->edid_chroma.ec_redy);
    244   1.1   gdamore 	printf("\tGrn X: 0.%03d\n", edid->edid_chroma.ec_greenx);
    245   1.1   gdamore 	printf("\tGrn Y: 0.%03d\n", edid->edid_chroma.ec_greeny);
    246   1.1   gdamore 	printf("\tBlu X: 0.%03d\n", edid->edid_chroma.ec_bluex);
    247   1.1   gdamore 	printf("\tBlu Y: 0.%03d\n", edid->edid_chroma.ec_bluey);
    248   1.1   gdamore 	printf("\tWht X: 0.%03d\n", edid->edid_chroma.ec_whitex);
    249   1.1   gdamore 	printf("\tWht Y: 0.%03d\n", edid->edid_chroma.ec_whitey);
    250   1.1   gdamore 
    251   1.1   gdamore 	if (edid->edid_have_range) {
    252   1.1   gdamore 		printf("Range:\n");
    253   1.1   gdamore 		printf("\tHorizontal: %d - %d kHz\n",
    254   1.1   gdamore 		    edid->edid_range.er_min_hfreq,
    255   1.1   gdamore 		    edid->edid_range.er_max_hfreq);
    256   1.1   gdamore 		printf("\tVertical: %d - %d Hz\n",
    257   1.1   gdamore 		    edid->edid_range.er_min_vfreq,
    258   1.1   gdamore 		    edid->edid_range.er_max_vfreq);
    259   1.1   gdamore 		printf("\tMax Dot Clock: %d MHz\n",
    260   1.1   gdamore 		    edid->edid_range.er_max_clock);
    261   1.1   gdamore 		if (edid->edid_range.er_have_gtf2) {
    262   1.1   gdamore 			printf("\tGTF2 hfreq: %d\n",
    263   1.1   gdamore 			    edid->edid_range.er_gtf2_hfreq);
    264   1.1   gdamore 			printf("\tGTF2 C: %d\n", edid->edid_range.er_gtf2_c);
    265   1.1   gdamore 			printf("\tGTF2 M: %d\n", edid->edid_range.er_gtf2_m);
    266   1.1   gdamore 			printf("\tGTF2 J: %d\n", edid->edid_range.er_gtf2_j);
    267   1.1   gdamore 			printf("\tGTF2 K: %d\n", edid->edid_range.er_gtf2_k);
    268   1.1   gdamore 		}
    269   1.1   gdamore 	}
    270   1.1   gdamore 	printf("Video modes:\n");
    271   1.1   gdamore 	for (i = 0; i < edid->edid_nmodes; i++) {
    272   1.8       jdc 		printf("\t%dx%d @ %dHz",
    273   1.1   gdamore 		    edid->edid_modes[i].hdisplay,
    274   1.1   gdamore 		    edid->edid_modes[i].vdisplay,
    275   1.1   gdamore 		    DIVIDE(DIVIDE(edid->edid_modes[i].dot_clock * 1000,
    276  1.10  christos 		    edid->edid_modes[i].htotal), edid->edid_modes[i].vtotal));
    277   1.8       jdc 		printf(" (%d %d %d %d %d %d %d",
    278   1.8       jdc 		    edid->edid_modes[i].dot_clock,
    279   1.8       jdc 		    edid->edid_modes[i].hsync_start,
    280   1.8       jdc 		    edid->edid_modes[i].hsync_end,
    281   1.8       jdc 		    edid->edid_modes[i].htotal,
    282   1.8       jdc 		    edid->edid_modes[i].vsync_start,
    283   1.8       jdc 		    edid->edid_modes[i].vsync_end,
    284   1.8       jdc 		    edid->edid_modes[i].vtotal);
    285   1.8       jdc 		printf(" %s%sH %s%sV)\n",
    286   1.8       jdc 		    edid->edid_modes[i].flags & VID_PHSYNC ? "+" : "",
    287   1.8       jdc 		    edid->edid_modes[i].flags & VID_NHSYNC ? "-" : "",
    288   1.8       jdc 		    edid->edid_modes[i].flags & VID_PVSYNC ? "+" : "",
    289   1.8       jdc 		    edid->edid_modes[i].flags & VID_NVSYNC ? "-" : "");
    290   1.1   gdamore 	}
    291   1.1   gdamore 	if (edid->edid_preferred_mode)
    292   1.1   gdamore 		printf("Preferred mode: %dx%d @ %dHz\n",
    293   1.1   gdamore 		    edid->edid_preferred_mode->hdisplay,
    294   1.1   gdamore 		    edid->edid_preferred_mode->vdisplay,
    295   1.1   gdamore 		    DIVIDE(DIVIDE(edid->edid_preferred_mode->dot_clock * 1000,
    296  1.10  christos 		    edid->edid_preferred_mode->htotal),
    297  1.10  christos 		    edid->edid_preferred_mode->vtotal));
    298  1.13  jmcneill 
    299  1.13  jmcneill 	printf("Number of extension blocks: %d\n", edid->edid_ext_block_count);
    300   1.1   gdamore }
    301   1.1   gdamore 
    302   1.1   gdamore static const struct videomode *
    303   1.1   gdamore edid_mode_lookup_list(const char *name)
    304   1.1   gdamore {
    305   1.1   gdamore 	int	i;
    306   1.1   gdamore 
    307   1.1   gdamore 	for (i = 0; i < videomode_count; i++)
    308   1.1   gdamore 		if (strcmp(name, videomode_list[i].name) == 0)
    309   1.1   gdamore 			return &videomode_list[i];
    310   1.1   gdamore 	return NULL;
    311   1.1   gdamore }
    312   1.1   gdamore 
    313   1.9       jdc static struct videomode *
    314   1.9       jdc edid_search_mode(struct edid_info *edid, const struct videomode *mode)
    315   1.9       jdc {
    316   1.9       jdc 	int	refresh, i;
    317   1.9       jdc 
    318   1.9       jdc 	refresh = DIVIDE(DIVIDE(mode->dot_clock * 1000,
    319   1.9       jdc 	    mode->htotal), mode->vtotal);
    320   1.9       jdc 	for (i = 0; i < edid->edid_nmodes; i++) {
    321   1.9       jdc 		if (mode->hdisplay == edid->edid_modes[i].hdisplay &&
    322   1.9       jdc 		    mode->vdisplay == edid->edid_modes[i].vdisplay &&
    323   1.9       jdc 		    refresh == DIVIDE(DIVIDE(
    324   1.9       jdc 		    edid->edid_modes[i].dot_clock * 1000,
    325  1.10  christos 		    edid->edid_modes[i].htotal), edid->edid_modes[i].vtotal)) {
    326  1.10  christos 			return &edid->edid_modes[i];
    327   1.9       jdc 		}
    328   1.9       jdc 	}
    329   1.9       jdc 	return NULL;
    330   1.9       jdc }
    331   1.9       jdc 
    332   1.1   gdamore static int
    333   1.1   gdamore edid_std_timing(uint8_t *data, struct videomode *vmp)
    334   1.1   gdamore {
    335   1.1   gdamore 	unsigned			x, y, f;
    336   1.1   gdamore 	const struct videomode		*lookup;
    337   1.1   gdamore 	char				name[80];
    338   1.1   gdamore 
    339   1.1   gdamore 	if ((data[0] == 1 && data[1] == 1) ||
    340   1.1   gdamore 	    (data[0] == 0 && data[1] == 0) ||
    341   1.1   gdamore 	    (data[0] == 0x20 && data[1] == 0x20))
    342   1.1   gdamore 		return 0;
    343   1.1   gdamore 
    344   1.1   gdamore 	x = EDID_STD_TIMING_HRES(data);
    345   1.1   gdamore 	switch (EDID_STD_TIMING_RATIO(data)) {
    346   1.1   gdamore 	case EDID_STD_TIMING_RATIO_16_10:
    347   1.1   gdamore 		y = x * 10 / 16;
    348   1.1   gdamore 		break;
    349   1.1   gdamore 	case EDID_STD_TIMING_RATIO_4_3:
    350   1.1   gdamore 		y = x * 3 / 4;
    351   1.1   gdamore 		break;
    352   1.1   gdamore 	case EDID_STD_TIMING_RATIO_5_4:
    353   1.1   gdamore 		y = x * 4 / 5;
    354   1.1   gdamore 		break;
    355   1.1   gdamore 	case EDID_STD_TIMING_RATIO_16_9:
    356   1.1   gdamore 	default:
    357   1.1   gdamore 		y = x * 9 / 16;
    358   1.1   gdamore 		break;
    359   1.1   gdamore 	}
    360   1.1   gdamore 	f = EDID_STD_TIMING_VFREQ(data);
    361   1.1   gdamore 
    362   1.1   gdamore 	/* first try to lookup the mode as a DMT timing */
    363  1.10  christos 	snprintf(name, sizeof(name), "%dx%dx%d", x, y, f);
    364   1.1   gdamore 	if ((lookup = edid_mode_lookup_list(name)) != NULL) {
    365   1.1   gdamore 		*vmp = *lookup;
    366  1.10  christos 	} else {
    367  1.10  christos 		/* failing that, calculate it using gtf */
    368   1.1   gdamore 		/*
    369   1.1   gdamore 		 * Hmm. I'm not using alternate GTF timings, which
    370   1.1   gdamore 		 * could, in theory, be present.
    371   1.1   gdamore 		 */
    372   1.1   gdamore 		vesagtf_mode(x, y, f, vmp);
    373   1.1   gdamore 	}
    374   1.1   gdamore 	return 1;
    375   1.1   gdamore }
    376   1.1   gdamore 
    377   1.1   gdamore static int
    378   1.1   gdamore edid_det_timing(uint8_t *data, struct videomode *vmp)
    379   1.1   gdamore {
    380   1.1   gdamore 	unsigned	hactive, hblank, hsyncwid, hsyncoff;
    381   1.1   gdamore 	unsigned	vactive, vblank, vsyncwid, vsyncoff;
    382   1.1   gdamore 	uint8_t		flags;
    383   1.1   gdamore 
    384   1.1   gdamore 	flags = EDID_DET_TIMING_FLAGS(data);
    385   1.1   gdamore 
    386   1.1   gdamore 	/* we don't support stereo modes (for now) */
    387   1.1   gdamore 	if (flags & (EDID_DET_TIMING_FLAG_STEREO |
    388   1.8       jdc 		EDID_DET_TIMING_FLAG_STEREO_MODE))
    389   1.1   gdamore 		return 0;
    390   1.1   gdamore 
    391   1.1   gdamore 	vmp->dot_clock = EDID_DET_TIMING_DOT_CLOCK(data) / 1000;
    392   1.1   gdamore 
    393   1.1   gdamore 	hactive = EDID_DET_TIMING_HACTIVE(data);
    394   1.1   gdamore 	hblank = EDID_DET_TIMING_HBLANK(data);
    395   1.1   gdamore 	hsyncwid = EDID_DET_TIMING_HSYNC_WIDTH(data);
    396   1.1   gdamore 	hsyncoff = EDID_DET_TIMING_HSYNC_OFFSET(data);
    397   1.1   gdamore 
    398   1.1   gdamore 	vactive = EDID_DET_TIMING_VACTIVE(data);
    399   1.1   gdamore 	vblank = EDID_DET_TIMING_VBLANK(data);
    400   1.1   gdamore 	vsyncwid = EDID_DET_TIMING_VSYNC_WIDTH(data);
    401   1.1   gdamore 	vsyncoff = EDID_DET_TIMING_VSYNC_OFFSET(data);
    402   1.1   gdamore 
    403   1.9       jdc 	/* Borders are contained within the blank areas. */
    404   1.1   gdamore 
    405   1.1   gdamore 	vmp->hdisplay = hactive;
    406   1.1   gdamore 	vmp->htotal = hactive + hblank;
    407   1.1   gdamore 	vmp->hsync_start = hactive + hsyncoff;
    408   1.1   gdamore 	vmp->hsync_end = vmp->hsync_start + hsyncwid;
    409   1.1   gdamore 
    410   1.1   gdamore 	vmp->vdisplay = vactive;
    411   1.1   gdamore 	vmp->vtotal = vactive + vblank;
    412   1.1   gdamore 	vmp->vsync_start = vactive + vsyncoff;
    413   1.1   gdamore 	vmp->vsync_end = vmp->vsync_start + vsyncwid;
    414   1.1   gdamore 
    415   1.1   gdamore 	vmp->flags = 0;
    416   1.1   gdamore 
    417   1.1   gdamore 	if (flags & EDID_DET_TIMING_FLAG_INTERLACE)
    418   1.1   gdamore 		vmp->flags |= VID_INTERLACE;
    419   1.1   gdamore 	if (flags & EDID_DET_TIMING_FLAG_HSYNC_POSITIVE)
    420   1.1   gdamore 		vmp->flags |= VID_PHSYNC;
    421   1.1   gdamore 	else
    422   1.1   gdamore 		vmp->flags |= VID_NHSYNC;
    423   1.1   gdamore 
    424   1.1   gdamore 	if (flags & EDID_DET_TIMING_FLAG_VSYNC_POSITIVE)
    425   1.1   gdamore 		vmp->flags |= VID_PVSYNC;
    426   1.1   gdamore 	else
    427   1.1   gdamore 		vmp->flags |= VID_NVSYNC;
    428   1.1   gdamore 
    429   1.1   gdamore 	return 1;
    430   1.1   gdamore }
    431   1.1   gdamore 
    432  1.14  macallan static void bump_preferred_mode(struct edid_info *edid, struct videomode *m)
    433  1.14  macallan {
    434  1.14  macallan 	/*
    435  1.14  macallan 	 * XXX
    436  1.14  macallan 	 * Iiyama 4800 series monitors may have their native resolution in the
    437  1.14  macallan 	 * 2nd detailed timing descriptor instead of the 1st. Try to detect
    438  1.14  macallan 	 * that here and pick the native mode anyway.
    439  1.14  macallan 	 */
    440  1.14  macallan 	if (edid->edid_preferred_mode == NULL) {
    441  1.14  macallan 		edid->edid_preferred_mode = m;
    442  1.16   mlelstv 	} else if ((strncmp((char*)edid->edid_vendor, "IVM", 3) == 0) &&
    443  1.14  macallan 	           (edid->edid_product == 0x4800) &&
    444  1.14  macallan 	           (edid->edid_preferred_mode->dot_clock < m->dot_clock))
    445  1.14  macallan 		edid->edid_preferred_mode = m;
    446  1.14  macallan }
    447  1.14  macallan 
    448   1.1   gdamore static void
    449   1.1   gdamore edid_block(struct edid_info *edid, uint8_t *data)
    450   1.1   gdamore {
    451   1.1   gdamore 	int			i;
    452   1.9       jdc 	struct videomode	mode, *exist_mode;
    453   1.1   gdamore 
    454   1.1   gdamore 	if (EDID_BLOCK_IS_DET_TIMING(data)) {
    455  1.11  christos 		if (!edid_det_timing(data, &mode))
    456  1.10  christos 			return;
    457  1.10  christos 		/* Does this mode already exist? */
    458  1.10  christos 		exist_mode = edid_search_mode(edid, &mode);
    459  1.10  christos 		if (exist_mode != NULL) {
    460  1.10  christos 			*exist_mode = mode;
    461  1.14  macallan 			bump_preferred_mode(edid, exist_mode);
    462  1.10  christos 		} else {
    463  1.10  christos 			edid->edid_modes[edid->edid_nmodes] = mode;
    464  1.14  macallan 			bump_preferred_mode(edid,
    465  1.14  macallan 			    &edid->edid_modes[edid->edid_nmodes]);
    466  1.10  christos 			edid->edid_nmodes++;
    467   1.1   gdamore 		}
    468   1.1   gdamore 		return;
    469   1.1   gdamore 	}
    470   1.1   gdamore 
    471   1.1   gdamore 	switch (EDID_BLOCK_TYPE(data)) {
    472   1.1   gdamore 	case EDID_DESC_BLOCK_TYPE_SERIAL:
    473  1.10  christos 		memcpy(edid->edid_serial, data + EDID_DESC_ASCII_DATA_OFFSET,
    474   1.1   gdamore 		    EDID_DESC_ASCII_DATA_LEN);
    475  1.10  christos 		edid->edid_serial[sizeof(edid->edid_serial) - 1] = 0;
    476   1.1   gdamore 		break;
    477   1.1   gdamore 
    478   1.1   gdamore 	case EDID_DESC_BLOCK_TYPE_ASCII:
    479  1.15      maxv 		memset(edid->edid_comment, 0, sizeof(edid->edid_comment));
    480  1.10  christos 		memcpy(edid->edid_comment, data + EDID_DESC_ASCII_DATA_OFFSET,
    481   1.1   gdamore 		    EDID_DESC_ASCII_DATA_LEN);
    482  1.10  christos 		edid->edid_comment[sizeof(edid->edid_comment) - 1] = 0;
    483   1.1   gdamore 		break;
    484   1.1   gdamore 
    485   1.1   gdamore 	case EDID_DESC_BLOCK_TYPE_RANGE:
    486   1.1   gdamore 		edid->edid_have_range = 1;
    487  1.10  christos 		edid->edid_range.er_min_vfreq =	EDID_DESC_RANGE_MIN_VFREQ(data);
    488  1.10  christos 		edid->edid_range.er_max_vfreq =	EDID_DESC_RANGE_MAX_VFREQ(data);
    489  1.10  christos 		edid->edid_range.er_min_hfreq =	EDID_DESC_RANGE_MIN_HFREQ(data);
    490  1.10  christos 		edid->edid_range.er_max_hfreq =	EDID_DESC_RANGE_MAX_HFREQ(data);
    491  1.10  christos 		edid->edid_range.er_max_clock = EDID_DESC_RANGE_MAX_CLOCK(data);
    492  1.10  christos 		if (!EDID_DESC_RANGE_HAVE_GTF2(data))
    493  1.10  christos 			break;
    494  1.10  christos 		edid->edid_range.er_have_gtf2 = 1;
    495  1.10  christos 		edid->edid_range.er_gtf2_hfreq =
    496  1.10  christos 		    EDID_DESC_RANGE_GTF2_HFREQ(data);
    497  1.10  christos 		edid->edid_range.er_gtf2_c = EDID_DESC_RANGE_GTF2_C(data);
    498  1.10  christos 		edid->edid_range.er_gtf2_m = EDID_DESC_RANGE_GTF2_M(data);
    499  1.10  christos 		edid->edid_range.er_gtf2_j = EDID_DESC_RANGE_GTF2_J(data);
    500  1.10  christos 		edid->edid_range.er_gtf2_k = EDID_DESC_RANGE_GTF2_K(data);
    501   1.1   gdamore 		break;
    502   1.1   gdamore 
    503   1.1   gdamore 	case EDID_DESC_BLOCK_TYPE_NAME:
    504   1.1   gdamore 		/* copy the product name into place */
    505   1.1   gdamore 		memcpy(edid->edid_productname,
    506   1.1   gdamore 		    data + EDID_DESC_ASCII_DATA_OFFSET,
    507   1.1   gdamore 		    EDID_DESC_ASCII_DATA_LEN);
    508   1.1   gdamore 		break;
    509   1.1   gdamore 
    510   1.1   gdamore 	case EDID_DESC_BLOCK_TYPE_STD_TIMING:
    511   1.1   gdamore 		data += EDID_DESC_STD_TIMING_START;
    512   1.1   gdamore 		for (i = 0; i < EDID_DESC_STD_TIMING_COUNT; i++) {
    513   1.1   gdamore 			if (edid_std_timing(data, &mode)) {
    514   1.9       jdc 				/* Does this mode already exist? */
    515   1.9       jdc 				exist_mode = edid_search_mode(edid, &mode);
    516   1.9       jdc 				if (exist_mode == NULL) {
    517   1.9       jdc 					edid->edid_modes[edid->edid_nmodes] =
    518   1.9       jdc 					    mode;
    519   1.9       jdc 					edid->edid_nmodes++;
    520   1.9       jdc 				}
    521   1.1   gdamore 			}
    522   1.1   gdamore 			data += 2;
    523   1.1   gdamore 		}
    524   1.1   gdamore 		break;
    525   1.1   gdamore 
    526   1.1   gdamore 	case EDID_DESC_BLOCK_TYPE_COLOR_POINT:
    527   1.1   gdamore 		/* XXX: not implemented yet */
    528   1.1   gdamore 		break;
    529   1.1   gdamore 	}
    530   1.1   gdamore }
    531   1.1   gdamore 
    532   1.1   gdamore /*
    533   1.1   gdamore  * Gets EDID version in BCD, e.g. EDID v1.3  returned as 0x0103
    534   1.1   gdamore  */
    535   1.2   gdamore int
    536   1.2   gdamore edid_parse(uint8_t *data, struct edid_info *edid)
    537   1.1   gdamore {
    538   1.1   gdamore 	uint16_t		manfid, estmodes;
    539   1.1   gdamore 	const struct videomode	*vmp;
    540   1.1   gdamore 	int			i;
    541   1.1   gdamore 	const char		*name;
    542   1.5  macallan 	int max_dotclock = 0;
    543   1.5  macallan 	int mhz;
    544   1.1   gdamore 
    545   1.1   gdamore 	if (edid_is_valid(data) != 0)
    546   1.2   gdamore 		return -1;
    547   1.1   gdamore 
    548   1.1   gdamore 	/* get product identification */
    549   1.1   gdamore 	manfid = EDID_VENDOR_ID(data);
    550   1.1   gdamore 	edid->edid_vendor[0] = EDID_MANFID_0(manfid);
    551   1.1   gdamore 	edid->edid_vendor[1] = EDID_MANFID_1(manfid);
    552   1.1   gdamore 	edid->edid_vendor[2] = EDID_MANFID_2(manfid);
    553   1.1   gdamore 	edid->edid_vendor[3] = 0;	/* null terminate for convenience */
    554   1.1   gdamore 
    555   1.1   gdamore 	edid->edid_product = data[EDID_OFFSET_PRODUCT_ID] +
    556   1.1   gdamore 	    (data[EDID_OFFSET_PRODUCT_ID + 1] << 8);
    557   1.1   gdamore 
    558  1.16   mlelstv 	name = edid_findvendor((char *)edid->edid_vendor);
    559  1.10  christos 	if (name != NULL)
    560  1.10  christos 		strlcpy(edid->edid_vendorname, name,
    561  1.10  christos 		    sizeof(edid->edid_vendorname));
    562  1.10  christos 	else
    563  1.10  christos 		edid->edid_vendorname[0] = '\0';
    564   1.1   gdamore 
    565  1.16   mlelstv 	name = edid_findproduct((char *)edid->edid_vendor, edid->edid_product);
    566  1.10  christos 	if (name != NULL)
    567  1.10  christos 		strlcpy(edid->edid_productname, name,
    568  1.10  christos 		    sizeof(edid->edid_productname));
    569  1.10  christos 	else
    570  1.10  christos 	    edid->edid_productname[0] = '\0';
    571   1.1   gdamore 
    572  1.10  christos 	snprintf(edid->edid_serial, sizeof(edid->edid_serial), "%08x",
    573   1.1   gdamore 	    EDID_SERIAL_NUMBER(data));
    574   1.1   gdamore 
    575  1.15      maxv 	edid->edid_comment[0] = '\0';
    576  1.15      maxv 
    577   1.1   gdamore 	edid->edid_week = EDID_WEEK(data);
    578   1.1   gdamore 	edid->edid_year = EDID_YEAR(data);
    579   1.1   gdamore 
    580   1.1   gdamore 	/* get edid revision */
    581   1.1   gdamore 	edid->edid_version = EDID_VERSION(data);
    582   1.1   gdamore 	edid->edid_revision = EDID_REVISION(data);
    583   1.1   gdamore 
    584   1.1   gdamore 	edid->edid_video_input = EDID_VIDEO_INPUT(data);
    585   1.1   gdamore 	edid->edid_max_hsize = EDID_MAX_HSIZE(data);
    586   1.1   gdamore 	edid->edid_max_vsize = EDID_MAX_VSIZE(data);
    587   1.1   gdamore 
    588   1.1   gdamore 	edid->edid_gamma = EDID_GAMMA(data);
    589   1.1   gdamore 	edid->edid_features = EDID_FEATURES(data);
    590   1.1   gdamore 
    591   1.1   gdamore 	edid->edid_chroma.ec_redx = EDID_CHROMA_REDX(data);
    592   1.1   gdamore 	edid->edid_chroma.ec_redy = EDID_CHROMA_REDX(data);
    593   1.1   gdamore 	edid->edid_chroma.ec_greenx = EDID_CHROMA_GREENX(data);
    594   1.1   gdamore 	edid->edid_chroma.ec_greeny = EDID_CHROMA_GREENY(data);
    595   1.1   gdamore 	edid->edid_chroma.ec_bluex = EDID_CHROMA_BLUEX(data);
    596   1.1   gdamore 	edid->edid_chroma.ec_bluey = EDID_CHROMA_BLUEY(data);
    597   1.1   gdamore 	edid->edid_chroma.ec_whitex = EDID_CHROMA_WHITEX(data);
    598   1.1   gdamore 	edid->edid_chroma.ec_whitey = EDID_CHROMA_WHITEY(data);
    599   1.1   gdamore 
    600  1.13  jmcneill 	edid->edid_ext_block_count = EDID_EXT_BLOCK_COUNT(data);
    601  1.13  jmcneill 
    602   1.1   gdamore 	/* lookup established modes */
    603   1.4  macallan 	edid->edid_nmodes = 0;
    604   1.5  macallan 	edid->edid_preferred_mode = NULL;
    605   1.1   gdamore 	estmodes = EDID_EST_TIMING(data);
    606   1.7       jdc 	/* Iterate in esztablished timing order */
    607   1.7       jdc 	for (i = 15; i >= 0; i--) {
    608   1.1   gdamore 		if (estmodes & (1 << i)) {
    609   1.1   gdamore 			vmp = edid_mode_lookup_list(_edid_modes[i]);
    610   1.1   gdamore 			if (vmp != NULL) {
    611   1.1   gdamore 				edid->edid_modes[edid->edid_nmodes] = *vmp;
    612   1.1   gdamore 				edid->edid_nmodes++;
    613   1.1   gdamore 			}
    614   1.5  macallan #ifdef DIAGNOSTIC
    615   1.5  macallan 			  else
    616   1.5  macallan 				printf("no data for est. mode %s\n",
    617   1.5  macallan 				    _edid_modes[i]);
    618   1.5  macallan #endif
    619   1.1   gdamore 		}
    620   1.1   gdamore 	}
    621   1.1   gdamore 
    622   1.1   gdamore 	/* do standard timing section */
    623   1.1   gdamore 	for (i = 0; i < EDID_STD_TIMING_COUNT; i++) {
    624   1.9       jdc 		struct videomode	mode, *exist_mode;
    625   1.1   gdamore 		if (edid_std_timing(data + EDID_OFFSET_STD_TIMING + i * 2,
    626   1.1   gdamore 			&mode)) {
    627   1.9       jdc 			/* Does this mode already exist? */
    628   1.9       jdc 			exist_mode = edid_search_mode(edid, &mode);
    629   1.9       jdc 			if (exist_mode == NULL) {
    630   1.9       jdc 				edid->edid_modes[edid->edid_nmodes] = mode;
    631   1.9       jdc 				edid->edid_nmodes++;
    632   1.9       jdc 			}
    633   1.1   gdamore 		}
    634   1.1   gdamore 	}
    635   1.1   gdamore 
    636   1.1   gdamore 	/* do detailed timings and descriptors */
    637   1.1   gdamore 	for (i = 0; i < EDID_BLOCK_COUNT; i++) {
    638   1.1   gdamore 		edid_block(edid, data + EDID_OFFSET_DESC_BLOCK +
    639   1.1   gdamore 		    i * EDID_BLOCK_SIZE);
    640   1.1   gdamore 	}
    641   1.1   gdamore 
    642   1.1   gdamore 	edid_strchomp(edid->edid_vendorname);
    643   1.1   gdamore 	edid_strchomp(edid->edid_productname);
    644   1.1   gdamore 	edid_strchomp(edid->edid_serial);
    645   1.1   gdamore 	edid_strchomp(edid->edid_comment);
    646   1.1   gdamore 
    647   1.5  macallan 	/*
    648   1.5  macallan 	 * XXX
    649   1.5  macallan 	 * some monitors lie about their maximum supported dot clock
    650   1.5  macallan 	 * by claiming to support modes which need a higher dot clock
    651   1.5  macallan 	 * than the stated maximum.
    652   1.5  macallan 	 * For sanity's sake we bump it to the highest dot clock we find
    653   1.5  macallan 	 * in the list of supported modes
    654   1.5  macallan 	 */
    655   1.5  macallan 	for (i = 0; i < edid->edid_nmodes; i++)
    656   1.5  macallan 		if (edid->edid_modes[i].dot_clock > max_dotclock)
    657   1.5  macallan 			max_dotclock = edid->edid_modes[i].dot_clock;
    658   1.5  macallan 
    659  1.16   mlelstv #ifdef _KERNEL
    660  1.12     skrll 	aprint_debug("max_dotclock according to supported modes: %d\n",
    661   1.5  macallan 	    max_dotclock);
    662  1.16   mlelstv #endif
    663   1.5  macallan 
    664   1.5  macallan 	mhz = (max_dotclock + 999) / 1000;
    665   1.5  macallan 
    666   1.5  macallan 	if (edid->edid_have_range) {
    667   1.5  macallan 		if (mhz > edid->edid_range.er_max_clock)
    668   1.5  macallan 			edid->edid_range.er_max_clock = mhz;
    669   1.5  macallan 	} else
    670   1.5  macallan 		edid->edid_range.er_max_clock = mhz;
    671   1.5  macallan 
    672   1.2   gdamore 	return 0;
    673   1.1   gdamore }
    674   1.1   gdamore 
    675