g364fb.c 15.9 KB
Newer Older
A
aurel32 已提交
1 2 3
/*
 * QEMU G364 framebuffer Emulator.
 *
H
Hervé Poussineau 已提交
4
 * Copyright (c) 2007-2011 Herve Poussineau
A
aurel32 已提交
5 6 7 8 9 10 11 12 13 14 15
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
16
 * You should have received a copy of the GNU General Public License along
17
 * with this program; if not, see <http://www.gnu.org/licenses/>.
A
aurel32 已提交
18 19
 */

P
Peter Maydell 已提交
20
#include "qemu/osdep.h"
21
#include "qemu/units.h"
22
#include "hw/hw.h"
23
#include "qemu/error-report.h"
24 25
#include "ui/console.h"
#include "ui/pixel_ops.h"
H
Hervé Poussineau 已提交
26
#include "trace.h"
27
#include "hw/sysbus.h"
A
aurel32 已提交
28

A
aurel32 已提交
29
typedef struct G364State {
A
aurel32 已提交
30 31
    /* hardware */
    uint8_t *vram;
H
Hervé Poussineau 已提交
32
    uint32_t vram_size;
A
aurel32 已提交
33
    qemu_irq irq;
H
Hervé Poussineau 已提交
34 35
    MemoryRegion mem_vram;
    MemoryRegion mem_ctrl;
A
aurel32 已提交
36 37 38 39 40
    /* registers */
    uint8_t color_palette[256][3];
    uint8_t cursor_palette[3][3];
    uint16_t cursor[512];
    uint32_t cursor_position;
A
aurel32 已提交
41
    uint32_t ctla;
A
aurel32 已提交
42 43
    uint32_t top_of_screen;
    uint32_t width, height; /* in pixels */
A
aurel32 已提交
44
    /* display refresh support */
45
    QemuConsole *con;
A
aurel32 已提交
46 47
    int depth;
    int blanked;
A
aurel32 已提交
48 49
} G364State;

H
Hervé Poussineau 已提交
50 51 52 53 54 55 56 57 58 59
#define REG_BOOT     0x000000
#define REG_DISPLAY  0x000118
#define REG_VDISPLAY 0x000150
#define REG_CTLA     0x000300
#define REG_TOP      0x000400
#define REG_CURS_PAL 0x000508
#define REG_CURS_POS 0x000638
#define REG_CLR_PAL  0x000800
#define REG_CURS_PAT 0x001000
#define REG_RESET    0x100000
A
aurel32 已提交
60 61 62 63

#define CTLA_FORCE_BLANK 0x00000400
#define CTLA_NO_CURSOR   0x00800000

B
Blue Swirl 已提交
64 65
#define G364_PAGE_SIZE 4096

66
static inline int check_dirty(G364State *s, DirtyBitmapSnapshot *snap, ram_addr_t page)
A
aurel32 已提交
67
{
68
    return memory_region_snapshot_get_dirty(&s->mem_vram, snap, page, G364_PAGE_SIZE);
A
aurel32 已提交
69 70 71
}

static void g364fb_draw_graphic8(G364State *s)
A
aurel32 已提交
72
{
73
    DisplaySurface *surface = qemu_console_surface(s->con);
74
    DirtyBitmapSnapshot *snap;
A
aurel32 已提交
75 76 77
    int i, w;
    uint8_t *vram;
    uint8_t *data_display, *dd;
78
    ram_addr_t page;
A
aurel32 已提交
79 80 81 82 83 84
    int x, y;
    int xmin, xmax;
    int ymin, ymax;
    int xcursor, ycursor;
    unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned int b);

