ui: fix virtual timers
[qemu/ar7.git] / hw / display / bochs-display.c
blob09d8944a1b3d41615fedf484a2c126ac7e402d6e
1 /*
2 * QEMU PCI bochs display adapter.
4 * This work is licensed under the terms of the GNU GPL, version 2 or later.
5 * See the COPYING file in the top-level directory.
6 */
7 #include "qemu/osdep.h"
8 #include "qemu/units.h"
9 #include "hw/hw.h"
10 #include "hw/pci/pci.h"
11 #include "hw/display/bochs-vbe.h"
13 #include "qapi/error.h"
15 #include "ui/console.h"
16 #include "ui/qemu-pixman.h"
18 typedef struct BochsDisplayMode {
19 pixman_format_code_t format;
20 uint32_t bytepp;
21 uint32_t width;
22 uint32_t height;
23 uint32_t stride;
24 uint64_t offset;
25 uint64_t size;
26 } BochsDisplayMode;
28 typedef struct BochsDisplayState {
29 /* parent */
30 PCIDevice pci;
32 /* device elements */
33 QemuConsole *con;
34 MemoryRegion vram;
35 MemoryRegion mmio;
36 MemoryRegion vbe;
37 MemoryRegion qext;
39 /* device config */
40 uint64_t vgamem;
42 /* device registers */
43 uint16_t vbe_regs[VBE_DISPI_INDEX_NB];
44 bool big_endian_fb;
46 /* device state */
47 BochsDisplayMode mode;
48 } BochsDisplayState;
50 #define TYPE_BOCHS_DISPLAY "bochs-display"
51 #define BOCHS_DISPLAY(obj) OBJECT_CHECK(BochsDisplayState, (obj), \
52 TYPE_BOCHS_DISPLAY)
54 static const VMStateDescription vmstate_bochs_display = {
55 .name = "bochs-display",
56 .fields = (VMStateField[]) {
57 VMSTATE_PCI_DEVICE(pci, BochsDisplayState),
58 VMSTATE_UINT16_ARRAY(vbe_regs, BochsDisplayState, VBE_DISPI_INDEX_NB),
59 VMSTATE_BOOL(big_endian_fb, BochsDisplayState),
60 VMSTATE_END_OF_LIST()
64 static uint64_t bochs_display_vbe_read(void *ptr, hwaddr addr,
65 unsigned size)
67 BochsDisplayState *s = ptr;
68 unsigned int index = addr >> 1;
70 switch (index) {
71 case VBE_DISPI_INDEX_ID:
72 return VBE_DISPI_ID5;
73 case VBE_DISPI_INDEX_VIDEO_MEMORY_64K:
74 return s->vgamem / (64 * KiB);
77 if (index >= ARRAY_SIZE(s->vbe_regs)) {
78 return -1;
80 return s->vbe_regs[index];
83 static void bochs_display_vbe_write(void *ptr, hwaddr addr,
84 uint64_t val, unsigned size)
86 BochsDisplayState *s = ptr;
87 unsigned int index = addr >> 1;
89 if (index >= ARRAY_SIZE(s->vbe_regs)) {
90 return;
92 s->vbe_regs[index] = val;
95 static const MemoryRegionOps bochs_display_vbe_ops = {
96 .read = bochs_display_vbe_read,
97 .write = bochs_display_vbe_write,
98 .valid.min_access_size = 1,
99 .valid.max_access_size = 4,
100 .impl.min_access_size = 2,
101 .impl.max_access_size = 2,
102 .endianness = DEVICE_LITTLE_ENDIAN,
105 static uint64_t bochs_display_qext_read(void *ptr, hwaddr addr,
106 unsigned size)
108 BochsDisplayState *s = ptr;
110 switch (addr) {
111 case PCI_VGA_QEXT_REG_SIZE:
112 return PCI_VGA_QEXT_SIZE;
113 case PCI_VGA_QEXT_REG_BYTEORDER:
114 return s->big_endian_fb ?
115 PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN;
116 default:
117 return 0;
121 static void bochs_display_qext_write(void *ptr, hwaddr addr,
122 uint64_t val, unsigned size)
124 BochsDisplayState *s = ptr;
126 switch (addr) {
127 case PCI_VGA_QEXT_REG_BYTEORDER:
128 if (val == PCI_VGA_QEXT_BIG_ENDIAN) {
129 s->big_endian_fb = true;
131 if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) {
132 s->big_endian_fb = false;
134 break;
138 static const MemoryRegionOps bochs_display_qext_ops = {
139 .read = bochs_display_qext_read,
140 .write = bochs_display_qext_write,
141 .valid.min_access_size = 4,
142 .valid.max_access_size = 4,
143 .endianness = DEVICE_LITTLE_ENDIAN,
146 static int bochs_display_get_mode(BochsDisplayState *s,
147 BochsDisplayMode *mode)
149 uint16_t *vbe = s->vbe_regs;
150 uint32_t virt_width;
152 if (!(vbe[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
153 return -1;
156 memset(mode, 0, sizeof(*mode));
157 switch (vbe[VBE_DISPI_INDEX_BPP]) {
158 case 16:
159 /* best effort: support native endianess only */
160 mode->format = PIXMAN_r5g6b5;
161 mode->bytepp = 2;
162 break;
163 case 32:
164 mode->format = s->big_endian_fb
165 ? PIXMAN_BE_x8r8g8b8
166 : PIXMAN_LE_x8r8g8b8;
167 mode->bytepp = 4;
168 break;
169 default:
170 return -1;
173 mode->width = vbe[VBE_DISPI_INDEX_XRES];
174 mode->height = vbe[VBE_DISPI_INDEX_YRES];
175 virt_width = vbe[VBE_DISPI_INDEX_VIRT_WIDTH];
176 if (virt_width < mode->width) {
177 virt_width = mode->width;
179 mode->stride = virt_width * mode->bytepp;
180 mode->size = (uint64_t)mode->stride * mode->height;
181 mode->offset = ((uint64_t)vbe[VBE_DISPI_INDEX_X_OFFSET] * mode->bytepp +
182 (uint64_t)vbe[VBE_DISPI_INDEX_Y_OFFSET] * mode->stride);
184 if (mode->width < 64 || mode->height < 64) {
185 return -1;
187 if (mode->offset + mode->size > s->vgamem) {
188 return -1;
190 return 0;
193 static void bochs_display_update(void *opaque)
195 BochsDisplayState *s = opaque;
196 DirtyBitmapSnapshot *snap = NULL;
197 bool full_update = false;
198 BochsDisplayMode mode;
199 DisplaySurface *ds;
200 uint8_t *ptr;
201 bool dirty;
202 int y, ys, ret;
204 ret = bochs_display_get_mode(s, &mode);
205 if (ret < 0) {
206 /* no (valid) video mode */
207 return;
210 if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) {
211 /* video mode switch */
212 s->mode = mode;
213 ptr = memory_region_get_ram_ptr(&s->vram);
214 ds = qemu_create_displaysurface_from(mode.width,
215 mode.height,
216 mode.format,
217 mode.stride,
218 ptr + mode.offset);
219 dpy_gfx_replace_surface(s->con, ds);
220 full_update = true;
223 if (full_update) {
224 dpy_gfx_update_full(s->con);
225 } else {
226 snap = memory_region_snapshot_and_clear_dirty(&s->vram,
227 mode.offset, mode.size,
228 DIRTY_MEMORY_VGA);
229 ys = -1;
230 for (y = 0; y < mode.height; y++) {
231 dirty = memory_region_snapshot_get_dirty(&s->vram, snap,
232 mode.offset + mode.stride * y,
233 mode.stride);
234 if (dirty && ys < 0) {
235 ys = y;
237 if (!dirty && ys >= 0) {
238 dpy_gfx_update(s->con, 0, ys,
239 mode.width, y - ys);
240 ys = -1;
243 if (ys >= 0) {
244 dpy_gfx_update(s->con, 0, ys,
245 mode.width, y - ys);
250 static const GraphicHwOps bochs_display_gfx_ops = {
251 .gfx_update = bochs_display_update,
254 static void bochs_display_realize(PCIDevice *dev, Error **errp)
256 BochsDisplayState *s = BOCHS_DISPLAY(dev);
257 Object *obj = OBJECT(dev);
258 int ret;
260 s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s);
262 if (s->vgamem < 4 * MiB) {
263 error_setg(errp, "bochs-display: video memory too small");
265 if (s->vgamem > 256 * MiB) {
266 error_setg(errp, "bochs-display: video memory too big");
268 s->vgamem = pow2ceil(s->vgamem);
270 memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem,
271 &error_fatal);
272 memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s,
273 "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
274 memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s,
275 "qemu extended regs", PCI_VGA_QEXT_SIZE);
277 memory_region_init(&s->mmio, obj, "bochs-display-mmio",
278 PCI_VGA_MMIO_SIZE);
279 memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe);
280 memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext);
282 pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2);
283 pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
284 pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
286 if (pci_bus_is_express(pci_get_bus(dev))) {
287 dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
288 ret = pcie_endpoint_cap_init(dev, 0x80);
289 assert(ret > 0);
292 memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA);
295 static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp)
297 BochsDisplayState *s = BOCHS_DISPLAY(obj);
299 return s->big_endian_fb;
302 static void bochs_display_set_big_endian_fb(Object *obj, bool value,
303 Error **errp)
305 BochsDisplayState *s = BOCHS_DISPLAY(obj);
307 s->big_endian_fb = value;
310 static void bochs_display_init(Object *obj)
312 /* Expose framebuffer byteorder via QOM */
313 object_property_add_bool(obj, "big-endian-framebuffer",
314 bochs_display_get_big_endian_fb,
315 bochs_display_set_big_endian_fb,
316 NULL);
319 static void bochs_display_exit(PCIDevice *dev)
321 BochsDisplayState *s = BOCHS_DISPLAY(dev);
323 graphic_console_close(s->con);
326 static Property bochs_display_properties[] = {
327 DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * MiB),
328 DEFINE_PROP_END_OF_LIST(),
331 static void bochs_display_class_init(ObjectClass *klass, void *data)
333 DeviceClass *dc = DEVICE_CLASS(klass);
334 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
336 k->class_id = PCI_CLASS_DISPLAY_OTHER;
337 k->vendor_id = PCI_VENDOR_ID_QEMU;
338 k->device_id = PCI_DEVICE_ID_QEMU_VGA;
340 k->realize = bochs_display_realize;
341 k->romfile = "vgabios-bochs-display.bin";
342 k->exit = bochs_display_exit;
343 dc->vmsd = &vmstate_bochs_display;
344 dc->props = bochs_display_properties;
345 set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
348 static const TypeInfo bochs_display_type_info = {
349 .name = TYPE_BOCHS_DISPLAY,
350 .parent = TYPE_PCI_DEVICE,
351 .instance_size = sizeof(BochsDisplayState),
352 .instance_init = bochs_display_init,
353 .class_init = bochs_display_class_init,
354 .interfaces = (InterfaceInfo[]) {
355 { INTERFACE_PCIE_DEVICE },
356 { INTERFACE_CONVENTIONAL_PCI_DEVICE },
357 { },
361 static void bochs_display_register_types(void)
363 type_register_static(&bochs_display_type_info);
366 type_init(bochs_display_register_types)