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