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