]> git.proxmox.com Git - mirror_qemu.git/blame - hw/display/bochs-display.c
include/hw/pci: Split pci_device.h off pci.h
[mirror_qemu.git] / hw / display / bochs-display.c
CommitLineData
765c9429
GH
1/*
2 * QEMU PCI bochs display adapter.
3 *
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 */
0b8fa32f 7
765c9429 8#include "qemu/osdep.h"
0b8fa32f 9#include "qemu/module.h"
f0353b0d 10#include "qemu/units.h"
edf5ca5d 11#include "hw/pci/pci_device.h"
a27bd6c7 12#include "hw/qdev-properties.h"
d6454270 13#include "migration/vmstate.h"
765c9429 14#include "hw/display/bochs-vbe.h"
a0d098b7 15#include "hw/display/edid.h"
765c9429
GH
16
17#include "qapi/error.h"
18
19#include "ui/console.h"
20#include "ui/qemu-pixman.h"
db1015e9 21#include "qom/object.h"
765c9429
GH
22
23typedef struct BochsDisplayMode {
24 pixman_format_code_t format;
25 uint32_t bytepp;
26 uint32_t width;
27 uint32_t height;
28 uint32_t stride;
29 uint64_t offset;
30 uint64_t size;
31} BochsDisplayMode;
32
db1015e9 33struct BochsDisplayState {
765c9429
GH
34 /* parent */
35 PCIDevice pci;
36
37 /* device elements */
38 QemuConsole *con;
39 MemoryRegion vram;
40 MemoryRegion mmio;
41 MemoryRegion vbe;
42 MemoryRegion qext;
a0d098b7 43 MemoryRegion edid;
765c9429
GH
44
45 /* device config */
46 uint64_t vgamem;
a0d098b7
GH
47 bool enable_edid;
48 qemu_edid_info edid_info;
49 uint8_t edid_blob[256];
765c9429
GH
50
51 /* device registers */
52 uint16_t vbe_regs[VBE_DISPI_INDEX_NB];
53 bool big_endian_fb;
54
55 /* device state */
56 BochsDisplayMode mode;
db1015e9 57};
765c9429
GH
58
59#define TYPE_BOCHS_DISPLAY "bochs-display"
8063396b 60OBJECT_DECLARE_SIMPLE_TYPE(BochsDisplayState, BOCHS_DISPLAY)
765c9429
GH
61
62static const VMStateDescription vmstate_bochs_display = {
63 .name = "bochs-display",
64 .fields = (VMStateField[]) {
65 VMSTATE_PCI_DEVICE(pci, BochsDisplayState),
66 VMSTATE_UINT16_ARRAY(vbe_regs, BochsDisplayState, VBE_DISPI_INDEX_NB),
67 VMSTATE_BOOL(big_endian_fb, BochsDisplayState),
68 VMSTATE_END_OF_LIST()
69 }
70};
71
72static uint64_t bochs_display_vbe_read(void *ptr, hwaddr addr,
73 unsigned size)
74{
75 BochsDisplayState *s = ptr;
76 unsigned int index = addr >> 1;
77
78 switch (index) {
79 case VBE_DISPI_INDEX_ID:
80 return VBE_DISPI_ID5;
81 case VBE_DISPI_INDEX_VIDEO_MEMORY_64K:
f0353b0d 82 return s->vgamem / (64 * KiB);
765c9429
GH
83 }
84
85 if (index >= ARRAY_SIZE(s->vbe_regs)) {
86 return -1;
87 }
88 return s->vbe_regs[index];
89}
90
91static void bochs_display_vbe_write(void *ptr, hwaddr addr,
92 uint64_t val, unsigned size)
93{
94 BochsDisplayState *s = ptr;
95 unsigned int index = addr >> 1;
96
97 if (index >= ARRAY_SIZE(s->vbe_regs)) {
98 return;
99 }
100 s->vbe_regs[index] = val;
101}
102
103static const MemoryRegionOps bochs_display_vbe_ops = {
104 .read = bochs_display_vbe_read,
105 .write = bochs_display_vbe_write,
106 .valid.min_access_size = 1,
107 .valid.max_access_size = 4,
108 .impl.min_access_size = 2,
109 .impl.max_access_size = 2,
110 .endianness = DEVICE_LITTLE_ENDIAN,
111};
112
113static uint64_t bochs_display_qext_read(void *ptr, hwaddr addr,
114 unsigned size)
115{
116 BochsDisplayState *s = ptr;
117
118 switch (addr) {
119 case PCI_VGA_QEXT_REG_SIZE:
120 return PCI_VGA_QEXT_SIZE;
121 case PCI_VGA_QEXT_REG_BYTEORDER:
122 return s->big_endian_fb ?
123 PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN;
124 default:
125 return 0;
126 }
127}
128
129static void bochs_display_qext_write(void *ptr, hwaddr addr,
130 uint64_t val, unsigned size)
131{
132 BochsDisplayState *s = ptr;
133
134 switch (addr) {
135 case PCI_VGA_QEXT_REG_BYTEORDER:
136 if (val == PCI_VGA_QEXT_BIG_ENDIAN) {
137 s->big_endian_fb = true;
138 }
139 if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) {
140 s->big_endian_fb = false;
141 }
142 break;
143 }
144}
145
146static const MemoryRegionOps bochs_display_qext_ops = {
147 .read = bochs_display_qext_read,
148 .write = bochs_display_qext_write,
149 .valid.min_access_size = 4,
150 .valid.max_access_size = 4,
151 .endianness = DEVICE_LITTLE_ENDIAN,
152};
153
154static int bochs_display_get_mode(BochsDisplayState *s,
155 BochsDisplayMode *mode)
156{
157 uint16_t *vbe = s->vbe_regs;
158 uint32_t virt_width;
159
160 if (!(vbe[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
161 return -1;
162 }
163
164 memset(mode, 0, sizeof(*mode));
165 switch (vbe[VBE_DISPI_INDEX_BPP]) {
166 case 16:
167 /* best effort: support native endianess only */
168 mode->format = PIXMAN_r5g6b5;
169 mode->bytepp = 2;
bc820db0 170 break;
765c9429
GH
171 case 32:
172 mode->format = s->big_endian_fb
173 ? PIXMAN_BE_x8r8g8b8
174 : PIXMAN_LE_x8r8g8b8;
175 mode->bytepp = 4;
176 break;
177 default:
178 return -1;
179 }
180
181 mode->width = vbe[VBE_DISPI_INDEX_XRES];
182 mode->height = vbe[VBE_DISPI_INDEX_YRES];
183 virt_width = vbe[VBE_DISPI_INDEX_VIRT_WIDTH];
184 if (virt_width < mode->width) {
185 virt_width = mode->width;
186 }
187 mode->stride = virt_width * mode->bytepp;
188 mode->size = (uint64_t)mode->stride * mode->height;
189 mode->offset = ((uint64_t)vbe[VBE_DISPI_INDEX_X_OFFSET] * mode->bytepp +
190 (uint64_t)vbe[VBE_DISPI_INDEX_Y_OFFSET] * mode->stride);
191
192 if (mode->width < 64 || mode->height < 64) {
193 return -1;
194 }
195 if (mode->offset + mode->size > s->vgamem) {
196 return -1;
197 }
198 return 0;
199}
200
201static void bochs_display_update(void *opaque)
202{
203 BochsDisplayState *s = opaque;
33ebad54
GH
204 DirtyBitmapSnapshot *snap = NULL;
205 bool full_update = false;
765c9429
GH
206 BochsDisplayMode mode;
207 DisplaySurface *ds;
208 uint8_t *ptr;
33ebad54
GH
209 bool dirty;
210 int y, ys, ret;
765c9429
GH
211
212 ret = bochs_display_get_mode(s, &mode);
213 if (ret < 0) {
214 /* no (valid) video mode */
215 return;
216 }
217
218 if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) {
219 /* video mode switch */
220 s->mode = mode;
221 ptr = memory_region_get_ram_ptr(&s->vram);
222 ds = qemu_create_displaysurface_from(mode.width,
223 mode.height,
224 mode.format,
225 mode.stride,
226 ptr + mode.offset);
227 dpy_gfx_replace_surface(s->con, ds);
33ebad54 228 full_update = true;
765c9429
GH
229 }
230
33ebad54
GH
231 if (full_update) {
232 dpy_gfx_update_full(s->con);
233 } else {
234 snap = memory_region_snapshot_and_clear_dirty(&s->vram,
235 mode.offset, mode.size,
236 DIRTY_MEMORY_VGA);
237 ys = -1;
238 for (y = 0; y < mode.height; y++) {
239 dirty = memory_region_snapshot_get_dirty(&s->vram, snap,
240 mode.offset + mode.stride * y,
241 mode.stride);
242 if (dirty && ys < 0) {
243 ys = y;
244 }
245 if (!dirty && ys >= 0) {
246 dpy_gfx_update(s->con, 0, ys,
247 mode.width, y - ys);
248 ys = -1;
249 }
250 }
251 if (ys >= 0) {
252 dpy_gfx_update(s->con, 0, ys,
253 mode.width, y - ys);
254 }
0d82411d
CE
255
256 g_free(snap);
33ebad54 257 }
765c9429
GH
258}
259
260static const GraphicHwOps bochs_display_gfx_ops = {
261 .gfx_update = bochs_display_update,
262};
263
264static void bochs_display_realize(PCIDevice *dev, Error **errp)
265{
266 BochsDisplayState *s = BOCHS_DISPLAY(dev);
267 Object *obj = OBJECT(dev);
f2581064 268 int ret;
765c9429 269
f0353b0d 270 if (s->vgamem < 4 * MiB) {
765c9429 271 error_setg(errp, "bochs-display: video memory too small");
ee29f6e9 272 return;
765c9429 273 }
f0353b0d 274 if (s->vgamem > 256 * MiB) {
765c9429 275 error_setg(errp, "bochs-display: video memory too big");
ee29f6e9 276 return;
765c9429
GH
277 }
278 s->vgamem = pow2ceil(s->vgamem);
279
ee29f6e9
MA
280 s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s);
281
765c9429
GH
282 memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem,
283 &error_fatal);
284 memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s,
285 "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
286 memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s,
287 "qemu extended regs", PCI_VGA_QEXT_SIZE);
288
f872c762
GH
289 memory_region_init_io(&s->mmio, obj, &unassigned_io_ops, NULL,
290 "bochs-display-mmio", PCI_VGA_MMIO_SIZE);
765c9429
GH
291 memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe);
292 memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext);
293
294 pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2);
295 pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
296 pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio);
33ebad54 297
a0d098b7
GH
298 if (s->enable_edid) {
299 qemu_edid_generate(s->edid_blob, sizeof(s->edid_blob), &s->edid_info);
300 qemu_edid_region_io(&s->edid, obj, s->edid_blob, sizeof(s->edid_blob));
301 memory_region_add_subregion(&s->mmio, 0, &s->edid);
302 }
303
f2581064 304 if (pci_bus_is_express(pci_get_bus(dev))) {
f2581064
GH
305 ret = pcie_endpoint_cap_init(dev, 0x80);
306 assert(ret > 0);
5e7bcdcf
GH
307 } else {
308 dev->cap_present &= ~QEMU_PCI_CAP_EXPRESS;
f2581064
GH
309 }
310
33ebad54 311 memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA);
765c9429
GH
312}
313
314static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp)
315{
316 BochsDisplayState *s = BOCHS_DISPLAY(obj);
317
318 return s->big_endian_fb;
319}
320
321static void bochs_display_set_big_endian_fb(Object *obj, bool value,
322 Error **errp)
323{
324 BochsDisplayState *s = BOCHS_DISPLAY(obj);
325
326 s->big_endian_fb = value;
327}
328
329static void bochs_display_init(Object *obj)
330{
5e7bcdcf
GH
331 PCIDevice *dev = PCI_DEVICE(obj);
332
765c9429
GH
333 /* Expose framebuffer byteorder via QOM */
334 object_property_add_bool(obj, "big-endian-framebuffer",
335 bochs_display_get_big_endian_fb,
d2623129 336 bochs_display_set_big_endian_fb);
5e7bcdcf
GH
337
338 dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
765c9429
GH
339}
340
341static void bochs_display_exit(PCIDevice *dev)
342{
343 BochsDisplayState *s = BOCHS_DISPLAY(dev);
344
345 graphic_console_close(s->con);
346}
347
348static Property bochs_display_properties[] = {
f0353b0d 349 DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * MiB),
0a719662 350 DEFINE_PROP_BOOL("edid", BochsDisplayState, enable_edid, true),
a0d098b7 351 DEFINE_EDID_PROPERTIES(BochsDisplayState, edid_info),
765c9429
GH
352 DEFINE_PROP_END_OF_LIST(),
353};
354
355static void bochs_display_class_init(ObjectClass *klass, void *data)
356{
357 DeviceClass *dc = DEVICE_CLASS(klass);
358 PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
359
360 k->class_id = PCI_CLASS_DISPLAY_OTHER;
361 k->vendor_id = PCI_VENDOR_ID_QEMU;
362 k->device_id = PCI_DEVICE_ID_QEMU_VGA;
363
364 k->realize = bochs_display_realize;
7c538789 365 k->romfile = "vgabios-bochs-display.bin";
765c9429
GH
366 k->exit = bochs_display_exit;
367 dc->vmsd = &vmstate_bochs_display;
4f67d30b 368 device_class_set_props(dc, bochs_display_properties);
765c9429
GH
369 set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
370}
371
372static const TypeInfo bochs_display_type_info = {
373 .name = TYPE_BOCHS_DISPLAY,
374 .parent = TYPE_PCI_DEVICE,
375 .instance_size = sizeof(BochsDisplayState),
376 .instance_init = bochs_display_init,
377 .class_init = bochs_display_class_init,
378 .interfaces = (InterfaceInfo[]) {
f2581064 379 { INTERFACE_PCIE_DEVICE },
765c9429
GH
380 { INTERFACE_CONVENTIONAL_PCI_DEVICE },
381 { },
382 },
383};
384
385static void bochs_display_register_types(void)
386{
387 type_register_static(&bochs_display_type_info);
388}
389
390type_init(bochs_display_register_types)