edid.c revision e07dc26b
1/* $Id: edid.c,v 1.1.1.1 2019/01/09 23:50:31 mrg Exp $ */
2/** @file
3 *
4 * Linux Additions X11 graphics driver, EDID construction
5 */
6
7/*
8 * Copyright (C) 2006-2017 Oracle Corporation
9 *
10 * This code is based on drmmode_display.c from the X.Org xf86-video-intel
11 * driver with the following copyright notice:
12 *
13 * Copyright © 2007 Red Hat, Inc.
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining a
16 * copy of this software and associated documentation files (the "Software"),
17 * to deal in the Software without restriction, including without limitation
18 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19 * and/or sell copies of the Software, and to permit persons to whom the
20 * Software is furnished to do so, subject to the following conditions:
21 *
22 * The above copyright notice and this permission notice (including the next
23 * paragraph) shall be included in all copies or substantial portions of the
24 * Software.
25 *
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
29 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 * SOFTWARE.
33 *
34 * Authors:
35 *    Dave Airlie <airlied@redhat.com>
36 *    Michael Thayer <michael.thayer@oracle.com>
37 */
38
39#include "misc.h"
40#include "xf86DDC.h"
41#include "xf86Crtc.h"
42#include "vboxvideo.h"
43
44enum { EDID_SIZE = 128 };
45
46const unsigned char g_acszEDIDBase[EDID_SIZE] =
47{
48   0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */
49   0x58, 0x58, /* manufacturer (VBX) */
50   0x00, 0x00, /* product code */
51   0x00, 0x00,0x00, 0x00, /* serial number goes here */
52   0x01, /* week of manufacture */
53   0x00, /* year of manufacture */
54   0x01, 0x03, /* EDID version */
55   0x80, /* capabilities - digital */
56   0x00, /* horiz. res in cm, zero for projectors */
57   0x00, /* vert. res in cm */
58   0x78, /* display gamma (120 == 2.2).  Should we ask the host for this? */
59   0xEE, /* features (standby, suspend, off, RGB, standard colour space,
60          * preferred timing mode) */
61   0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54,
62       /* chromaticity for standard colour space - should we ask the host? */
63   0x00, 0x00, 0x00, /* no default timings */
64   0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
65   0x01, 0x01, 0x01, 0x01, /* no standard timings */
66   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* descriptor block 1 goes here */
68   0x00, 0x00, 0x00, 0xFD, 0x00, /* descriptor block 2, monitor ranges */
69   0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20,
70   0x20, /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */
71   0x00, 0x00, 0x00, 0xFC, 0x00, /* descriptor block 3, monitor name */
72   'V', 'B', 'O', 'X', ' ', 'm', 'o', 'n', 'i', 't', 'o', 'r', '\n',
73   0x00, 0x00, 0x00, 0x10, 0x00, /* descriptor block 4: dummy data */
74   0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
75   0x20,
76   0x00, /* number of extensions */
77   0x00 /* checksum goes here */
78};
79
80static void fillDescBlockTimings(unsigned char *pchDescBlock,
81                                 DisplayModePtr mode)
82{
83    struct detailed_timings timing;
84
85    timing.clock = mode->Clock * 1000;
86    timing.h_active = mode->HDisplay;
87    timing.h_blanking = mode->HTotal - mode->HDisplay;
88    timing.v_active = mode->VDisplay;
89    timing.v_blanking = mode->VTotal - mode->VDisplay;
90    timing.h_sync_off = mode->HSyncStart - mode->HDisplay;
91    timing.h_sync_width = mode->HSyncEnd - mode->HSyncStart;
92    timing.v_sync_off = mode->VSyncStart - mode->VDisplay;
93    timing.v_sync_width = mode->VSyncEnd - mode->VSyncStart;
94    pchDescBlock[0]   = (timing.clock / 10000) & 0xff;
95    pchDescBlock[1]   = (timing.clock / 10000) >> 8;
96    pchDescBlock[2]   = timing.h_active & 0xff;
97    pchDescBlock[3]   = timing.h_blanking & 0xff;
98    pchDescBlock[4]   = (timing.h_active >> 4) & 0xf0;
99    pchDescBlock[4]  |= (timing.h_blanking >> 8) & 0xf;
100    pchDescBlock[5]   = timing.v_active & 0xff;
101    pchDescBlock[6]   = timing.v_blanking & 0xff;
102    pchDescBlock[7]   = (timing.v_active >> 4) & 0xf0;
103    pchDescBlock[7]  |= (timing.v_blanking >> 8) & 0xf;
104    pchDescBlock[8]   = timing.h_sync_off & 0xff;
105    pchDescBlock[9]   = timing.h_sync_width & 0xff;
106    pchDescBlock[10]  = (timing.v_sync_off << 4) & 0xf0;
107    pchDescBlock[10] |= timing.v_sync_width & 0xf;
108    pchDescBlock[11]  = (timing.h_sync_off >> 2) & 0xC0;
109    pchDescBlock[11] |= (timing.h_sync_width >> 4) & 0x30;
110    pchDescBlock[11] |= (timing.v_sync_off >> 2) & 0xC;
111    pchDescBlock[11] |= (timing.v_sync_width >> 4) & 0x3;
112    pchDescBlock[12] = pchDescBlock[13] = pchDescBlock[14]
113                     = pchDescBlock[15] = pchDescBlock[16]
114                     = pchDescBlock[17] = 0;
115}
116
117
118static void setEDIDChecksum(unsigned char *pch)
119{
120    unsigned i, sum = 0;
121    for (i = 0; i < EDID_SIZE - 1; ++i)
122        sum += pch[i];
123    pch[EDID_SIZE - 1] = (0x100 - (sum & 0xFF)) & 0xFF;
124}
125
126
127/**
128 * Construct an EDID for an output given a preferred mode.  The main reason for
129 * doing this is to confound gnome-settings-deamon which tries to reset the
130 * last mode configuration if the same monitors are plugged in again, which is
131 * a reasonable thing to do but not what we want in a VM.  We evily store
132 * the (empty) raw EDID data at the end of the structure so that it gets
133 * freed automatically along with the structure.
134 */
135Bool VBOXEDIDSet(xf86OutputPtr output, DisplayModePtr pmode)
136{
137    unsigned char *pch, *pchEDID;
138    xf86MonPtr pEDIDMon;
139
140    pch = calloc(1, sizeof(xf86Monitor) + EDID_SIZE);
141    if (!pch)
142    {
143        xf86DrvMsg(output->scrn->scrnIndex, X_ERROR,
144            "Can't allocate memory for EDID structure.\n");
145        return FALSE;
146    }
147    pchEDID = pch + sizeof(xf86Monitor);
148    memcpy(pchEDID, g_acszEDIDBase, EDID_SIZE);
149    pchEDID[12] = pmode->HDisplay & 0xff;
150    pchEDID[13] = pmode->HDisplay >> 8;
151    pchEDID[14] = pmode->VDisplay & 0xff;
152    pchEDID[15] = pmode->VDisplay >> 8;
153    fillDescBlockTimings(pchEDID + 54, pmode);
154    setEDIDChecksum(pchEDID);
155    pEDIDMon = xf86InterpretEDID(output->scrn->scrnIndex, pchEDID);
156    if (!pEDIDMon)
157    {
158        free(pch);
159        return FALSE;
160    }
161    memcpy(pch, pEDIDMon, sizeof(xf86Monitor));
162    free(pEDIDMon);
163    pEDIDMon = (xf86MonPtr)pch;
164    xf86OutputSetEDID(output, pEDIDMon);
165    return TRUE;
166}
167