1706f2543Smrg/* 2706f2543Smrg * Copyright 1998 by Egbert Eich <Egbert.Eich@Physik.TU-Darmstadt.DE> 3706f2543Smrg * Copyright 2007 Red Hat, Inc. 4706f2543Smrg * 5706f2543Smrg * Permission is hereby granted, free of charge, to any person obtaining a 6706f2543Smrg * copy of this software and associated documentation files (the "Software"), 7706f2543Smrg * to deal in the Software without restriction, including without limitation 8706f2543Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9706f2543Smrg * and/or sell copies of the Software, and to permit persons to whom the 10706f2543Smrg * Software is furnished to do so, subject to the following conditions: 11706f2543Smrg * 12706f2543Smrg * The above copyright notice and this permission notice (including the next 13706f2543Smrg * paragraph) shall be included in all copies or substantial portions of the 14706f2543Smrg * Software. 15706f2543Smrg * 16706f2543Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17706f2543Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18706f2543Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19706f2543Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20706f2543Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21706f2543Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22706f2543Smrg * DEALINGS IN THE SOFTWARE. 23706f2543Smrg * 24706f2543Smrg * interpret_edid.c: interpret a primary EDID block 25706f2543Smrg */ 26706f2543Smrg 27706f2543Smrg#ifdef HAVE_XORG_CONFIG_H 28706f2543Smrg#include <xorg-config.h> 29706f2543Smrg#endif 30706f2543Smrg 31706f2543Smrg#include "misc.h" 32706f2543Smrg#include "xf86.h" 33706f2543Smrg#include "xf86_OSproc.h" 34706f2543Smrg#define _PARSE_EDID_ 35706f2543Smrg#include "xf86DDC.h" 36706f2543Smrg#include <string.h> 37706f2543Smrg 38706f2543Smrgstatic void get_vendor_section(Uchar*, struct vendor *); 39706f2543Smrgstatic void get_version_section(Uchar*, struct edid_version *); 40706f2543Smrgstatic void get_display_section(Uchar*, struct disp_features *, 41706f2543Smrg struct edid_version *); 42706f2543Smrgstatic void get_established_timing_section(Uchar*, struct established_timings *); 43706f2543Smrgstatic void get_std_timing_section(Uchar*, struct std_timings *, 44706f2543Smrg struct edid_version *); 45706f2543Smrgstatic void fetch_detailed_block(Uchar *c, struct edid_version *ver, 46706f2543Smrg struct detailed_monitor_section *det_mon); 47706f2543Smrgstatic void get_dt_md_section(Uchar *, struct edid_version *, 48706f2543Smrg struct detailed_monitor_section *det_mon); 49706f2543Smrgstatic void copy_string(Uchar *, Uchar *); 50706f2543Smrgstatic void get_dst_timing_section(Uchar *, struct std_timings *, 51706f2543Smrg struct edid_version *); 52706f2543Smrgstatic void get_monitor_ranges(Uchar *, struct monitor_ranges *); 53706f2543Smrgstatic void get_whitepoint_section(Uchar *, struct whitePoints *); 54706f2543Smrgstatic void get_detailed_timing_section(Uchar*, struct detailed_timings *); 55706f2543Smrgstatic Bool validate_version(int scrnIndex, struct edid_version *); 56706f2543Smrg 57706f2543Smrgstatic void 58706f2543Smrgfind_ranges_section(struct detailed_monitor_section *det, void *ranges) 59706f2543Smrg{ 60706f2543Smrg if (det->type == DS_RANGES && det->section.ranges.max_clock) 61706f2543Smrg *(struct monitor_ranges **)ranges = &det->section.ranges; 62706f2543Smrg} 63706f2543Smrg 64706f2543Smrgstatic void 65706f2543Smrgfind_max_detailed_clock(struct detailed_monitor_section *det, void *ret) 66706f2543Smrg{ 67706f2543Smrg if (det->type == DT) { 68706f2543Smrg *(int *)ret = max(*((int *)ret), 69706f2543Smrg det->section.d_timings.clock); 70706f2543Smrg } 71706f2543Smrg} 72706f2543Smrg 73706f2543Smrgstatic void 74706f2543Smrghandle_edid_quirks(xf86MonPtr m) 75706f2543Smrg{ 76706f2543Smrg struct monitor_ranges *ranges = NULL; 77706f2543Smrg 78706f2543Smrg /* 79706f2543Smrg * max_clock is only encoded in EDID in tens of MHz, so occasionally we 80706f2543Smrg * find a monitor claiming a max of 160 with a mode requiring 162, or 81706f2543Smrg * similar. Strictly we should refuse to round up too far, but let's 82706f2543Smrg * see how well this works. 83706f2543Smrg */ 84706f2543Smrg 85706f2543Smrg /* Try to find Monitor Range and max clock, then re-set range value*/ 86706f2543Smrg xf86ForEachDetailedBlock(m, find_ranges_section, &ranges); 87706f2543Smrg if (ranges && ranges->max_clock) { 88706f2543Smrg int clock = 0; 89706f2543Smrg xf86ForEachDetailedBlock(m, find_max_detailed_clock, &clock); 90706f2543Smrg if (clock && (ranges->max_clock * 1e6 < clock)) { 91706f2543Smrg xf86Msg(X_WARNING, "EDID timing clock %.2f exceeds claimed max " 92706f2543Smrg "%dMHz, fixing\n", clock / 1.0e6, ranges->max_clock); 93706f2543Smrg ranges->max_clock = (clock+999999)/1e6; 94706f2543Smrg } 95706f2543Smrg } 96706f2543Smrg} 97706f2543Smrg 98706f2543Smrgstruct det_hv_parameter { 99706f2543Smrg int real_hsize; 100706f2543Smrg int real_vsize; 101706f2543Smrg float target_aspect; 102706f2543Smrg}; 103706f2543Smrg 104706f2543Smrgstatic void handle_detailed_hvsize(struct detailed_monitor_section *det_mon, 105706f2543Smrg void *data) 106706f2543Smrg{ 107706f2543Smrg struct det_hv_parameter *p = (struct det_hv_parameter *)data; 108706f2543Smrg float timing_aspect; 109706f2543Smrg 110706f2543Smrg if (det_mon->type == DT) { 111706f2543Smrg struct detailed_timings *timing; 112706f2543Smrg timing = &det_mon->section.d_timings; 113706f2543Smrg 114706f2543Smrg if (!timing->v_size) 115706f2543Smrg return; 116706f2543Smrg 117706f2543Smrg timing_aspect = (float)timing->h_size / timing->v_size; 118706f2543Smrg if (fabs(1 - (timing_aspect / p->target_aspect)) < 0.05) { 119706f2543Smrg p->real_hsize = max(p->real_hsize, timing->h_size); 120706f2543Smrg p->real_vsize = max(p->real_vsize, timing->v_size); 121706f2543Smrg } 122706f2543Smrg } 123706f2543Smrg} 124706f2543Smrg 125706f2543Smrgstatic void encode_aspect_ratio(xf86MonPtr m) 126706f2543Smrg{ 127706f2543Smrg /* 128706f2543Smrg * some monitors encode the aspect ratio instead of the physical size. 129706f2543Smrg * try to find the largest detailed timing that matches that aspect 130706f2543Smrg * ratio and use that to fill in the feature section. 131706f2543Smrg */ 132706f2543Smrg if ((m->features.hsize == 16 && m->features.vsize == 9) || 133706f2543Smrg (m->features.hsize == 16 && m->features.vsize == 10) || 134706f2543Smrg (m->features.hsize == 4 && m->features.vsize == 3) || 135706f2543Smrg (m->features.hsize == 5 && m->features.vsize == 4)) { 136706f2543Smrg 137706f2543Smrg struct det_hv_parameter p; 138706f2543Smrg p.real_hsize = 0; 139706f2543Smrg p.real_vsize = 0; 140706f2543Smrg p.target_aspect = (float)m->features.hsize /m->features.vsize; 141706f2543Smrg 142706f2543Smrg xf86ForEachDetailedBlock(m, handle_detailed_hvsize, &p); 143706f2543Smrg 144706f2543Smrg if (!p.real_hsize || !p.real_vsize) { 145706f2543Smrg m->features.hsize = m->features.vsize = 0; 146706f2543Smrg } else if ((m->features.hsize * 10 == p.real_hsize) && 147706f2543Smrg (m->features.vsize * 10 == p.real_vsize)) { 148706f2543Smrg /* exact match is just unlikely, should do a better check though */ 149706f2543Smrg m->features.hsize = m->features.vsize = 0; 150706f2543Smrg } else { 151706f2543Smrg /* convert mm to cm */ 152706f2543Smrg m->features.hsize = (p.real_hsize + 5) / 10; 153706f2543Smrg m->features.vsize = (p.real_vsize + 5) / 10; 154706f2543Smrg } 155706f2543Smrg 156706f2543Smrg xf86Msg(X_INFO, "Quirked EDID physical size to %dx%d cm\n", 157706f2543Smrg m->features.hsize, m->features.vsize); 158706f2543Smrg } 159706f2543Smrg} 160706f2543Smrg 161706f2543Smrgxf86MonPtr 162706f2543Smrgxf86InterpretEDID(int scrnIndex, Uchar *block) 163706f2543Smrg{ 164706f2543Smrg xf86MonPtr m; 165706f2543Smrg 166706f2543Smrg if (!block) return NULL; 167706f2543Smrg if (! (m = xnfcalloc(sizeof(xf86Monitor),1))) return NULL; 168706f2543Smrg m->scrnIndex = scrnIndex; 169706f2543Smrg m->rawData = block; 170706f2543Smrg 171706f2543Smrg get_vendor_section(SECTION(VENDOR_SECTION,block),&m->vendor); 172706f2543Smrg get_version_section(SECTION(VERSION_SECTION,block),&m->ver); 173706f2543Smrg if (!validate_version(scrnIndex, &m->ver)) goto error; 174706f2543Smrg get_display_section(SECTION(DISPLAY_SECTION,block),&m->features, 175706f2543Smrg &m->ver); 176706f2543Smrg get_established_timing_section(SECTION(ESTABLISHED_TIMING_SECTION,block), 177706f2543Smrg &m->timings1); 178706f2543Smrg get_std_timing_section(SECTION(STD_TIMING_SECTION,block),m->timings2, 179706f2543Smrg &m->ver); 180706f2543Smrg get_dt_md_section(SECTION(DET_TIMING_SECTION,block),&m->ver, m->det_mon); 181706f2543Smrg m->no_sections = (int)*(char *)SECTION(NO_EDID,block); 182706f2543Smrg 183706f2543Smrg handle_edid_quirks(m); 184706f2543Smrg encode_aspect_ratio(m); 185706f2543Smrg 186706f2543Smrg return m; 187706f2543Smrg 188706f2543Smrg error: 189706f2543Smrg free(m); 190706f2543Smrg return NULL; 191706f2543Smrg} 192706f2543Smrg 193706f2543Smrgstatic int get_cea_detail_timing(Uchar *blk, xf86MonPtr mon, 194706f2543Smrg struct detailed_monitor_section *det_mon) 195706f2543Smrg{ 196706f2543Smrg int dt_num; 197706f2543Smrg int dt_offset = ((struct cea_ext_body *)blk)->dt_offset; 198706f2543Smrg 199706f2543Smrg dt_num = 0; 200706f2543Smrg 201706f2543Smrg if (dt_offset < CEA_EXT_MIN_DATA_OFFSET) 202706f2543Smrg return dt_num; 203706f2543Smrg 204706f2543Smrg for (; dt_offset < (CEA_EXT_MAX_DATA_OFFSET - DET_TIMING_INFO_LEN) && 205706f2543Smrg dt_num < CEA_EXT_DET_TIMING_NUM; 206706f2543Smrg _NEXT_DT_MD_SECTION(dt_offset)) { 207706f2543Smrg 208706f2543Smrg fetch_detailed_block(blk + dt_offset, &mon->ver, det_mon + dt_num); 209706f2543Smrg dt_num = dt_num + 1 ; 210706f2543Smrg } 211706f2543Smrg 212706f2543Smrg return dt_num; 213706f2543Smrg} 214706f2543Smrg 215706f2543Smrgstatic void handle_cea_detail_block(Uchar *ext, xf86MonPtr mon, 216706f2543Smrg handle_detailed_fn fn, 217706f2543Smrg void *data) 218706f2543Smrg{ 219706f2543Smrg int i; 220706f2543Smrg struct detailed_monitor_section det_mon[CEA_EXT_DET_TIMING_NUM]; 221706f2543Smrg int det_mon_num; 222706f2543Smrg 223706f2543Smrg det_mon_num = get_cea_detail_timing(ext, mon, det_mon); 224706f2543Smrg 225706f2543Smrg for (i = 0; i < det_mon_num; i++) 226706f2543Smrg fn(det_mon + i, data); 227706f2543Smrg} 228706f2543Smrg 229706f2543Smrgvoid xf86ForEachDetailedBlock(xf86MonPtr mon, 230706f2543Smrg handle_detailed_fn fn, 231706f2543Smrg void *data) 232706f2543Smrg{ 233706f2543Smrg int i; 234706f2543Smrg Uchar *ext; 235706f2543Smrg 236706f2543Smrg if (mon == NULL) 237706f2543Smrg return; 238706f2543Smrg 239706f2543Smrg for (i = 0; i < DET_TIMINGS; i++) 240706f2543Smrg fn(mon->det_mon + i, data); 241706f2543Smrg 242706f2543Smrg for (i = 0; i < mon->no_sections; i++) { 243706f2543Smrg ext = mon->rawData + EDID1_LEN * (i + 1); 244706f2543Smrg switch (ext[EXT_TAG]){ 245706f2543Smrg case CEA_EXT: 246706f2543Smrg handle_cea_detail_block(ext, mon, fn, data); 247706f2543Smrg break; 248706f2543Smrg case VTB_EXT: 249706f2543Smrg case DI_EXT: 250706f2543Smrg case LS_EXT: 251706f2543Smrg case MI_EXT: 252706f2543Smrg break; 253706f2543Smrg } 254706f2543Smrg } 255706f2543Smrg} 256706f2543Smrg 257706f2543Smrgstatic struct cea_data_block * 258706f2543Smrgextract_cea_data_block(Uchar *ext, int data_type) 259706f2543Smrg{ 260706f2543Smrg struct cea_ext_body *cea; 261706f2543Smrg struct cea_data_block *data_collection; 262706f2543Smrg struct cea_data_block *data_end; 263706f2543Smrg 264706f2543Smrg cea = (struct cea_ext_body *)ext; 265706f2543Smrg 266706f2543Smrg if (cea->dt_offset <= CEA_EXT_MIN_DATA_OFFSET) 267706f2543Smrg return NULL; 268706f2543Smrg 269706f2543Smrg data_collection = &cea->data_collection; 270706f2543Smrg data_end = (struct cea_data_block *)(cea->dt_offset + ext); 271706f2543Smrg 272706f2543Smrg for ( ;data_collection < data_end;) { 273706f2543Smrg 274706f2543Smrg if (data_type == data_collection->tag) { 275706f2543Smrg return data_collection; 276706f2543Smrg } 277706f2543Smrg data_collection = (void *)((unsigned char *)data_collection + 278706f2543Smrg data_collection->len + 1); 279706f2543Smrg } 280706f2543Smrg 281706f2543Smrg return NULL; 282706f2543Smrg} 283706f2543Smrg 284706f2543Smrgstatic void handle_cea_video_block(Uchar *ext, handle_video_fn fn, void *data) 285706f2543Smrg{ 286706f2543Smrg struct cea_video_block *video; 287706f2543Smrg struct cea_video_block *video_end; 288706f2543Smrg struct cea_data_block *data_collection; 289706f2543Smrg 290706f2543Smrg data_collection = extract_cea_data_block(ext, CEA_VIDEO_BLK); 291706f2543Smrg if (data_collection == NULL) 292706f2543Smrg return; 293706f2543Smrg 294706f2543Smrg video = &data_collection->u.video; 295706f2543Smrg video_end = (struct cea_video_block *) 296706f2543Smrg ((Uchar *)video + data_collection->len); 297706f2543Smrg 298706f2543Smrg for (; video < video_end; video = video + 1) { 299706f2543Smrg fn(video, data); 300706f2543Smrg } 301706f2543Smrg} 302706f2543Smrg 303706f2543Smrgvoid xf86ForEachVideoBlock(xf86MonPtr mon, 304706f2543Smrg handle_video_fn fn, 305706f2543Smrg void *data) 306706f2543Smrg{ 307706f2543Smrg int i; 308706f2543Smrg Uchar *ext; 309706f2543Smrg 310706f2543Smrg if (mon == NULL) 311706f2543Smrg return; 312706f2543Smrg 313706f2543Smrg for (i = 0; i < mon->no_sections; i++) { 314706f2543Smrg ext = mon->rawData + EDID1_LEN * (i + 1); 315706f2543Smrg switch (ext[EXT_TAG]) { 316706f2543Smrg case CEA_EXT: 317706f2543Smrg handle_cea_video_block(ext, fn, data); 318706f2543Smrg break; 319706f2543Smrg case VTB_EXT: 320706f2543Smrg case DI_EXT: 321706f2543Smrg case LS_EXT: 322706f2543Smrg case MI_EXT: 323706f2543Smrg break; 324706f2543Smrg } 325706f2543Smrg } 326706f2543Smrg} 327706f2543Smrg 328706f2543Smrgxf86MonPtr 329706f2543Smrgxf86InterpretEEDID(int scrnIndex, Uchar *block) 330706f2543Smrg{ 331706f2543Smrg xf86MonPtr m; 332706f2543Smrg 333706f2543Smrg m = xf86InterpretEDID(scrnIndex, block); 334706f2543Smrg if (!m) 335706f2543Smrg return NULL; 336706f2543Smrg 337706f2543Smrg /* extension parse */ 338706f2543Smrg 339706f2543Smrg return m; 340706f2543Smrg} 341706f2543Smrg 342706f2543Smrgstatic void 343706f2543Smrgget_vendor_section(Uchar *c, struct vendor *r) 344706f2543Smrg{ 345706f2543Smrg r->name[0] = L1; 346706f2543Smrg r->name[1] = L2; 347706f2543Smrg r->name[2] = L3; 348706f2543Smrg r->name[3] = '\0'; 349706f2543Smrg 350706f2543Smrg r->prod_id = PROD_ID; 351706f2543Smrg r->serial = SERIAL_NO; 352706f2543Smrg r->week = WEEK; 353706f2543Smrg r->year = YEAR; 354706f2543Smrg} 355706f2543Smrg 356706f2543Smrgstatic void 357706f2543Smrgget_version_section(Uchar *c, struct edid_version *r) 358706f2543Smrg{ 359706f2543Smrg r->version = VERSION; 360706f2543Smrg r->revision = REVISION; 361706f2543Smrg} 362706f2543Smrg 363706f2543Smrgstatic void 364706f2543Smrgget_display_section(Uchar *c, struct disp_features *r, 365706f2543Smrg struct edid_version *v) 366706f2543Smrg{ 367706f2543Smrg r->input_type = INPUT_TYPE; 368706f2543Smrg if (!DIGITAL(r->input_type)) { 369706f2543Smrg r->input_voltage = INPUT_VOLTAGE; 370706f2543Smrg r->input_setup = SETUP; 371706f2543Smrg r->input_sync = SYNC; 372706f2543Smrg } else if (v->revision == 2 || v->revision == 3) { 373706f2543Smrg r->input_dfp = DFP; 374706f2543Smrg } else if (v->revision >= 4) { 375706f2543Smrg r->input_bpc = BPC; 376706f2543Smrg r->input_interface = DIGITAL_INTERFACE; 377706f2543Smrg } 378706f2543Smrg r->hsize = HSIZE_MAX; 379706f2543Smrg r->vsize = VSIZE_MAX; 380706f2543Smrg r->gamma = GAMMA; 381706f2543Smrg r->dpms = DPMS; 382706f2543Smrg r->display_type = DISPLAY_TYPE; 383706f2543Smrg r->msc = MSC; 384706f2543Smrg r->redx = REDX; 385706f2543Smrg r->redy = REDY; 386706f2543Smrg r->greenx = GREENX; 387706f2543Smrg r->greeny = GREENY; 388706f2543Smrg r->bluex = BLUEX; 389706f2543Smrg r->bluey = BLUEY; 390706f2543Smrg r->whitex = WHITEX; 391706f2543Smrg r->whitey = WHITEY; 392706f2543Smrg} 393706f2543Smrg 394706f2543Smrgstatic void 395706f2543Smrgget_established_timing_section(Uchar *c, struct established_timings *r) 396706f2543Smrg{ 397706f2543Smrg r->t1 = T1; 398706f2543Smrg r->t2 = T2; 399706f2543Smrg r->t_manu = T_MANU; 400706f2543Smrg} 401706f2543Smrg 402706f2543Smrgstatic void 403706f2543Smrgget_cvt_timing_section(Uchar *c, struct cvt_timings *r) 404706f2543Smrg{ 405706f2543Smrg int i; 406706f2543Smrg 407706f2543Smrg for (i = 0; i < 4; i++) { 408706f2543Smrg if (c[0] && c[1] && c[2]) { 409706f2543Smrg r[i].height = (c[0] + ((c[1] & 0xF0) << 8) + 1) * 2; 410706f2543Smrg switch (c[1] & 0xc0) { 411706f2543Smrg case 0x00: r[i].width = r[i].height * 4 / 3; break; 412706f2543Smrg case 0x40: r[i].width = r[i].height * 16 / 9; break; 413706f2543Smrg case 0x80: r[i].width = r[i].height * 16 / 10; break; 414706f2543Smrg case 0xc0: r[i].width = r[i].height * 15 / 9; break; 415706f2543Smrg } 416706f2543Smrg switch (c[2] & 0x60) { 417706f2543Smrg case 0x00: r[i].rate = 50; break; 418706f2543Smrg case 0x20: r[i].rate = 60; break; 419706f2543Smrg case 0x40: r[i].rate = 75; break; 420706f2543Smrg case 0x60: r[i].rate = 85; break; 421706f2543Smrg } 422706f2543Smrg r[i].rates = c[2] & 0x1f; 423706f2543Smrg } else { 424706f2543Smrg return; 425706f2543Smrg } 426706f2543Smrg c += 3; 427706f2543Smrg } 428706f2543Smrg} 429706f2543Smrg 430706f2543Smrgstatic void 431706f2543Smrgget_std_timing_section(Uchar *c, struct std_timings *r, 432706f2543Smrg struct edid_version *v) 433706f2543Smrg{ 434706f2543Smrg int i; 435706f2543Smrg 436706f2543Smrg for (i=0;i<STD_TIMINGS;i++){ 437706f2543Smrg if (VALID_TIMING) { 438706f2543Smrg r[i].hsize = HSIZE1; 439706f2543Smrg VSIZE1(r[i].vsize); 440706f2543Smrg r[i].refresh = REFRESH_R; 441706f2543Smrg r[i].id = STD_TIMING_ID; 442706f2543Smrg } else { 443706f2543Smrg r[i].hsize = r[i].vsize = r[i].refresh = r[i].id = 0; 444706f2543Smrg } 445706f2543Smrg NEXT_STD_TIMING; 446706f2543Smrg } 447706f2543Smrg} 448706f2543Smrg 449706f2543Smrgstatic const unsigned char empty_block[18]; 450706f2543Smrg 451706f2543Smrgstatic void 452706f2543Smrgfetch_detailed_block(Uchar *c, struct edid_version *ver, 453706f2543Smrg struct detailed_monitor_section *det_mon) 454706f2543Smrg{ 455706f2543Smrg if (ver->version == 1 && ver->revision >= 1 && IS_MONITOR_DESC) { 456706f2543Smrg switch (MONITOR_DESC_TYPE) { 457706f2543Smrg case SERIAL_NUMBER: 458706f2543Smrg det_mon->type = DS_SERIAL; 459706f2543Smrg copy_string(c,det_mon->section.serial); 460706f2543Smrg break; 461706f2543Smrg case ASCII_STR: 462706f2543Smrg det_mon->type = DS_ASCII_STR; 463706f2543Smrg copy_string(c,det_mon->section.ascii_data); 464706f2543Smrg break; 465706f2543Smrg case MONITOR_RANGES: 466706f2543Smrg det_mon->type = DS_RANGES; 467706f2543Smrg get_monitor_ranges(c,&det_mon->section.ranges); 468706f2543Smrg break; 469706f2543Smrg case MONITOR_NAME: 470706f2543Smrg det_mon->type = DS_NAME; 471706f2543Smrg copy_string(c,det_mon->section.name); 472706f2543Smrg break; 473706f2543Smrg case ADD_COLOR_POINT: 474706f2543Smrg det_mon->type = DS_WHITE_P; 475706f2543Smrg get_whitepoint_section(c,det_mon->section.wp); 476706f2543Smrg break; 477706f2543Smrg case ADD_STD_TIMINGS: 478706f2543Smrg det_mon->type = DS_STD_TIMINGS; 479706f2543Smrg get_dst_timing_section(c,det_mon->section.std_t, ver); 480706f2543Smrg break; 481706f2543Smrg case COLOR_MANAGEMENT_DATA: 482706f2543Smrg det_mon->type = DS_CMD; 483706f2543Smrg break; 484706f2543Smrg case CVT_3BYTE_DATA: 485706f2543Smrg det_mon->type = DS_CVT; 486706f2543Smrg get_cvt_timing_section(c, det_mon->section.cvt); 487706f2543Smrg break; 488706f2543Smrg case ADD_EST_TIMINGS: 489706f2543Smrg det_mon->type = DS_EST_III; 490706f2543Smrg memcpy(det_mon->section.est_iii, c + 6, 6); 491706f2543Smrg break; 492706f2543Smrg case ADD_DUMMY: 493706f2543Smrg det_mon->type = DS_DUMMY; 494706f2543Smrg break; 495706f2543Smrg default: 496706f2543Smrg det_mon->type = DS_UNKOWN; 497706f2543Smrg break; 498706f2543Smrg } 499706f2543Smrg if (c[3] <= 0x0F && memcmp(c, empty_block, sizeof(empty_block))) { 500706f2543Smrg det_mon->type = DS_VENDOR + c[3]; 501706f2543Smrg } 502706f2543Smrg } else { 503706f2543Smrg det_mon->type = DT; 504706f2543Smrg get_detailed_timing_section(c, &det_mon->section.d_timings); 505706f2543Smrg } 506706f2543Smrg} 507706f2543Smrg 508706f2543Smrgstatic void 509706f2543Smrgget_dt_md_section(Uchar *c, struct edid_version *ver, 510706f2543Smrg struct detailed_monitor_section *det_mon) 511706f2543Smrg{ 512706f2543Smrg int i; 513706f2543Smrg 514706f2543Smrg for (i=0; i < DET_TIMINGS; i++) { 515706f2543Smrg fetch_detailed_block(c, ver, det_mon + i); 516706f2543Smrg NEXT_DT_MD_SECTION; 517706f2543Smrg } 518706f2543Smrg} 519706f2543Smrg 520706f2543Smrgstatic void 521706f2543Smrgcopy_string(Uchar *c, Uchar *s) 522706f2543Smrg{ 523706f2543Smrg int i; 524706f2543Smrg c = c + 5; 525706f2543Smrg for (i = 0; (i < 13 && *c != 0x0A); i++) 526706f2543Smrg *(s++) = *(c++); 527706f2543Smrg *s = 0; 528706f2543Smrg while (i-- && (*--s == 0x20)) *s = 0; 529706f2543Smrg} 530706f2543Smrg 531706f2543Smrgstatic void 532706f2543Smrgget_dst_timing_section(Uchar *c, struct std_timings *t, 533706f2543Smrg struct edid_version *v) 534706f2543Smrg{ 535706f2543Smrg int j; 536706f2543Smrg c = c + 5; 537706f2543Smrg for (j = 0; j < 5; j++) { 538706f2543Smrg t[j].hsize = HSIZE1; 539706f2543Smrg VSIZE1(t[j].vsize); 540706f2543Smrg t[j].refresh = REFRESH_R; 541706f2543Smrg t[j].id = STD_TIMING_ID; 542706f2543Smrg NEXT_STD_TIMING; 543706f2543Smrg } 544706f2543Smrg} 545706f2543Smrg 546706f2543Smrgstatic void 547706f2543Smrgget_monitor_ranges(Uchar *c, struct monitor_ranges *r) 548706f2543Smrg{ 549706f2543Smrg r->min_v = MIN_V; 550706f2543Smrg r->max_v = MAX_V; 551706f2543Smrg r->min_h = MIN_H; 552706f2543Smrg r->max_h = MAX_H; 553706f2543Smrg r->max_clock = 0; 554706f2543Smrg if(MAX_CLOCK != 0xff) /* is specified? */ 555706f2543Smrg r->max_clock = MAX_CLOCK * 10 + 5; 556706f2543Smrg if (HAVE_2ND_GTF) { 557706f2543Smrg r->gtf_2nd_f = F_2ND_GTF; 558706f2543Smrg r->gtf_2nd_c = C_2ND_GTF; 559706f2543Smrg r->gtf_2nd_m = M_2ND_GTF; 560706f2543Smrg r->gtf_2nd_k = K_2ND_GTF; 561706f2543Smrg r->gtf_2nd_j = J_2ND_GTF; 562706f2543Smrg } else { 563706f2543Smrg r->gtf_2nd_f = 0; 564706f2543Smrg } 565706f2543Smrg if (HAVE_CVT) { 566706f2543Smrg r->max_clock_khz = MAX_CLOCK_KHZ; 567706f2543Smrg r->max_clock = r->max_clock_khz / 1000; 568706f2543Smrg r->maxwidth = MAXWIDTH; 569706f2543Smrg r->supported_aspect = SUPPORTED_ASPECT; 570706f2543Smrg r->preferred_aspect = PREFERRED_ASPECT; 571706f2543Smrg r->supported_blanking = SUPPORTED_BLANKING; 572706f2543Smrg r->supported_scaling = SUPPORTED_SCALING; 573706f2543Smrg r->preferred_refresh = PREFERRED_REFRESH; 574706f2543Smrg } else { 575706f2543Smrg r->max_clock_khz = 0; 576706f2543Smrg } 577706f2543Smrg} 578706f2543Smrg 579706f2543Smrgstatic void 580706f2543Smrgget_whitepoint_section(Uchar *c, struct whitePoints *wp) 581706f2543Smrg{ 582706f2543Smrg wp[0].white_x = WHITEX1; 583706f2543Smrg wp[0].white_y = WHITEY1; 584706f2543Smrg wp[1].white_x = WHITEX2; 585706f2543Smrg wp[1].white_y = WHITEY2; 586706f2543Smrg wp[0].index = WHITE_INDEX1; 587706f2543Smrg wp[1].index = WHITE_INDEX2; 588706f2543Smrg wp[0].white_gamma = WHITE_GAMMA1; 589706f2543Smrg wp[1].white_gamma = WHITE_GAMMA2; 590706f2543Smrg} 591706f2543Smrg 592706f2543Smrgstatic void 593706f2543Smrgget_detailed_timing_section(Uchar *c, struct detailed_timings *r) 594706f2543Smrg{ 595706f2543Smrg r->clock = PIXEL_CLOCK; 596706f2543Smrg r->h_active = H_ACTIVE; 597706f2543Smrg r->h_blanking = H_BLANK; 598706f2543Smrg r->v_active = V_ACTIVE; 599706f2543Smrg r->v_blanking = V_BLANK; 600706f2543Smrg r->h_sync_off = H_SYNC_OFF; 601706f2543Smrg r->h_sync_width = H_SYNC_WIDTH; 602706f2543Smrg r->v_sync_off = V_SYNC_OFF; 603706f2543Smrg r->v_sync_width = V_SYNC_WIDTH; 604706f2543Smrg r->h_size = H_SIZE; 605706f2543Smrg r->v_size = V_SIZE; 606706f2543Smrg r->h_border = H_BORDER; 607706f2543Smrg r->v_border = V_BORDER; 608706f2543Smrg r->interlaced = INTERLACED; 609706f2543Smrg r->stereo = STEREO; 610706f2543Smrg r->stereo_1 = STEREO1; 611706f2543Smrg r->sync = SYNC_T; 612706f2543Smrg r->misc = MISC; 613706f2543Smrg} 614706f2543Smrg 615706f2543Smrg#define MAX_EDID_MINOR 4 616706f2543Smrg 617706f2543Smrgstatic Bool 618706f2543Smrgvalidate_version(int scrnIndex, struct edid_version *r) 619706f2543Smrg{ 620706f2543Smrg if (r->version != 1) { 621706f2543Smrg xf86DrvMsg(scrnIndex, X_ERROR, "Unknown EDID version %d\n", 622706f2543Smrg r->version); 623706f2543Smrg return FALSE; 624706f2543Smrg } 625706f2543Smrg 626706f2543Smrg if (r->revision > MAX_EDID_MINOR) 627706f2543Smrg xf86DrvMsg(scrnIndex, X_WARNING, 628706f2543Smrg "Assuming version 1.%d is compatible with 1.%d\n", 629706f2543Smrg r->revision, MAX_EDID_MINOR); 630706f2543Smrg 631706f2543Smrg return TRUE; 632706f2543Smrg} 633706f2543Smrg 634706f2543Smrg/* 635706f2543Smrg * Returns true if HDMI, false if definitely not or unknown. 636706f2543Smrg */ 637706f2543SmrgBool 638706f2543Smrgxf86MonitorIsHDMI(xf86MonPtr mon) 639706f2543Smrg{ 640706f2543Smrg int i = 0, version, offset; 641706f2543Smrg char *edid = NULL; 642706f2543Smrg 643706f2543Smrg if (!mon) 644706f2543Smrg return FALSE; 645706f2543Smrg 646706f2543Smrg if (!(mon->flags & EDID_COMPLETE_RAWDATA)) 647706f2543Smrg return FALSE; 648706f2543Smrg 649706f2543Smrg if (!mon->no_sections) 650706f2543Smrg return FALSE; 651706f2543Smrg 652706f2543Smrg edid = (char *)mon->rawData; 653706f2543Smrg if (!edid) 654706f2543Smrg return FALSE; 655706f2543Smrg 656706f2543Smrg /* find the CEA extension block */ 657706f2543Smrg for (i = 1; i <= mon->no_sections; i++) 658706f2543Smrg if (edid[i * 128] == 0x02) 659706f2543Smrg break; 660706f2543Smrg if (i == mon->no_sections + 1) 661706f2543Smrg return FALSE; 662706f2543Smrg edid += (i * 128); 663706f2543Smrg 664706f2543Smrg version = edid[1]; 665706f2543Smrg offset = edid[2]; 666706f2543Smrg if (version < 3 || offset < 4) 667706f2543Smrg return FALSE; 668706f2543Smrg 669706f2543Smrg /* walk the cea data blocks */ 670706f2543Smrg for (i = 4; i < offset; i += (edid[i] & 0x1f) + 1) { 671706f2543Smrg char *x = edid + i; 672706f2543Smrg 673706f2543Smrg /* find a vendor specific block */ 674706f2543Smrg if ((x[0] & 0xe0) >> 5 == 0x03) { 675706f2543Smrg int oui = (x[3] << 16) + (x[2] << 8) + x[1]; 676706f2543Smrg 677706f2543Smrg /* find the HDMI vendor OUI */ 678706f2543Smrg if (oui == 0x000c03) 679706f2543Smrg return TRUE; 680706f2543Smrg } 681706f2543Smrg } 682706f2543Smrg 683706f2543Smrg /* guess it's not HDMI after all */ 684706f2543Smrg return FALSE; 685706f2543Smrg} 686