85
    switch (surface_bits_per_pixel(surface)) {
A
aurel32 已提交
86
        case 8:
A
aurel32 已提交
87 88
            rgb_to_pixel = rgb_to_pixel8;
            w = 1;
A
aurel32 已提交
89 90
            break;
        case 15:
A
aurel32 已提交
91 92
            rgb_to_pixel = rgb_to_pixel15;
            w = 2;
A
aurel32 已提交
93 94
            break;
        case 16:
A
aurel32 已提交
95 96
            rgb_to_pixel = rgb_to_pixel16;
            w = 2;
A
aurel32 已提交
97 98
            break;
        case 32:
A
aurel32 已提交
99 100
            rgb_to_pixel = rgb_to_pixel32;
            w = 4;
A
aurel32 已提交
101 102
            break;
        default:
H
Hervé Poussineau 已提交
103
            hw_error("g364: unknown host depth %d",
104
                     surface_bits_per_pixel(surface));
A
aurel32 已提交
105 106 107
            return;
    }

H
Hervé Poussineau 已提交
108
    page = 0;
A
aurel32 已提交
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124

    x = y = 0;
    xmin = s->width;
    xmax = 0;
    ymin = s->height;
    ymax = 0;

    if (!(s->ctla & CTLA_NO_CURSOR)) {
        xcursor = s->cursor_position >> 12;
        ycursor = s->cursor_position & 0xfff;
    } else {
        xcursor = ycursor = -65;
    }

    vram = s->vram + s->top_of_screen;
    /* XXX: out of range in vram? */
125
    data_display = dd = surface_data(surface);
126 127
    snap = memory_region_snapshot_and_clear_dirty(&s->mem_vram, 0, s->vram_size,
                                                  DIRTY_MEMORY_VGA);
A
aurel32 已提交
128
    while (y < s->height) {
129
        if (check_dirty(s, snap, page)) {
A
aurel32 已提交
130 131 132 133
            if (y < ymin)
                ymin = ymax = y;
            if (x < xmin)
                xmin = x;
B
Blue Swirl 已提交
134
            for (i = 0; i < G364_PAGE_SIZE; i++) {
A
aurel32 已提交
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
                uint8_t index;
                unsigned int color;
                if (unlikely((y >= ycursor && y < ycursor + 64) &&
                    (x >= xcursor && x < xcursor + 64))) {
                    /* pointer area */
                    int xdiff = x - xcursor;
                    uint16_t curs = s->cursor[(y - ycursor) * 8 + xdiff / 8];
                    int op = (curs >> ((xdiff & 7) * 2)) & 3;
                    if (likely(op == 0)) {
                        /* transparent */
                        index = *vram;
                        color = (*rgb_to_pixel)(
                            s->color_palette[index][0],
                            s->color_palette[index][1],
                            s->color_palette[index][2]);
                    } else {
                        /* get cursor color */
                        index = op - 1;
                        color = (*rgb_to_pixel)(
                            s->cursor_palette[index][0],
                            s->cursor_palette[index][1],
                            s->cursor_palette[index][2]);
                    }
                } else {
                    /* normal area */
                    index = *vram;
                    color = (*rgb_to_pixel)(
                        s->color_palette[index][0],
                        s->color_palette[index][1],
                        s->color_palette[index][2]);
                }
                memcpy(dd, &color, w);
                dd += w;
                x++;
                vram++;
                if (x == s->width) {
                    xmax = s->width - 1;
                    y++;
                    if (y == s->height) {
                        ymax = s->height - 1;
                        goto done;
                    }
177
                    data_display = dd = data_display + surface_stride(surface);
A
aurel32 已提交
178 179 180 181 182 183 184 185 186 187
                    xmin = 0;
                    x = 0;
                }
            }
            if (x > xmax)
                xmax = x;
            if (y > ymax)
                ymax = y;
        } else {
            int dy;
188
            if (xmax || ymax) {
189
                dpy_gfx_update(s->con, xmin, ymin,
190
                               xmax - xmin + 1, ymax - ymin + 1);
A
aurel32 已提交
191 192 193 194 195
                xmin = s->width;
                xmax = 0;
                ymin = s->height;
                ymax = 0;
            }
B
Blue Swirl 已提交
196
            x += G364_PAGE_SIZE;
A
aurel32 已提交
197 198 199
            dy = x / s->width;
            x = x % s->width;
            y += dy;
B
Blue Swirl 已提交
200
            vram += G364_PAGE_SIZE;
201
            data_display += dy * surface_stride(surface);
A
aurel32 已提交
202 203
            dd = data_display + x * w;
        }
B
Blue Swirl 已提交
204
        page += G364_PAGE_SIZE;
A
aurel32 已提交
205 206 207
    }

