interpret_edid.c revision ed6184df
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
38static void get_vendor_section(Uchar *, struct vendor *);
39static void get_version_section(Uchar *, struct edid_version *);
40static void get_display_section(Uchar *, struct disp_features *,
41                                struct edid_version *);
42static void get_established_timing_section(Uchar *,
43                                           struct established_timings *);
44static void get_std_timing_section(Uchar *, struct std_timings *,
45                                   struct edid_version *);
46static void fetch_detailed_block(Uchar * c, struct edid_version *ver,
47                                 struct detailed_monitor_section *det_mon);
48static void get_dt_md_section(Uchar *, struct edid_version *,
49                              struct detailed_monitor_section *det_mon);
50static void copy_string(Uchar *, Uchar *);
51static void get_dst_timing_section(Uchar *, struct std_timings *,
52                                   struct edid_version *);
53static void get_monitor_ranges(Uchar *, struct monitor_ranges *);
54static void get_whitepoint_section(Uchar *, struct whitePoints *);
55static void get_detailed_timing_section(Uchar *, struct detailed_timings *);
56static Bool validate_version(int scrnIndex, struct edid_version *);
57
58static void
59find_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
65static void
66find_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
73static void
74handle_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
99struct det_hv_parameter {
100    int real_hsize;
101    int real_vsize;
102    float target_aspect;
103};
104
105static void
106handle_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
127static void
128encode_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
167xf86MonPtr
168xf86InterpretEDID(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
201static int
202get_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
223static void
224handle_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
237void
238xf86ForEachDetailedBlock(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
264static struct cea_data_block *
265extract_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
291static void
292handle_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
311void
312xf86ForEachVideoBlock(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
335static Bool
336cea_db_offsets(Uchar *cea, int *start, int *end)
337{
338    /* Data block offset in CEA extension block */
339    *start = CEA_EXT_MIN_DATA_OFFSET;
340    *end = cea[2];
341    if (*end == 0)
342        *end = CEA_EXT_MAX_DATA_OFFSET;
343    if (*end < CEA_EXT_MIN_DATA_OFFSET || *end > CEA_EXT_MAX_DATA_OFFSET)
344        return FALSE;
345    return TRUE;
346}
347
348static int
349cea_db_len(Uchar *db)
350{
351    return db[0] & 0x1f;
352}
353
354static int
355cea_db_tag(Uchar *db)
356{
357    return db[0] >> 5;
358}
359
360typedef void (*handle_cea_db_fn) (Uchar *, void *);
361
362static void
363cea_for_each_db(xf86MonPtr mon, handle_cea_db_fn fn, void *data)
364{
365    int i;
366
367    if (!mon)
368        return;
369
370    if (!(mon->flags & EDID_COMPLETE_RAWDATA))
371        return;
372
373    if (!mon->no_sections)
374        return;
375
376    if (!mon->rawData)
377        return;
378
379    for (i = 0; i < mon->no_sections; i++) {
380        int start, end, offset;
381        Uchar *ext;
382
383        ext = mon->rawData + EDID1_LEN * (i + 1);
384        if (ext[EXT_TAG] != CEA_EXT)
385            continue;
386
387        if (!cea_db_offsets(ext, &start, &end))
388            continue;
389
390        for (offset = start;
391             offset < end && offset + cea_db_len(&ext[offset]) < end;
392             offset += cea_db_len(&ext[offset]) + 1)
393                fn(&ext[offset], data);
394    }
395}
396
397struct find_hdmi_block_data {
398    struct cea_data_block *hdmi;
399};
400
401static void find_hdmi_block(Uchar *db, void *data)
402{
403    struct find_hdmi_block_data *result = data;
404    int oui;
405
406    if (cea_db_tag(db) != CEA_VENDOR_BLK)
407        return;
408
409    if (cea_db_len(db) < 5)
410        return;
411
412    oui = (db[3] << 16) | (db[2] << 8) | db[1];
413    if (oui == IEEE_ID_HDMI)
414        result->hdmi = (struct cea_data_block *)db;
415}
416
417struct cea_data_block *xf86MonitorFindHDMIBlock(xf86MonPtr mon)
418{
419    struct find_hdmi_block_data result = { NULL };
420
421    cea_for_each_db(mon, find_hdmi_block, &result);
422
423    return result.hdmi;
424}
425
426xf86MonPtr
427xf86InterpretEEDID(int scrnIndex, Uchar * block)
428{
429    xf86MonPtr m;
430
431    m = xf86InterpretEDID(scrnIndex, block);
432    if (!m)
433        return NULL;
434
435    /* extension parse */
436
437    return m;
438}
439
440static void
441get_vendor_section(Uchar * c, struct vendor *r)
442{
443    r->name[0] = L1;
444    r->name[1] = L2;
445    r->name[2] = L3;
446    r->name[3] = '\0';
447
448    r->prod_id = PROD_ID;
449    r->serial = SERIAL_NO;
450    r->week = WEEK;
451    r->year = YEAR;
452}
453
454static void
455get_version_section(Uchar * c, struct edid_version *r)
456{
457    r->version = VERSION;
458    r->revision = REVISION;
459}
460
461static void
462get_display_section(Uchar * c, struct disp_features *r, struct edid_version *v)
463{
464    r->input_type = INPUT_TYPE;
465    if (!DIGITAL(r->input_type)) {
466        r->input_voltage = INPUT_VOLTAGE;
467        r->input_setup = SETUP;
468        r->input_sync = SYNC;
469    }
470    else if (v->revision == 2 || v->revision == 3) {
471        r->input_dfp = DFP;
472    }
473    else if (v->revision >= 4) {
474        r->input_bpc = BPC;
475        r->input_interface = DIGITAL_INTERFACE;
476    }
477    r->hsize = HSIZE_MAX;
478    r->vsize = VSIZE_MAX;
479    r->gamma = GAMMA;
480    r->dpms = DPMS;
481    r->display_type = DISPLAY_TYPE;
482    r->msc = MSC;
483    r->redx = REDX;
484    r->redy = REDY;
485    r->greenx = GREENX;
486    r->greeny = GREENY;
487    r->bluex = BLUEX;
488    r->bluey = BLUEY;
489    r->whitex = WHITEX;
490    r->whitey = WHITEY;
491}
492
493static void
494get_established_timing_section(Uchar * c, struct established_timings *r)
495{
496    r->t1 = T1;
497    r->t2 = T2;
498    r->t_manu = T_MANU;
499}
500
501static void
502get_cvt_timing_section(Uchar * c, struct cvt_timings *r)
503{
504    int i;
505
506    for (i = 0; i < 4; i++) {
507        if (c[0] && c[1] && c[2]) {
508            r[i].height = (c[0] + ((c[1] & 0xF0) << 8) + 1) * 2;
509            switch (c[1] & 0xc0) {
510            case 0x00:
511                r[i].width = r[i].height * 4 / 3;
512                break;
513            case 0x40:
514                r[i].width = r[i].height * 16 / 9;
515                break;
516            case 0x80:
517                r[i].width = r[i].height * 16 / 10;
518                break;
519            case 0xc0:
520                r[i].width = r[i].height * 15 / 9;
521                break;
522            }
523            switch (c[2] & 0x60) {
524            case 0x00:
525                r[i].rate = 50;
526                break;
527            case 0x20:
528                r[i].rate = 60;
529                break;
530            case 0x40:
531                r[i].rate = 75;
532                break;
533            case 0x60:
534                r[i].rate = 85;
535                break;
536            }
537            r[i].rates = c[2] & 0x1f;
538        }
539        else {
540            return;
541        }
542        c += 3;
543    }
544}
545
546static void
547get_std_timing_section(Uchar * c, struct std_timings *r, struct edid_version *v)
548{
549    int i;
550
551    for (i = 0; i < STD_TIMINGS; i++) {
552        if (VALID_TIMING) {
553            r[i].hsize = HSIZE1;
554            VSIZE1(r[i].vsize);
555            r[i].refresh = REFRESH_R;
556            r[i].id = STD_TIMING_ID;
557        }
558        else {
559            r[i].hsize = r[i].vsize = r[i].refresh = r[i].id = 0;
560        }
561        NEXT_STD_TIMING;
562    }
563}
564
565static const unsigned char empty_block[18];
566
567static void
568fetch_detailed_block(Uchar * c, struct edid_version *ver,
569                     struct detailed_monitor_section *det_mon)
570{
571    if (ver->version == 1 && ver->revision >= 1 && IS_MONITOR_DESC) {
572        switch (MONITOR_DESC_TYPE) {
573        case SERIAL_NUMBER:
574            det_mon->type = DS_SERIAL;
575            copy_string(c, det_mon->section.serial);
576            break;
577        case ASCII_STR:
578            det_mon->type = DS_ASCII_STR;
579            copy_string(c, det_mon->section.ascii_data);
580            break;
581        case MONITOR_RANGES:
582            det_mon->type = DS_RANGES;
583            get_monitor_ranges(c, &det_mon->section.ranges);
584            break;
585        case MONITOR_NAME:
586            det_mon->type = DS_NAME;
587            copy_string(c, det_mon->section.name);
588            break;
589        case ADD_COLOR_POINT:
590            det_mon->type = DS_WHITE_P;
591            get_whitepoint_section(c, det_mon->section.wp);
592            break;
593        case ADD_STD_TIMINGS:
594            det_mon->type = DS_STD_TIMINGS;
595            get_dst_timing_section(c, det_mon->section.std_t, ver);
596            break;
597        case COLOR_MANAGEMENT_DATA:
598            det_mon->type = DS_CMD;
599            break;
600        case CVT_3BYTE_DATA:
601            det_mon->type = DS_CVT;
602            get_cvt_timing_section(c, det_mon->section.cvt);
603            break;
604        case ADD_EST_TIMINGS:
605            det_mon->type = DS_EST_III;
606            memcpy(det_mon->section.est_iii, c + 6, 6);
607            break;
608        case ADD_DUMMY:
609            det_mon->type = DS_DUMMY;
610            break;
611        default:
612            det_mon->type = DS_UNKOWN;
613            break;
614        }
615        if (c[3] <= 0x0F && memcmp(c, empty_block, sizeof(empty_block))) {
616            det_mon->type = DS_VENDOR + c[3];
617        }
618    }
619    else {
620        det_mon->type = DT;
621        get_detailed_timing_section(c, &det_mon->section.d_timings);
622    }
623}
624
625static void
626get_dt_md_section(Uchar * c, struct edid_version *ver,
627                  struct detailed_monitor_section *det_mon)
628{
629    int i;
630
631    for (i = 0; i < DET_TIMINGS; i++) {
632        fetch_detailed_block(c, ver, det_mon + i);
633        NEXT_DT_MD_SECTION;
634    }
635}
636
637static void
638copy_string(Uchar * c, Uchar * s)
639{
640    int i;
641
642    c = c + 5;
643    for (i = 0; (i < 13 && *c != 0x0A); i++)
644        *(s++) = *(c++);
645    *s = 0;
646    while (i-- && (*--s == 0x20))
647        *s = 0;
648}
649
650static void
651get_dst_timing_section(Uchar * c, struct std_timings *t, struct edid_version *v)
652{
653    int j;
654
655    c = c + 5;
656    for (j = 0; j < 5; j++) {
657        t[j].hsize = HSIZE1;
658        VSIZE1(t[j].vsize);
659        t[j].refresh = REFRESH_R;
660        t[j].id = STD_TIMING_ID;
661        NEXT_STD_TIMING;
662    }
663}
664
665static void
666get_monitor_ranges(Uchar * c, struct monitor_ranges *r)
667{
668    r->min_v = MIN_V;
669    r->max_v = MAX_V;
670    r->min_h = MIN_H;
671    r->max_h = MAX_H;
672    r->max_clock = 0;
673    if (MAX_CLOCK != 0xff)      /* is specified? */
674        r->max_clock = MAX_CLOCK * 10 + 5;
675
676    r->display_range_timing_flags = c[10];
677
678    if (HAVE_2ND_GTF) {
679        r->gtf_2nd_f = F_2ND_GTF;
680        r->gtf_2nd_c = C_2ND_GTF;
681        r->gtf_2nd_m = M_2ND_GTF;
682        r->gtf_2nd_k = K_2ND_GTF;
683        r->gtf_2nd_j = J_2ND_GTF;
684    }
685    else {
686        r->gtf_2nd_f = 0;
687    }
688    if (HAVE_CVT) {
689        r->max_clock_khz = MAX_CLOCK_KHZ;
690        r->max_clock = r->max_clock_khz / 1000;
691        r->maxwidth = MAXWIDTH;
692        r->supported_aspect = SUPPORTED_ASPECT;
693        r->preferred_aspect = PREFERRED_ASPECT;
694        r->supported_blanking = SUPPORTED_BLANKING;
695        r->supported_scaling = SUPPORTED_SCALING;
696        r->preferred_refresh = PREFERRED_REFRESH;
697    }
698    else {
699        r->max_clock_khz = 0;
700    }
701}
702
703static void
704get_whitepoint_section(Uchar * c, struct whitePoints *wp)
705{
706    wp[0].white_x = WHITEX1;
707    wp[0].white_y = WHITEY1;
708    wp[1].white_x = WHITEX2;
709    wp[1].white_y = WHITEY2;
710    wp[0].index = WHITE_INDEX1;
711    wp[1].index = WHITE_INDEX2;
712    wp[0].white_gamma = WHITE_GAMMA1;
713    wp[1].white_gamma = WHITE_GAMMA2;
714}
715
716static void
717get_detailed_timing_section(Uchar * c, struct detailed_timings *r)
718{
719    r->clock = PIXEL_CLOCK;
720    r->h_active = H_ACTIVE;
721    r->h_blanking = H_BLANK;
722    r->v_active = V_ACTIVE;
723    r->v_blanking = V_BLANK;
724    r->h_sync_off = H_SYNC_OFF;
725    r->h_sync_width = H_SYNC_WIDTH;
726    r->v_sync_off = V_SYNC_OFF;
727    r->v_sync_width = V_SYNC_WIDTH;
728    r->h_size = H_SIZE;
729    r->v_size = V_SIZE;
730    r->h_border = H_BORDER;
731    r->v_border = V_BORDER;
732    r->interlaced = INTERLACED;
733    r->stereo = STEREO;
734    r->stereo_1 = STEREO1;
735    r->sync = SYNC_T;
736    r->misc = MISC;
737}
738
739#define MAX_EDID_MINOR 4
740
741static Bool
742validate_version(int scrnIndex, struct edid_version *r)
743{
744    if (r->version != 1) {
745        xf86DrvMsg(scrnIndex, X_ERROR, "Unknown EDID version %d\n", r->version);
746        return FALSE;
747    }
748
749    if (r->revision > MAX_EDID_MINOR)
750        xf86DrvMsg(scrnIndex, X_WARNING,
751                   "Assuming version 1.%d is compatible with 1.%d\n",
752                   r->revision, MAX_EDID_MINOR);
753
754    return TRUE;
755}
756
757Bool
758gtf_supported(xf86MonPtr mon)
759{
760    int i;
761
762    if (!mon)
763        return FALSE;
764
765    if ((mon->ver.version == 1) && (mon->ver.revision < 4)) {
766        if (mon->features.msc & 0x1)
767	    return TRUE;
768    } else {
769        for (i = 0; i < DET_TIMINGS; i++) {
770            struct detailed_monitor_section *det_timing_des = &(mon->det_mon[i]);
771            if (det_timing_des && (det_timing_des->type == DS_RANGES) && (mon->features.msc & 0x1) &&
772                (det_timing_des->section.ranges.display_range_timing_flags == DR_DEFAULT_GTF
773		|| det_timing_des->section.ranges.display_range_timing_flags == DR_SECONDARY_GTF))
774		    return TRUE;
775	}
776    }
777
778    return FALSE;
779}
780
781/*
782 * Returns true if HDMI, false if definitely not or unknown.
783 */
784Bool
785xf86MonitorIsHDMI(xf86MonPtr mon)
786{
787    return xf86MonitorFindHDMIBlock(mon) != NULL;
788}
789