diff options
author | Alon Levy <alevy@redhat.com> | 2012-06-27 12:11:41 +0300 |
---|---|---|
committer | Marc-André Lureau <marcandre.lureau@gmail.com> | 2012-07-15 19:21:34 +0200 |
commit | 75619d076bb029b76bed7a885864e191c386aa23 (patch) | |
tree | c3d7d0ce41991c7e68710181722cfac002fa8c43 | |
parent | de547245e75ea88f780205da072967bff119a636 (diff) |
add qxl_edid
Taken from Virtual Box, following exactly the same logic:
gnome-settings-daemon relies on the serial given in the edid to set the
resolution to the same one last used on that screen. Since this is not
what we want with a virtual machine, we produce a serial that is
different for every resolution.
-rw-r--r-- | src/Makefile.am | 5 | ||||
-rw-r--r-- | src/qxl.h | 6 | ||||
-rw-r--r-- | src/qxl_driver.c | 21 | ||||
-rw-r--r-- | src/qxl_edid.c | 209 |
4 files changed, 239 insertions, 2 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 9d9e965..45ba8c9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -50,6 +50,7 @@ qxl_drv_la_SOURCES = \ qxl_cursor.c \ qxl_option_helpers.c \ qxl_option_helpers.h \ + qxl_edid.c \ compat-api.h endif @@ -87,5 +88,7 @@ spiceqxl_drv_la_SOURCES = \ mspace.h \ murmurhash3.c \ murmurhash3.h \ - qxl_cursor.c + qxl_cursor.c \ + qxl_edid.c \ + compat-api.h endif @@ -264,6 +264,7 @@ struct _qxl_screen_t typedef struct qxl_output_private { qxl_screen_t *qxl; int head; + xf86OutputStatus status; } qxl_output_private; typedef struct qxl_crtc_private { @@ -469,6 +470,11 @@ void qxl_io_notify_oom(qxl_screen_t *qxl); void qxl_io_flush_surfaces(qxl_screen_t *qxl); void qxl_io_destroy_all_surfaces (qxl_screen_t *qxl); +/* + * qxl_edid.c + */ +Bool qxl_output_edid_set(xf86OutputPtr output, int head, DisplayModePtr mode); + #ifdef XSPICE /* device to spice-server, now xspice to spice-server */ void ioport_write(qxl_screen_t *qxl, uint32_t io_port, uint32_t val); diff --git a/src/qxl_driver.c b/src/qxl_driver.c index f792d07..e4d1bbe 100644 --- a/src/qxl_driver.c +++ b/src/qxl_driver.c @@ -895,6 +895,21 @@ qxl_create_desired_modes(qxl_screen_t *qxl) return TRUE; } +static void +qxl_update_edid(qxl_screen_t *qxl) +{ + int i; + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(qxl->pScrn); + + for (i = 0 ; i < config->num_crtc; ++i) { + xf86CrtcPtr crtc = config->crtc[i]; + if (!crtc->enabled) { + continue; + } + qxl_output_edid_set(qxl->outputs[i], i, &crtc->desiredMode); + } +} + static Bool qxl_create_screen_resources(ScreenPtr pScreen) { @@ -927,6 +942,7 @@ qxl_create_screen_resources(ScreenPtr pScreen) } qxl_create_desired_modes(qxl); + qxl_update_edid(qxl); return TRUE; } @@ -1607,7 +1623,9 @@ static Bool qxl_output_set_property(xf86OutputPtr output, Atom property, RRPropertyValuePtr value) { - return FALSE; + /* EDID data is stored in the "EDID" atom property, we must return + * TRUE here for that. No penalty to say ok to everything else. */ + return TRUE; } static Bool @@ -1670,6 +1688,7 @@ qxl_crtc_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, #if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,5,99,0,0) crtc->transformPresent = FALSE; #endif + qxl_output_edid_set(crtc_private->output, crtc_private->head, mode); qxl_update_monitors_config(qxl); return TRUE; } diff --git a/src/qxl_edid.c b/src/qxl_edid.c new file mode 100644 index 0000000..de52f11 --- /dev/null +++ b/src/qxl_edid.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2012 Red Hat, Inc. + * + * ------------------------------------------------------------------- + * This code is Based on Virtual Box OSE edid.c, with the following copyright + * notice: + * + * Copyright (C) 2006-2010 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * -------------------------------------------------------------------- + * + * This code is based on drmmode_display.c from the X.Org xf86-video-intel + * driver with the following copyright notice: + * + * Copyright © 2007 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Dave Airlie <airlied@redhat.com> + */ + +#include <xorg-server.h> +#include <misc.h> +#include <xf86DDC.h> +#include <xf86Crtc.h> + +#include "qxl.h" + +enum { EDID_SIZE = 128 }; + +typedef struct __attribute__ ((__packed__)) { + unsigned char header[8]; + unsigned char manufacturer[2]; + unsigned char product_code[2]; + unsigned char serial[4]; + unsigned char week; + unsigned char year; + unsigned char version[2]; + unsigned char capabilities; + unsigned char horizontal_resolution; + unsigned char vertical_resolution; + unsigned char gamma; + unsigned char features; + unsigned char chromaticity[10]; + unsigned char default_timings[3]; + unsigned char standard_timings[16]; + unsigned char descriptor1[18]; + unsigned char descriptor2[18]; + unsigned char descriptor3[18]; + unsigned char descriptor4[18]; + unsigned char num_extensions; + unsigned char neg_checksum; +} EDIDv13; + +int qxl_compile_time_test_edid_size[(sizeof(EDIDv13) == EDID_SIZE) - 1]; + +static const EDIDv13 edid_base = +{ + .header = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}, + /* hex(sum([(ord(x) - ord('A') + 1) * 2**(5*i) for i,x in enumerate(reversed('QXL'))])) */ + .manufacturer = {0x47, 0x0c}, /* (QXL) 5 bit per char (A-Z), last bit 0 */ + .product_code = {0x00, 0x00}, + .serial = {0x00, 0x00, 0x00, 0x00}, /* set differently per mode */ + .year = 0x01, + .week = 0x00, + .version = {0x01, 0x03}, + .capabilities = 0x80, /* digital */ + .horizontal_resolution = 0x00, /* horiz. res in cm, zero for projectors */ + .vertical_resolution = 0x00, /* vert. res in cm */ + .gamma = 0x78, /* display gamma (120 == 2.2). Should we ask the host for this? */ + .features = 0xEE, /* features (standby, suspend, off, RGB, standard colour space, + * preferred timing mode) */ + .chromaticity = {0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54}, + /* chromaticity for standard colour space - should we ask the host? */ + .default_timings = {0x00, 0x00, 0x00}, /* no default timings */ + .standard_timings = {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01}, /* no standard timings */ + .descriptor1 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* descriptor block 1 goes here */ + .descriptor2 = {0x00, 0x00, 0x00, 0xFD, 0x00, /* descriptor block 2, monitor ranges */ + 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}, + /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */ + .descriptor3 = {0x00, 0x00, 0x00, 0xFC, 0x00, /* descriptor block 3, monitor name */ + 'Q', 'X', 'L', ' ', '1', '\n', ' ', ' ', ' ', ' ', ' ', ' ', ' '}, + .descriptor4 = {0x00, 0x00, 0x00, 0x10, 0x00, /* descriptor block 4: dummy data */ + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}, + .num_extensions = 0x00, /* number of extensions to follow */ + .neg_checksum = 0x00 /* checksum goes here */ +}; + +static void fillDescBlockTimings(unsigned char *pchDescBlock, + DisplayModePtr mode) +{ + struct detailed_timings timing; + + timing.clock = mode->Clock * 1000; + timing.h_active = mode->HDisplay; + timing.h_blanking = mode->HTotal - mode->HDisplay; + timing.v_active = mode->VDisplay; + timing.v_blanking = mode->VTotal - mode->VDisplay; + timing.h_sync_off = mode->HSyncStart - mode->HDisplay; + timing.h_sync_width = mode->HSyncEnd - mode->HSyncStart; + timing.v_sync_off = mode->VSyncStart - mode->VDisplay; + timing.v_sync_width = mode->VSyncEnd - mode->VSyncStart; + pchDescBlock[0] = (timing.clock / 10000) & 0xff; + pchDescBlock[1] = (timing.clock / 10000) >> 8; + pchDescBlock[2] = timing.h_active & 0xff; + pchDescBlock[3] = timing.h_blanking & 0xff; + pchDescBlock[4] = (timing.h_active >> 4) & 0xf0; + pchDescBlock[4] |= (timing.h_blanking >> 8) & 0xf; + pchDescBlock[5] = timing.v_active & 0xff; + pchDescBlock[6] = timing.v_blanking & 0xff; + pchDescBlock[7] = (timing.v_active >> 4) & 0xf0; + pchDescBlock[7] |= (timing.v_blanking >> 8) & 0xf; + pchDescBlock[8] = timing.h_sync_off & 0xff; + pchDescBlock[9] = timing.h_sync_width & 0xff; + pchDescBlock[10] = (timing.v_sync_off << 4) & 0xf0; + pchDescBlock[10] |= timing.v_sync_width & 0xf; + pchDescBlock[11] = (timing.h_sync_off >> 2) & 0xC0; + pchDescBlock[11] |= (timing.h_sync_width >> 4) & 0x30; + pchDescBlock[11] |= (timing.v_sync_off >> 2) & 0xC; + pchDescBlock[11] |= (timing.v_sync_width >> 4) & 0x3; + pchDescBlock[12] = pchDescBlock[13] = pchDescBlock[14] + = pchDescBlock[15] = pchDescBlock[16] + = pchDescBlock[17] = 0; +} + +static void setEDIDChecksum(EDIDv13 *edid) +{ + unsigned i, sum = 0; + unsigned char *p = (unsigned char *)edid; + + for (i = 0; i < EDID_SIZE - 1; ++i) + sum += p[i]; + edid->neg_checksum = (0x100 - (sum & 0xFF)) & 0xFF; +} + +/** + * Construct an EDID for an output given a preferred mode. The main reason for + * doing this is to confound gnome-settings-deamon which tries to reset the + * last mode configuration if the same monitors are plugged in again, which is + * a reasonable thing to do but not what we want in a VM. We evily store + * the (empty) raw EDID data at the end of the structure so that it gets + * freed automatically along with the structure. + */ +Bool qxl_output_edid_set(xf86OutputPtr output, int head, DisplayModePtr mode) +{ + unsigned char *pch; + EDIDv13 *edid; + xf86MonPtr edid_mon; + int eol_pos; + + pch = calloc(1, sizeof(xf86Monitor) + EDID_SIZE); + if (!pch) + { + xf86DrvMsg(output->scrn->scrnIndex, X_ERROR, + "Can't allocate memory for EDID structure.\n"); + return FALSE; + } + edid = (EDIDv13 *)(pch + sizeof(xf86Monitor)); + *edid = edid_base; + edid->serial[0] = mode ? mode->HDisplay & 0xff : 0x00; + edid->serial[1] = mode ? mode->HDisplay >> 8 : 0x00; + edid->serial[2] = mode ? mode->VDisplay & 0xff : 0x00; + edid->serial[3] = mode ? mode->VDisplay >> 8 : 0x00; + snprintf((char *)&edid->descriptor3[5], 12, "QXL %d\n%n", head + 1, &eol_pos); + edid->descriptor3[5 + eol_pos] = ' '; + + if (mode) { + fillDescBlockTimings(edid->descriptor1, mode); + } + setEDIDChecksum(edid); + edid_mon = xf86InterpretEDID(output->scrn->scrnIndex, (unsigned char *)edid); + if (!edid_mon) + { + free(pch); + return FALSE; + } + memcpy(pch, edid_mon, sizeof(xf86Monitor)); + free(edid_mon); + edid_mon = (xf86MonPtr)pch; + xf86OutputSetEDID(output, edid_mon); + return TRUE; +} |