|
|
1.1 root 1:
2: /*
3: * QEMU model of the Milkymist VGA framebuffer.
4: *
5: * Copyright (c) 2010 Michael Walle <[email protected]>
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://www.milkymist.org/socdoc/vgafb.pdf
23: */
24:
25: #include "hw.h"
26: #include "sysbus.h"
27: #include "trace.h"
28: #include "console.h"
29: #include "framebuffer.h"
30: #include "pixel_ops.h"
31: #include "qemu-error.h"
32:
33: #define BITS 8
34: #include "milkymist-vgafb_template.h"
35: #define BITS 15
36: #include "milkymist-vgafb_template.h"
37: #define BITS 16
38: #include "milkymist-vgafb_template.h"
39: #define BITS 24
40: #include "milkymist-vgafb_template.h"
41: #define BITS 32
42: #include "milkymist-vgafb_template.h"
43:
44: enum {
45: R_CTRL = 0,
46: R_HRES,
47: R_HSYNC_START,
48: R_HSYNC_END,
49: R_HSCAN,
50: R_VRES,
51: R_VSYNC_START,
52: R_VSYNC_END,
53: R_VSCAN,
54: R_BASEADDRESS,
55: R_BASEADDRESS_ACT,
56: R_BURST_COUNT,
57: R_SOURCE_CLOCK,
58: R_MAX
59: };
60:
61: enum {
62: CTRL_RESET = (1<<0),
63: };
64:
65: struct MilkymistVgafbState {
66: SysBusDevice busdev;
1.1.1.2 ! root 67: MemoryRegion regs_region;
1.1 root 68: DisplayState *ds;
69:
70: int invalidate;
71: uint32_t fb_offset;
72: uint32_t fb_mask;
73:
74: uint32_t regs[R_MAX];
75: };
76: typedef struct MilkymistVgafbState MilkymistVgafbState;
77:
78: static int vgafb_enabled(MilkymistVgafbState *s)
79: {
80: return !(s->regs[R_CTRL] & CTRL_RESET);
81: }
82:
83: static void vgafb_update_display(void *opaque)
84: {
85: MilkymistVgafbState *s = opaque;
86: int first = 0;
87: int last = 0;
88: drawfn fn;
89:
90: if (!vgafb_enabled(s)) {
91: return;
92: }
93:
94: int dest_width = s->regs[R_HRES];
95:
96: switch (ds_get_bits_per_pixel(s->ds)) {
97: case 0:
98: return;
99: case 8:
100: fn = draw_line_8;
101: break;
102: case 15:
103: fn = draw_line_15;
104: dest_width *= 2;
105: break;
106: case 16:
107: fn = draw_line_16;
108: dest_width *= 2;
109: break;
110: case 24:
111: fn = draw_line_24;
112: dest_width *= 3;
113: break;
114: case 32:
115: fn = draw_line_32;
116: dest_width *= 4;
117: break;
118: default:
119: hw_error("milkymist_vgafb: bad color depth\n");
120: break;
121: }
122:
123: framebuffer_update_display(s->ds,
124: s->regs[R_BASEADDRESS] + s->fb_offset,
125: s->regs[R_HRES],
126: s->regs[R_VRES],
127: s->regs[R_HRES] * 2,
128: dest_width,
129: 0,
130: s->invalidate,
131: fn,
132: NULL,
133: &first, &last);
134:
135: if (first >= 0) {
136: dpy_update(s->ds, 0, first, s->regs[R_HRES], last - first + 1);
137: }
138: s->invalidate = 0;
139: }
140:
141: static void vgafb_invalidate_display(void *opaque)
142: {
143: MilkymistVgafbState *s = opaque;
144: s->invalidate = 1;
145: }
146:
147: static void vgafb_resize(MilkymistVgafbState *s)
148: {
149: if (!vgafb_enabled(s)) {
150: return;
151: }
152:
153: qemu_console_resize(s->ds, s->regs[R_HRES], s->regs[R_VRES]);
154: s->invalidate = 1;
155: }
156:
1.1.1.2 ! root 157: static uint64_t vgafb_read(void *opaque, target_phys_addr_t addr,
! 158: unsigned size)
1.1 root 159: {
160: MilkymistVgafbState *s = opaque;
161: uint32_t r = 0;
162:
163: addr >>= 2;
164: switch (addr) {
165: case R_CTRL:
166: case R_HRES:
167: case R_HSYNC_START:
168: case R_HSYNC_END:
169: case R_HSCAN:
170: case R_VRES:
171: case R_VSYNC_START:
172: case R_VSYNC_END:
173: case R_VSCAN:
174: case R_BASEADDRESS:
175: case R_BURST_COUNT:
176: case R_SOURCE_CLOCK:
177: r = s->regs[addr];
178: break;
179: case R_BASEADDRESS_ACT:
180: r = s->regs[R_BASEADDRESS];
181: break;
182:
183: default:
184: error_report("milkymist_vgafb: read access to unknown register 0x"
185: TARGET_FMT_plx, addr << 2);
186: break;
187: }
188:
189: trace_milkymist_vgafb_memory_read(addr << 2, r);
190:
191: return r;
192: }
193:
1.1.1.2 ! root 194: static void vgafb_write(void *opaque, target_phys_addr_t addr, uint64_t value,
! 195: unsigned size)
1.1 root 196: {
197: MilkymistVgafbState *s = opaque;
198:
199: trace_milkymist_vgafb_memory_write(addr, value);
200:
201: addr >>= 2;
202: switch (addr) {
203: case R_CTRL:
204: s->regs[addr] = value;
205: vgafb_resize(s);
206: break;
207: case R_HSYNC_START:
208: case R_HSYNC_END:
209: case R_HSCAN:
210: case R_VSYNC_START:
211: case R_VSYNC_END:
212: case R_VSCAN:
213: case R_BURST_COUNT:
214: case R_SOURCE_CLOCK:
215: s->regs[addr] = value;
216: break;
217: case R_BASEADDRESS:
218: if (value & 0x1f) {
219: error_report("milkymist_vgafb: framebuffer base address have to "
220: "be 32 byte aligned");
221: break;
222: }
223: s->regs[addr] = value & s->fb_mask;
224: s->invalidate = 1;
225: break;
226: case R_HRES:
227: case R_VRES:
228: s->regs[addr] = value;
229: vgafb_resize(s);
230: break;
231: case R_BASEADDRESS_ACT:
232: error_report("milkymist_vgafb: write to read-only register 0x"
233: TARGET_FMT_plx, addr << 2);
234: break;
235:
236: default:
237: error_report("milkymist_vgafb: write access to unknown register 0x"
238: TARGET_FMT_plx, addr << 2);
239: break;
240: }
241: }
242:
1.1.1.2 ! root 243: static const MemoryRegionOps vgafb_mmio_ops = {
! 244: .read = vgafb_read,
! 245: .write = vgafb_write,
! 246: .valid = {
! 247: .min_access_size = 4,
! 248: .max_access_size = 4,
! 249: },
! 250: .endianness = DEVICE_NATIVE_ENDIAN,
1.1 root 251: };
252:
253: static void milkymist_vgafb_reset(DeviceState *d)
254: {
255: MilkymistVgafbState *s = container_of(d, MilkymistVgafbState, busdev.qdev);
256: int i;
257:
258: for (i = 0; i < R_MAX; i++) {
259: s->regs[i] = 0;
260: }
261:
262: /* defaults */
263: s->regs[R_CTRL] = CTRL_RESET;
264: s->regs[R_HRES] = 640;
265: s->regs[R_VRES] = 480;
266: s->regs[R_BASEADDRESS] = 0;
267: }
268:
269: static int milkymist_vgafb_init(SysBusDevice *dev)
270: {
271: MilkymistVgafbState *s = FROM_SYSBUS(typeof(*s), dev);
272:
1.1.1.2 ! root 273: memory_region_init_io(&s->regs_region, &vgafb_mmio_ops, s,
! 274: "milkymist-vgafb", R_MAX * 4);
! 275: sysbus_init_mmio_region(dev, &s->regs_region);
1.1 root 276:
277: s->ds = graphic_console_init(vgafb_update_display,
278: vgafb_invalidate_display,
279: NULL, NULL, s);
280:
281: return 0;
282: }
283:
284: static int vgafb_post_load(void *opaque, int version_id)
285: {
286: vgafb_invalidate_display(opaque);
287: return 0;
288: }
289:
290: static const VMStateDescription vmstate_milkymist_vgafb = {
291: .name = "milkymist-vgafb",
292: .version_id = 1,
293: .minimum_version_id = 1,
294: .minimum_version_id_old = 1,
295: .post_load = vgafb_post_load,
296: .fields = (VMStateField[]) {
297: VMSTATE_UINT32_ARRAY(regs, MilkymistVgafbState, R_MAX),
298: VMSTATE_END_OF_LIST()
299: }
300: };
301:
302: static SysBusDeviceInfo milkymist_vgafb_info = {
303: .init = milkymist_vgafb_init,
304: .qdev.name = "milkymist-vgafb",
305: .qdev.size = sizeof(MilkymistVgafbState),
306: .qdev.vmsd = &vmstate_milkymist_vgafb,
307: .qdev.reset = milkymist_vgafb_reset,
308: .qdev.props = (Property[]) {
309: DEFINE_PROP_UINT32("fb_offset", MilkymistVgafbState, fb_offset, 0x0),
310: DEFINE_PROP_UINT32("fb_mask", MilkymistVgafbState, fb_mask, 0xffffffff),
311: DEFINE_PROP_END_OF_LIST(),
312: }
313: };
314:
315: static void milkymist_vgafb_register(void)
316: {
317: sysbus_register_withprop(&milkymist_vgafb_info);
318: }
319:
320: device_init(milkymist_vgafb_register)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.