Statistics
| Branch: | Revision:

root / hw / milkymist-vgafb.c @ 505597e4

History | View | Annotate | Download (7.5 kB)

1

    
2
/*
3
 *  QEMU model of the Milkymist VGA framebuffer.
4
 *
5
 *  Copyright (c) 2010 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://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;
67
    DisplayState *ds;
68

    
69
    int invalidate;
70
    uint32_t fb_offset;
71
    uint32_t fb_mask;
72

    
73
    uint32_t regs[R_MAX];
74
};
75
typedef struct MilkymistVgafbState MilkymistVgafbState;
76

    
77
static int vgafb_enabled(MilkymistVgafbState *s)
78
{
79
    return !(s->regs[R_CTRL] & CTRL_RESET);
80
}
81

    
82
static void vgafb_update_display(void *opaque)
83
{
84
    MilkymistVgafbState *s = opaque;
85
    int first = 0;
86
    int last = 0;
87
    drawfn fn;
88

    
89
    if (!vgafb_enabled(s)) {
90
        return;
91
    }
92

    
93
    int dest_width = s->regs[R_HRES];
94

    
95
    switch (ds_get_bits_per_pixel(s->ds)) {
96
    case 0:
97
        return;
98
    case 8:
99
        fn = draw_line_8;
100
        break;
101
    case 15:
102
        fn = draw_line_15;
103
        dest_width *= 2;
104
        break;
105
    case 16:
106
        fn = draw_line_16;
107
        dest_width *= 2;
108
        break;
109
    case 24:
110
        fn = draw_line_24;
111
        dest_width *= 3;
112
        break;
113
    case 32:
114
        fn = draw_line_32;
115
        dest_width *= 4;
116
        break;
117
    default:
118
        hw_error("milkymist_vgafb: bad color depth\n");
119
        break;
120
    }
121

    
122
    framebuffer_update_display(s->ds,
123
                               s->regs[R_BASEADDRESS] + s->fb_offset,
124
                               s->regs[R_HRES],
125
                               s->regs[R_VRES],
126
                               s->regs[R_HRES] * 2,
127
                               dest_width,
128
                               0,
129
                               s->invalidate,
130
                               fn,
131
                               NULL,
132
                               &first, &last);
133

    
134
    if (first >= 0) {
135
        dpy_update(s->ds, 0, first, s->regs[R_HRES], last - first + 1);
136
    }
137
    s->invalidate = 0;
138
}
139

    
140
static void vgafb_invalidate_display(void *opaque)
141
{
142
    MilkymistVgafbState *s = opaque;
143
    s->invalidate = 1;
144
}
145

    
146
static void vgafb_resize(MilkymistVgafbState *s)
147
{
148
    if (!vgafb_enabled(s)) {
149
        return;
150
    }
151

    
152
    qemu_console_resize(s->ds, s->regs[R_HRES], s->regs[R_VRES]);
153
    s->invalidate = 1;
154
}
155

    
156
static uint32_t vgafb_read(void *opaque, target_phys_addr_t addr)
157
{
158
    MilkymistVgafbState *s = opaque;
159
    uint32_t r = 0;
160

    
161
    addr >>= 2;
162
    switch (addr) {
163
    case R_CTRL:
164
    case R_HRES:
165
    case R_HSYNC_START:
166
    case R_HSYNC_END:
167
    case R_HSCAN:
168
    case R_VRES:
169
    case R_VSYNC_START:
170
    case R_VSYNC_END:
171
    case R_VSCAN:
172
    case R_BASEADDRESS:
173
    case R_BURST_COUNT:
174
    case R_SOURCE_CLOCK:
175
        r = s->regs[addr];
176
    break;
177
    case R_BASEADDRESS_ACT:
178
        r = s->regs[R_BASEADDRESS];
179
    break;
180

    
181
    default:
182
        error_report("milkymist_vgafb: read access to unknown register 0x"
183
                TARGET_FMT_plx, addr << 2);
184
        break;
185
    }
186

    
187
    trace_milkymist_vgafb_memory_read(addr << 2, r);
188

    
189
    return r;
190
}
191

    
192
static void
193
vgafb_write(void *opaque, target_phys_addr_t addr, uint32_t value)
194
{
195
    MilkymistVgafbState *s = opaque;
196

    
197
    trace_milkymist_vgafb_memory_write(addr, value);
198

    
199
    addr >>= 2;
200
    switch (addr) {
201
    case R_CTRL:
202
        s->regs[addr] = value;
203
        vgafb_resize(s);
204
        break;
205
    case R_HSYNC_START:
206
    case R_HSYNC_END:
207
    case R_HSCAN:
208
    case R_VSYNC_START:
209
    case R_VSYNC_END:
210
    case R_VSCAN:
211
    case R_BURST_COUNT:
212
    case R_SOURCE_CLOCK:
213
        s->regs[addr] = value;
214
        break;
215
    case R_BASEADDRESS:
216
        if (value & 0x1f) {
217
            error_report("milkymist_vgafb: framebuffer base address have to "
218
                     "be 32 byte aligned");
219
            break;
220
        }
221
        s->regs[addr] = value & s->fb_mask;
222
        s->invalidate = 1;
223
        break;
224
    case R_HRES:
225
    case R_VRES:
226
        s->regs[addr] = value;
227
        vgafb_resize(s);
228
        break;
229
    case R_BASEADDRESS_ACT:
230
        error_report("milkymist_vgafb: write to read-only register 0x"
231
                TARGET_FMT_plx, addr << 2);
232
        break;
233

    
234
    default:
235
        error_report("milkymist_vgafb: write access to unknown register 0x"
236
                TARGET_FMT_plx, addr << 2);
237
        break;
238
    }
239
}
240

    
241
static CPUReadMemoryFunc * const vgafb_read_fn[] = {
242
   NULL,
243
   NULL,
244
   &vgafb_read
245
};
246

    
247
static CPUWriteMemoryFunc * const vgafb_write_fn[] = {
248
   NULL,
249
   NULL,
250
   &vgafb_write
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
    int vgafb_regs;
273

    
274
    vgafb_regs = cpu_register_io_memory(vgafb_read_fn, vgafb_write_fn, s,
275
            DEVICE_NATIVE_ENDIAN);
276
    sysbus_init_mmio(dev, R_MAX * 4, vgafb_regs);
277

    
278
    s->ds = graphic_console_init(vgafb_update_display,
279
                                 vgafb_invalidate_display,
280
                                 NULL, NULL, s);
281

    
282
    return 0;
283
}
284

    
285
static int vgafb_post_load(void *opaque, int version_id)
286
{
287
    vgafb_invalidate_display(opaque);
288
    return 0;
289
}
290

    
291
static const VMStateDescription vmstate_milkymist_vgafb = {
292
    .name = "milkymist-vgafb",
293
    .version_id = 1,
294
    .minimum_version_id = 1,
295
    .minimum_version_id_old = 1,
296
    .post_load = vgafb_post_load,
297
    .fields      = (VMStateField[]) {
298
        VMSTATE_UINT32_ARRAY(regs, MilkymistVgafbState, R_MAX),
299
        VMSTATE_END_OF_LIST()
300
    }
301
};
302

    
303
static SysBusDeviceInfo milkymist_vgafb_info = {
304
    .init = milkymist_vgafb_init,
305
    .qdev.name  = "milkymist-vgafb",
306
    .qdev.size  = sizeof(MilkymistVgafbState),
307
    .qdev.vmsd  = &vmstate_milkymist_vgafb,
308
    .qdev.reset = milkymist_vgafb_reset,
309
    .qdev.props = (Property[]) {
310
        DEFINE_PROP_UINT32("fb_offset", MilkymistVgafbState, fb_offset, 0x0),
311
        DEFINE_PROP_UINT32("fb_mask", MilkymistVgafbState, fb_mask, 0xffffffff),
312
        DEFINE_PROP_END_OF_LIST(),
313
    }
314
};
315

    
316
static void milkymist_vgafb_register(void)
317
{
318
    sysbus_register_withprop(&milkymist_vgafb_info);
319
}
320

    
321
device_init(milkymist_vgafb_register)