done:
208
    if (xmax || ymax) {
209
        dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
A
aurel32 已提交
210
    }
211
    g_free(snap);
A
aurel32 已提交
212 213
}

A
aurel32 已提交
214
static void g364fb_draw_blank(G364State *s)
A
aurel32 已提交
215
{
216
    DisplaySurface *surface = qemu_console_surface(s->con);
A
aurel32 已提交
217 218 219
    int i, w;
    uint8_t *d;

A
aurel32 已提交
220 221
    if (s->blanked) {
        /* Screen is already blank. No need to redraw it */
A
aurel32 已提交
222
        return;
A
aurel32 已提交
223
    }
A
aurel32 已提交
224

225 226
    w = s->width * surface_bytes_per_pixel(surface);
    d = surface_data(surface);
A
aurel32 已提交
227
    for (i = 0; i < s->height; i++) {
A
aurel32 已提交
228
        memset(d, 0, w);
229
        d += surface_stride(surface);
A
aurel32 已提交
230
    }
A
aurel32 已提交
231

G
Gerd Hoffmann 已提交
232
    dpy_gfx_update_full(s->con);
A
aurel32 已提交
233
    s->blanked = 1;
A
aurel32 已提交
234 235 236 237 238
}

static void g364fb_update_display(void *opaque)
{
    G364State *s = opaque;
239
    DisplaySurface *surface = qemu_console_surface(s->con);
A
aurel32 已提交
240

241 242
    qemu_flush_coalesced_mmio_buffer();

A
aurel32 已提交
243
    if (s->width == 0 || s->height == 0)
A
aurel32 已提交
244 245
        return;

246 247 248
    if (s->width != surface_width(surface) ||
        s->height != surface_height(surface)) {
        qemu_console_resize(s->con, s->width, s->height);
A
aurel32 已提交
249
    }
A
aurel32 已提交
250 251 252 253 254 255

    if (s->ctla & CTLA_FORCE_BLANK) {
        g364fb_draw_blank(s);
    } else if (s->depth == 8) {
        g364fb_draw_graphic8(s);
    } else {
H
Hervé Poussineau 已提交
256
        error_report("g364: unknown guest depth %d", s->depth);
A
aurel32 已提交
257
    }
A
aurel32 已提交
258 259

    qemu_irq_raise(s->irq);
A
aurel32 已提交
260 261
}

262
static inline void g364fb_invalidate_display(void *opaque)
A
aurel32 已提交
263 264
{
    G364State *s = opaque;
A
aurel32 已提交
265 266

    s->blanked = 0;
267
    memory_region_set_dirty(&s->mem_vram, 0, s->vram_size);
A
aurel32 已提交
268 269
}

H
Hervé Poussineau 已提交
270
static void g364fb_reset(G364State *s)
A
aurel32 已提交
271
{
A
aurel32 已提交
272 273 274 275 276 277 278 279 280 281
    qemu_irq_lower(s->irq);

    memset(s->color_palette, 0, sizeof(s->color_palette));
    memset(s->cursor_palette, 0, sizeof(s->cursor_palette));
    memset(s->cursor, 0, sizeof(s->cursor));
    s->cursor_position = 0;
    s->ctla = 0;
    s->top_of_screen = 0;
    s->width = s->height = 0;
    memset(s->vram, 0, s->vram_size);
H
Hervé Poussineau 已提交
282
    g364fb_invalidate_display(s);
A
aurel32 已提交
283 284 285
}

