14642e01fSmrg/*
205b261ecSmrg * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE>
34642e01fSmrg * Copyright 2007 Red Hat, Inc.
44642e01fSmrg *
54642e01fSmrg * Permission is hereby granted, free of charge, to any person obtaining a
66747b715Smrg * copy of this software and associated documentation files (the "Software"),
76747b715Smrg * to deal in the Software without restriction, including without limitation
86747b715Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
96747b715Smrg * and/or sell copies of the Software, and to permit persons to whom the
106747b715Smrg * Software is furnished to do so, subject to the following conditions:
114642e01fSmrg *
124642e01fSmrg * The above copyright notice and this permission notice (including the next
134642e01fSmrg * paragraph) shall be included in all copies or substantial portions of the
144642e01fSmrg * Software.
154642e01fSmrg *
164642e01fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
176747b715Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
186747b715Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
196747b715Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
206747b715Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
216747b715Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
226747b715Smrg * DEALINGS IN THE SOFTWARE.
234642e01fSmrg *
244642e01fSmrg * interpret_edid.c: interpret a primary EDID block
2505b261ecSmrg */
264642e01fSmrg
2705b261ecSmrg#ifdef HAVE_XORG_CONFIG_H
2805b261ecSmrg#include <xorg-config.h>
2905b261ecSmrg#endif
3005b261ecSmrg
3105b261ecSmrg#include "misc.h"
3205b261ecSmrg#include "xf86.h"
3305b261ecSmrg#include "xf86_OSproc.h"
3405b261ecSmrg#define _PARSE_EDID_
3505b261ecSmrg#include "xf86DDC.h"
3605b261ecSmrg#include <string.h>
3705b261ecSmrg
3835c4bbdfSmrgstatic void get_vendor_section(Uchar *, struct vendor *);
3935c4bbdfSmrgstatic void get_version_section(Uchar *, struct edid_version *);
4035c4bbdfSmrgstatic void get_display_section(Uchar *, struct disp_features *,
4135c4bbdfSmrg                                struct edid_version *);
4235c4bbdfSmrgstatic void get_established_timing_section(Uchar *,
4335c4bbdfSmrg                                           struct established_timings *);
4435c4bbdfSmrgstatic void get_std_timing_section(Uchar *, struct std_timings *,
4535c4bbdfSmrg                                   struct edid_version *);
4635c4bbdfSmrgstatic void fetch_detailed_block(Uchar * c, struct edid_version *ver,
476747b715Smrg                                 struct detailed_monitor_section *det_mon);
4805b261ecSmrgstatic void get_dt_md_section(Uchar *, struct edid_version *,
4935c4bbdfSmrg                              struct detailed_monitor_section *det_mon);
5005b261ecSmrgstatic void copy_string(Uchar *, Uchar *);
5105b261ecSmrgstatic void get_dst_timing_section(Uchar *, struct std_timings *,
5235c4bbdfSmrg                                   struct edid_version *);
5305b261ecSmrgstatic void get_monitor_ranges(Uchar *, struct monitor_ranges *);
5405b261ecSmrgstatic void get_whitepoint_section(Uchar *, struct whitePoints *);
5535c4bbdfSmrgstatic void get_detailed_timing_section(Uchar *, struct detailed_timings *);
5605b261ecSmrgstatic Bool validate_version(int scrnIndex, struct edid_version *);
5705b261ecSmrg
586747b715Smrgstatic void
596747b715Smrgfind_ranges_section(struct detailed_monitor_section *det, void *ranges)
606747b715Smrg{
6135c4bbdfSmrg    if (det->type == DS_RANGES && det->section.ranges.max_clock)
6235c4bbdfSmrg        *(struct monitor_ranges **) ranges = &det->section.ranges;
636747b715Smrg}
646747b715Smrg
656747b715Smrgstatic void
666747b715Smrgfind_max_detailed_clock(struct detailed_monitor_section *det, void *ret)
676747b715Smrg{
686747b715Smrg    if (det->type == DT) {
6935c4bbdfSmrg        *(int *) ret = max(*((int *) ret), det->section.d_timings.clock);
706747b715Smrg    }
716747b715Smrg}
726747b715Smrg
7305b261ecSmrgstatic void
7405b261ecSmrghandle_edid_quirks(xf86MonPtr m)
7505b261ecSmrg{
766747b715Smrg    struct monitor_ranges *ranges = NULL;
7705b261ecSmrg
7805b261ecSmrg    /*
7905b261ecSmrg     * max_clock is only encoded in EDID in tens of MHz, so occasionally we
8005b261ecSmrg     * find a monitor claiming a max of 160 with a mode requiring 162, or
8105b261ecSmrg     * similar.  Strictly we should refuse to round up too far, but let's
8205b261ecSmrg     * see how well this works.
8305b261ecSmrg     */
846747b715Smrg
8535c4bbdfSmrg    /* Try to find Monitor Range and max clock, then re-set range value */
866747b715Smrg    xf86ForEachDetailedBlock(m, find_ranges_section, &ranges);
876747b715Smrg    if (ranges && ranges->max_clock) {
886747b715Smrg        int clock = 0;
8935c4bbdfSmrg
906747b715Smrg        xf86ForEachDetailedBlock(m, find_max_detailed_clock, &clock);
916747b715Smrg        if (clock && (ranges->max_clock * 1e6 < clock)) {
926747b715Smrg            xf86Msg(X_WARNING, "EDID timing clock %.2f exceeds claimed max "
936747b715Smrg                    "%dMHz, fixing\n", clock / 1.0e6, ranges->max_clock);
9435c4bbdfSmrg            ranges->max_clock = (clock + 999999) / 1e6;
956747b715Smrg        }
9605b261ecSmrg    }
976747b715Smrg}
986747b715Smrg
996747b715Smrgstruct det_hv_parameter {
1006747b715Smrg    int real_hsize;
1016747b715Smrg    int real_vsize;
1026747b715Smrg    float target_aspect;
1036747b715Smrg};
1046747b715Smrg
10535c4bbdfSmrgstatic void
10635c4bbdfSmrghandle_detailed_hvsize(struct detailed_monitor_section *det_mon, void *data)
1076747b715Smrg{
10835c4bbdfSmrg    struct det_hv_parameter *p = (struct det_hv_parameter *) data;
1096747b715Smrg    float timing_aspect;
1106747b715Smrg
1116747b715Smrg    if (det_mon->type == DT) {
1126747b715Smrg        struct detailed_timings *timing;
11335c4bbdfSmrg
1146747b715Smrg        timing = &det_mon->section.d_timings;
1156747b715Smrg
1166747b715Smrg        if (!timing->v_size)
1176747b715Smrg            return;
1186747b715Smrg
11935c4bbdfSmrg        timing_aspect = (float) timing->h_size / timing->v_size;
1206747b715Smrg        if (fabs(1 - (timing_aspect / p->target_aspect)) < 0.05) {
1216747b715Smrg            p->real_hsize = max(p->real_hsize, timing->h_size);
1226747b715Smrg            p->real_vsize = max(p->real_vsize, timing->v_size);
1236747b715Smrg        }
1246747b715Smrg    }
1256747b715Smrg}
1264642e01fSmrg
12735c4bbdfSmrgstatic void
12835c4bbdfSmrgencode_aspect_ratio(xf86MonPtr m)
1296747b715Smrg{
1304642e01fSmrg    /*
1314642e01fSmrg     * some monitors encode the aspect ratio instead of the physical size.
1324642e01fSmrg     * try to find the largest detailed timing that matches that aspect
1334642e01fSmrg     * ratio and use that to fill in the feature section.
1344642e01fSmrg     */
1354642e01fSmrg    if ((m->features.hsize == 16 && m->features.vsize == 9) ||
13635c4bbdfSmrg        (m->features.hsize == 16 && m->features.vsize == 10) ||
13735c4bbdfSmrg        (m->features.hsize == 4 && m->features.vsize == 3) ||
13835c4bbdfSmrg        (m->features.hsize == 5 && m->features.vsize == 4)) {
1394642e01fSmrg
1406747b715Smrg        struct det_hv_parameter p;
14135c4bbdfSmrg
1426747b715Smrg        p.real_hsize = 0;
1436747b715Smrg        p.real_vsize = 0;
14435c4bbdfSmrg        p.target_aspect = (float) m->features.hsize / m->features.vsize;
1456747b715Smrg
1466747b715Smrg        xf86ForEachDetailedBlock(m, handle_detailed_hvsize, &p);
1476747b715Smrg
14835c4bbdfSmrg        if (!p.real_hsize || !p.real_vsize) {
14935c4bbdfSmrg            m->features.hsize = m->features.vsize = 0;
15035c4bbdfSmrg        }
15135c4bbdfSmrg        else if ((m->features.hsize * 10 == p.real_hsize) &&
15235c4bbdfSmrg                 (m->features.vsize * 10 == p.real_vsize)) {
15335c4bbdfSmrg            /* exact match is just unlikely, should do a better check though */
15435c4bbdfSmrg            m->features.hsize = m->features.vsize = 0;
15535c4bbdfSmrg        }
15635c4bbdfSmrg        else {
15735c4bbdfSmrg            /* convert mm to cm */
15835c4bbdfSmrg            m->features.hsize = (p.real_hsize + 5) / 10;
15935c4bbdfSmrg            m->features.vsize = (p.real_vsize + 5) / 10;
16035c4bbdfSmrg        }
16135c4bbdfSmrg
16235c4bbdfSmrg        xf86Msg(X_INFO, "Quirked EDID physical size to %dx%d cm\n",
16335c4bbdfSmrg                m->features.hsize, m->features.vsize);
1644642e01fSmrg    }
16505b261ecSmrg}
16605b261ecSmrg
16705b261ecSmrgxf86MonPtr
16835c4bbdfSmrgxf86InterpretEDID(int scrnIndex, Uchar * block)
16905b261ecSmrg{
17005b261ecSmrg    xf86MonPtr m;
17105b261ecSmrg
17235c4bbdfSmrg    if (!block)
17335c4bbdfSmrg        return NULL;
17435c4bbdfSmrg    if (!(m = xnfcalloc(sizeof(xf86Monitor), 1)))
17535c4bbdfSmrg        return NULL;
17605b261ecSmrg    m->scrnIndex = scrnIndex;
17705b261ecSmrg    m->rawData = block;
17805b261ecSmrg
17935c4bbdfSmrg    get_vendor_section(SECTION(VENDOR_SECTION, block), &m->vendor);
18035c4bbdfSmrg    get_version_section(SECTION(VERSION_SECTION, block), &m->ver);
18135c4bbdfSmrg    if (!validate_version(scrnIndex, &m->ver))
18235c4bbdfSmrg        goto error;
18335c4bbdfSmrg    get_display_section(SECTION(DISPLAY_SECTION, block), &m->features, &m->ver);
18435c4bbdfSmrg    get_established_timing_section(SECTION(ESTABLISHED_TIMING_SECTION, block),
18535c4bbdfSmrg                                   &m->timings1);
18635c4bbdfSmrg    get_std_timing_section(SECTION(STD_TIMING_SECTION, block), m->timings2,
18735c4bbdfSmrg                           &m->ver);
18835c4bbdfSmrg    get_dt_md_section(SECTION(DET_TIMING_SECTION, block), &m->ver, m->det_mon);
18935c4bbdfSmrg    m->no_sections = (int) *(char *) SECTION(NO_EDID, block);
19005b261ecSmrg
19105b261ecSmrg    handle_edid_quirks(m);
1926747b715Smrg    encode_aspect_ratio(m);
19305b261ecSmrg
1946747b715Smrg    return m;
19505b261ecSmrg
19605b261ecSmrg error:
1976747b715Smrg    free(m);
19805b261ecSmrg    return NULL;
19905b261ecSmrg}
20005b261ecSmrg
20135c4bbdfSmrgstatic int
20235c4bbdfSmrgget_cea_detail_timing(Uchar * blk, xf86MonPtr mon,
20335c4bbdfSmrg                      struct detailed_monitor_section *det_mon)
2046747b715Smrg{
2056747b715Smrg    int dt_num;
20635c4bbdfSmrg    int dt_offset = ((struct cea_ext_body *) blk)->dt_offset;
2076747b715Smrg
2086747b715Smrg    dt_num = 0;
2096747b715Smrg
2106747b715Smrg    if (dt_offset < CEA_EXT_MIN_DATA_OFFSET)
2116747b715Smrg        return dt_num;
2126747b715Smrg
2136747b715Smrg    for (; dt_offset < (CEA_EXT_MAX_DATA_OFFSET - DET_TIMING_INFO_LEN) &&
21435c4bbdfSmrg         dt_num < CEA_EXT_DET_TIMING_NUM; _NEXT_DT_MD_SECTION(dt_offset)) {
2156747b715Smrg
2166747b715Smrg        fetch_detailed_block(blk + dt_offset, &mon->ver, det_mon + dt_num);
21735c4bbdfSmrg        dt_num = dt_num + 1;
2186747b715Smrg    }
2196747b715Smrg
2206747b715Smrg    return dt_num;
2216747b715Smrg}
2226747b715Smrg
22335c4bbdfSmrgstatic void
22435c4bbdfSmrghandle_cea_detail_block(Uchar * ext, xf86MonPtr mon,
22535c4bbdfSmrg                        handle_detailed_fn fn, void *data)
2266747b715Smrg{
2276747b715Smrg    int i;
2286747b715Smrg    struct detailed_monitor_section det_mon[CEA_EXT_DET_TIMING_NUM];
2296747b715Smrg    int det_mon_num;
2306747b715Smrg
2316747b715Smrg    det_mon_num = get_cea_detail_timing(ext, mon, det_mon);
2326747b715Smrg
2336747b715Smrg    for (i = 0; i < det_mon_num; i++)
2346747b715Smrg        fn(det_mon + i, data);
2356747b715Smrg}
2366747b715Smrg
23735c4bbdfSmrgvoid
23835c4bbdfSmrgxf86ForEachDetailedBlock(xf86MonPtr mon, handle_detailed_fn fn, void *data)
2396747b715Smrg{
2406747b715Smrg    int i;
2416747b715Smrg    Uchar *ext;
2426747b715Smrg
2436747b715Smrg    if (mon == NULL)
2446747b715Smrg        return;
2456747b715Smrg
2466747b715Smrg    for (i = 0; i < DET_TIMINGS; i++)
2476747b715Smrg        fn(mon->det_mon + i, data);
2486747b715Smrg
2496747b715Smrg    for (i = 0; i < mon->no_sections; i++) {
2506747b715Smrg        ext = mon->rawData + EDID1_LEN * (i + 1);
25135c4bbdfSmrg        switch (ext[EXT_TAG]) {
2526747b715Smrg        case CEA_EXT:
2536747b715Smrg            handle_cea_detail_block(ext, mon, fn, data);
2546747b715Smrg            break;
2556747b715Smrg        case VTB_EXT:
2566747b715Smrg        case DI_EXT:
2576747b715Smrg        case LS_EXT:
2586747b715Smrg        case MI_EXT:
25935c4bbdfSmrg            break;
2606747b715Smrg        }
2616747b715Smrg    }
2626747b715Smrg}
2636747b715Smrg
2646747b715Smrgstatic struct cea_data_block *
26535c4bbdfSmrgextract_cea_data_block(Uchar * ext, int data_type)
2666747b715Smrg{
2676747b715Smrg    struct cea_ext_body *cea;
2686747b715Smrg    struct cea_data_block *data_collection;
2696747b715Smrg    struct cea_data_block *data_end;
2706747b715Smrg
27135c4bbdfSmrg    cea = (struct cea_ext_body *) ext;
2726747b715Smrg
2736747b715Smrg    if (cea->dt_offset <= CEA_EXT_MIN_DATA_OFFSET)
2746747b715Smrg        return NULL;
2756747b715Smrg
2766747b715Smrg    data_collection = &cea->data_collection;
27735c4bbdfSmrg    data_end = (struct cea_data_block *) (cea->dt_offset + ext);
2786747b715Smrg
27935c4bbdfSmrg    for (; data_collection < data_end;) {
2806747b715Smrg
28135c4bbdfSmrg        if (data_type == data_collection->tag) {
28235c4bbdfSmrg            return data_collection;
28335c4bbdfSmrg        }
28435c4bbdfSmrg        data_collection = (void *) ((unsigned char *) data_collection +
28535c4bbdfSmrg                                    data_collection->len + 1);
2866747b715Smrg    }
2876747b715Smrg
2886747b715Smrg    return NULL;
2896747b715Smrg}
2906747b715Smrg
29135c4bbdfSmrgstatic void
29235c4bbdfSmrghandle_cea_video_block(Uchar * ext, handle_video_fn fn, void *data)
2936747b715Smrg{
2946747b715Smrg    struct cea_video_block *video;
2956747b715Smrg    struct cea_video_block *video_end;
2966747b715Smrg    struct cea_data_block *data_collection;
2976747b715Smrg
2986747b715Smrg    data_collection = extract_cea_data_block(ext, CEA_VIDEO_BLK);
2996747b715Smrg    if (data_collection == NULL)
3006747b715Smrg        return;
3016747b715Smrg
3026747b715Smrg    video = &data_collection->u.video;
3036747b715Smrg    video_end = (struct cea_video_block *)
30435c4bbdfSmrg        ((Uchar *) video + data_collection->len);
3056747b715Smrg
3066747b715Smrg    for (; video < video_end; video = video + 1) {
30735c4bbdfSmrg        fn(video, data);
3086747b715Smrg    }
3096747b715Smrg}
3106747b715Smrg
31135c4bbdfSmrgvoid
31235c4bbdfSmrgxf86ForEachVideoBlock(xf86MonPtr mon, handle_video_fn fn, void *data)
3136747b715Smrg{
3146747b715Smrg    int i;
3156747b715Smrg    Uchar *ext;
3166747b715Smrg
3176747b715Smrg    if (mon == NULL)
31835c4bbdfSmrg        return;
3196747b715Smrg
3206747b715Smrg    for (i = 0; i < mon->no_sections; i++) {
32135c4bbdfSmrg        ext = mon->rawData + EDID1_LEN * (i + 1);
32235c4bbdfSmrg        switch (ext[EXT_TAG]) {
32335c4bbdfSmrg        case CEA_EXT:
32435c4bbdfSmrg            handle_cea_video_block(ext, fn, data);
32535c4bbdfSmrg            break;
32635c4bbdfSmrg        case VTB_EXT:
32735c4bbdfSmrg        case DI_EXT:
32835c4bbdfSmrg        case LS_EXT:
32935c4bbdfSmrg        case MI_EXT:
33035c4bbdfSmrg            break;
33135c4bbdfSmrg        }
3326747b715Smrg    }
3336747b715Smrg}
3346747b715Smrg
33535c4bbdfSmrgstatic Bool
33635c4bbdfSmrgcea_db_offsets(Uchar *cea, int *start, int *end)
33735c4bbdfSmrg{
33835c4bbdfSmrg    /* Data block offset in CEA extension block */
33935c4bbdfSmrg    *start = CEA_EXT_MIN_DATA_OFFSET;
34035c4bbdfSmrg    *end = cea[2];
34135c4bbdfSmrg    if (*end == 0)
34235c4bbdfSmrg        *end = CEA_EXT_MAX_DATA_OFFSET;
34335c4bbdfSmrg    if (*end < CEA_EXT_MIN_DATA_OFFSET || *end > CEA_EXT_MAX_DATA_OFFSET)
34435c4bbdfSmrg        return FALSE;
34535c4bbdfSmrg    return TRUE;
34635c4bbdfSmrg}
34735c4bbdfSmrg
34835c4bbdfSmrgstatic int
34935c4bbdfSmrgcea_db_len(Uchar *db)
35035c4bbdfSmrg{
35135c4bbdfSmrg    return db[0] & 0x1f;
35235c4bbdfSmrg}
35335c4bbdfSmrg
35435c4bbdfSmrgstatic int
35535c4bbdfSmrgcea_db_tag(Uchar *db)
35635c4bbdfSmrg{
35735c4bbdfSmrg    return db[0] >> 5;
35835c4bbdfSmrg}
35935c4bbdfSmrg
36035c4bbdfSmrgtypedef void (*handle_cea_db_fn) (Uchar *, void *);
36135c4bbdfSmrg
36235c4bbdfSmrgstatic void
36335c4bbdfSmrgcea_for_each_db(xf86MonPtr mon, handle_cea_db_fn fn, void *data)
36435c4bbdfSmrg{
36535c4bbdfSmrg    int i;
36635c4bbdfSmrg
36735c4bbdfSmrg    if (!mon)
36835c4bbdfSmrg        return;
36935c4bbdfSmrg
37035c4bbdfSmrg    if (!(mon->flags & EDID_COMPLETE_RAWDATA))
37135c4bbdfSmrg        return;
37235c4bbdfSmrg
37335c4bbdfSmrg    if (!mon->no_sections)
37435c4bbdfSmrg        return;
37535c4bbdfSmrg
37635c4bbdfSmrg    if (!mon->rawData)
37735c4bbdfSmrg        return;
37835c4bbdfSmrg
37935c4bbdfSmrg    for (i = 0; i < mon->no_sections; i++) {
38035c4bbdfSmrg        int start, end, offset;
38135c4bbdfSmrg        Uchar *ext;
38235c4bbdfSmrg
38335c4bbdfSmrg        ext = mon->rawData + EDID1_LEN * (i + 1);
38435c4bbdfSmrg        if (ext[EXT_TAG] != CEA_EXT)
38535c4bbdfSmrg            continue;
38635c4bbdfSmrg
38735c4bbdfSmrg        if (!cea_db_offsets(ext, &start, &end))
38835c4bbdfSmrg            continue;
38935c4bbdfSmrg
39035c4bbdfSmrg        for (offset = start;
39135c4bbdfSmrg             offset < end && offset + cea_db_len(&ext[offset]) < end;
39235c4bbdfSmrg             offset += cea_db_len(&ext[offset]) + 1)
39335c4bbdfSmrg                fn(&ext[offset], data);
39435c4bbdfSmrg    }
39535c4bbdfSmrg}
39635c4bbdfSmrg
39735c4bbdfSmrgstruct find_hdmi_block_data {
39835c4bbdfSmrg    struct cea_data_block *hdmi;
39935c4bbdfSmrg};
40035c4bbdfSmrg
40135c4bbdfSmrgstatic void find_hdmi_block(Uchar *db, void *data)
40235c4bbdfSmrg{
40335c4bbdfSmrg    struct find_hdmi_block_data *result = data;
40435c4bbdfSmrg    int oui;
40535c4bbdfSmrg
40635c4bbdfSmrg    if (cea_db_tag(db) != CEA_VENDOR_BLK)
40735c4bbdfSmrg        return;
40835c4bbdfSmrg
40935c4bbdfSmrg    if (cea_db_len(db) < 5)
41035c4bbdfSmrg        return;
41135c4bbdfSmrg
41235c4bbdfSmrg    oui = (db[3] << 16) | (db[2] << 8) | db[1];
41335c4bbdfSmrg    if (oui == IEEE_ID_HDMI)
41435c4bbdfSmrg        result->hdmi = (struct cea_data_block *)db;
41535c4bbdfSmrg}
41635c4bbdfSmrg
41735c4bbdfSmrgstruct cea_data_block *xf86MonitorFindHDMIBlock(xf86MonPtr mon)
41835c4bbdfSmrg{
41935c4bbdfSmrg    struct find_hdmi_block_data result = { NULL };
42035c4bbdfSmrg
42135c4bbdfSmrg    cea_for_each_db(mon, find_hdmi_block, &result);
42235c4bbdfSmrg
42335c4bbdfSmrg    return result.hdmi;
42435c4bbdfSmrg}
42535c4bbdfSmrg
4264642e01fSmrgxf86MonPtr
42735c4bbdfSmrgxf86InterpretEEDID(int scrnIndex, Uchar * block)
4284642e01fSmrg{
4294642e01fSmrg    xf86MonPtr m;
4304642e01fSmrg
4314642e01fSmrg    m = xf86InterpretEDID(scrnIndex, block);
4324642e01fSmrg    if (!m)
43335c4bbdfSmrg        return NULL;
4344642e01fSmrg
4354642e01fSmrg    /* extension parse */
4364642e01fSmrg
4374642e01fSmrg    return m;
4384642e01fSmrg}
4394642e01fSmrg
44005b261ecSmrgstatic void
44135c4bbdfSmrgget_vendor_section(Uchar * c, struct vendor *r)
44205b261ecSmrg{
44305b261ecSmrg    r->name[0] = L1;
44405b261ecSmrg    r->name[1] = L2;
44505b261ecSmrg    r->name[2] = L3;
44605b261ecSmrg    r->name[3] = '\0';
44735c4bbdfSmrg
44805b261ecSmrg    r->prod_id = PROD_ID;
44935c4bbdfSmrg    r->serial = SERIAL_NO;
45035c4bbdfSmrg    r->week = WEEK;
45135c4bbdfSmrg    r->year = YEAR;
45205b261ecSmrg}
45305b261ecSmrg
45435c4bbdfSmrgstatic void
45535c4bbdfSmrgget_version_section(Uchar * c, struct edid_version *r)
45605b261ecSmrg{
45735c4bbdfSmrg    r->version = VERSION;
45805b261ecSmrg    r->revision = REVISION;
45905b261ecSmrg}
46005b261ecSmrg
46135c4bbdfSmrgstatic void
46235c4bbdfSmrgget_display_section(Uchar * c, struct disp_features *r, struct edid_version *v)
46305b261ecSmrg{
46405b261ecSmrg    r->input_type = INPUT_TYPE;
46505b261ecSmrg    if (!DIGITAL(r->input_type)) {
46635c4bbdfSmrg        r->input_voltage = INPUT_VOLTAGE;
46735c4bbdfSmrg        r->input_setup = SETUP;
46835c4bbdfSmrg        r->input_sync = SYNC;
46935c4bbdfSmrg    }
47035c4bbdfSmrg    else if (v->revision == 2 || v->revision == 3) {
47135c4bbdfSmrg        r->input_dfp = DFP;
47235c4bbdfSmrg    }
47335c4bbdfSmrg    else if (v->revision >= 4) {
47435c4bbdfSmrg        r->input_bpc = BPC;
47535c4bbdfSmrg        r->input_interface = DIGITAL_INTERFACE;
4764642e01fSmrg    }
47705b261ecSmrg    r->hsize = HSIZE_MAX;
47805b261ecSmrg    r->vsize = VSIZE_MAX;
47905b261ecSmrg    r->gamma = GAMMA;
48035c4bbdfSmrg    r->dpms = DPMS;
48105b261ecSmrg    r->display_type = DISPLAY_TYPE;
48205b261ecSmrg    r->msc = MSC;
48305b261ecSmrg    r->redx = REDX;
48405b261ecSmrg    r->redy = REDY;
48505b261ecSmrg    r->greenx = GREENX;
48605b261ecSmrg    r->greeny = GREENY;
48705b261ecSmrg    r->bluex = BLUEX;
48805b261ecSmrg    r->bluey = BLUEY;
48905b261ecSmrg    r->whitex = WHITEX;
49005b261ecSmrg    r->whitey = WHITEY;
49105b261ecSmrg}
49205b261ecSmrg
49335c4bbdfSmrgstatic void
49435c4bbdfSmrgget_established_timing_section(Uchar * c, struct established_timings *r)
49505b261ecSmrg{
49605b261ecSmrg    r->t1 = T1;
49705b261ecSmrg    r->t2 = T2;
49805b261ecSmrg    r->t_manu = T_MANU;
49905b261ecSmrg}
50005b261ecSmrg
5014642e01fSmrgstatic void
50235c4bbdfSmrgget_cvt_timing_section(Uchar * c, struct cvt_timings *r)
5034642e01fSmrg{
5044642e01fSmrg    int i;
5054642e01fSmrg
5064642e01fSmrg    for (i = 0; i < 4; i++) {
50735c4bbdfSmrg        if (c[0] && c[1] && c[2]) {
50835c4bbdfSmrg            r[i].height = (c[0] + ((c[1] & 0xF0) << 8) + 1) * 2;
50935c4bbdfSmrg            switch (c[1] & 0xc0) {
51035c4bbdfSmrg            case 0x00:
51135c4bbdfSmrg                r[i].width = r[i].height * 4 / 3;
51235c4bbdfSmrg                break;
51335c4bbdfSmrg            case 0x40:
51435c4bbdfSmrg                r[i].width = r[i].height * 16 / 9;
51535c4bbdfSmrg                break;
51635c4bbdfSmrg            case 0x80:
51735c4bbdfSmrg                r[i].width = r[i].height * 16 / 10;
51835c4bbdfSmrg                break;
51935c4bbdfSmrg            case 0xc0:
52035c4bbdfSmrg                r[i].width = r[i].height * 15 / 9;
52135c4bbdfSmrg                break;
52235c4bbdfSmrg            }
52335c4bbdfSmrg            switch (c[2] & 0x60) {
52435c4bbdfSmrg            case 0x00:
52535c4bbdfSmrg                r[i].rate = 50;
52635c4bbdfSmrg                break;
52735c4bbdfSmrg            case 0x20:
52835c4bbdfSmrg                r[i].rate = 60;
52935c4bbdfSmrg                break;
53035c4bbdfSmrg            case 0x40:
53135c4bbdfSmrg                r[i].rate = 75;
53235c4bbdfSmrg                break;
53335c4bbdfSmrg            case 0x60:
53435c4bbdfSmrg                r[i].rate = 85;
53535c4bbdfSmrg                break;
53635c4bbdfSmrg            }
53735c4bbdfSmrg            r[i].rates = c[2] & 0x1f;
53835c4bbdfSmrg        }
53935c4bbdfSmrg        else {
54035c4bbdfSmrg            return;
54135c4bbdfSmrg        }
54235c4bbdfSmrg        c += 3;
5434642e01fSmrg    }
5444642e01fSmrg}
5454642e01fSmrg
54605b261ecSmrgstatic void
54735c4bbdfSmrgget_std_timing_section(Uchar * c, struct std_timings *r, struct edid_version *v)
54805b261ecSmrg{
54905b261ecSmrg    int i;
55005b261ecSmrg
55135c4bbdfSmrg    for (i = 0; i < STD_TIMINGS; i++) {
55235c4bbdfSmrg        if (VALID_TIMING) {
55335c4bbdfSmrg            r[i].hsize = HSIZE1;
55435c4bbdfSmrg            VSIZE1(r[i].vsize);
55535c4bbdfSmrg            r[i].refresh = REFRESH_R;
55635c4bbdfSmrg            r[i].id = STD_TIMING_ID;
55735c4bbdfSmrg        }
55835c4bbdfSmrg        else {
55935c4bbdfSmrg            r[i].hsize = r[i].vsize = r[i].refresh = r[i].id = 0;
56035c4bbdfSmrg        }
56135c4bbdfSmrg        NEXT_STD_TIMING;
56205b261ecSmrg    }
56305b261ecSmrg}
56405b261ecSmrg
56552397711Smrgstatic const unsigned char empty_block[18];
56652397711Smrg
56705b261ecSmrgstatic void
56835c4bbdfSmrgfetch_detailed_block(Uchar * c, struct edid_version *ver,
5696747b715Smrg                     struct detailed_monitor_section *det_mon)
57005b261ecSmrg{
57105b261ecSmrg    if (ver->version == 1 && ver->revision >= 1 && IS_MONITOR_DESC) {
5726747b715Smrg        switch (MONITOR_DESC_TYPE) {
5736747b715Smrg        case SERIAL_NUMBER:
5746747b715Smrg            det_mon->type = DS_SERIAL;
57535c4bbdfSmrg            copy_string(c, det_mon->section.serial);
5766747b715Smrg            break;
5776747b715Smrg        case ASCII_STR:
5786747b715Smrg            det_mon->type = DS_ASCII_STR;
57935c4bbdfSmrg            copy_string(c, det_mon->section.ascii_data);
5806747b715Smrg            break;
5816747b715Smrg        case MONITOR_RANGES:
5826747b715Smrg            det_mon->type = DS_RANGES;
58335c4bbdfSmrg            get_monitor_ranges(c, &det_mon->section.ranges);
5846747b715Smrg            break;
5856747b715Smrg        case MONITOR_NAME:
5866747b715Smrg            det_mon->type = DS_NAME;
58735c4bbdfSmrg            copy_string(c, det_mon->section.name);
5886747b715Smrg            break;
5896747b715Smrg        case ADD_COLOR_POINT:
5906747b715Smrg            det_mon->type = DS_WHITE_P;
59135c4bbdfSmrg            get_whitepoint_section(c, det_mon->section.wp);
5926747b715Smrg            break;
5936747b715Smrg        case ADD_STD_TIMINGS:
5946747b715Smrg            det_mon->type = DS_STD_TIMINGS;
59535c4bbdfSmrg            get_dst_timing_section(c, det_mon->section.std_t, ver);
5966747b715Smrg            break;
5976747b715Smrg        case COLOR_MANAGEMENT_DATA:
5986747b715Smrg            det_mon->type = DS_CMD;
5996747b715Smrg            break;
6006747b715Smrg        case CVT_3BYTE_DATA:
6016747b715Smrg            det_mon->type = DS_CVT;
6026747b715Smrg            get_cvt_timing_section(c, det_mon->section.cvt);
6036747b715Smrg            break;
6046747b715Smrg        case ADD_EST_TIMINGS:
6056747b715Smrg            det_mon->type = DS_EST_III;
60635c4bbdfSmrg            memcpy(det_mon->section.est_iii, c + 6, 6);
6076747b715Smrg            break;
6086747b715Smrg        case ADD_DUMMY:
6096747b715Smrg            det_mon->type = DS_DUMMY;
6106747b715Smrg            break;
6116747b715Smrg        default:
6126747b715Smrg            det_mon->type = DS_UNKOWN;
6136747b715Smrg            break;
6146747b715Smrg        }
61535c4bbdfSmrg        if (c[3] <= 0x0F && memcmp(c, empty_block, sizeof(empty_block))) {
6166747b715Smrg            det_mon->type = DS_VENDOR + c[3];
6176747b715Smrg        }
61835c4bbdfSmrg    }
61935c4bbdfSmrg    else {
6206747b715Smrg        det_mon->type = DT;
6216747b715Smrg        get_detailed_timing_section(c, &det_mon->section.d_timings);
6226747b715Smrg    }
6236747b715Smrg}
6246747b715Smrg
6256747b715Smrgstatic void
62635c4bbdfSmrgget_dt_md_section(Uchar * c, struct edid_version *ver,
62735c4bbdfSmrg                  struct detailed_monitor_section *det_mon)
6286747b715Smrg{
6296747b715Smrg    int i;
6306747b715Smrg
63135c4bbdfSmrg    for (i = 0; i < DET_TIMINGS; i++) {
6326747b715Smrg        fetch_detailed_block(c, ver, det_mon + i);
6336747b715Smrg        NEXT_DT_MD_SECTION;
63405b261ecSmrg    }
63505b261ecSmrg}
63605b261ecSmrg
63705b261ecSmrgstatic void
63835c4bbdfSmrgcopy_string(Uchar * c, Uchar * s)
63905b261ecSmrg{
64035c4bbdfSmrg    int i;
64135c4bbdfSmrg
64235c4bbdfSmrg    c = c + 5;
64335c4bbdfSmrg    for (i = 0; (i < 13 && *c != 0x0A); i++)
64435c4bbdfSmrg        *(s++) = *(c++);
64535c4bbdfSmrg    *s = 0;
64635c4bbdfSmrg    while (i-- && (*--s == 0x20))
64735c4bbdfSmrg        *s = 0;
64805b261ecSmrg}
64905b261ecSmrg
65005b261ecSmrgstatic void
65135c4bbdfSmrgget_dst_timing_section(Uchar * c, struct std_timings *t, struct edid_version *v)
65205b261ecSmrg{
65335c4bbdfSmrg    int j;
65435c4bbdfSmrg
65505b261ecSmrg    c = c + 5;
65605b261ecSmrg    for (j = 0; j < 5; j++) {
65735c4bbdfSmrg        t[j].hsize = HSIZE1;
65835c4bbdfSmrg        VSIZE1(t[j].vsize);
65935c4bbdfSmrg        t[j].refresh = REFRESH_R;
66035c4bbdfSmrg        t[j].id = STD_TIMING_ID;
66135c4bbdfSmrg        NEXT_STD_TIMING;
66205b261ecSmrg    }
66305b261ecSmrg}
66405b261ecSmrg
66505b261ecSmrgstatic void
66635c4bbdfSmrgget_monitor_ranges(Uchar * c, struct monitor_ranges *r)
66705b261ecSmrg{
66805b261ecSmrg    r->min_v = MIN_V;
66905b261ecSmrg    r->max_v = MAX_V;
67005b261ecSmrg    r->min_h = MIN_H;
67105b261ecSmrg    r->max_h = MAX_H;
67205b261ecSmrg    r->max_clock = 0;
67335c4bbdfSmrg    if (MAX_CLOCK != 0xff)      /* is specified? */
67435c4bbdfSmrg        r->max_clock = MAX_CLOCK * 10 + 5;
6755a7dfde8Smrg
6765a7dfde8Smrg    r->display_range_timing_flags = c[10];
6775a7dfde8Smrg
67805b261ecSmrg    if (HAVE_2ND_GTF) {
67935c4bbdfSmrg        r->gtf_2nd_f = F_2ND_GTF;
68035c4bbdfSmrg        r->gtf_2nd_c = C_2ND_GTF;
68135c4bbdfSmrg        r->gtf_2nd_m = M_2ND_GTF;
68235c4bbdfSmrg        r->gtf_2nd_k = K_2ND_GTF;
68335c4bbdfSmrg        r->gtf_2nd_j = J_2ND_GTF;
68435c4bbdfSmrg    }
68535c4bbdfSmrg    else {
68635c4bbdfSmrg        r->gtf_2nd_f = 0;
6874642e01fSmrg    }
6884642e01fSmrg    if (HAVE_CVT) {
68935c4bbdfSmrg        r->max_clock_khz = MAX_CLOCK_KHZ;
69035c4bbdfSmrg        r->max_clock = r->max_clock_khz / 1000;
69135c4bbdfSmrg        r->maxwidth = MAXWIDTH;
69235c4bbdfSmrg        r->supported_aspect = SUPPORTED_ASPECT;
69335c4bbdfSmrg        r->preferred_aspect = PREFERRED_ASPECT;
69435c4bbdfSmrg        r->supported_blanking = SUPPORTED_BLANKING;
69535c4bbdfSmrg        r->supported_scaling = SUPPORTED_SCALING;
69635c4bbdfSmrg        r->preferred_refresh = PREFERRED_REFRESH;
69735c4bbdfSmrg    }
69835c4bbdfSmrg    else {
69935c4bbdfSmrg        r->max_clock_khz = 0;
7004642e01fSmrg    }
70105b261ecSmrg}
70205b261ecSmrg
70305b261ecSmrgstatic void
70435c4bbdfSmrgget_whitepoint_section(Uchar * c, struct whitePoints *wp)
70505b261ecSmrg{
7064642e01fSmrg    wp[0].white_x = WHITEX1;
7074642e01fSmrg    wp[0].white_y = WHITEY1;
7084642e01fSmrg    wp[1].white_x = WHITEX2;
7094642e01fSmrg    wp[1].white_y = WHITEY2;
71035c4bbdfSmrg    wp[0].index = WHITE_INDEX1;
71135c4bbdfSmrg    wp[1].index = WHITE_INDEX2;
71235c4bbdfSmrg    wp[0].white_gamma = WHITE_GAMMA1;
71335c4bbdfSmrg    wp[1].white_gamma = WHITE_GAMMA2;
71405b261ecSmrg}
71505b261ecSmrg
71605b261ecSmrgstatic void
71735c4bbdfSmrgget_detailed_timing_section(Uchar * c, struct detailed_timings *r)
71835c4bbdfSmrg{
71935c4bbdfSmrg    r->clock = PIXEL_CLOCK;
72035c4bbdfSmrg    r->h_active = H_ACTIVE;
72135c4bbdfSmrg    r->h_blanking = H_BLANK;
72235c4bbdfSmrg    r->v_active = V_ACTIVE;
72335c4bbdfSmrg    r->v_blanking = V_BLANK;
72435c4bbdfSmrg    r->h_sync_off = H_SYNC_OFF;
72535c4bbdfSmrg    r->h_sync_width = H_SYNC_WIDTH;
72635c4bbdfSmrg    r->v_sync_off = V_SYNC_OFF;
72735c4bbdfSmrg    r->v_sync_width = V_SYNC_WIDTH;
72835c4bbdfSmrg    r->h_size = H_SIZE;
72935c4bbdfSmrg    r->v_size = V_SIZE;
73035c4bbdfSmrg    r->h_border = H_BORDER;
73135c4bbdfSmrg    r->v_border = V_BORDER;
73235c4bbdfSmrg    r->interlaced = INTERLACED;
73335c4bbdfSmrg    r->stereo = STEREO;
73435c4bbdfSmrg    r->stereo_1 = STEREO1;
73535c4bbdfSmrg    r->sync = SYNC_T;
73635c4bbdfSmrg    r->misc = MISC;
73705b261ecSmrg}
73805b261ecSmrg
7394642e01fSmrg#define MAX_EDID_MINOR 4
74005b261ecSmrg
74105b261ecSmrgstatic Bool
74205b261ecSmrgvalidate_version(int scrnIndex, struct edid_version *r)
74305b261ecSmrg{
7444642e01fSmrg    if (r->version != 1) {
74535c4bbdfSmrg        xf86DrvMsg(scrnIndex, X_ERROR, "Unknown EDID version %d\n", r->version);
74635c4bbdfSmrg        return FALSE;
7474642e01fSmrg    }
74805b261ecSmrg
74905b261ecSmrg    if (r->revision > MAX_EDID_MINOR)
75035c4bbdfSmrg        xf86DrvMsg(scrnIndex, X_WARNING,
75135c4bbdfSmrg                   "Assuming version 1.%d is compatible with 1.%d\n",
75235c4bbdfSmrg                   r->revision, MAX_EDID_MINOR);
75305b261ecSmrg
75405b261ecSmrg    return TRUE;
75505b261ecSmrg}
7564642e01fSmrg
7575a7dfde8SmrgBool
7585a7dfde8Smrggtf_supported(xf86MonPtr mon)
7595a7dfde8Smrg{
7605a7dfde8Smrg    int i;
7615a7dfde8Smrg
7625a7dfde8Smrg    if (!mon)
7635a7dfde8Smrg        return FALSE;
7645a7dfde8Smrg
7655a7dfde8Smrg    if ((mon->ver.version == 1) && (mon->ver.revision < 4)) {
7665a7dfde8Smrg        if (mon->features.msc & 0x1)
7675a7dfde8Smrg	    return TRUE;
7685a7dfde8Smrg    } else {
7695a7dfde8Smrg        for (i = 0; i < DET_TIMINGS; i++) {
7705a7dfde8Smrg            struct detailed_monitor_section *det_timing_des = &(mon->det_mon[i]);
771ed6184dfSmrg            if (det_timing_des && (det_timing_des->type == DS_RANGES) && (mon->features.msc & 0x1) &&
7725a7dfde8Smrg                (det_timing_des->section.ranges.display_range_timing_flags == DR_DEFAULT_GTF
7735a7dfde8Smrg		|| det_timing_des->section.ranges.display_range_timing_flags == DR_SECONDARY_GTF))
7745a7dfde8Smrg		    return TRUE;
7755a7dfde8Smrg	}
7765a7dfde8Smrg    }
7775a7dfde8Smrg
7785a7dfde8Smrg    return FALSE;
7795a7dfde8Smrg}
7805a7dfde8Smrg
7814642e01fSmrg/*
7824642e01fSmrg * Returns true if HDMI, false if definitely not or unknown.
7834642e01fSmrg */
7846747b715SmrgBool
7854642e01fSmrgxf86MonitorIsHDMI(xf86MonPtr mon)
7864642e01fSmrg{
78735c4bbdfSmrg    return xf86MonitorFindHDMIBlock(mon) != NULL;
7884642e01fSmrg}
789