qemu/hw/display/g364fb.c
<<
>>
Prefs
   1/*
   2 * QEMU G364 framebuffer Emulator.
   3 *
   4 * Copyright (c) 2007-2011 Herve Poussineau
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public License as
   8 * published by the Free Software Foundation; either version 2 of
   9 * the License, or (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  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/>.
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "hw/hw.h"
  22#include "qemu/error-report.h"
  23#include "ui/console.h"
  24#include "ui/pixel_ops.h"
  25#include "trace.h"
  26#include "hw/sysbus.h"
  27
  28typedef struct G364State {
  29    /* hardware */
  30    uint8_t *vram;
  31    uint32_t vram_size;
  32    qemu_irq irq;
  33    MemoryRegion mem_vram;
  34    MemoryRegion mem_ctrl;
  35    /* registers */
  36    uint8_t color_palette[256][3];
  37    uint8_t cursor_palette[3][3];
  38    uint16_t cursor[512];
  39    uint32_t cursor_position;
  40    uint32_t ctla;
  41    uint32_t top_of_screen;
  42    uint32_t width, height; /* in pixels */
  43    /* display refresh support */
  44    QemuConsole *con;
  45    int depth;
  46    int blanked;
  47} G364State;
  48
  49#define REG_BOOT     0x000000
  50#define REG_DISPLAY  0x000118
  51#define REG_VDISPLAY 0x000150
  52#define REG_CTLA     0x000300
  53#define REG_TOP      0x000400
  54#define REG_CURS_PAL 0x000508
  55#define REG_CURS_POS 0x000638
  56#define REG_CLR_PAL  0x000800
  57#define REG_CURS_PAT 0x001000
  58#define REG_RESET    0x100000
  59
  60#define CTLA_FORCE_BLANK 0x00000400
  61#define CTLA_NO_CURSOR   0x00800000
  62
  63#define G364_PAGE_SIZE 4096
  64
  65static inline int check_dirty(G364State *s, DirtyBitmapSnapshot *snap, ram_addr_t page)
  66{
  67    return memory_region_snapshot_get_dirty(&s->mem_vram, snap, page, G364_PAGE_SIZE);
  68}
  69
  70static void g364fb_draw_graphic8(G364State *s)
  71{
  72    DisplaySurface *surface = qemu_console_surface(s->con);
  73    DirtyBitmapSnapshot *snap;
  74    int i, w;
  75    uint8_t *vram;
  76    uint8_t *data_display, *dd;
  77    ram_addr_t page;
  78    int x, y;
  79    int xmin, xmax;
  80    int ymin, ymax;
  81    int xcursor, ycursor;
  82    unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned int b);
  83
  84    switch (surface_bits_per_pixel(surface)) {
  85        case 8:
  86            rgb_to_pixel = rgb_to_pixel8;
  87            w = 1;
  88            break;
  89        case 15:
  90            rgb_to_pixel = rgb_to_pixel15;
  91            w = 2;
  92            break;
  93        case 16:
  94            rgb_to_pixel = rgb_to_pixel16;
  95            w = 2;
  96            break;
  97        case 32:
  98            rgb_to_pixel = rgb_to_pixel32;
  99            w = 4;
 100            break;
 101        default:
 102            hw_error("g364: unknown host depth %d",
 103                     surface_bits_per_pixel(surface));
 104            return;
 105    }
 106
 107    page = 0;
 108
 109    x = y = 0;
 110    xmin = s->width;
 111    xmax = 0;
 112    ymin = s->height;
 113    ymax = 0;
 114
 115    if (!(s->ctla & CTLA_NO_CURSOR)) {
 116        xcursor = s->cursor_position >> 12;
 117        ycursor = s->cursor_position & 0xfff;
 118    } else {
 119        xcursor = ycursor = -65;
 120    }
 121
 122    vram = s->vram + s->top_of_screen;
 123    /* XXX: out of range in vram? */
 124    data_display = dd = surface_data(surface);
 125    snap = memory_region_snapshot_and_clear_dirty(&s->mem_vram, 0, s->vram_size,
 126                                                  DIRTY_MEMORY_VGA);
 127    while (y < s->height) {
 128        if (check_dirty(s, snap, page)) {
 129            if (y < ymin)
 130                ymin = ymax = y;
 131            if (x < xmin)
 132                xmin = x;
 133            for (i = 0; i < G364_PAGE_SIZE; i++) {
 134                uint8_t index;
 135                unsigned int color;
 136                if (unlikely((y >= ycursor && y < ycursor + 64) &&
 137                    (x >= xcursor && x < xcursor + 64))) {
 138                    /* pointer area */
 139                    int xdiff = x - xcursor;
 140                    uint16_t curs = s->cursor[(y - ycursor) * 8 + xdiff / 8];
 141                    int op = (curs >> ((xdiff & 7) * 2)) & 3;
 142                    if (likely(op == 0)) {
 143                        /* transparent */
 144                        index = *vram;
 145                        color = (*rgb_to_pixel)(
 146                            s->color_palette[index][0],
 147                            s->color_palette[index][1],
 148                            s->color_palette[index][2]);
 149                    } else {
 150                        /* get cursor color */
 151                        index = op - 1;
 152                        color = (*rgb_to_pixel)(
 153                            s->cursor_palette[index][0],
 154                            s->cursor_palette[index][1],
 155                            s->cursor_palette[index][2]);
 156                    }
 157                } else {
 158                    /* normal area */
 159                    index = *vram;
 160                    color = (*rgb_to_pixel)(
 161                        s->color_palette[index][0],
 162                        s->color_palette[index][1],
 163                        s->color_palette[index][2]);
 164                }
 165                memcpy(dd, &color, w);
 166                dd += w;
 167                x++;
 168                vram++;
 169                if (x == s->width) {
 170                    xmax = s->width - 1;
 171                    y++;
 172                    if (y == s->height) {
 173                        ymax = s->height - 1;
 174                        goto done;
 175                    }
 176                    data_display = dd = data_display + surface_stride(surface);
 177                    xmin = 0;
 178                    x = 0;
 179                }
 180            }
 181            if (x > xmax)
 182                xmax = x;
 183            if (y > ymax)
 184                ymax = y;
 185        } else {
 186            int dy;
 187            if (xmax || ymax) {
 188                dpy_gfx_update(s->con, xmin, ymin,
 189                               xmax - xmin + 1, ymax - ymin + 1);
 190                xmin = s->width;
 191                xmax = 0;
 192                ymin = s->height;
 193                ymax = 0;
 194            }
 195            x += G364_PAGE_SIZE;
 196            dy = x / s->width;
 197            x = x % s->width;
 198            y += dy;
 199            vram += G364_PAGE_SIZE;
 200            data_display += dy * surface_stride(surface);
 201            dd = data_display + x * w;
 202        }
 203        page += G364_PAGE_SIZE;
 204    }
 205
 206done:
 207    if (xmax || ymax) {
 208        dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
 209    }
 210    g_free(snap);
 211}
 212
 213static void g364fb_draw_blank(G364State *s)
 214{
 215    DisplaySurface *surface = qemu_console_surface(s->con);
 216    int i, w;
 217    uint8_t *d;
 218
 219    if (s->blanked) {
 220        /* Screen is already blank. No need to redraw it */
 221        return;
 222    }
 223
 224    w = s->width * surface_bytes_per_pixel(surface);
 225    d = surface_data(surface);
 226    for (i = 0; i < s->height; i++) {
 227        memset(d, 0, w);
 228        d += surface_stride(surface);
 229    }
 230
 231    dpy_gfx_update(s->con, 0, 0, s->width, s->height);
 232    s->blanked = 1;
 233}
 234
 235static void g364fb_update_display(void *opaque)
 236{
 237    G364State *s = opaque;
 238    DisplaySurface *surface = qemu_console_surface(s->con);
 239
 240    qemu_flush_coalesced_mmio_buffer();
 241
 242    if (s->width == 0 || s->height == 0)
 243        return;
 244
 245    if (s->width != surface_width(surface) ||
 246        s->height != surface_height(surface)) {
 247        qemu_console_resize(s->con, s->width, s->height);
 248    }
 249
 250    if (s->ctla & CTLA_FORCE_BLANK) {
 251        g364fb_draw_blank(s);
 252    } else if (s->depth == 8) {
 253        g364fb_draw_graphic8(s);
 254    } else {
 255        error_report("g364: unknown guest depth %d", s->depth);
 256    }
 257
 258    qemu_irq_raise(s->irq);
 259}
 260
 261static inline void g364fb_invalidate_display(void *opaque)
 262{
 263    G364State *s = opaque;
 264
 265    s->blanked = 0;
 266    memory_region_set_dirty(&s->mem_vram, 0, s->vram_size);
 267}
 268
 269static void g364fb_reset(G364State *s)
 270{
 271    qemu_irq_lower(s->irq);
 272
 273    memset(s->color_palette, 0, sizeof(s->color_palette));
 274    memset(s->cursor_palette, 0, sizeof(s->cursor_palette));
 275    memset(s->cursor, 0, sizeof(s->cursor));
 276    s->cursor_position = 0;
 277    s->ctla = 0;
 278    s->top_of_screen = 0;
 279    s->width = s->height = 0;
 280    memset(s->vram, 0, s->vram_size);
 281    g364fb_invalidate_display(s);
 282}
 283
 284/* called for accesses to io ports */
 285static uint64_t g364fb_ctrl_read(void *opaque,
 286                                 hwaddr addr,
 287                                 unsigned int size)
 288{
 289    G364State *s = opaque;
 290    uint32_t val;
 291
 292    if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) {
 293        /* cursor pattern */
 294        int idx = (addr - REG_CURS_PAT) >> 3;
 295        val = s->cursor[idx];
 296    } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) {
 297        /* cursor palette */
 298        int idx = (addr - REG_CURS_PAL) >> 3;
 299        val = ((uint32_t)s->cursor_palette[idx][0] << 16);
 300        val |= ((uint32_t)s->cursor_palette[idx][1] << 8);
 301        val |= ((uint32_t)s->cursor_palette[idx][2] << 0);
 302    } else {
 303        switch (addr) {
 304            case REG_DISPLAY:
 305                val = s->width / 4;
 306                break;
 307            case REG_VDISPLAY:
 308                val = s->height * 2;
 309                break;
 310            case REG_CTLA:
 311                val = s->ctla;
 312                break;
 313            default:
 314            {
 315                error_report("g364: invalid read at [" TARGET_FMT_plx "]",
 316                             addr);
 317                val = 0;
 318                break;
 319            }
 320        }
 321    }
 322
 323    trace_g364fb_read(addr, val);
 324
 325    return val;
 326}
 327
 328static void g364fb_update_depth(G364State *s)
 329{
 330    static const int depths[8] = { 1, 2, 4, 8, 15, 16, 0 };
 331    s->depth = depths[(s->ctla & 0x00700000) >> 20];
 332}
 333
 334static void g364_invalidate_cursor_position(G364State *s)
 335{
 336    DisplaySurface *surface = qemu_console_surface(s->con);
 337    int ymin, ymax, start, end;
 338
 339    /* invalidate only near the cursor */
 340    ymin = s->cursor_position & 0xfff;
 341    ymax = MIN(s->height, ymin + 64);
 342    start = ymin * surface_stride(surface);
 343    end = (ymax + 1) * surface_stride(surface);
 344
 345    memory_region_set_dirty(&s->mem_vram, start, end - start);
 346}
 347
 348static void g364fb_ctrl_write(void *opaque,
 349                              hwaddr addr,
 350                              uint64_t val,
 351                              unsigned int size)
 352{
 353    G364State *s = opaque;
 354
 355    trace_g364fb_write(addr, val);
 356
 357    if (addr >= REG_CLR_PAL && addr < REG_CLR_PAL + 0x800) {
 358        /* color palette */
 359        int idx = (addr - REG_CLR_PAL) >> 3;
 360        s->color_palette[idx][0] = (val >> 16) & 0xff;
 361        s->color_palette[idx][1] = (val >> 8) & 0xff;
 362        s->color_palette[idx][2] = val & 0xff;
 363        g364fb_invalidate_display(s);
 364    } else if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) {
 365        /* cursor pattern */
 366        int idx = (addr - REG_CURS_PAT) >> 3;
 367        s->cursor[idx] = val;
 368        g364fb_invalidate_display(s);
 369    } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) {
 370        /* cursor palette */
 371        int idx = (addr - REG_CURS_PAL) >> 3;
 372        s->cursor_palette[idx][0] = (val >> 16) & 0xff;
 373        s->cursor_palette[idx][1] = (val >> 8) & 0xff;
 374        s->cursor_palette[idx][2] = val & 0xff;
 375        g364fb_invalidate_display(s);
 376    } else {
 377        switch (addr) {
 378        case REG_BOOT: /* Boot timing */
 379        case 0x00108: /* Line timing: half sync */
 380        case 0x00110: /* Line timing: back porch */
 381        case 0x00120: /* Line timing: short display */
 382        case 0x00128: /* Frame timing: broad pulse */
 383        case 0x00130: /* Frame timing: v sync */
 384        case 0x00138: /* Frame timing: v preequalise */
 385        case 0x00140: /* Frame timing: v postequalise */
 386        case 0x00148: /* Frame timing: v blank */
 387        case 0x00158: /* Line timing: line time */
 388        case 0x00160: /* Frame store: line start */
 389        case 0x00168: /* vram cycle: mem init */
 390        case 0x00170: /* vram cycle: transfer delay */
 391        case 0x00200: /* vram cycle: mask register */
 392            /* ignore */
 393            break;
 394        case REG_TOP:
 395            s->top_of_screen = val;
 396            g364fb_invalidate_display(s);
 397            break;
 398        case REG_DISPLAY:
 399            s->width = val * 4;
 400            break;
 401        case REG_VDISPLAY:
 402            s->height = val / 2;
 403            break;
 404        case REG_CTLA:
 405            s->ctla = val;
 406            g364fb_update_depth(s);
 407            g364fb_invalidate_display(s);
 408            break;
 409        case REG_CURS_POS:
 410            g364_invalidate_cursor_position(s);
 411            s->cursor_position = val;
 412            g364_invalidate_cursor_position(s);
 413            break;
 414        case REG_RESET:
 415            g364fb_reset(s);
 416            break;
 417        default:
 418            error_report("g364: invalid write of 0x%" PRIx64
 419                         " at [" TARGET_FMT_plx "]", val, addr);
 420            break;
 421        }
 422    }
 423    qemu_irq_lower(s->irq);
 424}
 425
 426static const MemoryRegionOps g364fb_ctrl_ops = {
 427    .read = g364fb_ctrl_read,
 428    .write = g364fb_ctrl_write,
 429    .endianness = DEVICE_LITTLE_ENDIAN,
 430    .impl.min_access_size = 4,
 431    .impl.max_access_size = 4,
 432};
 433
 434static int g364fb_post_load(void *opaque, int version_id)
 435{
 436    G364State *s = opaque;
 437
 438    /* force refresh */
 439    g364fb_update_depth(s);
 440    g364fb_invalidate_display(s);
 441
 442    return 0;
 443}
 444
 445static const VMStateDescription vmstate_g364fb = {
 446    .name = "g364fb",
 447    .version_id = 1,
 448    .minimum_version_id = 1,
 449    .post_load = g364fb_post_load,
 450    .fields = (VMStateField[]) {
 451        VMSTATE_VBUFFER_UINT32(vram, G364State, 1, NULL, vram_size),
 452        VMSTATE_BUFFER_UNSAFE(color_palette, G364State, 0, 256 * 3),
 453        VMSTATE_BUFFER_UNSAFE(cursor_palette, G364State, 0, 9),
 454        VMSTATE_UINT16_ARRAY(cursor, G364State, 512),
 455        VMSTATE_UINT32(cursor_position, G364State),
 456        VMSTATE_UINT32(ctla, G364State),
 457        VMSTATE_UINT32(top_of_screen, G364State),
 458        VMSTATE_UINT32(width, G364State),
 459        VMSTATE_UINT32(height, G364State),
 460        VMSTATE_END_OF_LIST()
 461    }
 462};
 463
 464static const GraphicHwOps g364fb_ops = {
 465    .invalidate  = g364fb_invalidate_display,
 466    .gfx_update  = g364fb_update_display,
 467};
 468
 469static void g364fb_init(DeviceState *dev, G364State *s)
 470{
 471    s->vram = g_malloc0(s->vram_size);
 472
 473    s->con = graphic_console_init(dev, 0, &g364fb_ops, s);
 474
 475    memory_region_init_io(&s->mem_ctrl, NULL, &g364fb_ctrl_ops, s, "ctrl", 0x180000);
 476    memory_region_init_ram_ptr(&s->mem_vram, NULL, "vram",
 477                               s->vram_size, s->vram);
 478    vmstate_register_ram(&s->mem_vram, dev);
 479    memory_region_set_log(&s->mem_vram, true, DIRTY_MEMORY_VGA);
 480}
 481
 482#define TYPE_G364 "sysbus-g364"
 483#define G364(obj) OBJECT_CHECK(G364SysBusState, (obj), TYPE_G364)
 484
 485typedef struct {
 486    SysBusDevice parent_obj;
 487
 488    G364State g364;
 489} G364SysBusState;
 490
 491static int g364fb_sysbus_init(SysBusDevice *sbd)
 492{
 493    DeviceState *dev = DEVICE(sbd);
 494    G364SysBusState *sbs = G364(dev);
 495    G364State *s = &sbs->g364;
 496
 497    g364fb_init(dev, s);
 498    sysbus_init_irq(sbd, &s->irq);
 499    sysbus_init_mmio(sbd, &s->mem_ctrl);
 500    sysbus_init_mmio(sbd, &s->mem_vram);
 501
 502    return 0;
 503}
 504
 505static void g364fb_sysbus_reset(DeviceState *d)
 506{
 507    G364SysBusState *s = G364(d);
 508
 509    g364fb_reset(&s->g364);
 510}
 511
 512static Property g364fb_sysbus_properties[] = {
 513    DEFINE_PROP_UINT32("vram_size", G364SysBusState, g364.vram_size,
 514    8 * 1024 * 1024),
 515    DEFINE_PROP_END_OF_LIST(),
 516};
 517
 518static void g364fb_sysbus_class_init(ObjectClass *klass, void *data)
 519{
 520    DeviceClass *dc = DEVICE_CLASS(klass);
 521    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 522
 523    k->init = g364fb_sysbus_init;
 524    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
 525    dc->desc = "G364 framebuffer";
 526    dc->reset = g364fb_sysbus_reset;
 527    dc->vmsd = &vmstate_g364fb;
 528    dc->props = g364fb_sysbus_properties;
 529}
 530
 531static const TypeInfo g364fb_sysbus_info = {
 532    .name          = TYPE_G364,
 533    .parent        = TYPE_SYS_BUS_DEVICE,
 534    .instance_size = sizeof(G364SysBusState),
 535    .class_init    = g364fb_sysbus_class_init,
 536};
 537
 538static void g364fb_register_types(void)
 539{
 540    type_register_static(&g364fb_sysbus_info);
 541}
 542
 543type_init(g364fb_register_types)
 544