drivers/emulation/qemu/bochs: Rewrite driver

Support MMIO mapped BOCHS interface supported since qemu 3.0.

This allows to use multiple virtual GPUs by specifying:
qemu -device bochs-display -device bochs-display ...

Tested on qemu. std, qxl, vmware and multiple bochs displays are working fine.

Change-Id: Ib0eba4815942625ce4859946efccca500301bb65
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41406
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
Reviewed-by: Himanshu Sahdev <sahdev.himan@gmail.com>
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
diff --git a/src/drivers/emulation/qemu/bochs.c b/src/drivers/emulation/qemu/bochs.c
index 6372404..5a6603e 100644
--- a/src/drivers/emulation/qemu/bochs.c
+++ b/src/drivers/emulation/qemu/bochs.c
@@ -7,6 +7,7 @@
 #include <device/device.h>
 #include <device/pci.h>
 #include <device/pci_ops.h>
+#include <device/pci_ids.h>
 #include <pc80/vga.h>
 #include <pc80/vga_io.h>
 
@@ -40,66 +41,103 @@
 static int width  = CONFIG_DRIVERS_EMULATION_QEMU_BOCHS_XRES;
 static int height = CONFIG_DRIVERS_EMULATION_QEMU_BOCHS_YRES;
 
-static void bochs_write(int index, int val)
+static void bochs_write(struct resource *res, int index, int val)
 {
-	outw(index, VBE_DISPI_IOPORT_INDEX);
-	outw(val, VBE_DISPI_IOPORT_DATA);
+	if (res->flags & IORESOURCE_IO) {
+		outw(index, res->base);
+		outw(val, res->base + 1);
+	} else {
+		write16(res2mmio(res, 0x500 + index * 2, 0), val);
+	}
 }
 
-static int bochs_read(int index)
+static int bochs_read(struct resource *res, int index)
 {
-	outw(index, VBE_DISPI_IOPORT_INDEX);
-	return inw(VBE_DISPI_IOPORT_DATA);
+	if (res->flags & IORESOURCE_IO) {
+		outw(index, res->base);
+		return inw(res->base + 1);
+	} else {
+		return read16(res2mmio(res, 0x500 + index * 2, 0));
+	}
 }
 