/* called for accesses to io ports */
H
Hervé Poussineau 已提交
286
static uint64_t g364fb_ctrl_read(void *opaque,
A
Avi Kivity 已提交
287
                                 hwaddr addr,
H
Hervé Poussineau 已提交
288
                                 unsigned int size)
A
aurel32 已提交
289
{
A
aurel32 已提交
290
    G364State *s = opaque;
A
aurel32 已提交
291 292
    uint32_t val;

A
aurel32 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
    if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) {
        /* cursor pattern */
        int idx = (addr - REG_CURS_PAT) >> 3;
        val = s->cursor[idx];
    } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) {
        /* cursor palette */
        int idx = (addr - REG_CURS_PAL) >> 3;
        val = ((uint32_t)s->cursor_palette[idx][0] << 16);
        val |= ((uint32_t)s->cursor_palette[idx][1] << 8);
        val |= ((uint32_t)s->cursor_palette[idx][2] << 0);
    } else {
        switch (addr) {
            case REG_DISPLAY:
                val = s->width / 4;
                break;
            case REG_VDISPLAY:
                val = s->height * 2;
                break;
            case REG_CTLA:
                val = s->ctla;
                break;
            default:
            {
H
Hervé Poussineau 已提交
316 317
                error_report("g364: invalid read at [" TARGET_FMT_plx "]",
                             addr);
A
aurel32 已提交
318 319 320 321
                val = 0;
                break;
            }
        }
A
aurel32 已提交
322 323
    }

H
Hervé Poussineau 已提交
324
    trace_g364fb_read(addr, val);
A
aurel32 已提交
325 326 327 328

    return val;
}

A
aurel32 已提交
329
static void g364fb_update_depth(G364State *s)
A
aurel32 已提交
330
{
331
    static const int depths[8] = { 1, 2, 4, 8, 15, 16, 0 };
A
aurel32 已提交
332 333
    s->depth = depths[(s->ctla & 0x00700000) >> 20];
}
A
aurel32 已提交
334

A
aurel32 已提交
335 336
static void g364_invalidate_cursor_position(G364State *s)
{
337
    DisplaySurface *surface = qemu_console_surface(s->con);
338
    int ymin, ymax, start, end;
A
aurel32 已提交
339

A
aurel32 已提交
340 341 342
    /* invalidate only near the cursor */
    ymin = s->cursor_position & 0xfff;
    ymax = MIN(s->height, ymin + 64);
343 344
    start = ymin * surface_stride(surface);
    end = (ymax + 1) * surface_stride(surface);
A
aurel32 已提交
345

346
    memory_region_set_dirty(&s->mem_vram, start, end - start);
A
aurel32 已提交
347 348
}

H
Hervé Poussineau 已提交
349
static void g364fb_ctrl_write(void *opaque,
A
Avi Kivity 已提交
350
                              hwaddr addr,
H
Hervé Poussineau 已提交
351 352
                              uint64_t val,
                              unsigned int size)
