Statistics
| Branch: | Revision:

root / hw / syborg_fb.c @ c1ded3dc

History | View | Annotate | Download (13.1 kB)

1
/*
2
 * Syborg Framebuffer
3
 *
4
 * Copyright (c) 2009 CodeSourcery
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 */
24

    
25
#include "sysbus.h"
26
#include "console.h"
27
#include "syborg.h"
28
#include "framebuffer.h"
29

    
30
//#define DEBUG_SYBORG_FB
31

    
32
#ifdef DEBUG_SYBORG_FB
33
#define DPRINTF(fmt, ...) \
34
do { printf("syborg_fb: " fmt , ## __VA_ARGS__); } while (0)
35
#define BADF(fmt, ...) \
36
do { fprintf(stderr, "syborg_fb: error: " fmt , ## __VA_ARGS__); \
37
    exit(1);} while (0)
38
#else
39
#define DPRINTF(fmt, ...) do {} while(0)
40
#define BADF(fmt, ...) \
41
do { fprintf(stderr, "syborg_fb: error: " fmt , ## __VA_ARGS__);} while (0)
42
#endif
43

    
44
enum {
45
    FB_ID               = 0,
46
    FB_BASE             = 1,
47
    FB_HEIGHT           = 2,
48
    FB_WIDTH            = 3,
49
    FB_ORIENTATION      = 4,
50
    FB_BLANK            = 5,
51
    FB_INT_MASK         = 6,
52
    FB_INTERRUPT_CAUSE  = 7,
53
    FB_BPP              = 8,
54
    FB_COLOR_ORDER      = 9,
55
    FB_BYTE_ORDER       = 10,
56
    FB_PIXEL_ORDER      = 11,
57
    FB_ROW_PITCH        = 12,
58
    FB_ENABLED          = 13,
59
    FB_PALETTE_START    = 0x400 >> 2,
60
    FB_PALETTE_END   = FB_PALETTE_START+256-1,
61
};
62

    
63
#define FB_INT_VSYNC            (1U << 0)
64
#define FB_INT_BASE_UPDATE_DONE (1U << 1)
65

    
66
typedef struct {
67
    SysBusDevice busdev;
68
    DisplayState *ds;
69
    /*QEMUConsole *console;*/
70
    uint32_t need_update : 1;
71
    uint32_t need_int : 1;
72
    uint32_t enabled : 1;
73
    uint32_t int_status;
74
    uint32_t int_enable;
75
    qemu_irq irq;
76

    
77
    uint32_t base;
78
    uint32_t pitch;
79
    uint32_t rows;
80
    uint32_t cols;
81
    int blank;
82
    int bpp;
83
    int rgb; /* 0 = BGR, 1 = RGB */
84
    int endian; /* 0 = Little, 1 = Big */
85
    uint32_t raw_palette[256];
86
    uint32_t palette[256];
87
} SyborgFBState;
88

    
89
enum {
90
    BPP_SRC_1,
91
    BPP_SRC_2,
92
    BPP_SRC_4,
93
    BPP_SRC_8,
94
    BPP_SRC_16,
95
    BPP_SRC_32,
96
    /* TODO: Implement these.  */
97
    BPP_SRC_15 = -1,
98
    BPP_SRC_24 = -2
99
};
100

    
101
#include "pixel_ops.h"
102

    
103
#define BITS 8
104
#include "pl110_template.h"
105
#define BITS 15
106
#include "pl110_template.h"
107
#define BITS 16
108
#include "pl110_template.h"
109
#define BITS 24
110
#include "pl110_template.h"
111
#define BITS 32
112
#include "pl110_template.h"
113

    
114
/* Update interrupts.  */
115
static void syborg_fb_update(SyborgFBState *s)
116
{
117
    if ((s->int_status & s->int_enable) != 0) {
118
        DPRINTF("Raise IRQ\n");
119
        qemu_irq_raise(s->irq);
120
    } else {
121
        DPRINTF("Lower IRQ\n");
122
        qemu_irq_lower(s->irq);
123
    }
124
}
125

    
126
static int syborg_fb_enabled(const SyborgFBState *s)
127
{
128
    return s->enabled;
129
}
130

    
131
static void syborg_fb_update_palette(SyborgFBState *s)
132
{
133
    int n, i;
134
    uint32_t raw;
135
    unsigned int r, g, b;
136

    
137
    switch (s->bpp) {
138
    case BPP_SRC_1: n = 2; break;
139
    case BPP_SRC_2: n = 4; break;
140
    case BPP_SRC_4: n = 16; break;
141
    case BPP_SRC_8: n = 256; break;
142
    default: return;
143
    }
144

    
145
    for (i = 0; i < n; i++) {
146
        raw = s->raw_palette[i];
147
        r = (raw >> 16) & 0xff;
148
        g = (raw >> 8) & 0xff;
149
        b = raw & 0xff;
150
        switch (ds_get_bits_per_pixel(s->ds)) {
151
        case 8:
152
            s->palette[i] = rgb_to_pixel8(r, g, b);
153
            break;
154
        case 15:
155
            s->palette[i] = rgb_to_pixel15(r, g, b);
156
            break;
157
        case 16:
158
            s->palette[i] = rgb_to_pixel16(r, g, b);
159
            break;
160
        case 24:
161
        case 32:
162
            s->palette[i] = rgb_to_pixel32(r, g, b);
163
            break;
164
        default:
165
            abort();
166
        }
167
    }
168

    
169
}
170

    
171
static void syborg_fb_update_display(void *opaque)
172
{
173
    SyborgFBState *s = (SyborgFBState *)opaque;
174
    drawfn* fntable;
175
    drawfn fn;
176
    int dest_width;
177
    int src_width;
178
    int bpp_offset;
179
    int first;
180
    int last;
181

    
182
    if (!syborg_fb_enabled(s))
183
        return;
184

    
185
    switch (ds_get_bits_per_pixel(s->ds)) {
186
    case 0:
187
        return;
188
    case 8:
189
        fntable = pl110_draw_fn_8;
190
        dest_width = 1;
191
        break;
192
    case 15:
193
        fntable = pl110_draw_fn_15;
194
        dest_width = 2;
195
        break;
196
    case 16:
197
        fntable = pl110_draw_fn_16;
198
        dest_width = 2;
199
        break;
200
    case 24:
201
        fntable = pl110_draw_fn_24;
202
        dest_width = 3;
203
        break;
204
    case 32:
205
        fntable = pl110_draw_fn_32;
206
        dest_width = 4;
207
        break;
208
    default:
209
        fprintf(stderr, "syborg_fb: Bad color depth\n");
210
        exit(1);
211
    }
212

    
213
    if (s->need_int) {
214
        s->int_status |= FB_INT_BASE_UPDATE_DONE;
215
        syborg_fb_update(s);
216
        s->need_int = 0;
217
    }
218

    
219
    if (s->rgb) {
220
        bpp_offset = 18;
221
    } else {
222
        bpp_offset = 0;
223
    }
224
    if (s->endian) {
225
        bpp_offset += 6;
226
    }
227

    
228
    fn = fntable[s->bpp + bpp_offset];
229

    
230
    if (s->pitch) {
231
        src_width = s->pitch;
232
    } else {
233
        src_width = s->cols;
234
        switch (s->bpp) {
235
        case BPP_SRC_1:
236
            src_width >>= 3;
237
            break;
238
        case BPP_SRC_2:
239
            src_width >>= 2;
240
            break;
241
        case BPP_SRC_4:
242
            src_width >>= 1;
243
            break;
244
        case BPP_SRC_8:
245
            break;
246
        case BPP_SRC_15:
247
        case BPP_SRC_16:
248
            src_width <<= 1;
249
            break;
250
        case BPP_SRC_24:
251
            src_width *= 3;
252
            break;
253
        case BPP_SRC_32:
254
            src_width <<= 2;
255
            break;
256
        }
257
    }
258
    dest_width *= s->cols;
259
    first = 0;
260
    /* TODO: Implement blanking.  */
261
    if (!s->blank) {
262
        if (s->need_update && s->bpp <= BPP_SRC_8) {
263
            syborg_fb_update_palette(s);
264
        }
265
        framebuffer_update_display(s->ds,
266
                                   s->base, s->cols, s->rows,
267
                                   src_width, dest_width, 0,
268
                                   s->need_update,
269
                                   fn, s->palette,
270
                                   &first, &last);
271
        if (first >= 0) {
272
            dpy_update(s->ds, 0, first, s->cols, last - first + 1);
273
        }
274

    
275
        s->int_status |= FB_INT_VSYNC;
276
        syborg_fb_update(s);
277
    }
278

    
279
    s->need_update = 0;
280
}
281

    
282
static void syborg_fb_invalidate_display(void * opaque)
283
{
284
    SyborgFBState *s = (SyborgFBState *)opaque;
285
    s->need_update = 1;
286
}
287

    
288
static uint32_t syborg_fb_read(void *opaque, target_phys_addr_t offset)
289
{
290
    SyborgFBState *s = opaque;
291

    
292
    DPRINTF("read reg %d\n", (int)offset);
293
    offset &= 0xfff;
294
    switch (offset >> 2) {
295
    case FB_ID:
296
        return SYBORG_ID_FRAMEBUFFER;
297

    
298
    case FB_BASE:
299
        return s->base;
300

    
301
    case FB_HEIGHT:
302
        return s->rows;
303

    
304
    case FB_WIDTH:
305
        return s->cols;
306

    
307
    case FB_ORIENTATION:
308
        return 0;
309

    
310
    case FB_BLANK:
311
        return s->blank;
312

    
313
    case FB_INT_MASK:
314
        return s->int_enable;
315

    
316
    case FB_INTERRUPT_CAUSE:
317
        return s->int_status;
318

    
319
    case FB_BPP:
320
        switch (s->bpp) {
321
        case BPP_SRC_1: return 1;
322
        case BPP_SRC_2: return 2;
323
        case BPP_SRC_4: return 4;
324
        case BPP_SRC_8: return 8;
325
        case BPP_SRC_15: return 15;
326
        case BPP_SRC_16: return 16;
327
        case BPP_SRC_24: return 24;
328
        case BPP_SRC_32: return 32;
329
        default: return 0;
330
        }
331

    
332
    case FB_COLOR_ORDER:
333
        return s->rgb;
334

    
335
    case FB_BYTE_ORDER:
336
        return s->endian;
337

    
338
    case FB_PIXEL_ORDER:
339
        return 0;
340

    
341
    case FB_ROW_PITCH:
342
        return s->pitch;
343

    
344
    case FB_ENABLED:
345
        return s->enabled;
346

    
347
    default:
348
        if ((offset >> 2) >= FB_PALETTE_START
349
            && (offset >> 2) <= FB_PALETTE_END) {
350
            return s->raw_palette[(offset >> 2) - FB_PALETTE_START];
351
        } else {
352
            cpu_abort (cpu_single_env, "syborg_fb_read: Bad offset %x\n",
353
                         (int)offset);
354
        }
355
        return 0;
356
    }
357
}
358

    
359
static void syborg_fb_write(void *opaque, target_phys_addr_t offset,
360
                            uint32_t val)
361
{
362
    SyborgFBState *s = opaque;
363

    
364
    DPRINTF("write reg %d = %d\n", (int)offset, val);
365
    s->need_update = 1;
366
    offset &= 0xfff;
367
    switch (offset >> 2) {
368
    case FB_BASE:
369
        s->base = val;
370
        s->need_int = 1;
371
        s->need_update = 1;
372
        syborg_fb_update(s);
373
        break;
374

    
375
    case FB_HEIGHT:
376
        s->rows = val;
377
        break;
378

    
379
    case FB_WIDTH:
380
        s->cols = val;
381
        break;
382

    
383
    case FB_ORIENTATION:
384
        /* TODO: Implement rotation.  */
385
        break;
386

    
387
    case FB_BLANK:
388
        s->blank = val & 1;
389
        break;
390

    
391
    case FB_INT_MASK:
392
        s->int_enable = val;
393
        syborg_fb_update(s);
394
        break;
395

    
396
    case FB_INTERRUPT_CAUSE:
397
        s->int_status &= ~val;
398
        syborg_fb_update(s);
399
        break;
400

    
401
    case FB_BPP:
402
        switch (val) {
403
        case 1: val = BPP_SRC_1; break;
404
        case 2: val = BPP_SRC_2; break;
405
        case 4: val = BPP_SRC_4; break;
406
        case 8: val = BPP_SRC_8; break;
407
        /* case 15: val = BPP_SRC_15; break; */
408
        case 16: val = BPP_SRC_16; break;
409
        /* case 24: val = BPP_SRC_24; break; */
410
        case 32: val = BPP_SRC_32; break;
411
        default: val = s->bpp; break;
412
        }
413
        s->bpp = val;
414
        break;
415

    
416
    case FB_COLOR_ORDER:
417
        s->rgb = (val != 0);
418
        break;
419

    
420
    case FB_BYTE_ORDER:
421
        s->endian = (val != 0);
422
        break;
423

    
424
    case FB_PIXEL_ORDER:
425
        /* TODO: Implement this.  */
426
        break;
427

    
428
    case FB_ROW_PITCH:
429
        s->pitch = val;
430
        break;
431

    
432
    case FB_ENABLED:
433
        s->enabled = val;
434
        break;
435

    
436
    default:
437
        if ((offset >> 2) >= FB_PALETTE_START
438
            && (offset >> 2) <= FB_PALETTE_END) {
439
            s->raw_palette[(offset >> 2) - FB_PALETTE_START] = val;
440
        } else {
441
            cpu_abort (cpu_single_env, "syborg_fb_write: Bad offset %x\n",
442
                      (int)offset);
443
        }
444
        break;
445
    }
446
}
447

    
448
static CPUReadMemoryFunc * const syborg_fb_readfn[] = {
449
    syborg_fb_read,
450
    syborg_fb_read,
451
    syborg_fb_read
452
};
453

    
454
static CPUWriteMemoryFunc * const syborg_fb_writefn[] = {
455
    syborg_fb_write,
456
    syborg_fb_write,
457
    syborg_fb_write
458
};
459

    
460
static void syborg_fb_save(QEMUFile *f, void *opaque)
461
{
462
    SyborgFBState *s = opaque;
463
    int i;
464

    
465
    qemu_put_be32(f, s->need_int);
466
    qemu_put_be32(f, s->int_status);
467
    qemu_put_be32(f, s->int_enable);
468
    qemu_put_be32(f, s->enabled);
469
    qemu_put_be32(f, s->base);
470
    qemu_put_be32(f, s->pitch);
471
    qemu_put_be32(f, s->rows);
472
    qemu_put_be32(f, s->cols);
473
    qemu_put_be32(f, s->bpp);
474
    qemu_put_be32(f, s->rgb);
475
    for (i = 0; i < 256; i++) {
476
        qemu_put_be32(f, s->raw_palette[i]);
477
    }
478
}
479

    
480
static int syborg_fb_load(QEMUFile *f, void *opaque, int version_id)
481
{
482
    SyborgFBState *s = opaque;
483
    int i;
484

    
485
    if (version_id != 1)
486
        return -EINVAL;
487

    
488
    s->need_int = qemu_get_be32(f);
489
    s->int_status = qemu_get_be32(f);
490
    s->int_enable = qemu_get_be32(f);
491
    s->enabled = qemu_get_be32(f);
492
    s->base = qemu_get_be32(f);
493
    s->pitch = qemu_get_be32(f);
494
    s->rows = qemu_get_be32(f);
495
    s->cols = qemu_get_be32(f);
496
    s->bpp = qemu_get_be32(f);
497
    s->rgb = qemu_get_be32(f);
498
    for (i = 0; i < 256; i++) {
499
        s->raw_palette[i] = qemu_get_be32(f);
500
    }
501
    s->need_update = 1;
502

    
503
    return 0;
504
}
505

    
506
static int syborg_fb_init(SysBusDevice *dev)
507
{
508
    SyborgFBState *s = FROM_SYSBUS(SyborgFBState, dev);
509
    int iomemtype;
510

    
511
    sysbus_init_irq(dev, &s->irq);
512
    iomemtype = cpu_register_io_memory(syborg_fb_readfn,
513
                                       syborg_fb_writefn, s);
514
    sysbus_init_mmio(dev, 0x1000, iomemtype);
515

    
516
    s->ds = graphic_console_init(syborg_fb_update_display,
517
                                 syborg_fb_invalidate_display,
518
                                 NULL, NULL, s);
519

    
520
    if (s->cols != 0 && s->rows != 0) {
521
        qemu_console_resize(s->ds, s->cols, s->rows);
522
    }
523

    
524
    if (!s->cols)
525
        s->cols = ds_get_width(s->ds);
526
    if (!s->rows)
527
        s->rows = ds_get_height(s->ds);
528

    
529
    register_savevm(&dev->qdev, "syborg_framebuffer", -1, 1,
530
                    syborg_fb_save, syborg_fb_load, s);
531
    return 0;
532
}
533

    
534
static SysBusDeviceInfo syborg_fb_info = {
535
    .init = syborg_fb_init,
536
    .qdev.name  = "syborg,framebuffer",
537
    .qdev.size  = sizeof(SyborgFBState),
538
    .qdev.props = (Property[]) {
539
        DEFINE_PROP_UINT32("width",  SyborgFBState, cols, 0),
540
        DEFINE_PROP_UINT32("height", SyborgFBState, rows, 0),
541
        DEFINE_PROP_END_OF_LIST(),
542
    }
543
};
544

    
545
static void syborg_fb_register_devices(void)
546
{
547
    sysbus_register_withprop(&syborg_fb_info);
548
}
549

    
550
device_init(syborg_fb_register_devices)