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