A
aurel32 已提交
353 354 355
{
    G364State *s = opaque;

H
Hervé Poussineau 已提交
356
    trace_g364fb_write(addr, val);
A
aurel32 已提交
357 358

    if (addr >= REG_CLR_PAL && addr < REG_CLR_PAL + 0x800) {
A
aurel32 已提交
359
        /* color palette */
A
aurel32 已提交
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
        int idx = (addr - REG_CLR_PAL) >> 3;
        s->color_palette[idx][0] = (val >> 16) & 0xff;
        s->color_palette[idx][1] = (val >> 8) & 0xff;
        s->color_palette[idx][2] = val & 0xff;
        g364fb_invalidate_display(s);
    } else if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) {
        /* cursor pattern */
        int idx = (addr - REG_CURS_PAT) >> 3;
        s->cursor[idx] = val;
        g364fb_invalidate_display(s);
    } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) {
        /* cursor palette */
        int idx = (addr - REG_CURS_PAL) >> 3;
        s->cursor_palette[idx][0] = (val >> 16) & 0xff;
        s->cursor_palette[idx][1] = (val >> 8) & 0xff;
        s->cursor_palette[idx][2] = val & 0xff;
        g364fb_invalidate_display(s);
A
aurel32 已提交
377 378
    } else {
        switch (addr) {
H
Hervé Poussineau 已提交
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
        case REG_BOOT: /* Boot timing */
        case 0x00108: /* Line timing: half sync */
        case 0x00110: /* Line timing: back porch */
        case 0x00120: /* Line timing: short display */
        case 0x00128: /* Frame timing: broad pulse */
        case 0x00130: /* Frame timing: v sync */
        case 0x00138: /* Frame timing: v preequalise */
        case 0x00140: /* Frame timing: v postequalise */
        case 0x00148: /* Frame timing: v blank */
        case 0x00158: /* Line timing: line time */
        case 0x00160: /* Frame store: line start */
        case 0x00168: /* vram cycle: mem init */
        case 0x00170: /* vram cycle: transfer delay */
        case 0x00200: /* vram cycle: mask register */
            /* ignore */
            break;
        case REG_TOP:
            s->top_of_screen = val;
            g364fb_invalidate_display(s);
            break;
        case REG_DISPLAY:
            s->width = val * 4;
            break;
        case REG_VDISPLAY:
            s->height = val / 2;
            break;
        case REG_CTLA:
            s->ctla = val;
            g364fb_update_depth(s);
            g364fb_invalidate_display(s);
            break;
        case REG_CURS_POS:
            g364_invalidate_cursor_position(s);
            s->cursor_position = val;
            g364_invalidate_cursor_position(s);
            break;
        case REG_RESET:
            g364fb_reset(s);
            break;
        default:
            error_report("g364: invalid write of 0x%" PRIx64
                         " at [" TARGET_FMT_plx "]", val, addr);
            break;
A
aurel32 已提交
422 423
        }
    }
A
aurel32 已提交
424
    qemu_irq_lower(s->irq);
A
aurel32 已提交
425 426
}

H
Hervé Poussineau 已提交
427 428 429 430 431 432
static const MemoryRegionOps g364fb_ctrl_ops = {
    .read = g364fb_ctrl_read,
    .write = g364fb_ctrl_write,
    .endianness = DEVICE_LITTLE_ENDIAN,
    .impl.min_access_size = 4,
    .impl.max_access_size = 4,
A
aurel32 已提交
433 434
};

H
Hervé Poussineau 已提交
435
static int g364fb_post_load(void *opaque, int version_id)
A
aurel32 已提交
436 437
{
    G364State *s = opaque;
A
aurel32 已提交
438 439 440 441

    /* force refresh */
    g364fb_update_depth(s);
    g364fb_invalidate_display(s);
A
aurel32 已提交
442

A
aurel32 已提交
443
    return 0;
A
aurel32 已提交
444 445
}

H
Hervé Poussineau 已提交
446 447 448 449 450 451
static const VMStateDescription vmstate_g364fb = {
    .name = "g364fb",
    .version_id = 1,
    .minimum_version_id = 1,
    .post_load = g364fb_post_load,
    .fields = (VMStateField[]) {
452
        VMSTATE_VBUFFER_UINT32(vram, G364State, 1, NULL, vram_size),
H
Hervé Poussineau 已提交
453 454 455 456 457 458 459 460 461 462 463
        VMSTATE_BUFFER_UNSAFE(color_palette, G364State, 0, 256 * 3),
        VMSTATE_BUFFER_UNSAFE(cursor_palette, G364State, 0, 9),
        VMSTATE_UINT16_ARRAY(cursor, G364State, 512),
        VMSTATE_UINT32(cursor_position, G364State),
        VMSTATE_UINT32(ctla, G364State),
        VMSTATE_UINT32(top_of_screen, G364State),
        VMSTATE_UINT32(width, G364State),
        VMSTATE_UINT32(height, G364State),
        VMSTATE_END_OF_LIST()
    }
};
A
aurel32 已提交
464

