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