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