blob: 73fa19e2ca5687916bade018d0101d874688da42 [file] [log] [blame]
Angel Pons8a3453f2020-04-02 23:48:19 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Martin Rothe20f3d02017-04-04 14:26:57 -06002
Ronald G. Minnichcae09e02013-11-19 17:42:41 -08003#include <stdint.h>
Gerd Hoffmann414b9472013-06-18 23:41:21 +02004#include <arch/io.h>
Gerd Hoffmann414b9472013-06-18 23:41:21 +02005#include <console/console.h>
6#include <device/device.h>
7#include <device/pci.h>
Gerd Hoffmann414b9472013-06-18 23:41:21 +02008#include <device/pci_ops.h>
Patrick Rudolphefaf1b32020-05-14 16:37:46 +02009#include <device/pci_ids.h>
Vladimir Serbinenkodb7d04d2014-02-22 10:35:45 +010010#include <pc80/vga.h>
11#include <pc80/vga_io.h>
Patrick Rudolph8b56c8c2020-02-19 12:57:00 +010012#include <framebuffer_info.h>
Gerd Hoffmann414b9472013-06-18 23:41:21 +020013
14/* VGA init. We use the Bochs VESA VBE extensions */
15#define VBE_DISPI_IOPORT_INDEX 0x01CE
16#define VBE_DISPI_IOPORT_DATA 0x01CF
17
18#define VBE_DISPI_INDEX_ID 0x0
19#define VBE_DISPI_INDEX_XRES 0x1
20#define VBE_DISPI_INDEX_YRES 0x2
21#define VBE_DISPI_INDEX_BPP 0x3
22#define VBE_DISPI_INDEX_ENABLE 0x4
23#define VBE_DISPI_INDEX_BANK 0x5
24#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
25#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
26#define VBE_DISPI_INDEX_X_OFFSET 0x8
27#define VBE_DISPI_INDEX_Y_OFFSET 0x9
28#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa
29
30#define VBE_DISPI_ID0 0xB0C0
31#define VBE_DISPI_ID1 0xB0C1
32#define VBE_DISPI_ID2 0xB0C2
33#define VBE_DISPI_ID4 0xB0C4
34#define VBE_DISPI_ID5 0xB0C5
35
36#define VBE_DISPI_DISABLED 0x00
37#define VBE_DISPI_ENABLED 0x01
38#define VBE_DISPI_LFB_ENABLED 0x40
39#define VBE_DISPI_NOCLEARMEM 0x80
40
41static int width = CONFIG_DRIVERS_EMULATION_QEMU_BOCHS_XRES;
42static int height = CONFIG_DRIVERS_EMULATION_QEMU_BOCHS_YRES;
Gerd Hoffmann414b9472013-06-18 23:41:21 +020043
Patrick Rudolphefaf1b32020-05-14 16:37:46 +020044static void bochs_write(struct resource *res, int index, int val)
Gerd Hoffmann414b9472013-06-18 23:41:21 +020045{
Patrick Rudolphefaf1b32020-05-14 16:37:46 +020046 if (res->flags & IORESOURCE_IO) {
47 outw(index, res->base);
48 outw(val, res->base + 1);
49 } else {
50 write16(res2mmio(res, 0x500 + index * 2, 0), val);
51 }
Gerd Hoffmann414b9472013-06-18 23:41:21 +020052}
53
Patrick Rudolphefaf1b32020-05-14 16:37:46 +020054static int bochs_read(struct resource *res, int index)
Gerd Hoffmann414b9472013-06-18 23:41:21 +020055{
Patrick Rudolphefaf1b32020-05-14 16:37:46 +020056 if (res->flags & IORESOURCE_IO) {
57 outw(index, res->base);
58 return inw(res->base + 1);
59 } else {
60 return read16(res2mmio(res, 0x500 + index * 2, 0));
61 }
Gerd Hoffmann414b9472013-06-18 23:41:21 +020062}
63
Patrick Rudolphefaf1b32020-05-14 16:37:46 +020064static void bochs_vga_write(struct resource *res, int index, uint8_t val)
65{
66 if (res->flags & IORESOURCE_IO)
67 outb(val, index + 0x3c0);
68 else
69 write8(res2mmio(res, (0x400 - 0x3c0) + index, 0), val);
70}
71
72static struct resource res_legacy = {
73 VBE_DISPI_IOPORT_INDEX,
74 VBE_DISPI_IOPORT_DATA - VBE_DISPI_IOPORT_INDEX,
75 VBE_DISPI_IOPORT_DATA,
76 NULL,
77 IORESOURCE_IO,
78 0,
79 1,
80 1
81};
82
Nico Huber6d8266b2017-05-20 16:46:01 +020083static void bochs_init_linear_fb(struct device *dev)
Gerd Hoffmann414b9472013-06-18 23:41:21 +020084{
Patrick Rudolphefaf1b32020-05-14 16:37:46 +020085 struct resource *res_fb, *res_io;
Gerd Hoffmann414b9472013-06-18 23:41:21 +020086 int id, mem, bar;
Patrick Rudolphefaf1b32020-05-14 16:37:46 +020087
88 res_fb = probe_resource(dev, PCI_BASE_ADDRESS_0);
89 if (res_fb && res_fb->flags & IORESOURCE_MEM) {
90 /* qemu -vga {std,qxl} */
91 bar = 0;
92 } else {
93 res_fb = probe_resource(dev, PCI_BASE_ADDRESS_1);
94 if (res_fb && res_fb->flags & IORESOURCE_MEM) {
95 /* qemu -vga vmware */
96 bar = 1;
97 } else {
98 printk(BIOS_ERR, "%s: Not bochs compatible\n", dev_name(dev));
99 return;
100 }
101 }
102
103 /* MMIO bar supported since qemu 3.0+ */
104 res_io = probe_resource(dev, PCI_BASE_ADDRESS_2);
105 if (((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) ||
106 !res_io || !(res_io->flags & IORESOURCE_MEM)) {
107 printk(BIOS_DEBUG, "QEMU VGA: Using legacy VGA\n");
108 res_io = &res_legacy;
109 } else {
110 printk(BIOS_DEBUG, "QEMU VGA: Using I/O bar at %llx\n", res_io->base);
111 }
Gerd Hoffmann414b9472013-06-18 23:41:21 +0200112
113 /* bochs dispi detection */
Patrick Rudolphefaf1b32020-05-14 16:37:46 +0200114 id = bochs_read(res_io, VBE_DISPI_INDEX_ID);
Gerd Hoffmann414b9472013-06-18 23:41:21 +0200115 if ((id & 0xfff0) != VBE_DISPI_ID0) {
116 printk(BIOS_DEBUG, "QEMU VGA: bochs dispi: ID mismatch.\n");
117 return;
118 }
Patrick Rudolphefaf1b32020-05-14 16:37:46 +0200119 mem = bochs_read(res_io, VBE_DISPI_INDEX_VIDEO_MEMORY_64K) * 64 * 1024;
Ronald G. Minnichcae09e02013-11-19 17:42:41 -0800120
121 printk(BIOS_DEBUG, "QEMU VGA: bochs dispi interface found, "
Elyes HAOUASa342f392018-10-17 10:56:26 +0200122 "%d MiB video memory\n", mem / (1024 * 1024));
Patrick Rudolphefaf1b32020-05-14 16:37:46 +0200123 printk(BIOS_DEBUG, "QEMU VGA: framebuffer @ %llx (pci bar %d)\n",
124 res_fb->base, bar);
Gerd Hoffmann414b9472013-06-18 23:41:21 +0200125
126 /* setup video mode */
Patrick Rudolphefaf1b32020-05-14 16:37:46 +0200127 bochs_write(res_io, VBE_DISPI_INDEX_ENABLE, 0);
128 bochs_write(res_io, VBE_DISPI_INDEX_BANK, 0);
129 bochs_write(res_io, VBE_DISPI_INDEX_BPP, 32);
130 bochs_write(res_io, VBE_DISPI_INDEX_XRES, width);
131 bochs_write(res_io, VBE_DISPI_INDEX_YRES, height);
132 bochs_write(res_io, VBE_DISPI_INDEX_VIRT_WIDTH, width);
133 bochs_write(res_io, VBE_DISPI_INDEX_VIRT_HEIGHT, height);
134 bochs_write(res_io, VBE_DISPI_INDEX_X_OFFSET, 0);
135 bochs_write(res_io, VBE_DISPI_INDEX_Y_OFFSET, 0);
136 bochs_write(res_io, VBE_DISPI_INDEX_ENABLE,
Gerd Hoffmann414b9472013-06-18 23:41:21 +0200137 VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
138
Patrick Rudolphefaf1b32020-05-14 16:37:46 +0200139 bochs_vga_write(res_io, 0, 0x20); /* disable blanking */
Gerd Hoffmann748a6b12013-11-25 17:12:07 +0100140
Patrick Rudolph8b56c8c2020-02-19 12:57:00 +0100141 /* Advertise new mode */
142 fb_add_framebuffer_info(res_fb->base, width, height, 4 * width, 32);
Nico Huber6d8266b2017-05-20 16:46:01 +0200143}
144
145static void bochs_init_text_mode(struct device *dev)
146{
Vladimir Serbinenkodb7d04d2014-02-22 10:35:45 +0100147 vga_misc_write(0x1);
148 vga_textmode_init();
Nico Huber6d8266b2017-05-20 16:46:01 +0200149}
150
151static void bochs_init(struct device *dev)
152{
Julius Wernercd49cce2019-03-05 16:53:33 -0800153 if (CONFIG(LINEAR_FRAMEBUFFER))
Nico Huber6d8266b2017-05-20 16:46:01 +0200154 bochs_init_linear_fb(dev);
Julius Wernercd49cce2019-03-05 16:53:33 -0800155 else if (CONFIG(VGA_TEXT_FRAMEBUFFER))
Nico Huber6d8266b2017-05-20 16:46:01 +0200156 bochs_init_text_mode(dev);
Gerd Hoffmann414b9472013-06-18 23:41:21 +0200157}
158
159static struct device_operations qemu_graph_ops = {
160 .read_resources = pci_dev_read_resources,
161 .set_resources = pci_dev_set_resources,
162 .enable_resources = pci_dev_enable_resources,
163 .init = bochs_init,
Gerd Hoffmann414b9472013-06-18 23:41:21 +0200164};
165
166static const struct pci_driver qemu_stdvga_driver __pci_driver = {
167 .ops = &qemu_graph_ops,
168 .vendor = 0x1234,
169 .device = 0x1111,
170};
171
172static const struct pci_driver qemu_vmware_driver __pci_driver = {
173 .ops = &qemu_graph_ops,
174 .vendor = 0x15ad,
175 .device = 0x0405,
176};
177static const struct pci_driver qemu_qxl_driver __pci_driver = {
178 .ops = &qemu_graph_ops,
179 .vendor = 0x1b36,
180 .device = 0x0100,
181};