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