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