+static void bochs_vga_write(struct resource *res, int index, uint8_t val)
+{
+	if (res->flags & IORESOURCE_IO)
+		outb(val, index + 0x3c0);
+	else
+		write8(res2mmio(res, (0x400 - 0x3c0) + index, 0), val);
+}
+
+static struct resource res_legacy = {
+	VBE_DISPI_IOPORT_INDEX,
+	VBE_DISPI_IOPORT_DATA - VBE_DISPI_IOPORT_INDEX,
+	VBE_DISPI_IOPORT_DATA,
+	NULL,
+	IORESOURCE_IO,
+	0,
+	1,
+	1
+};
+
 static void bochs_init_linear_fb(struct device *dev)
 {
 	struct edid edid;
+	struct resource *res_fb, *res_io;
 	int id, mem, bar;
-	u32 addr;
+
+	res_fb = probe_resource(dev, PCI_BASE_ADDRESS_0);
+	if (res_fb && res_fb->flags & IORESOURCE_MEM) {
+		/* qemu -vga {std,qxl} */
+		bar = 0;
+	} else {
+		res_fb = probe_resource(dev, PCI_BASE_ADDRESS_1);
+		if (res_fb && res_fb->flags & IORESOURCE_MEM) {
+			/* qemu -vga vmware */
+			bar = 1;
+		} else {
+			printk(BIOS_ERR, "%s: Not bochs compatible\n", dev_name(dev));
+			return;
+		}
+	}
+
+	/* MMIO bar supported since qemu 3.0+ */
+	res_io = probe_resource(dev, PCI_BASE_ADDRESS_2);
+	if (((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) ||
+	    !res_io || !(res_io->flags & IORESOURCE_MEM)) {
+		printk(BIOS_DEBUG, "QEMU VGA: Using legacy VGA\n");
+		res_io = &res_legacy;
+	} else {
+		printk(BIOS_DEBUG, "QEMU VGA: Using I/O bar at %llx\n", res_io->base);
+	}
 
 	/* bochs dispi detection */
-	id = bochs_read(VBE_DISPI_INDEX_ID);
+	id = bochs_read(res_io, VBE_DISPI_INDEX_ID);
 	if ((id & 0xfff0) != VBE_DISPI_ID0) {
 		printk(BIOS_DEBUG, "QEMU VGA: bochs dispi: ID mismatch.\n");
 		return;
 	}
-	mem = bochs_read(VBE_DISPI_INDEX_VIDEO_MEMORY_64K) * 64 * 1024;
-
-	/* find lfb pci bar */
-	addr = pci_read_config32(dev, PCI_BASE_ADDRESS_0);
-	if ((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) {
-		/* qemu -vga {std,qxl} */
-		bar = 0;
-	} else {
-		/* qemu -vga vmware */
-		addr = pci_read_config32(dev, PCI_BASE_ADDRESS_1);
-		bar = 1;
-	}
-	addr &= ~PCI_BASE_ADDRESS_MEM_ATTR_MASK;
-
-	if (!addr)
-		return;
+	mem = bochs_read(res_io, VBE_DISPI_INDEX_VIDEO_MEMORY_64K) * 64 * 1024;
 
 	printk(BIOS_DEBUG, "QEMU VGA: bochs dispi interface found, "
 	       "%d MiB video memory\n", mem / (1024 * 1024));
-	printk(BIOS_DEBUG, "QEMU VGA: framebuffer @ %x (pci bar %d)\n",
-	       addr, bar);
+	printk(BIOS_DEBUG, "QEMU VGA: framebuffer @ %llx (pci bar %d)\n",
+	       res_fb->base, bar);
 
 	/* setup video mode */
-	bochs_write(VBE_DISPI_INDEX_ENABLE,	 0);
-	bochs_write(VBE_DISPI_INDEX_BANK,	 0);
-	bochs_write(VBE_DISPI_INDEX_BPP,	 32);
-	bochs_write(VBE_DISPI_INDEX_XRES,	 width);
-	bochs_write(VBE_DISPI_INDEX_YRES,	 height);
-	bochs_write(VBE_DISPI_INDEX_VIRT_WIDTH,	 width);
-	bochs_write(VBE_DISPI_INDEX_VIRT_HEIGHT, height);
-	bochs_write(VBE_DISPI_INDEX_X_OFFSET,	 0);
-	bochs_write(VBE_DISPI_INDEX_Y_OFFSET,	 0);
-	bochs_write(VBE_DISPI_INDEX_ENABLE,
+	bochs_write(res_io, VBE_DISPI_INDEX_ENABLE,	 0);
+	bochs_write(res_io, VBE_DISPI_INDEX_BANK,	 0);
+	bochs_write(res_io, VBE_DISPI_INDEX_BPP,	 32);
+	bochs_write(res_io, VBE_DISPI_INDEX_XRES,	 width);
+	bochs_write(res_io, VBE_DISPI_INDEX_YRES,	 height);
+	bochs_write(res_io, VBE_DISPI_INDEX_VIRT_WIDTH,	 width);
+	bochs_write(res_io, VBE_DISPI_INDEX_VIRT_HEIGHT, height);
+	bochs_write(res_io, VBE_DISPI_INDEX_X_OFFSET,	 0);
+	bochs_write(res_io, VBE_DISPI_INDEX_Y_OFFSET,	 0);
+	bochs_write(res_io, VBE_DISPI_INDEX_ENABLE,
 		    VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
 
-	outb(0x20, 0x3c0); /* disable blanking */
+	bochs_vga_write(res_io, 0, 0x20);	/* disable blanking */
 
 	/* setup coreboot framebuffer */
 	edid.mode.ha = width;
@@ -107,7 +145,7 @@
 	edid.panel_bits_per_color = 8;
 	edid.panel_bits_per_pixel = 24;
 	edid_set_framebuffer_bits_per_pixel(&edid, 32, 0);
-	set_vbe_mode_info_valid(&edid, addr);
+	set_vbe_mode_info_valid(&edid, res_fb->base);
 }
 
 static void bochs_init_text_mode(struct device *dev)