edid.c revision 1.4 1 1.4 macallan /* $NetBSD: edid.c,v 1.4 2007/03/07 18:49:31 macallan 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.4 macallan __KERNEL_RCSID(0, "$NetBSD: edid.c,v 1.4 2007/03/07 18:49:31 macallan 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.4 macallan edid->edid_nmodes = 0;
541 1.1 gdamore estmodes = EDID_EST_TIMING(data);
542 1.1 gdamore for (i = 0; i < 16; i++) {
543 1.1 gdamore if (estmodes & (1 << i)) {
544 1.1 gdamore vmp = edid_mode_lookup_list(_edid_modes[i]);
545 1.1 gdamore if (vmp != NULL) {
546 1.1 gdamore edid->edid_modes[edid->edid_nmodes] = *vmp;
547 1.1 gdamore edid->edid_nmodes++;
548 1.1 gdamore }
549 1.1 gdamore }
550 1.1 gdamore }
551 1.1 gdamore
552 1.1 gdamore /* do standard timing section */
553 1.1 gdamore for (i = 0; i < EDID_STD_TIMING_COUNT; i++) {
554 1.1 gdamore struct videomode mode;
555 1.1 gdamore if (edid_std_timing(data + EDID_OFFSET_STD_TIMING + i * 2,
556 1.1 gdamore &mode)) {
557 1.1 gdamore edid->edid_modes[edid->edid_nmodes] = mode;
558 1.1 gdamore edid->edid_nmodes++;
559 1.1 gdamore }
560 1.1 gdamore }
561 1.1 gdamore
562 1.1 gdamore /* do detailed timings and descriptors */
563 1.1 gdamore for (i = 0; i < EDID_BLOCK_COUNT; i++) {
564 1.1 gdamore edid_block(edid, data + EDID_OFFSET_DESC_BLOCK +
565 1.1 gdamore i * EDID_BLOCK_SIZE);
566 1.1 gdamore }
567 1.1 gdamore
568 1.1 gdamore edid_strchomp(edid->edid_vendorname);
569 1.1 gdamore edid_strchomp(edid->edid_productname);
570 1.1 gdamore edid_strchomp(edid->edid_serial);
571 1.1 gdamore edid_strchomp(edid->edid_comment);
572 1.1 gdamore
573 1.2 gdamore return 0;
574 1.1 gdamore }
575 1.1 gdamore
576