Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE> | |
3 | * Copyright 2007 Red Hat, Inc. | |
4 | * | |
5 | * Permission is hereby granted, free of charge, to any person obtaining a | |
6 | * copy of this software and associated documentation files (the "Software"), | |
7 | * to deal in the Software without restriction, including without limitation | |
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
9 | * and/or sell copies of the Software, and to permit persons to whom the | |
10 | * Software is furnished to do so, subject to the following conditions: | |
11 | * | |
12 | * The above copyright notice and this permission notice (including the next | |
13 | * paragraph) shall be included in all copies or substantial portions of the | |
14 | * Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
22 | * DEALINGS IN THE SOFTWARE. | |
23 | * | |
24 | * interpret_edid.c: interpret a primary EDID block | |
25 | */ | |
26 | ||
27 | #ifdef HAVE_XORG_CONFIG_H | |
28 | #include <xorg-config.h> | |
29 | #endif | |
30 | ||
31 | #include "misc.h" | |
32 | #include "xf86.h" | |
33 | #include "xf86_OSproc.h" | |
34 | #define _PARSE_EDID_ | |
35 | #include "xf86DDC.h" | |
36 | #include <string.h> | |
37 | ||
38 | static void get_vendor_section(Uchar *, struct vendor *); | |
39 | static void get_version_section(Uchar *, struct edid_version *); | |
40 | static void get_display_section(Uchar *, struct disp_features *, | |
41 | struct edid_version *); | |
42 | static void get_established_timing_section(Uchar *, | |
43 | struct established_timings *); | |
44 | static void get_std_timing_section(Uchar *, struct std_timings *, | |
45 | struct edid_version *); | |
46 | static void fetch_detailed_block(Uchar * c, struct edid_version *ver, | |
47 | struct detailed_monitor_section *det_mon); | |
48 | static void get_dt_md_section(Uchar *, struct edid_version *, | |
49 | struct detailed_monitor_section *det_mon); | |
50 | static void copy_string(Uchar *, Uchar *); | |
51 | static void get_dst_timing_section(Uchar *, struct std_timings *, | |
52 | struct edid_version *); | |
53 | static void get_monitor_ranges(Uchar *, struct monitor_ranges *); | |
54 | static void get_whitepoint_section(Uchar *, struct whitePoints *); | |
55 | static void get_detailed_timing_section(Uchar *, struct detailed_timings *); | |
56 | static Bool validate_version(int scrnIndex, struct edid_version *); | |
57 | ||
58 | static void | |
59 | find_ranges_section(struct detailed_monitor_section *det, void *ranges) | |
60 | { | |
61 | if (det->type == DS_RANGES && det->section.ranges.max_clock) | |
62 | *(struct monitor_ranges **) ranges = &det->section.ranges; | |
63 | } | |
64 | ||
65 | static void | |
66 | find_max_detailed_clock(struct detailed_monitor_section *det, void *ret) | |
67 | { | |
68 | if (det->type == DT) { | |
69 | *(int *) ret = max(*((int *) ret), det->section.d_timings.clock); | |
70 | } | |
71 | } | |
72 | ||
73 | static void | |
74 | handle_edid_quirks(xf86MonPtr m) | |
75 | { | |
76 | struct monitor_ranges *ranges = NULL; | |
77 | ||
78 | /* | |
79 | * max_clock is only encoded in EDID in tens of MHz, so occasionally we | |
80 | * find a monitor claiming a max of 160 with a mode requiring 162, or | |
81 | * similar. Strictly we should refuse to round up too far, but let's | |
82 | * see how well this works. | |
83 | */ | |
84 | ||
85 | /* Try to find Monitor Range and max clock, then re-set range value */ | |
86 | xf86ForEachDetailedBlock(m, find_ranges_section, &ranges); | |
87 | if (ranges && ranges->max_clock) { | |
88 | int clock = 0; | |
89 | ||
90 | xf86ForEachDetailedBlock(m, find_max_detailed_clock, &clock); | |
91 | if (clock && (ranges->max_clock * 1e6 < clock)) { | |
92 | xf86Msg(X_WARNING, "EDID timing clock %.2f exceeds claimed max " | |
93 | "%dMHz, fixing\n", clock / 1.0e6, ranges->max_clock); | |
94 | ranges->max_clock = (clock + 999999) / 1e6; | |
95 | } | |
96 | } | |
97 | } | |
98 | ||
99 | struct det_hv_parameter { | |
100 | int real_hsize; | |
101 | int real_vsize; | |
102 | float target_aspect; | |
103 | }; | |
104 | ||
105 | static void | |
106 | handle_detailed_hvsize(struct detailed_monitor_section *det_mon, void *data) | |
107 | { | |
108 | struct det_hv_parameter *p = (struct det_hv_parameter *) data; | |
109 | float timing_aspect; | |
110 | ||
111 | if (det_mon->type == DT) { | |
112 | struct detailed_timings *timing; | |
113 | ||
114 | timing = &det_mon->section.d_timings; | |
115 | ||
116 | if (!timing->v_size) | |
117 | return; | |
118 | ||
119 | timing_aspect = (float) timing->h_size / timing->v_size; | |
120 | if (fabs(1 - (timing_aspect / p->target_aspect)) < 0.05) { | |
121 | p->real_hsize = max(p->real_hsize, timing->h_size); | |
122 | p->real_vsize = max(p->real_vsize, timing->v_size); | |
123 | } | |
124 | } | |
125 | } | |
126 | ||
127 | static void | |
128 | encode_aspect_ratio(xf86MonPtr m) | |
129 | { | |
130 | /* | |
131 | * some monitors encode the aspect ratio instead of the physical size. | |
132 | * try to find the largest detailed timing that matches that aspect | |
133 | * ratio and use that to fill in the feature section. | |
134 | */ | |
135 | if ((m->features.hsize == 16 && m->features.vsize == 9) || | |
136 | (m->features.hsize == 16 && m->features.vsize == 10) || | |
137 | (m->features.hsize == 4 && m->features.vsize == 3) || | |
138 | (m->features.hsize == 5 && m->features.vsize == 4)) { | |
139 | ||
140 | struct det_hv_parameter p; | |
141 | ||
142 | p.real_hsize = 0; | |
143 | p.real_vsize = 0; | |
144 | p.target_aspect = (float) m->features.hsize / m->features.vsize; | |
145 | ||
146 | xf86ForEachDetailedBlock(m, handle_detailed_hvsize, &p); | |
147 | ||
148 | if (!p.real_hsize || !p.real_vsize) { | |
149 | m->features.hsize = m->features.vsize = 0; | |
150 | } | |
151 | else if ((m->features.hsize * 10 == p.real_hsize) && | |
152 | (m->features.vsize * 10 == p.real_vsize)) { | |
153 | /* exact match is just unlikely, should do a better check though */ | |
154 | m->features.hsize = m->features.vsize = 0; | |
155 | } | |
156 | else { | |
157 | /* convert mm to cm */ | |
158 | m->features.hsize = (p.real_hsize + 5) / 10; | |
159 | m->features.vsize = (p.real_vsize + 5) / 10; | |
160 | } | |
161 | ||
162 | xf86Msg(X_INFO, "Quirked EDID physical size to %dx%d cm\n", | |
163 | m->features.hsize, m->features.vsize); | |
164 | } | |
165 | } | |
166 | ||
167 | xf86MonPtr | |
168 | xf86InterpretEDID(int scrnIndex, Uchar * block) | |
169 | { | |
170 | xf86MonPtr m; | |
171 | ||
172 | if (!block) | |
173 | return NULL; | |
174 | if (!(m = xnfcalloc(sizeof(xf86Monitor), 1))) | |
175 | return NULL; | |
176 | m->scrnIndex = scrnIndex; | |
177 | m->rawData = block; | |
178 | ||
179 | get_vendor_section(SECTION(VENDOR_SECTION, block), &m->vendor); | |
180 | get_version_section(SECTION(VERSION_SECTION, block), &m->ver); | |
181 | if (!validate_version(scrnIndex, &m->ver)) | |
182 | goto error; | |
183 | get_display_section(SECTION(DISPLAY_SECTION, block), &m->features, &m->ver); | |
184 | get_established_timing_section(SECTION(ESTABLISHED_TIMING_SECTION, block), | |
185 | &m->timings1); | |
186 | get_std_timing_section(SECTION(STD_TIMING_SECTION, block), m->timings2, | |
187 | &m->ver); | |
188 | get_dt_md_section(SECTION(DET_TIMING_SECTION, block), &m->ver, m->det_mon); | |
189 | m->no_sections = (int) *(char *) SECTION(NO_EDID, block); | |
190 | ||
191 | handle_edid_quirks(m); | |
192 | encode_aspect_ratio(m); | |
193 | ||
194 | return m; | |
195 | ||
196 | error: | |
197 | free(m); | |
198 | return NULL; | |
199 | } | |
200 | ||
201 | static int | |
202 | get_cea_detail_timing(Uchar * blk, xf86MonPtr mon, | |
203 | struct detailed_monitor_section *det_mon) | |
204 | { | |
205 | int dt_num; | |
206 | int dt_offset = ((struct cea_ext_body *) blk)->dt_offset; | |
207 | ||
208 | dt_num = 0; | |
209 | ||
210 | if (dt_offset < CEA_EXT_MIN_DATA_OFFSET) | |
211 | return dt_num; | |
212 | ||
213 | for (; dt_offset < (CEA_EXT_MAX_DATA_OFFSET - DET_TIMING_INFO_LEN) && | |
214 | dt_num < CEA_EXT_DET_TIMING_NUM; _NEXT_DT_MD_SECTION(dt_offset)) { | |
215 | ||
216 | fetch_detailed_block(blk + dt_offset, &mon->ver, det_mon + dt_num); | |
217 | dt_num = dt_num + 1; | |
218 | } | |
219 | ||
220 | return dt_num; | |
221 | } | |
222 | ||
223 | static void | |
224 | handle_cea_detail_block(Uchar * ext, xf86MonPtr mon, | |
225 | handle_detailed_fn fn, void *data) | |
226 | { | |
227 | int i; | |
228 | struct detailed_monitor_section det_mon[CEA_EXT_DET_TIMING_NUM]; | |
229 | int det_mon_num; | |
230 | ||
231 | det_mon_num = get_cea_detail_timing(ext, mon, det_mon); | |
232 | ||
233 | for (i = 0; i < det_mon_num; i++) | |
234 | fn(det_mon + i, data); | |
235 | } | |
236 | ||
237 | void | |
238 | xf86ForEachDetailedBlock(xf86MonPtr mon, handle_detailed_fn fn, void *data) | |
239 | { | |
240 | int i; | |
241 | Uchar *ext; | |
242 | ||
243 | if (mon == NULL) | |
244 | return; | |
245 | ||
246 | for (i = 0; i < DET_TIMINGS; i++) | |
247 | fn(mon->det_mon + i, data); | |
248 | ||
249 | for (i = 0; i < mon->no_sections; i++) { | |
250 | ext = mon->rawData + EDID1_LEN * (i + 1); | |
251 | switch (ext[EXT_TAG]) { | |
252 | case CEA_EXT: | |
253 | handle_cea_detail_block(ext, mon, fn, data); | |
254 | break; | |
255 | case VTB_EXT: | |
256 | case DI_EXT: | |
257 | case LS_EXT: | |
258 | case MI_EXT: | |
259 | break; | |
260 | } | |
261 | } | |
262 | } | |
263 | ||
264 | static struct cea_data_block * | |
265 | extract_cea_data_block(Uchar * ext, int data_type) | |
266 | { | |
267 | struct cea_ext_body *cea; | |
268 | struct cea_data_block *data_collection; | |
269 | struct cea_data_block *data_end; | |
270 | ||
271 | cea = (struct cea_ext_body *) ext; | |
272 | ||
273 | if (cea->dt_offset <= CEA_EXT_MIN_DATA_OFFSET) | |
274 | return NULL; | |
275 | ||
276 | data_collection = &cea->data_collection; | |
277 | data_end = (struct cea_data_block *) (cea->dt_offset + ext); | |
278 | ||
279 | for (; data_collection < data_end;) { | |
280 | ||
281 | if (data_type == data_collection->tag) { | |
282 | return data_collection; | |
283 | } | |
284 | data_collection = (void *) ((unsigned char *) data_collection + | |
285 | data_collection->len + 1); | |
286 | } | |
287 | ||
288 | return NULL; | |
289 | } | |
290 | ||
291 | static void | |
292 | handle_cea_video_block(Uchar * ext, handle_video_fn fn, void *data) | |
293 | { | |
294 | struct cea_video_block *video; | |
295 | struct cea_video_block *video_end; | |
296 | struct cea_data_block *data_collection; | |
297 | ||
298 | data_collection = extract_cea_data_block(ext, CEA_VIDEO_BLK); | |
299 | if (data_collection == NULL) | |
300 | return; | |
301 | ||
302 | video = &data_collection->u.video; | |
303 | video_end = (struct cea_video_block *) | |
304 | ((Uchar *) video + data_collection->len); | |
305 | ||
306 | for (; video < video_end; video = video + 1) { | |
307 | fn(video, data); | |
308 | } | |
309 | } | |
310 | ||
311 | void | |
312 | xf86ForEachVideoBlock(xf86MonPtr mon, handle_video_fn fn, void *data) | |
313 | { | |
314 | int i; | |
315 | Uchar *ext; | |
316 | ||
317 | if (mon == NULL) | |
318 | return; | |
319 | ||
320 | for (i = 0; i < mon->no_sections; i++) { | |
321 | ext = mon->rawData + EDID1_LEN * (i + 1); | |
322 | switch (ext[EXT_TAG]) { | |
323 | case CEA_EXT: | |
324 | handle_cea_video_block(ext, fn, data); | |
325 | break; | |
326 | case VTB_EXT: | |
327 | case DI_EXT: | |
328 | case LS_EXT: | |
329 | case MI_EXT: | |
330 | break; | |
331 | } | |
332 | } | |
333 | } | |
334 | ||
335 | xf86MonPtr | |
336 | xf86InterpretEEDID(int scrnIndex, Uchar * block) | |
337 | { | |
338 | xf86MonPtr m; | |
339 | ||
340 | m = xf86InterpretEDID(scrnIndex, block); | |
341 | if (!m) | |
342 | return NULL; | |
343 | ||
344 | /* extension parse */ | |
345 | ||
346 | return m; | |
347 | } | |
348 | ||
349 | static void | |
350 | get_vendor_section(Uchar * c, struct vendor *r) | |
351 | { | |
352 | r->name[0] = L1; | |
353 | r->name[1] = L2; | |
354 | r->name[2] = L3; | |
355 | r->name[3] = '\0'; | |
356 | ||
357 | r->prod_id = PROD_ID; | |
358 | r->serial = SERIAL_NO; | |
359 | r->week = WEEK; | |
360 | r->year = YEAR; | |
361 | } | |
362 | ||
363 | static void | |
364 | get_version_section(Uchar * c, struct edid_version *r) | |
365 | { | |
366 | r->version = VERSION; | |
367 | r->revision = REVISION; | |
368 | } | |
369 | ||
370 | static void | |
371 | get_display_section(Uchar * c, struct disp_features *r, struct edid_version *v) | |
372 | { | |
373 | r->input_type = INPUT_TYPE; | |
374 | if (!DIGITAL(r->input_type)) { | |
375 | r->input_voltage = INPUT_VOLTAGE; | |
376 | r->input_setup = SETUP; | |
377 | r->input_sync = SYNC; | |
378 | } | |
379 | else if (v->revision == 2 || v->revision == 3) { | |
380 | r->input_dfp = DFP; | |
381 | } | |
382 | else if (v->revision >= 4) { | |
383 | r->input_bpc = BPC; | |
384 | r->input_interface = DIGITAL_INTERFACE; | |
385 | } | |
386 | r->hsize = HSIZE_MAX; | |
387 | r->vsize = VSIZE_MAX; | |
388 | r->gamma = GAMMA; | |
389 | r->dpms = DPMS; | |
390 | r->display_type = DISPLAY_TYPE; | |
391 | r->msc = MSC; | |
392 | r->redx = REDX; | |
393 | r->redy = REDY; | |
394 | r->greenx = GREENX; | |
395 | r->greeny = GREENY; | |
396 | r->bluex = BLUEX; | |
397 | r->bluey = BLUEY; | |
398 | r->whitex = WHITEX; | |
399 | r->whitey = WHITEY; | |
400 | } | |
401 | ||
402 | static void | |
403 | get_established_timing_section(Uchar * c, struct established_timings *r) | |
404 | { | |
405 | r->t1 = T1; | |
406 | r->t2 = T2; | |
407 | r->t_manu = T_MANU; | |
408 | } | |
409 | ||
410 | static void | |
411 | get_cvt_timing_section(Uchar * c, struct cvt_timings *r) | |
412 | { | |
413 | int i; | |
414 | ||
415 | for (i = 0; i < 4; i++) { | |
416 | if (c[0] && c[1] && c[2]) { | |
417 | r[i].height = (c[0] + ((c[1] & 0xF0) << 8) + 1) * 2; | |
418 | switch (c[1] & 0xc0) { | |
419 | case 0x00: | |
420 | r[i].width = r[i].height * 4 / 3; | |
421 | break; | |
422 | case 0x40: | |
423 | r[i].width = r[i].height * 16 / 9; | |
424 | break; | |
425 | case 0x80: | |
426 | r[i].width = r[i].height * 16 / 10; | |
427 | break; | |
428 | case 0xc0: | |
429 | r[i].width = r[i].height * 15 / 9; | |
430 | break; | |
431 | } | |
432 | switch (c[2] & 0x60) { | |
433 | case 0x00: | |
434 | r[i].rate = 50; | |
435 | break; | |
436 | case 0x20: | |
437 | r[i].rate = 60; | |
438 | break; | |
439 | case 0x40: | |
440 | r[i].rate = 75; | |
441 | break; | |
442 | case 0x60: | |
443 | r[i].rate = 85; | |
444 | break; | |
445 | } | |
446 | r[i].rates = c[2] & 0x1f; | |
447 | } | |
448 | else { | |
449 | return; | |
450 | } | |
451 | c += 3; | |
452 | } | |
453 | } | |
454 | ||
455 | static void | |
456 | get_std_timing_section(Uchar * c, struct std_timings *r, struct edid_version *v) | |
457 | { | |
458 | int i; | |
459 | ||
460 | for (i = 0; i < STD_TIMINGS; i++) { | |
461 | if (VALID_TIMING) { | |
462 | r[i].hsize = HSIZE1; | |
463 | VSIZE1(r[i].vsize); | |
464 | r[i].refresh = REFRESH_R; | |
465 | r[i].id = STD_TIMING_ID; | |
466 | } | |
467 | else { | |
468 | r[i].hsize = r[i].vsize = r[i].refresh = r[i].id = 0; | |
469 | } | |
470 | NEXT_STD_TIMING; | |
471 | } | |
472 | } | |
473 | ||
474 | static const unsigned char empty_block[18]; | |
475 | ||
476 | static void | |
477 | fetch_detailed_block(Uchar * c, struct edid_version *ver, | |
478 | struct detailed_monitor_section *det_mon) | |
479 | { | |
480 | if (ver->version == 1 && ver->revision >= 1 && IS_MONITOR_DESC) { | |
481 | switch (MONITOR_DESC_TYPE) { | |
482 | case SERIAL_NUMBER: | |
483 | det_mon->type = DS_SERIAL; | |
484 | copy_string(c, det_mon->section.serial); | |
485 | break; | |
486 | case ASCII_STR: | |
487 | det_mon->type = DS_ASCII_STR; | |
488 | copy_string(c, det_mon->section.ascii_data); | |
489 | break; | |
490 | case MONITOR_RANGES: | |
491 | det_mon->type = DS_RANGES; | |
492 | get_monitor_ranges(c, &det_mon->section.ranges); | |
493 | break; | |
494 | case MONITOR_NAME: | |
495 | det_mon->type = DS_NAME; | |
496 | copy_string(c, det_mon->section.name); | |
497 | break; | |
498 | case ADD_COLOR_POINT: | |
499 | det_mon->type = DS_WHITE_P; | |
500 | get_whitepoint_section(c, det_mon->section.wp); | |
501 | break; | |
502 | case ADD_STD_TIMINGS: | |
503 | det_mon->type = DS_STD_TIMINGS; | |
504 | get_dst_timing_section(c, det_mon->section.std_t, ver); | |
505 | break; | |
506 | case COLOR_MANAGEMENT_DATA: | |
507 | det_mon->type = DS_CMD; | |
508 | break; | |
509 | case CVT_3BYTE_DATA: | |
510 | det_mon->type = DS_CVT; | |
511 | get_cvt_timing_section(c, det_mon->section.cvt); | |
512 | break; | |
513 | case ADD_EST_TIMINGS: | |
514 | det_mon->type = DS_EST_III; | |
515 | memcpy(det_mon->section.est_iii, c + 6, 6); | |
516 | break; | |
517 | case ADD_DUMMY: | |
518 | det_mon->type = DS_DUMMY; | |
519 | break; | |
520 | default: | |
521 | det_mon->type = DS_UNKOWN; | |
522 | break; | |
523 | } | |
524 | if (c[3] <= 0x0F && memcmp(c, empty_block, sizeof(empty_block))) { | |
525 | det_mon->type = DS_VENDOR + c[3]; | |
526 | } | |
527 | } | |
528 | else { | |
529 | det_mon->type = DT; | |
530 | get_detailed_timing_section(c, &det_mon->section.d_timings); | |
531 | } | |
532 | } | |
533 | ||
534 | static void | |
535 | get_dt_md_section(Uchar * c, struct edid_version *ver, | |
536 | struct detailed_monitor_section *det_mon) | |
537 | { | |
538 | int i; | |
539 | ||
540 | for (i = 0; i < DET_TIMINGS; i++) { | |
541 | fetch_detailed_block(c, ver, det_mon + i); | |
542 | NEXT_DT_MD_SECTION; | |
543 | } | |
544 | } | |
545 | ||
546 | static void | |
547 | copy_string(Uchar * c, Uchar * s) | |
548 | { | |
549 | int i; | |
550 | ||
551 | c = c + 5; | |
552 | for (i = 0; (i < 13 && *c != 0x0A); i++) | |
553 | *(s++) = *(c++); | |
554 | *s = 0; | |
555 | while (i-- && (*--s == 0x20)) | |
556 | *s = 0; | |
557 | } | |
558 | ||
559 | static void | |
560 | get_dst_timing_section(Uchar * c, struct std_timings *t, struct edid_version *v) | |
561 | { | |
562 | int j; | |
563 | ||
564 | c = c + 5; | |
565 | for (j = 0; j < 5; j++) { | |
566 | t[j].hsize = HSIZE1; | |
567 | VSIZE1(t[j].vsize); | |
568 | t[j].refresh = REFRESH_R; | |
569 | t[j].id = STD_TIMING_ID; | |
570 | NEXT_STD_TIMING; | |
571 | } | |
572 | } | |
573 | ||
574 | static void | |
575 | get_monitor_ranges(Uchar * c, struct monitor_ranges *r) | |
576 | { | |
577 | r->min_v = MIN_V; | |
578 | r->max_v = MAX_V; | |
579 | r->min_h = MIN_H; | |
580 | r->max_h = MAX_H; | |
581 | r->max_clock = 0; | |
582 | if (MAX_CLOCK != 0xff) /* is specified? */ | |
583 | r->max_clock = MAX_CLOCK * 10 + 5; | |
584 | if (HAVE_2ND_GTF) { | |
585 | r->gtf_2nd_f = F_2ND_GTF; | |
586 | r->gtf_2nd_c = C_2ND_GTF; | |
587 | r->gtf_2nd_m = M_2ND_GTF; | |
588 | r->gtf_2nd_k = K_2ND_GTF; | |
589 | r->gtf_2nd_j = J_2ND_GTF; | |
590 | } | |
591 | else { | |
592 | r->gtf_2nd_f = 0; | |
593 | } | |
594 | if (HAVE_CVT) { | |
595 | r->max_clock_khz = MAX_CLOCK_KHZ; | |
596 | r->max_clock = r->max_clock_khz / 1000; | |
597 | r->maxwidth = MAXWIDTH; | |
598 | r->supported_aspect = SUPPORTED_ASPECT; | |
599 | r->preferred_aspect = PREFERRED_ASPECT; | |
600 | r->supported_blanking = SUPPORTED_BLANKING; | |
601 | r->supported_scaling = SUPPORTED_SCALING; | |
602 | r->preferred_refresh = PREFERRED_REFRESH; | |
603 | } | |
604 | else { | |
605 | r->max_clock_khz = 0; | |
606 | } | |
607 | } | |
608 | ||
609 | static void | |
610 | get_whitepoint_section(Uchar * c, struct whitePoints *wp) | |
611 | { | |
612 | wp[0].white_x = WHITEX1; | |
613 | wp[0].white_y = WHITEY1; | |
614 | wp[1].white_x = WHITEX2; | |
615 | wp[1].white_y = WHITEY2; | |
616 | wp[0].index = WHITE_INDEX1; | |
617 | wp[1].index = WHITE_INDEX2; | |
618 | wp[0].white_gamma = WHITE_GAMMA1; | |
619 | wp[1].white_gamma = WHITE_GAMMA2; | |
620 | } | |
621 | ||
622 | static void | |
623 | get_detailed_timing_section(Uchar * c, struct detailed_timings *r) | |
624 | { | |
625 | r->clock = PIXEL_CLOCK; | |
626 | r->h_active = H_ACTIVE; | |
627 | r->h_blanking = H_BLANK; | |
628 | r->v_active = V_ACTIVE; | |
629 | r->v_blanking = V_BLANK; | |
630 | r->h_sync_off = H_SYNC_OFF; | |
631 | r->h_sync_width = H_SYNC_WIDTH; | |
632 | r->v_sync_off = V_SYNC_OFF; | |
633 | r->v_sync_width = V_SYNC_WIDTH; | |
634 | r->h_size = H_SIZE; | |
635 | r->v_size = V_SIZE; | |
636 | r->h_border = H_BORDER; | |
637 | r->v_border = V_BORDER; | |
638 | r->interlaced = INTERLACED; | |
639 | r->stereo = STEREO; | |
640 | r->stereo_1 = STEREO1; | |
641 | r->sync = SYNC_T; | |
642 | r->misc = MISC; | |
643 | } | |
644 | ||
645 | #define MAX_EDID_MINOR 4 | |
646 | ||
647 | static Bool | |
648 | validate_version(int scrnIndex, struct edid_version *r) | |
649 | { | |
650 | if (r->version != 1) { | |
651 | xf86DrvMsg(scrnIndex, X_ERROR, "Unknown EDID version %d\n", r->version); | |
652 | return FALSE; | |
653 | } | |
654 | ||
655 | if (r->revision > MAX_EDID_MINOR) | |
656 | xf86DrvMsg(scrnIndex, X_WARNING, | |
657 | "Assuming version 1.%d is compatible with 1.%d\n", | |
658 | r->revision, MAX_EDID_MINOR); | |
659 | ||
660 | return TRUE; | |
661 | } | |
662 | ||
663 | /* | |
664 | * Returns true if HDMI, false if definitely not or unknown. | |
665 | */ | |
666 | Bool | |
667 | xf86MonitorIsHDMI(xf86MonPtr mon) | |
668 | { | |
669 | int i = 0, version, offset; | |
670 | char *edid = NULL; | |
671 | ||
672 | if (!mon) | |
673 | return FALSE; | |
674 | ||
675 | if (!(mon->flags & EDID_COMPLETE_RAWDATA)) | |
676 | return FALSE; | |
677 | ||
678 | if (!mon->no_sections) | |
679 | return FALSE; | |
680 | ||
681 | edid = (char *) mon->rawData; | |
682 | if (!edid) | |
683 | return FALSE; | |
684 | ||
685 | /* find the CEA extension block */ | |
686 | for (i = 1; i <= mon->no_sections; i++) | |
687 | if (edid[i * 128] == 0x02) | |
688 | break; | |
689 | if (i == mon->no_sections + 1) | |
690 | return FALSE; | |
691 | edid += (i * 128); | |
692 | ||
693 | version = edid[1]; | |
694 | offset = edid[2]; | |
695 | if (version < 3 || offset < 4) | |
696 | return FALSE; | |
697 | ||
698 | /* walk the cea data blocks */ | |
699 | for (i = 4; i < offset; i += (edid[i] & 0x1f) + 1) { | |
700 | char *x = edid + i; | |
701 | ||
702 | /* find a vendor specific block */ | |
703 | if ((x[0] & 0xe0) >> 5 == 0x03) { | |
704 | int oui = (x[3] << 16) + (x[2] << 8) + x[1]; | |
705 | ||
706 | /* find the HDMI vendor OUI */ | |
707 | if (oui == 0x000c03) | |
708 | return TRUE; | |
709 | } | |
710 | } | |
711 | ||
712 | /* guess it's not HDMI after all */ | |
713 | return FALSE; | |
714 | } |