G
Gerd Hoffmann 已提交
465 466 467 468 469
static const GraphicHwOps g364fb_ops = {
    .invalidate  = g364fb_invalidate_display,
    .gfx_update  = g364fb_update_display,
};

H
Hervé Poussineau 已提交
470
static void g364fb_init(DeviceState *dev, G364State *s)
A
aurel32 已提交
471
{
H
Hervé Poussineau 已提交
472
    s->vram = g_malloc0(s->vram_size);
A
aurel32 已提交
473

474
    s->con = graphic_console_init(dev, 0, &g364fb_ops, s);
A
aurel32 已提交
475

476 477
    memory_region_init_io(&s->mem_ctrl, NULL, &g364fb_ctrl_ops, s, "ctrl", 0x180000);
    memory_region_init_ram_ptr(&s->mem_vram, NULL, "vram",
H
Hervé Poussineau 已提交
478
                               s->vram_size, s->vram);
479
    vmstate_register_ram(&s->mem_vram, dev);
480
    memory_region_set_log(&s->mem_vram, true, DIRTY_MEMORY_VGA);
H
Hervé Poussineau 已提交
481 482
}

A
Andreas Färber 已提交
483 484 485
#define TYPE_G364 "sysbus-g364"
#define G364(obj) OBJECT_CHECK(G364SysBusState, (obj), TYPE_G364)

H
Hervé Poussineau 已提交
486
typedef struct {
A
Andreas Färber 已提交
487 488
    SysBusDevice parent_obj;

H
Hervé Poussineau 已提交
489 490
    G364State g364;
} G364SysBusState;
A
aurel32 已提交
491

492
static void g364fb_sysbus_realize(DeviceState *dev, Error **errp)
H
Hervé Poussineau 已提交
493
{
A
Andreas Färber 已提交
494 495
    G364SysBusState *sbs = G364(dev);
    G364State *s = &sbs->g364;
496
    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
H
Hervé Poussineau 已提交
497

A
Andreas Färber 已提交
498 499 500 501
    g364fb_init(dev, s);
    sysbus_init_irq(sbd, &s->irq);
    sysbus_init_mmio(sbd, &s->mem_ctrl);
    sysbus_init_mmio(sbd, &s->mem_vram);
A
aurel32 已提交
502
}
H
Hervé Poussineau 已提交
503 504 505

static void g364fb_sysbus_reset(DeviceState *d)
{
A
Andreas Färber 已提交
506 507
    G364SysBusState *s = G364(d);

H
Hervé Poussineau 已提交
508 509 510
    g364fb_reset(&s->g364);
}

511
static Property g364fb_sysbus_properties[] = {
512
    DEFINE_PROP_UINT32("vram_size", G364SysBusState, g364.vram_size, 8 * MiB),
513 514 515 516 517
    DEFINE_PROP_END_OF_LIST(),
};

static void g364fb_sysbus_class_init(ObjectClass *klass, void *data)
{
518
    DeviceClass *dc = DEVICE_CLASS(klass);
519

520
    dc->realize = g364fb_sysbus_realize;
521
    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
522 523 524 525
    dc->desc = "G364 framebuffer";
    dc->reset = g364fb_sysbus_reset;
    dc->vmsd = &vmstate_g364fb;
    dc->props = g364fb_sysbus_properties;
526 527
}

528
static const TypeInfo g364fb_sysbus_info = {
A
Andreas Färber 已提交
529
    .name          = TYPE_G364,
530 531 532
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(G364SysBusState),
    .class_init    = g364fb_sysbus_class_init,
H
Hervé Poussineau 已提交
533 534
};

A
Andreas Färber 已提交
535
static void g364fb_register_types(void)
H
Hervé Poussineau 已提交
536
{
537
    type_register_static(&g364fb_sysbus_info);
H
Hervé Poussineau 已提交
538 539
}

A
Andreas Färber 已提交
540
type_init(g364fb_register_types)