1/* $Id: edid.c,v 1.3 2024/07/04 06:40:40 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#ifdef HAVE_CONFIG_H 40#include "config.h" 41#endif 42 43#include "misc.h" 44#include "xf86DDC.h" 45#include "xf86Crtc.h" 46#include "vboxvideo_drv.h" 47 48enum { EDID_SIZE = 128 }; 49 50const unsigned char g_acszEDIDBase[EDID_SIZE] = 51{ 52 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */ 53 0x58, 0x58, /* manufacturer (VBX) */ 54 0x00, 0x00, /* product code */ 55 0x00, 0x00,0x00, 0x00, /* serial number goes here */ 56 0x01, /* week of manufacture */ 57 0x00, /* year of manufacture */ 58 0x01, 0x03, /* EDID version */ 59 0x80, /* capabilities - digital */ 60 0x00, /* horiz. res in cm, zero for projectors */ 61 0x00, /* vert. res in cm */ 62 0x78, /* display gamma (120 == 2.2). Should we ask the host for this? */ 63 0xEE, /* features (standby, suspend, off, RGB, standard colour space, 64 * preferred timing mode) */ 65 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54, 66 /* chromaticity for standard colour space - should we ask the host? */ 67 0x00, 0x00, 0x00, /* no default timings */ 68 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 69 0x01, 0x01, 0x01, 0x01, /* no standard timings */ 70 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* descriptor block 1 goes here */ 72 0x00, 0x00, 0x00, 0xFD, 0x00, /* descriptor block 2, monitor ranges */ 73 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 74 0x20, /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */ 75 0x00, 0x00, 0x00, 0xFC, 0x00, /* descriptor block 3, monitor name */ 76 'V', 'B', 'O', 'X', ' ', 'm', 'o', 'n', 'i', 't', 'o', 'r', '\n', 77 0x00, 0x00, 0x00, 0x10, 0x00, /* descriptor block 4: dummy data */ 78 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 79 0x20, 80 0x00, /* number of extensions */ 81 0x00 /* checksum goes here */ 82}; 83 84static void fillDescBlockTimings(unsigned char *pchDescBlock, 85 DisplayModePtr mode) 86{ 87 struct detailed_timings timing; 88 89 timing.clock = mode->Clock * 1000; 90 timing.h_active = mode->HDisplay; 91 timing.h_blanking = mode->HTotal - mode->HDisplay; 92 timing.v_active = mode->VDisplay; 93 timing.v_blanking = mode->VTotal - mode->VDisplay; 94 timing.h_sync_off = mode->HSyncStart - mode->HDisplay; 95 timing.h_sync_width = mode->HSyncEnd - mode->HSyncStart; 96 timing.v_sync_off = mode->VSyncStart - mode->VDisplay; 97 timing.v_sync_width = mode->VSyncEnd - mode->VSyncStart; 98 pchDescBlock[0] = (timing.clock / 10000) & 0xff; 99 pchDescBlock[1] = (timing.clock / 10000) >> 8; 100 pchDescBlock[2] = timing.h_active & 0xff; 101 pchDescBlock[3] = timing.h_blanking & 0xff; 102 pchDescBlock[4] = (timing.h_active >> 4) & 0xf0; 103 pchDescBlock[4] |= (timing.h_blanking >> 8) & 0xf; 104 pchDescBlock[5] = timing.v_active & 0xff; 105 pchDescBlock[6] = timing.v_blanking & 0xff; 106 pchDescBlock[7] = (timing.v_active >> 4) & 0xf0; 107 pchDescBlock[7] |= (timing.v_blanking >> 8) & 0xf; 108 pchDescBlock[8] = timing.h_sync_off & 0xff; 109 pchDescBlock[9] = timing.h_sync_width & 0xff; 110 pchDescBlock[10] = (timing.v_sync_off << 4) & 0xf0; 111 pchDescBlock[10] |= timing.v_sync_width & 0xf; 112 pchDescBlock[11] = (timing.h_sync_off >> 2) & 0xC0; 113 pchDescBlock[11] |= (timing.h_sync_width >> 4) & 0x30; 114 pchDescBlock[11] |= (timing.v_sync_off >> 2) & 0xC; 115 pchDescBlock[11] |= (timing.v_sync_width >> 4) & 0x3; 116 pchDescBlock[12] = pchDescBlock[13] = pchDescBlock[14] 117 = pchDescBlock[15] = pchDescBlock[16] 118 = pchDescBlock[17] = 0; 119} 120 121 122static void setEDIDChecksum(unsigned char *pch) 123{ 124 unsigned i, sum = 0; 125 for (i = 0; i < EDID_SIZE - 1; ++i) 126 sum += pch[i]; 127 pch[EDID_SIZE - 1] = (0x100 - (sum & 0xFF)) & 0xFF; 128} 129 130 131/** 132 * Construct an EDID for an output given a preferred mode. The main reason for 133 * doing this is to confound gnome-settings-deamon which tries to reset the 134 * last mode configuration if the same monitors are plugged in again, which is 135 * a reasonable thing to do but not what we want in a VM. We evily store 136 * the (empty) raw EDID data at the end of the structure so that it gets 137 * freed automatically along with the structure. 138 */ 139Bool VBOXEDIDSet(xf86OutputPtr output, DisplayModePtr pmode) 140{ 141 unsigned char *pch, *pchEDID; 142 xf86MonPtr pEDIDMon; 143 144 pch = calloc(1, sizeof(xf86Monitor) + EDID_SIZE); 145 if (!pch) 146 { 147 xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, 148 "Can't allocate memory for EDID structure.\n"); 149 return FALSE; 150 } 151 pchEDID = pch + sizeof(xf86Monitor); 152 memcpy(pchEDID, g_acszEDIDBase, EDID_SIZE); 153 pchEDID[12] = pmode->HDisplay & 0xff; 154 pchEDID[13] = pmode->HDisplay >> 8; 155 pchEDID[14] = pmode->VDisplay & 0xff; 156 pchEDID[15] = pmode->VDisplay >> 8; 157 fillDescBlockTimings(pchEDID + 54, pmode); 158 setEDIDChecksum(pchEDID); 159 pEDIDMon = xf86InterpretEDID(output->scrn->scrnIndex, pchEDID); 160 if (!pEDIDMon) 161 { 162 free(pch); 163 return FALSE; 164 } 165 memcpy(pch, pEDIDMon, sizeof(xf86Monitor)); 166 free(pEDIDMon); 167 pEDIDMon = (xf86MonPtr)pch; 168 xf86OutputSetEDID(output, pEDIDMon); 169 return TRUE; 170} 171