This patch adds edid support to the qemu stdvga. It is turned off by default and can be enabled with the new edid property. The patch also adds xres and yres properties to specify the video mode you want the guest use. Works only with edid enabled and updated guest driver. The mmio bar of the stdvga has some unused address space at the start. It was reserved just in case it'll be needed for virtio, but it turned out to not be needed for that. So let's use that region to place the EDID data block there. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Message-id: 20180925075646.25114-6-kraxel@redhat.com
224 lines
6.2 KiB
C
224 lines
6.2 KiB
C
#include "qemu/osdep.h"
|
|
#include "hw/hw.h"
|
|
#include "hw/pci/pci.h"
|
|
#include "vga_int.h"
|
|
#include "hw/virtio/virtio-pci.h"
|
|
#include "qapi/error.h"
|
|
|
|
/*
|
|
* virtio-vga: This extends VirtioPCIProxy.
|
|
*/
|
|
#define TYPE_VIRTIO_VGA "virtio-vga"
|
|
#define VIRTIO_VGA(obj) \
|
|
OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA)
|
|
|
|
typedef struct VirtIOVGA {
|
|
VirtIOPCIProxy parent_obj;
|
|
VirtIOGPU vdev;
|
|
VGACommonState vga;
|
|
MemoryRegion vga_mrs[3];
|
|
} VirtIOVGA;
|
|
|
|
static void virtio_vga_invalidate_display(void *opaque)
|
|
{
|
|
VirtIOVGA *vvga = opaque;
|
|
|
|
if (vvga->vdev.enable) {
|
|
virtio_gpu_ops.invalidate(&vvga->vdev);
|
|
} else {
|
|
vvga->vga.hw_ops->invalidate(&vvga->vga);
|
|
}
|
|
}
|
|
|
|
static void virtio_vga_update_display(void *opaque)
|
|
{
|
|
VirtIOVGA *vvga = opaque;
|
|
|
|
if (vvga->vdev.enable) {
|
|
virtio_gpu_ops.gfx_update(&vvga->vdev);
|
|
} else {
|
|
vvga->vga.hw_ops->gfx_update(&vvga->vga);
|
|
}
|
|
}
|
|
|
|
static void virtio_vga_text_update(void *opaque, console_ch_t *chardata)
|
|
{
|
|
VirtIOVGA *vvga = opaque;
|
|
|
|
if (vvga->vdev.enable) {
|
|
if (virtio_gpu_ops.text_update) {
|
|
virtio_gpu_ops.text_update(&vvga->vdev, chardata);
|
|
}
|
|
} else {
|
|
if (vvga->vga.hw_ops->text_update) {
|
|
vvga->vga.hw_ops->text_update(&vvga->vga, chardata);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int virtio_vga_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
|
|
{
|
|
VirtIOVGA *vvga = opaque;
|
|
|
|
if (virtio_gpu_ops.ui_info) {
|
|
return virtio_gpu_ops.ui_info(&vvga->vdev, idx, info);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void virtio_vga_gl_block(void *opaque, bool block)
|
|
{
|
|
VirtIOVGA *vvga = opaque;
|
|
|
|
if (virtio_gpu_ops.gl_block) {
|
|
virtio_gpu_ops.gl_block(&vvga->vdev, block);
|
|
}
|
|
}
|
|
|
|
static const GraphicHwOps virtio_vga_ops = {
|
|
.invalidate = virtio_vga_invalidate_display,
|
|
.gfx_update = virtio_vga_update_display,
|
|
.text_update = virtio_vga_text_update,
|
|
.ui_info = virtio_vga_ui_info,
|
|
.gl_block = virtio_vga_gl_block,
|
|
};
|
|
|
|
static const VMStateDescription vmstate_virtio_vga = {
|
|
.name = "virtio-vga",
|
|
.version_id = 2,
|
|
.minimum_version_id = 2,
|
|
.fields = (VMStateField[]) {
|
|
/* no pci stuff here, saving the virtio device will handle that */
|
|
VMSTATE_STRUCT(vga, VirtIOVGA, 0, vmstate_vga_common, VGACommonState),
|
|
VMSTATE_END_OF_LIST()
|
|
}
|
|
};
|
|
|
|
/* VGA device wrapper around PCI device around virtio GPU */
|
|
static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
|
|
{
|
|
VirtIOVGA *vvga = VIRTIO_VGA(vpci_dev);
|
|
VirtIOGPU *g = &vvga->vdev;
|
|
VGACommonState *vga = &vvga->vga;
|
|
Error *err = NULL;
|
|
uint32_t offset;
|
|
int i;
|
|
|
|
/* init vga compat bits */
|
|
vga->vram_size_mb = 8;
|
|
vga_common_init(vga, OBJECT(vpci_dev));
|
|
vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev),
|
|
pci_address_space_io(&vpci_dev->pci_dev), true);
|
|
pci_register_bar(&vpci_dev->pci_dev, 0,
|
|
PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram);
|
|
|
|
/*
|
|
* Configure virtio bar and regions
|
|
*
|
|
* We use bar #2 for the mmio regions, to be compatible with stdvga.
|
|
* virtio regions are moved to the end of bar #2, to make room for
|
|
* the stdvga mmio registers at the start of bar #2.
|
|
*/
|
|
vpci_dev->modern_mem_bar_idx = 2;
|
|
vpci_dev->msix_bar_idx = 4;
|
|
|
|
if (!(vpci_dev->flags & VIRTIO_PCI_FLAG_PAGE_PER_VQ)) {
|
|
/*
|
|
* with page-per-vq=off there is no padding space we can use
|
|
* for the stdvga registers. Make the common and isr regions
|
|
* smaller then.
|
|
*/
|
|
vpci_dev->common.size /= 2;
|
|
vpci_dev->isr.size /= 2;
|
|
}
|
|
|
|
offset = memory_region_size(&vpci_dev->modern_bar);
|
|
offset -= vpci_dev->notify.size;
|
|
vpci_dev->notify.offset = offset;
|
|
offset -= vpci_dev->device.size;
|
|
vpci_dev->device.offset = offset;
|
|
offset -= vpci_dev->isr.size;
|
|
vpci_dev->isr.offset = offset;
|
|
offset -= vpci_dev->common.size;
|
|
vpci_dev->common.offset = offset;
|
|
|
|
/* init virtio bits */
|
|
qdev_set_parent_bus(DEVICE(g), BUS(&vpci_dev->bus));
|
|
virtio_pci_force_virtio_1(vpci_dev);
|
|
object_property_set_bool(OBJECT(g), true, "realized", &err);
|
|
if (err) {
|
|
error_propagate(errp, err);
|
|
return;
|
|
}
|
|
|
|
/* add stdvga mmio regions */
|
|
pci_std_vga_mmio_region_init(vga, OBJECT(vvga), &vpci_dev->modern_bar,
|
|
vvga->vga_mrs, true, false);
|
|
|
|
vga->con = g->scanout[0].con;
|
|
graphic_console_set_hwops(vga->con, &virtio_vga_ops, vvga);
|
|
|
|
for (i = 0; i < g->conf.max_outputs; i++) {
|
|
object_property_set_link(OBJECT(g->scanout[i].con),
|
|
OBJECT(vpci_dev),
|
|
"device", errp);
|
|
}
|
|
}
|
|
|
|
static void virtio_vga_reset(DeviceState *dev)
|
|
{
|
|
VirtIOVGA *vvga = VIRTIO_VGA(dev);
|
|
|
|
/* reset virtio-gpu */
|
|
virtio_gpu_reset(VIRTIO_DEVICE(&vvga->vdev));
|
|
|
|
/* reset vga */
|
|
vga_common_reset(&vvga->vga);
|
|
vga_dirty_log_start(&vvga->vga);
|
|
}
|
|
|
|
static Property virtio_vga_properties[] = {
|
|
DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy),
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
};
|
|
|
|
static void virtio_vga_class_init(ObjectClass *klass, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
|
|
PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
|
|
|
|
set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
|
|
dc->props = virtio_vga_properties;
|
|
dc->reset = virtio_vga_reset;
|
|
dc->vmsd = &vmstate_virtio_vga;
|
|
dc->hotpluggable = false;
|
|
|
|
k->realize = virtio_vga_realize;
|
|
pcidev_k->romfile = "vgabios-virtio.bin";
|
|
pcidev_k->class_id = PCI_CLASS_DISPLAY_VGA;
|
|
}
|
|
|
|
static void virtio_vga_inst_initfn(Object *obj)
|
|
{
|
|
VirtIOVGA *dev = VIRTIO_VGA(obj);
|
|
|
|
virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
|
|
TYPE_VIRTIO_GPU);
|
|
}
|
|
|
|
static TypeInfo virtio_vga_info = {
|
|
.name = TYPE_VIRTIO_VGA,
|
|
.parent = TYPE_VIRTIO_PCI,
|
|
.instance_size = sizeof(struct VirtIOVGA),
|
|
.instance_init = virtio_vga_inst_initfn,
|
|
.class_init = virtio_vga_class_init,
|
|
};
|
|
|
|
static void virtio_vga_register_types(void)
|
|
{
|
|
type_register_static(&virtio_vga_info);
|
|
}
|
|
|
|
type_init(virtio_vga_register_types)
|