3 * QEMU model of the Milkymist VGA framebuffer.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 * Specification available at:
22 * http://www.milkymist.org/socdoc/vgafb.pdf
26 #include "hw/sysbus.h"
28 #include "ui/console.h"
29 #include "hw/framebuffer.h"
30 #include "ui/pixel_ops.h"
31 #include "qemu/error-report.h"
34 #include "hw/milkymist-vgafb_template.h"
36 #include "hw/milkymist-vgafb_template.h"
38 #include "hw/milkymist-vgafb_template.h"
40 #include "hw/milkymist-vgafb_template.h"
42 #include "hw/milkymist-vgafb_template.h"
66 struct MilkymistVgafbState {
68 MemoryRegion regs_region;
77 typedef struct MilkymistVgafbState MilkymistVgafbState;
79 static int vgafb_enabled(MilkymistVgafbState *s)
81 return !(s->regs[R_CTRL] & CTRL_RESET);
84 static void vgafb_update_display(void *opaque)
86 MilkymistVgafbState *s = opaque;
87 DisplaySurface *surface = qemu_console_surface(s->con);
92 if (!vgafb_enabled(s)) {
96 int dest_width = s->regs[R_HRES];
98 switch (surface_bits_per_pixel(surface)) {
121 hw_error("milkymist_vgafb: bad color depth\n");
125 framebuffer_update_display(surface, sysbus_address_space(&s->busdev),
126 s->regs[R_BASEADDRESS] + s->fb_offset,
138 dpy_gfx_update(s->con, 0, first, s->regs[R_HRES], last - first + 1);
143 static void vgafb_invalidate_display(void *opaque)
145 MilkymistVgafbState *s = opaque;
149 static void vgafb_resize(MilkymistVgafbState *s)
151 if (!vgafb_enabled(s)) {
155 qemu_console_resize(s->con, s->regs[R_HRES], s->regs[R_VRES]);
159 static uint64_t vgafb_read(void *opaque, hwaddr addr,
162 MilkymistVgafbState *s = opaque;
182 case R_BASEADDRESS_ACT:
183 r = s->regs[R_BASEADDRESS];
187 error_report("milkymist_vgafb: read access to unknown register 0x"
188 TARGET_FMT_plx, addr << 2);
192 trace_milkymist_vgafb_memory_read(addr << 2, r);
197 static void vgafb_write(void *opaque, hwaddr addr, uint64_t value,
200 MilkymistVgafbState *s = opaque;
202 trace_milkymist_vgafb_memory_write(addr, value);
207 s->regs[addr] = value;
219 s->regs[addr] = value;
223 error_report("milkymist_vgafb: framebuffer base address have to "
224 "be 32 byte aligned");
227 s->regs[addr] = value & s->fb_mask;
232 s->regs[addr] = value;
235 case R_BASEADDRESS_ACT:
236 error_report("milkymist_vgafb: write to read-only register 0x"
237 TARGET_FMT_plx, addr << 2);
241 error_report("milkymist_vgafb: write access to unknown register 0x"
242 TARGET_FMT_plx, addr << 2);
247 static const MemoryRegionOps vgafb_mmio_ops = {
249 .write = vgafb_write,
251 .min_access_size = 4,
252 .max_access_size = 4,
254 .endianness = DEVICE_NATIVE_ENDIAN,
257 static void milkymist_vgafb_reset(DeviceState *d)
259 MilkymistVgafbState *s = container_of(d, MilkymistVgafbState, busdev.qdev);
262 for (i = 0; i < R_MAX; i++) {
267 s->regs[R_CTRL] = CTRL_RESET;
268 s->regs[R_HRES] = 640;
269 s->regs[R_VRES] = 480;
270 s->regs[R_BASEADDRESS] = 0;
273 static int milkymist_vgafb_init(SysBusDevice *dev)
275 MilkymistVgafbState *s = FROM_SYSBUS(typeof(*s), dev);
277 memory_region_init_io(&s->regs_region, &vgafb_mmio_ops, s,
278 "milkymist-vgafb", R_MAX * 4);
279 sysbus_init_mmio(dev, &s->regs_region);
281 s->con = graphic_console_init(vgafb_update_display,
282 vgafb_invalidate_display,
288 static int vgafb_post_load(void *opaque, int version_id)
290 vgafb_invalidate_display(opaque);
294 static const VMStateDescription vmstate_milkymist_vgafb = {
295 .name = "milkymist-vgafb",
297 .minimum_version_id = 1,
298 .minimum_version_id_old = 1,
299 .post_load = vgafb_post_load,
300 .fields = (VMStateField[]) {
301 VMSTATE_UINT32_ARRAY(regs, MilkymistVgafbState, R_MAX),
302 VMSTATE_END_OF_LIST()
306 static Property milkymist_vgafb_properties[] = {
307 DEFINE_PROP_UINT32("fb_offset", MilkymistVgafbState, fb_offset, 0x0),
308 DEFINE_PROP_UINT32("fb_mask", MilkymistVgafbState, fb_mask, 0xffffffff),
309 DEFINE_PROP_END_OF_LIST(),
312 static void milkymist_vgafb_class_init(ObjectClass *klass, void *data)
314 DeviceClass *dc = DEVICE_CLASS(klass);
315 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
317 k->init = milkymist_vgafb_init;
318 dc->reset = milkymist_vgafb_reset;
319 dc->vmsd = &vmstate_milkymist_vgafb;
320 dc->props = milkymist_vgafb_properties;
323 static const TypeInfo milkymist_vgafb_info = {
324 .name = "milkymist-vgafb",
325 .parent = TYPE_SYS_BUS_DEVICE,
326 .instance_size = sizeof(MilkymistVgafbState),
327 .class_init = milkymist_vgafb_class_init,
330 static void milkymist_vgafb_register_types(void)
332 type_register_static(&milkymist_vgafb_info);
335 type_init(milkymist_vgafb_register_types)