6f2b11d6c9bd9455c8c967066aa573339a1768ff
[qemu.git] / hw / display / milkymist-vgafb.c
1
2 /*
3  *  QEMU model of the Milkymist VGA framebuffer.
4  *
5  *  Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
6  *
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.
11  *
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.
16  *
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/>.
19  *
20  *
21  * Specification available at:
22  *   http://milkymist.walle.cc/socdoc/vgafb.pdf
23  */
24
25 #include "qemu/osdep.h"
26 #include "hw/hw.h"
27 #include "hw/qdev-properties.h"
28 #include "hw/sysbus.h"
29 #include "trace.h"
30 #include "ui/console.h"
31 #include "framebuffer.h"
32 #include "ui/pixel_ops.h"
33 #include "qemu/error-report.h"
34 #include "qemu/module.h"
35 #include "qom/object.h"
36
37 #define BITS 8
38 #include "migration/vmstate.h"
39 #include "milkymist-vgafb_template.h"
40 #define BITS 15
41 #include "milkymist-vgafb_template.h"
42 #define BITS 16
43 #include "milkymist-vgafb_template.h"
44 #define BITS 24
45 #include "milkymist-vgafb_template.h"
46 #define BITS 32
47 #include "milkymist-vgafb_template.h"
48
49 enum {
50     R_CTRL = 0,
51     R_HRES,
52     R_HSYNC_START,
53     R_HSYNC_END,
54     R_HSCAN,
55     R_VRES,
56     R_VSYNC_START,
57     R_VSYNC_END,
58     R_VSCAN,
59     R_BASEADDRESS,
60     R_BASEADDRESS_ACT,
61     R_BURST_COUNT,
62     R_DDC,
63     R_SOURCE_CLOCK,
64     R_MAX
65 };
66
67 enum {
68     CTRL_RESET = (1<<0),
69 };
70
71 #define TYPE_MILKYMIST_VGAFB "milkymist-vgafb"
72 OBJECT_DECLARE_SIMPLE_TYPE(MilkymistVgafbState, MILKYMIST_VGAFB)
73
74 struct MilkymistVgafbState {
75     SysBusDevice parent_obj;
76
77     MemoryRegion regs_region;
78     MemoryRegionSection fbsection;
79     QemuConsole *con;
80
81     int invalidate;
82     uint32_t fb_offset;
83     uint32_t fb_mask;
84
85     uint32_t regs[R_MAX];
86 };
87
88 static int vgafb_enabled(MilkymistVgafbState *s)
89 {
90     return !(s->regs[R_CTRL] & CTRL_RESET);
91 }
92
93 static void vgafb_update_display(void *opaque)
94 {
95     MilkymistVgafbState *s = opaque;
96     SysBusDevice *sbd;
97     DisplaySurface *surface = qemu_console_surface(s->con);
98     int src_width;
99     int first = 0;
100     int last = 0;
101     drawfn fn;
102
103     if (!vgafb_enabled(s)) {
104         return;
105     }
106
107     sbd = SYS_BUS_DEVICE(s);
108     int dest_width = s->regs[R_HRES];
109
110     switch (surface_bits_per_pixel(surface)) {
111     case 0:
112         return;
113     case 8:
114         fn = draw_line_8;
115         break;
116     case 15:
117         fn = draw_line_15;
118         dest_width *= 2;
119         break;
120     case 16:
121         fn = draw_line_16;
122         dest_width *= 2;
123         break;
124     case 24:
125         fn = draw_line_24;
126         dest_width *= 3;
127         break;
128     case 32:
129         fn = draw_line_32;
130         dest_width *= 4;
131         break;
132     default:
133         hw_error("milkymist_vgafb: bad color depth\n");
134         break;
135     }
136
137     src_width = s->regs[R_HRES] * 2;
138     if (s->invalidate) {
139         framebuffer_update_memory_section(&s->fbsection,
140                                           sysbus_address_space(sbd),
141                                           s->regs[R_BASEADDRESS] + s->fb_offset,
142                                           s->regs[R_VRES], src_width);
143     }
144
145     framebuffer_update_display(surface, &s->fbsection,
146                                s->regs[R_HRES],
147                                s->regs[R_VRES],
148                                src_width,
149                                dest_width,
150                                0,
151                                s->invalidate,
152                                fn,
153                                NULL,
154                                &first, &last);
155
156     if (first >= 0) {
157         dpy_gfx_update(s->con, 0, first, s->regs[R_HRES], last - first + 1);
158     }
159     s->invalidate = 0;
160 }
161
162 static void vgafb_invalidate_display(void *opaque)
163 {
164     MilkymistVgafbState *s = opaque;
165     s->invalidate = 1;
166 }
167
168 static void vgafb_resize(MilkymistVgafbState *s)
169 {
170     if (!vgafb_enabled(s)) {
171         return;
172     }
173
174     qemu_console_resize(s->con, s->regs[R_HRES], s->regs[R_VRES]);
175     s->invalidate = 1;
176 }
177
178 static uint64_t vgafb_read(void *opaque, hwaddr addr,
179                            unsigned size)
180 {
181     MilkymistVgafbState *s = opaque;
182     uint32_t r = 0;
183
184     addr >>= 2;
185     switch (addr) {
186     case R_CTRL:
187     case R_HRES:
188     case R_HSYNC_START:
189     case R_HSYNC_END:
190     case R_HSCAN:
191     case R_VRES:
192     case R_VSYNC_START:
193     case R_VSYNC_END:
194     case R_VSCAN:
195     case R_BASEADDRESS:
196     case R_BURST_COUNT:
197     case R_DDC:
198     case R_SOURCE_CLOCK:
199         r = s->regs[addr];
200     break;
201     case R_BASEADDRESS_ACT:
202         r = s->regs[R_BASEADDRESS];
203     break;
204
205     default:
206         error_report("milkymist_vgafb: read access to unknown register 0x"
207                 TARGET_FMT_plx, addr << 2);
208         break;
209     }
210
211     trace_milkymist_vgafb_memory_read(addr << 2, r);
212
213     return r;
214 }
215
216 static void vgafb_write(void *opaque, hwaddr addr, uint64_t value,
217                         unsigned size)
218 {
219     MilkymistVgafbState *s = opaque;
220
221     trace_milkymist_vgafb_memory_write(addr, value);
222
223     addr >>= 2;
224     switch (addr) {
225     case R_CTRL:
226         s->regs[addr] = value;
227         vgafb_resize(s);
228         break;
229     case R_HSYNC_START:
230     case R_HSYNC_END:
231     case R_HSCAN:
232     case R_VSYNC_START:
233     case R_VSYNC_END:
234     case R_VSCAN:
235     case R_BURST_COUNT:
236     case R_DDC:
237     case R_SOURCE_CLOCK:
238         s->regs[addr] = value;
239         break;
240     case R_BASEADDRESS:
241         if (value & 0x1f) {
242             error_report("milkymist_vgafb: framebuffer base address have to "
243                      "be 32 byte aligned");
244             break;
245         }
246         s->regs[addr] = value & s->fb_mask;
247         s->invalidate = 1;
248         break;
249     case R_HRES:
250     case R_VRES:
251         s->regs[addr] = value;
252         vgafb_resize(s);
253         break;
254     case R_BASEADDRESS_ACT:
255         error_report("milkymist_vgafb: write to read-only register 0x"
256                 TARGET_FMT_plx, addr << 2);
257         break;
258
259     default:
260         error_report("milkymist_vgafb: write access to unknown register 0x"
261                 TARGET_FMT_plx, addr << 2);
262         break;
263     }
264 }
265
266 static const MemoryRegionOps vgafb_mmio_ops = {
267     .read = vgafb_read,
268     .write = vgafb_write,
269     .valid = {
270         .min_access_size = 4,
271         .max_access_size = 4,
272     },
273     .endianness = DEVICE_NATIVE_ENDIAN,
274 };
275
276 static void milkymist_vgafb_reset(DeviceState *d)
277 {
278     MilkymistVgafbState *s = MILKYMIST_VGAFB(d);
279     int i;
280
281     for (i = 0; i < R_MAX; i++) {
282         s->regs[i] = 0;
283     }
284
285     /* defaults */
286     s->regs[R_CTRL] = CTRL_RESET;
287     s->regs[R_HRES] = 640;
288     s->regs[R_VRES] = 480;
289     s->regs[R_BASEADDRESS] = 0;
290 }
291
292 static const GraphicHwOps vgafb_ops = {
293     .invalidate  = vgafb_invalidate_display,
294     .gfx_update  = vgafb_update_display,
295 };
296
297 static void milkymist_vgafb_init(Object *obj)
298 {
299     MilkymistVgafbState *s = MILKYMIST_VGAFB(obj);
300     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
301
302     memory_region_init_io(&s->regs_region, OBJECT(s), &vgafb_mmio_ops, s,
303             "milkymist-vgafb", R_MAX * 4);
304     sysbus_init_mmio(dev, &s->regs_region);
305 }
306
307 static void milkymist_vgafb_realize(DeviceState *dev, Error **errp)
308 {
309     MilkymistVgafbState *s = MILKYMIST_VGAFB(dev);
310
311     s->con = graphic_console_init(dev, 0, &vgafb_ops, s);
312 }
313
314 static int vgafb_post_load(void *opaque, int version_id)
315 {
316     vgafb_invalidate_display(opaque);
317     return 0;
318 }
319
320 static const VMStateDescription vmstate_milkymist_vgafb = {
321     .name = "milkymist-vgafb",
322     .version_id = 1,
323     .minimum_version_id = 1,
324     .post_load = vgafb_post_load,
325     .fields = (VMStateField[]) {
326         VMSTATE_UINT32_ARRAY(regs, MilkymistVgafbState, R_MAX),
327         VMSTATE_END_OF_LIST()
328     }
329 };
330
331 static Property milkymist_vgafb_properties[] = {
332     DEFINE_PROP_UINT32("fb_offset", MilkymistVgafbState, fb_offset, 0x0),
333     DEFINE_PROP_UINT32("fb_mask", MilkymistVgafbState, fb_mask, 0xffffffff),
334     DEFINE_PROP_END_OF_LIST(),
335 };
336
337 static void milkymist_vgafb_class_init(ObjectClass *klass, void *data)
338 {
339     DeviceClass *dc = DEVICE_CLASS(klass);
340
341     dc->reset = milkymist_vgafb_reset;
342     dc->vmsd = &vmstate_milkymist_vgafb;
343     device_class_set_props(dc, milkymist_vgafb_properties);
344     dc->realize = milkymist_vgafb_realize;
345 }
346
347 static const TypeInfo milkymist_vgafb_info = {
348     .name          = TYPE_MILKYMIST_VGAFB,
349     .parent        = TYPE_SYS_BUS_DEVICE,
350     .instance_size = sizeof(MilkymistVgafbState),
351     .instance_init = milkymist_vgafb_init,
352     .class_init    = milkymist_vgafb_class_init,
353 };
354
355 static void milkymist_vgafb_register_types(void)
356 {
357     type_register_static(&milkymist_vgafb_info);
358 }
359
360 type_init(milkymist_vgafb_register_types)
This page took 0.03486 seconds and 2 git commands to generate.