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 * print_edid.c: print out all information retrieved from display device
25 */
26
27#ifdef HAVE_XORG_CONFIG_H
28#include <xorg-config.h>
29#endif
30
31/* XXX kinda gross */
32#define _PARSE_EDID_
33
34#include "misc.h"
35#include "xf86.h"
36#include "xf86_OSproc.h"
37#include "xf86DDC.h"
38#include "edid.h"
39
40#define EDID_WIDTH	16
41
42static void
43print_vendor(int scrnIndex, struct vendor *c)
44{
45    xf86DrvMsg(scrnIndex, X_INFO, "Manufacturer: %s  Model: %x  Serial#: %u\n",
46	(char *)&c->name, c->prod_id, c->serial);
47    xf86DrvMsg(scrnIndex, X_INFO, "Year: %u  Week: %u\n", c->year, c->week);
48}
49
50static void
51print_version(int scrnIndex, struct edid_version *c)
52{
53    xf86DrvMsg(scrnIndex,X_INFO,"EDID Version: %u.%u\n",c->version,
54	       c->revision);
55}
56
57static const char *digital_interfaces[] = {
58    "undefined",
59    "DVI",
60    "HDMI-a",
61    "HDMI-b",
62    "MDDI",
63    "DisplayPort",
64    "unknown"
65};
66
67static void
68print_input_features(int scrnIndex, struct disp_features *c,
69		     struct edid_version *v)
70{
71    if (DIGITAL(c->input_type)) {
72	xf86DrvMsg(scrnIndex, X_INFO, "Digital Display Input\n");
73	if (v->revision == 2 || v->revision == 3) {
74	    if (DFP1(c->input_dfp))
75		xf86DrvMsg(scrnIndex, X_INFO, "DFP 1.x compatible TMDS\n");
76	} else if (v->revision >= 4) {
77	    int interface = c->input_interface;
78	    int bpc = c->input_bpc;
79	    if (interface > 6)
80		interface = 6; /* unknown */
81	    if (bpc == 0 || bpc == 7)
82		xf86DrvMsg(scrnIndex, X_INFO, "Undefined color depth\n");
83	    else
84		xf86DrvMsg(scrnIndex, X_INFO, "%d bits per channel\n",
85			   bpc * 2 + 4);
86	    xf86DrvMsg(scrnIndex, X_INFO, "Digital interface is %s\n",
87		       digital_interfaces[interface]);
88	}
89    } else {
90	xf86DrvMsg(scrnIndex,X_INFO,"Analog Display Input,  ");
91	xf86ErrorF("Input Voltage Level: ");
92	switch (c->input_voltage){
93	case V070:
94	    xf86ErrorF("0.700/0.300 V\n");
95	    break;
96	case V071:
97	    xf86ErrorF("0.714/0.286 V\n");
98	    break;
99	case V100:
100	    xf86ErrorF("1.000/0.400 V\n");
101	    break;
102	case V007:
103            xf86ErrorF("0.700/0.700 V\n");
104	    break;
105	default:
106	    xf86ErrorF("undefined\n");
107	}
108	if (SIG_SETUP(c->input_setup))
109	    xf86DrvMsg(scrnIndex,X_INFO,"Signal levels configurable\n");
110	xf86DrvMsg(scrnIndex,X_INFO,"Sync:");
111	if (SEP_SYNC(c->input_sync))
112	    xf86ErrorF("  Separate");
113	if (COMP_SYNC(c->input_sync))
114	    xf86ErrorF("  Composite");
115	if (SYNC_O_GREEN(c->input_sync))
116	    xf86ErrorF("  SyncOnGreen");
117	if (SYNC_SERR(c->input_sync))
118	    xf86ErrorF("Serration on. "
119		       "V.Sync Pulse req. if CompSync or SyncOnGreen\n");
120	else xf86ErrorF("\n");
121    }
122}
123
124static void
125print_dpms_features(int scrnIndex, struct disp_features *c,
126		    struct edid_version *v)
127{
128     if (c->dpms) {
129	 xf86DrvMsg(scrnIndex,X_INFO,"DPMS capabilities:");
130	 if (DPMS_STANDBY(c->dpms)) xf86ErrorF(" StandBy");
131	 if (DPMS_SUSPEND(c->dpms)) xf86ErrorF(" Suspend");
132	 if (DPMS_OFF(c->dpms)) xf86ErrorF(" Off");
133     } else
134	 xf86DrvMsg(scrnIndex,X_INFO,"No DPMS capabilities specified");
135    if (!c->input_type) { /* analog */
136	switch (c->display_type){
137	    case DISP_MONO:
138		xf86ErrorF("; Monochorome/GrayScale Display\n");
139		break;
140	    case DISP_RGB:
141		xf86ErrorF("; RGB/Color Display\n");
142		break;
143	    case DISP_MULTCOLOR:
144		xf86ErrorF("; Non RGB Multicolor Display\n");
145		break;
146	    default:
147		xf86ErrorF("\n");
148		break;
149	}
150    } else {
151	int enc = c->display_type;
152	xf86ErrorF("\n");
153	xf86DrvMsg(scrnIndex, X_INFO, "Supported color encodings: "
154		   "RGB 4:4:4 %s%s\n",
155		   enc & DISP_YCRCB444 ? "YCrCb 4:4:4 " : "",
156		   enc & DISP_YCRCB422 ? "YCrCb 4:2:2" : "");
157    }
158
159    if (STD_COLOR_SPACE(c->msc))
160	xf86DrvMsg(scrnIndex,X_INFO,
161		   "Default color space is primary color space\n");
162
163    if (PREFERRED_TIMING_MODE(c->msc) || v->revision >= 4) {
164	xf86DrvMsg(scrnIndex, X_INFO,
165		   "First detailed timing is preferred mode\n");
166	if (v->revision >= 4)
167	    xf86DrvMsg(scrnIndex, X_INFO,
168		"Preferred mode is native pixel format and refresh rate\n");
169    } else if (v->revision == 3) {
170	xf86DrvMsg(scrnIndex,X_INFO,
171		   "First detailed timing not preferred "
172		   "mode in violation of standard!\n");
173    }
174
175    if (v->revision >= 4) {
176	if (GFT_SUPPORTED(c->msc)) {
177	    xf86DrvMsg(scrnIndex, X_INFO, "Display is continuous-frequency\n");
178	}
179    } else {
180	if (GFT_SUPPORTED(c->msc))
181	    xf86DrvMsg(scrnIndex, X_INFO, "GTF timings supported\n");
182    }
183}
184
185static void
186print_whitepoint(int scrnIndex, struct disp_features *disp)
187{
188    xf86DrvMsg(scrnIndex,X_INFO,"redX: %.3f redY: %.3f   ",
189	       disp->redx,disp->redy);
190    xf86ErrorF("greenX: %.3f greenY: %.3f\n",
191	       disp->greenx,disp->greeny);
192    xf86DrvMsg(scrnIndex,X_INFO,"blueX: %.3f blueY: %.3f   ",
193	       disp->bluex,disp->bluey);
194    xf86ErrorF("whiteX: %.3f whiteY: %.3f\n",
195	       disp->whitex,disp->whitey);
196}
197
198static void
199print_display(int scrnIndex, struct disp_features *disp,
200	      struct edid_version *v)
201{
202    print_input_features(scrnIndex, disp, v);
203    if (disp->hsize && disp->vsize) {
204	xf86DrvMsg(scrnIndex, X_INFO, "Max Image Size [cm]: ");
205	xf86ErrorF("horiz.: %i  ", disp->hsize);
206	xf86ErrorF("vert.: %i\n", disp->vsize);
207    } else if (v->revision >= 4 && (disp->hsize || disp->vsize)) {
208	if (disp->hsize)
209	    xf86DrvMsg(scrnIndex, X_INFO, "Aspect ratio: %.2f (landscape)\n",
210		       (disp->hsize + 99) / 100.0);
211	if (disp->vsize)
212	    xf86DrvMsg(scrnIndex, X_INFO, "Aspect ratio: %.2f (portrait)\n",
213		       100.0 / (float)(disp->vsize + 99));
214
215    } else {
216	xf86DrvMsg(scrnIndex, X_INFO, "Indeterminate output size\n");
217    }
218
219    if (!disp->gamma && v->revision >= 1.4)
220	xf86DrvMsg(scrnIndex, X_INFO, "Gamma defined in extension block\n");
221    else
222	xf86DrvMsg(scrnIndex, X_INFO, "Gamma: %.2f\n", disp->gamma);
223
224    print_dpms_features(scrnIndex, disp, v);
225    print_whitepoint(scrnIndex, disp);
226}
227
228static void
229print_established_timings(int scrnIndex, struct established_timings *t)
230{
231    unsigned char c;
232
233    if (t->t1 || t->t2 || t->t_manu)
234	xf86DrvMsg(scrnIndex,X_INFO,"Supported established timings:\n");
235    c=t->t1;
236    if (c&0x80) xf86DrvMsg(scrnIndex,X_INFO,"720x400@70Hz\n");
237    if (c&0x40) xf86DrvMsg(scrnIndex,X_INFO,"720x400@88Hz\n");
238    if (c&0x20) xf86DrvMsg(scrnIndex,X_INFO,"640x480@60Hz\n");
239    if (c&0x10) xf86DrvMsg(scrnIndex,X_INFO,"640x480@67Hz\n");
240    if (c&0x08) xf86DrvMsg(scrnIndex,X_INFO,"640x480@72Hz\n");
241    if (c&0x04) xf86DrvMsg(scrnIndex,X_INFO,"640x480@75Hz\n");
242    if (c&0x02) xf86DrvMsg(scrnIndex,X_INFO,"800x600@56Hz\n");
243    if (c&0x01) xf86DrvMsg(scrnIndex,X_INFO,"800x600@60Hz\n");
244    c=t->t2;
245    if (c&0x80) xf86DrvMsg(scrnIndex,X_INFO,"800x600@72Hz\n");
246    if (c&0x40) xf86DrvMsg(scrnIndex,X_INFO,"800x600@75Hz\n");
247    if (c&0x20) xf86DrvMsg(scrnIndex,X_INFO,"832x624@75Hz\n");
248    if (c&0x10) xf86DrvMsg(scrnIndex,X_INFO,"1024x768@87Hz (interlaced)\n");
249    if (c&0x08) xf86DrvMsg(scrnIndex,X_INFO,"1024x768@60Hz\n");
250    if (c&0x04) xf86DrvMsg(scrnIndex,X_INFO,"1024x768@70Hz\n");
251    if (c&0x02) xf86DrvMsg(scrnIndex,X_INFO,"1024x768@75Hz\n");
252    if (c&0x01) xf86DrvMsg(scrnIndex,X_INFO,"1280x1024@75Hz\n");
253    c=t->t_manu;
254    if (c&0x80) xf86DrvMsg(scrnIndex,X_INFO,"1152x864@75Hz\n");
255    xf86DrvMsg(scrnIndex,X_INFO,"Manufacturer's mask: %X\n",c&0x7F);
256}
257
258static void
259print_std_timings(int scrnIndex, struct std_timings *t)
260{
261    int i;
262    char done = 0;
263    for (i=0;i<STD_TIMINGS;i++) {
264	if (t[i].hsize > 256) {  /* sanity check */
265	    if (!done) {
266		xf86DrvMsg(scrnIndex,X_INFO,"Supported standard timings:\n");
267		done = 1;
268	    }
269	    xf86DrvMsg(scrnIndex,X_INFO,
270		       "#%i: hsize: %i  vsize %i  refresh: %i  vid: %i\n",
271		       i, t[i].hsize, t[i].vsize, t[i].refresh, t[i].id);
272	}
273    }
274}
275
276static void
277print_cvt_timings(int si, struct cvt_timings *t)
278{
279    int i;
280
281    for (i = 0; i < 4; i++) {
282	if (t[i].height) {
283	    xf86DrvMsg(si, X_INFO, "%dx%d @ %s%s%s%s%s Hz\n",
284		    t[i].width, t[i].height,
285		    t[i].rates & 0x10 ? "50," : "",
286		    t[i].rates & 0x08 ? "60," : "",
287		    t[i].rates & 0x04 ? "75," : "",
288		    t[i].rates & 0x02 ? "85," : "",
289		    t[i].rates & 0x01 ? "60RB" : "");
290	} else break;
291    }
292}
293
294static void
295print_detailed_timings(int scrnIndex, struct detailed_timings *t)
296{
297
298    if (t->clock > 15000000) {  /* sanity check */
299	xf86DrvMsg(scrnIndex,X_INFO,"Supported detailed timing:\n");
300	xf86DrvMsg(scrnIndex,X_INFO,"clock: %.1f MHz   ",t->clock/1000000.0);
301	xf86ErrorF("Image Size:  %i x %i mm\n",t->h_size,t->v_size);
302	xf86DrvMsg(scrnIndex,X_INFO,
303		   "h_active: %i  h_sync: %i  h_sync_end %i h_blank_end %i ",
304		   t->h_active, t->h_sync_off + t->h_active,
305		   t->h_sync_off + t->h_sync_width + t->h_active,
306		   t->h_active + t->h_blanking);
307	xf86ErrorF("h_border: %i\n",t->h_border);
308	xf86DrvMsg(scrnIndex,X_INFO,
309		   "v_active: %i  v_sync: %i  v_sync_end %i v_blanking: %i ",
310		   t->v_active, t->v_sync_off + t->v_active,
311		   t->v_sync_off + t->v_sync_width + t->v_active,
312		   t->v_active + t->v_blanking);
313	xf86ErrorF("v_border: %i\n",t->v_border);
314	if (IS_STEREO(t->stereo)) {
315	    xf86DrvMsg(scrnIndex,X_INFO,"Stereo: ");
316	    if (IS_RIGHT_STEREO(t->stereo)) {
317		if (!t->stereo_1)
318		    xf86ErrorF("right channel on sync\n");
319		else
320		    xf86ErrorF("left channel on sync\n");
321	    } else if (IS_LEFT_STEREO(t->stereo)) {
322		if (!t->stereo_1)
323		    xf86ErrorF("right channel on even line\n");
324		else
325		    xf86ErrorF("left channel on evel line\n");
326	    }
327	    if (IS_4WAY_STEREO(t->stereo)) {
328		if (!t->stereo_1)
329		    xf86ErrorF("4-way interleaved\n");
330		else
331		    xf86ErrorF("side-by-side interleaved");
332	    }
333	}
334    }
335}
336
337/* This function handle all detailed patchs,
338 * including EDID and EDID-extension
339 */
340struct det_print_parameter{
341  xf86MonPtr m;
342  int index;
343  ddc_quirk_t quirks;
344};
345
346static void
347handle_detailed_print(struct detailed_monitor_section *det_mon,
348                      void *data)
349{
350    int j, scrnIndex;
351    struct det_print_parameter *p;
352
353    p = (struct det_print_parameter *)data;
354    scrnIndex = p->m->scrnIndex;
355    xf86DetTimingApplyQuirks(det_mon,p->quirks,
356                             p->m->features.hsize,
357                             p->m->features.vsize);
358
359    switch (det_mon->type) {
360    case DT:
361        print_detailed_timings(scrnIndex,&det_mon->section.d_timings);
362        break;
363    case DS_SERIAL:
364        xf86DrvMsg(scrnIndex,X_INFO,"Serial No: %s\n",det_mon->section.serial);
365        break;
366    case DS_ASCII_STR:
367        xf86DrvMsg(scrnIndex,X_INFO," %s\n",det_mon->section.ascii_data);
368        break;
369    case DS_NAME:
370        xf86DrvMsg(scrnIndex,X_INFO,"Monitor name: %s\n",det_mon->section.name);
371        break;
372    case DS_RANGES:
373    {
374        struct monitor_ranges *r = &det_mon->section.ranges;
375        xf86DrvMsg(scrnIndex,X_INFO,
376                   "Ranges: V min: %i V max: %i Hz, H min: %i H max: %i kHz,",
377                   r->min_v, r->max_v, r->min_h, r->max_h);
378        if (r->max_clock_khz != 0) {
379            xf86ErrorF(" PixClock max %i kHz\n", r->max_clock_khz);
380        if (r->maxwidth)
381            xf86DrvMsg(scrnIndex, X_INFO, "Maximum pixel width: %d\n",
382                       r->maxwidth);
383	xf86DrvMsg(scrnIndex, X_INFO, "Supported aspect ratios:");
384	if (r->supported_aspect & SUPPORTED_ASPECT_4_3)
385	    xf86ErrorF(" 4:3%s",
386                       r->preferred_aspect == PREFERRED_ASPECT_4_3?"*":"");
387	if (r->supported_aspect & SUPPORTED_ASPECT_16_9)
388	    xf86ErrorF(" 16:9%s",
389                       r->preferred_aspect == PREFERRED_ASPECT_16_9?"*":"");
390        if (r->supported_aspect & SUPPORTED_ASPECT_16_10)
391            xf86ErrorF(" 16:10%s",
392                       r->preferred_aspect == PREFERRED_ASPECT_16_10?"*":"");
393	if (r->supported_aspect & SUPPORTED_ASPECT_5_4)
394	    xf86ErrorF(" 5:4%s",
395                       r->preferred_aspect == PREFERRED_ASPECT_5_4?"*":"");
396	if (r->supported_aspect & SUPPORTED_ASPECT_15_9)
397	    xf86ErrorF(" 15:9%s",
398                       r->preferred_aspect == PREFERRED_ASPECT_15_9?"*":"");
399        xf86ErrorF("\n");
400	xf86DrvMsg(scrnIndex, X_INFO, "Supported blankings:");
401	if (r->supported_blanking & CVT_STANDARD)
402	    xf86ErrorF(" standard");
403	if (r->supported_blanking & CVT_REDUCED)
404	    xf86ErrorF(" reduced");
405	xf86ErrorF("\n");
406	xf86DrvMsg(scrnIndex, X_INFO, "Supported scalings:");
407	if (r->supported_scaling & SCALING_HSHRINK)
408	    xf86ErrorF(" hshrink");
409	if (r->supported_scaling & SCALING_HSTRETCH)
410	    xf86ErrorF(" hstretch");
411	if (r->supported_scaling & SCALING_VSHRINK)
412	    xf86ErrorF(" vshrink");
413	if (r->supported_scaling & SCALING_VSTRETCH)
414	    xf86ErrorF(" vstretch");
415        xf86ErrorF("\n");
416	if (r->preferred_refresh)
417	    xf86DrvMsg(scrnIndex, X_INFO, "Preferred refresh rate: %d\n",
418		    r->preferred_refresh);
419	else
420	    xf86DrvMsg(scrnIndex, X_INFO, "Buggy monitor, no preferred "
421		       "refresh rate given\n");
422        } else if (r->max_clock != 0) {
423	    xf86ErrorF(" PixClock max %i MHz\n", r->max_clock);
424        } else {
425	    xf86ErrorF("\n");
426        }
427        if (r->gtf_2nd_f > 0)
428            xf86DrvMsg(scrnIndex,X_INFO," 2nd GTF parameters: f: %i kHz "
429                       "c: %i m: %i k %i j %i\n", r->gtf_2nd_f,
430                       r->gtf_2nd_c, r->gtf_2nd_m, r->gtf_2nd_k,
431                       r->gtf_2nd_j);
432        break;
433    }
434    case DS_STD_TIMINGS:
435        for (j = 0; j<5; j++)
436	    xf86DrvMsg(scrnIndex,X_INFO,
437		    "#%i: hsize: %i  vsize %i  refresh: %i  "
438		    "vid: %i\n",p->index ,det_mon->section.std_t[j].hsize,
439		    det_mon->section.std_t[j].vsize,
440		    det_mon->section.std_t[j].refresh,
441		    det_mon->section.std_t[j].id);
442        break;
443    case DS_WHITE_P:
444        for (j = 0; j<2; j++)
445        if (det_mon->section.wp[j].index != 0)
446            xf86DrvMsg(scrnIndex,X_INFO,
447                       "White point %i: whiteX: %f, whiteY: %f; gamma: %f\n",
448                       det_mon->section.wp[j].index,det_mon->section.wp[j].white_x,
449                       det_mon->section.wp[j].white_y,
450                       det_mon->section.wp[j].white_gamma);
451        break;
452    case DS_CMD:
453        xf86DrvMsg(scrnIndex, X_INFO,
454                   "Color management data: (not decoded)\n");
455        break;
456    case DS_CVT:
457        xf86DrvMsg(scrnIndex, X_INFO,
458                   "CVT 3-byte-code modes:\n");
459        print_cvt_timings(scrnIndex, det_mon->section.cvt);
460        break;
461    case DS_EST_III:
462        xf86DrvMsg(scrnIndex, X_INFO,
463                   "Established timings III: (not decoded)\n");
464        break;
465    case DS_DUMMY:
466    default:
467        break;
468    }
469    if (det_mon->type >= DS_VENDOR && det_mon->type <= DS_VENDOR_MAX) {
470        xf86DrvMsg(scrnIndex, X_INFO,
471                   "Unknown vendor-specific block %hx\n",
472                   det_mon->type - DS_VENDOR);
473    }
474
475    p->index = p->index + 1;
476}
477
478static void
479print_number_sections(int scrnIndex, int num)
480{
481    if (num)
482	xf86DrvMsg(scrnIndex,X_INFO,"Number of EDID sections to follow: %i\n",
483		   num);
484}
485
486xf86MonPtr
487xf86PrintEDID(xf86MonPtr m)
488{
489    CARD16 i, j, n;
490    char buf[EDID_WIDTH * 2 + 1];
491    struct det_print_parameter p;
492
493    if (!m) return NULL;
494
495    print_vendor(m->scrnIndex, &m->vendor);
496    print_version(m->scrnIndex, &m->ver);
497    print_display(m->scrnIndex, &m->features, &m->ver);
498    print_established_timings(m->scrnIndex, &m->timings1);
499    print_std_timings(m->scrnIndex, m->timings2);
500    p.m = m;
501    p.index = 0;
502    p.quirks = xf86DDCDetectQuirks(m->scrnIndex, m, FALSE);
503    xf86ForEachDetailedBlock(m,
504                             handle_detailed_print ,
505                             &p);
506    print_number_sections(m->scrnIndex, m->no_sections);
507
508    /* extension block section stuff */
509
510    xf86DrvMsg(m->scrnIndex, X_INFO, "EDID (in hex):\n");
511
512    n = 128;
513    if (m->flags & EDID_COMPLETE_RAWDATA)
514	n += m->no_sections * 128;
515
516    for (i = 0; i < n; i += j) {
517	for (j = 0; j < EDID_WIDTH; ++j) {
518	    sprintf(&buf[j * 2], "%02x", m->rawData[i + j]);
519	}
520	xf86DrvMsg(m->scrnIndex, X_INFO, "\t%s\n", buf);
521    }
522
523    return m;
524}
525