qemu/hw/display/tcx.c
<<
>>
Prefs
   1/*
   2 * QEMU TCX Frame buffer
   3 *
   4 * Copyright (c) 2003-2005 Fabrice Bellard
   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 "qemu-common.h"
  26#include "ui/console.h"
  27#include "ui/pixel_ops.h"
  28#include "hw/sysbus.h"
  29
  30#define MAXX 1024
  31#define MAXY 768
  32#define TCX_DAC_NREGS 16
  33#define TCX_THC_NREGS_8  0x081c
  34#define TCX_THC_NREGS_24 0x1000
  35#define TCX_TEC_NREGS    0x1000
  36
  37#define TYPE_TCX "SUNW,tcx"
  38#define TCX(obj) OBJECT_CHECK(TCXState, (obj), TYPE_TCX)
  39
  40typedef struct TCXState {
  41    SysBusDevice parent_obj;
  42
  43    QemuConsole *con;
  44    uint8_t *vram;
  45    uint32_t *vram24, *cplane;
  46    MemoryRegion vram_mem;
  47    MemoryRegion vram_8bit;
  48    MemoryRegion vram_24bit;
  49    MemoryRegion vram_cplane;
  50    MemoryRegion dac;
  51    MemoryRegion tec;
  52    MemoryRegion thc24;
  53    MemoryRegion thc8;
  54    ram_addr_t vram24_offset, cplane_offset;
  55    uint32_t vram_size;
  56    uint32_t palette[256];
  57    uint8_t r[256], g[256], b[256];
  58    uint16_t width, height, depth;
  59    uint8_t dac_index, dac_state;
  60} TCXState;
  61
  62static void tcx_set_dirty(TCXState *s)
  63{
  64    memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY);
  65}
  66
  67static void tcx24_set_dirty(TCXState *s)
  68{
  69    memory_region_set_dirty(&s->vram_mem, s->vram24_offset, MAXX * MAXY * 4);
  70    memory_region_set_dirty(&s->vram_mem, s->cplane_offset, MAXX * MAXY * 4);
  71}
  72
  73static void update_palette_entries(TCXState *s, int start, int end)
  74{
  75    DisplaySurface *surface = qemu_console_surface(s->con);
  76    int i;
  77
  78    for (i = start; i < end; i++) {
  79        switch (surface_bits_per_pixel(surface)) {
  80        default:
  81        case 8:
  82            s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
  83            break;
  84        case 15:
  85            s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
  86            break;
  87        case 16:
  88            s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
  89            break;
  90        case 32:
  91            if (is_surface_bgr(surface)) {
  92                s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
  93            } else {
  94                s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
  95            }
  96            break;
  97        }
  98    }
  99    if (s->depth == 24) {
 100        tcx24_set_dirty(s);
 101    } else {
 102        tcx_set_dirty(s);
 103    }
 104}
 105
 106static void tcx_draw_line32(TCXState *s1, uint8_t *d,
 107                            const uint8_t *s, int width)
 108{
 109    int x;
 110    uint8_t val;
 111    uint32_t *p = (uint32_t *)d;
 112
 113    for(x = 0; x < width; x++) {
 114        val = *s++;
 115        *p++ = s1->palette[val];
 116    }
 117}
 118
 119static void tcx_draw_line16(TCXState *s1, uint8_t *d,
 120                            const uint8_t *s, int width)
 121{
 122    int x;
 123    uint8_t val;
 124    uint16_t *p = (uint16_t *)d;
 125
 126    for(x = 0; x < width; x++) {
 127        val = *s++;
 128        *p++ = s1->palette[val];
 129    }
 130}
 131
 132static void tcx_draw_line8(TCXState *s1, uint8_t *d,
 133                           const uint8_t *s, int width)
 134{
 135    int x;
 136    uint8_t val;
 137
 138    for(x = 0; x < width; x++) {
 139        val = *s++;
 140        *d++ = s1->palette[val];
 141    }
 142}
 143
 144/*
 145  XXX Could be much more optimal:
 146  * detect if line/page/whole screen is in 24 bit mode
 147  * if destination is also BGR, use memcpy
 148  */
 149static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
 150                                     const uint8_t *s, int width,
 151                                     const uint32_t *cplane,
 152                                     const uint32_t *s24)
 153{
 154    DisplaySurface *surface = qemu_console_surface(s1->con);
 155    int x, bgr, r, g, b;
 156    uint8_t val, *p8;
 157    uint32_t *p = (uint32_t *)d;
 158    uint32_t dval;
 159
 160    bgr = is_surface_bgr(surface);
 161    for(x = 0; x < width; x++, s++, s24++) {
 162        if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) {
 163            // 24-bit direct, BGR order
 164            p8 = (uint8_t *)s24;
 165            p8++;
 166            b = *p8++;
 167            g = *p8++;
 168            r = *p8;
 169            if (bgr)
 170                dval = rgb_to_pixel32bgr(r, g, b);
 171            else
 172                dval = rgb_to_pixel32(r, g, b);
 173        } else {
 174            val = *s;
 175            dval = s1->palette[val];
 176        }
 177        *p++ = dval;
 178    }
 179}
 180
 181static inline int check_dirty(TCXState *s, ram_addr_t page, ram_addr_t page24,
 182                              ram_addr_t cpage)
 183{
 184    int ret;
 185
 186    ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE,
 187                                  DIRTY_MEMORY_VGA);
 188    ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4,
 189                                   DIRTY_MEMORY_VGA);
 190    ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4,
 191                                   DIRTY_MEMORY_VGA);
 192    return ret;
 193}
 194
 195static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
 196                               ram_addr_t page_max, ram_addr_t page24,
 197                              ram_addr_t cpage)
 198{
 199    memory_region_reset_dirty(&ts->vram_mem,
 200                              page_min,
 201                              (page_max - page_min) + TARGET_PAGE_SIZE,
 202                              DIRTY_MEMORY_VGA);
 203    memory_region_reset_dirty(&ts->vram_mem,
 204                              page24 + page_min * 4,
 205                              (page_max - page_min) * 4 + TARGET_PAGE_SIZE,
 206                              DIRTY_MEMORY_VGA);
 207    memory_region_reset_dirty(&ts->vram_mem,
 208                              cpage + page_min * 4,
 209                              (page_max - page_min) * 4 + TARGET_PAGE_SIZE,
 210                              DIRTY_MEMORY_VGA);
 211}
 212
 213/* Fixed line length 1024 allows us to do nice tricks not possible on
 214   VGA... */
 215static void tcx_update_display(void *opaque)
 216{
 217    TCXState *ts = opaque;
 218    DisplaySurface *surface = qemu_console_surface(ts->con);
 219    ram_addr_t page, page_min, page_max;
 220    int y, y_start, dd, ds;
 221    uint8_t *d, *s;
 222    void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
 223
 224    if (surface_bits_per_pixel(surface) == 0) {
 225        return;
 226    }
 227
 228    page = 0;
 229    y_start = -1;
 230    page_min = -1;
 231    page_max = 0;
 232    d = surface_data(surface);
 233    s = ts->vram;
 234    dd = surface_stride(surface);
 235    ds = 1024;
 236
 237    switch (surface_bits_per_pixel(surface)) {
 238    case 32:
 239        f = tcx_draw_line32;
 240        break;
 241    case 15:
 242    case 16:
 243        f = tcx_draw_line16;
 244        break;
 245    default:
 246    case 8:
 247        f = tcx_draw_line8;
 248        break;
 249    case 0:
 250        return;
 251    }
 252
 253    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
 254        if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE,
 255                                    DIRTY_MEMORY_VGA)) {
 256            if (y_start < 0)
 257                y_start = y;
 258            if (page < page_min)
 259                page_min = page;
 260            if (page > page_max)
 261                page_max = page;
 262            f(ts, d, s, ts->width);
 263            d += dd;
 264            s += ds;
 265            f(ts, d, s, ts->width);
 266            d += dd;
 267            s += ds;
 268            f(ts, d, s, ts->width);
 269            d += dd;
 270            s += ds;
 271            f(ts, d, s, ts->width);
 272            d += dd;
 273            s += ds;
 274        } else {
 275            if (y_start >= 0) {
 276                /* flush to display */
 277                dpy_gfx_update(ts->con, 0, y_start,
 278                               ts->width, y - y_start);
 279                y_start = -1;
 280            }
 281            d += dd * 4;
 282            s += ds * 4;
 283        }
 284    }
 285    if (y_start >= 0) {
 286        /* flush to display */
 287        dpy_gfx_update(ts->con, 0, y_start,
 288                       ts->width, y - y_start);
 289    }
 290    /* reset modified pages */
 291    if (page_max >= page_min) {
 292        memory_region_reset_dirty(&ts->vram_mem,
 293                                  page_min,
 294                                  (page_max - page_min) + TARGET_PAGE_SIZE,
 295                                  DIRTY_MEMORY_VGA);
 296    }
 297}
 298
 299static void tcx24_update_display(void *opaque)
 300{
 301    TCXState *ts = opaque;
 302    DisplaySurface *surface = qemu_console_surface(ts->con);
 303    ram_addr_t page, page_min, page_max, cpage, page24;
 304    int y, y_start, dd, ds;
 305    uint8_t *d, *s;
 306    uint32_t *cptr, *s24;
 307
 308    if (surface_bits_per_pixel(surface) != 32) {
 309            return;
 310    }
 311
 312    page = 0;
 313    page24 = ts->vram24_offset;
 314    cpage = ts->cplane_offset;
 315    y_start = -1;
 316    page_min = -1;
 317    page_max = 0;
 318    d = surface_data(surface);
 319    s = ts->vram;
 320    s24 = ts->vram24;
 321    cptr = ts->cplane;
 322    dd = surface_stride(surface);
 323    ds = 1024;
 324
 325    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
 326            page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
 327        if (check_dirty(ts, page, page24, cpage)) {
 328            if (y_start < 0)
 329                y_start = y;
 330            if (page < page_min)
 331                page_min = page;
 332            if (page > page_max)
 333                page_max = page;
 334            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
 335            d += dd;
 336            s += ds;
 337            cptr += ds;
 338            s24 += ds;
 339            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
 340            d += dd;
 341            s += ds;
 342            cptr += ds;
 343            s24 += ds;
 344            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
 345            d += dd;
 346            s += ds;
 347            cptr += ds;
 348            s24 += ds;
 349            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
 350            d += dd;
 351            s += ds;
 352            cptr += ds;
 353            s24 += ds;
 354        } else {
 355            if (y_start >= 0) {
 356                /* flush to display */
 357                dpy_gfx_update(ts->con, 0, y_start,
 358                               ts->width, y - y_start);
 359                y_start = -1;
 360            }
 361            d += dd * 4;
 362            s += ds * 4;
 363            cptr += ds * 4;
 364            s24 += ds * 4;
 365        }
 366    }
 367    if (y_start >= 0) {
 368        /* flush to display */
 369        dpy_gfx_update(ts->con, 0, y_start,
 370                       ts->width, y - y_start);
 371    }
 372    /* reset modified pages */
 373    if (page_max >= page_min) {
 374        reset_dirty(ts, page_min, page_max, page24, cpage);
 375    }
 376}
 377
 378static void tcx_invalidate_display(void *opaque)
 379{
 380    TCXState *s = opaque;
 381
 382    tcx_set_dirty(s);
 383    qemu_console_resize(s->con, s->width, s->height);
 384}
 385
 386static void tcx24_invalidate_display(void *opaque)
 387{
 388    TCXState *s = opaque;
 389
 390    tcx_set_dirty(s);
 391    tcx24_set_dirty(s);
 392    qemu_console_resize(s->con, s->width, s->height);
 393}
 394
 395static int vmstate_tcx_post_load(void *opaque, int version_id)
 396{
 397    TCXState *s = opaque;
 398
 399    update_palette_entries(s, 0, 256);
 400    if (s->depth == 24) {
 401        tcx24_set_dirty(s);
 402    } else {
 403        tcx_set_dirty(s);
 404    }
 405
 406    return 0;
 407}
 408
 409static const VMStateDescription vmstate_tcx = {
 410    .name ="tcx",
 411    .version_id = 4,
 412    .minimum_version_id = 4,
 413    .minimum_version_id_old = 4,
 414    .post_load = vmstate_tcx_post_load,
 415    .fields      = (VMStateField []) {
 416        VMSTATE_UINT16(height, TCXState),
 417        VMSTATE_UINT16(width, TCXState),
 418        VMSTATE_UINT16(depth, TCXState),
 419        VMSTATE_BUFFER(r, TCXState),
 420        VMSTATE_BUFFER(g, TCXState),
 421        VMSTATE_BUFFER(b, TCXState),
 422        VMSTATE_UINT8(dac_index, TCXState),
 423        VMSTATE_UINT8(dac_state, TCXState),
 424        VMSTATE_END_OF_LIST()
 425    }
 426};
 427
 428static void tcx_reset(DeviceState *d)
 429{
 430    TCXState *s = TCX(d);
 431
 432    /* Initialize palette */
 433    memset(s->r, 0, 256);
 434    memset(s->g, 0, 256);
 435    memset(s->b, 0, 256);
 436    s->r[255] = s->g[255] = s->b[255] = 255;
 437    update_palette_entries(s, 0, 256);
 438    memset(s->vram, 0, MAXX*MAXY);
 439    memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4),
 440                              DIRTY_MEMORY_VGA);
 441    s->dac_index = 0;
 442    s->dac_state = 0;
 443}
 444
 445static uint64_t tcx_dac_readl(void *opaque, hwaddr addr,
 446                              unsigned size)
 447{
 448    return 0;
 449}
 450
 451static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val,
 452                           unsigned size)
 453{
 454    TCXState *s = opaque;
 455
 456    switch (addr) {
 457    case 0:
 458        s->dac_index = val >> 24;
 459        s->dac_state = 0;
 460        break;
 461    case 4:
 462        switch (s->dac_state) {
 463        case 0:
 464            s->r[s->dac_index] = val >> 24;
 465            update_palette_entries(s, s->dac_index, s->dac_index + 1);
 466            s->dac_state++;
 467            break;
 468        case 1:
 469            s->g[s->dac_index] = val >> 24;
 470            update_palette_entries(s, s->dac_index, s->dac_index + 1);
 471            s->dac_state++;
 472            break;
 473        case 2:
 474            s->b[s->dac_index] = val >> 24;
 475            update_palette_entries(s, s->dac_index, s->dac_index + 1);
 476            s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
 477        default:
 478            s->dac_state = 0;
 479            break;
 480        }
 481        break;
 482    default:
 483        break;
 484    }
 485}
 486
 487static const MemoryRegionOps tcx_dac_ops = {
 488    .read = tcx_dac_readl,
 489    .write = tcx_dac_writel,
 490    .endianness = DEVICE_NATIVE_ENDIAN,
 491    .valid = {
 492        .min_access_size = 4,
 493        .max_access_size = 4,
 494    },
 495};
 496
 497static uint64_t dummy_readl(void *opaque, hwaddr addr,
 498                            unsigned size)
 499{
 500    return 0;
 501}
 502
 503static void dummy_writel(void *opaque, hwaddr addr,
 504                         uint64_t val, unsigned size)
 505{
 506}
 507
 508static const MemoryRegionOps dummy_ops = {
 509    .read = dummy_readl,
 510    .write = dummy_writel,
 511    .endianness = DEVICE_NATIVE_ENDIAN,
 512    .valid = {
 513        .min_access_size = 4,
 514        .max_access_size = 4,
 515    },
 516};
 517
 518static const GraphicHwOps tcx_ops = {
 519    .invalidate = tcx_invalidate_display,
 520    .gfx_update = tcx_update_display,
 521};
 522
 523static const GraphicHwOps tcx24_ops = {
 524    .invalidate = tcx24_invalidate_display,
 525    .gfx_update = tcx24_update_display,
 526};
 527
 528static int tcx_init1(SysBusDevice *dev)
 529{
 530    TCXState *s = TCX(dev);
 531    ram_addr_t vram_offset = 0;
 532    int size;
 533    uint8_t *vram_base;
 534
 535    memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram",
 536                           s->vram_size * (1 + 4 + 4));
 537    vmstate_register_ram_global(&s->vram_mem);
 538    vram_base = memory_region_get_ram_ptr(&s->vram_mem);
 539
 540    /* 8-bit plane */
 541    s->vram = vram_base;
 542    size = s->vram_size;
 543    memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit",
 544                             &s->vram_mem, vram_offset, size);
 545    sysbus_init_mmio(dev, &s->vram_8bit);
 546    vram_offset += size;
 547    vram_base += size;
 548
 549    /* DAC */
 550    memory_region_init_io(&s->dac, OBJECT(s), &tcx_dac_ops, s,
 551                          "tcx.dac", TCX_DAC_NREGS);
 552    sysbus_init_mmio(dev, &s->dac);
 553
 554    /* TEC (dummy) */
 555    memory_region_init_io(&s->tec, OBJECT(s), &dummy_ops, s,
 556                          "tcx.tec", TCX_TEC_NREGS);
 557    sysbus_init_mmio(dev, &s->tec);
 558    /* THC: NetBSD writes here even with 8-bit display: dummy */
 559    memory_region_init_io(&s->thc24, OBJECT(s), &dummy_ops, s, "tcx.thc24",
 560                          TCX_THC_NREGS_24);
 561    sysbus_init_mmio(dev, &s->thc24);
 562
 563    if (s->depth == 24) {
 564        /* 24-bit plane */
 565        size = s->vram_size * 4;
 566        s->vram24 = (uint32_t *)vram_base;
 567        s->vram24_offset = vram_offset;
 568        memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit",
 569                                 &s->vram_mem, vram_offset, size);
 570        sysbus_init_mmio(dev, &s->vram_24bit);
 571        vram_offset += size;
 572        vram_base += size;
 573
 574        /* Control plane */
 575        size = s->vram_size * 4;
 576        s->cplane = (uint32_t *)vram_base;
 577        s->cplane_offset = vram_offset;
 578        memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane",
 579                                 &s->vram_mem, vram_offset, size);
 580        sysbus_init_mmio(dev, &s->vram_cplane);
 581
 582        s->con = graphic_console_init(DEVICE(dev), &tcx24_ops, s);
 583    } else {
 584        /* THC 8 bit (dummy) */
 585        memory_region_init_io(&s->thc8, OBJECT(s), &dummy_ops, s, "tcx.thc8",
 586                              TCX_THC_NREGS_8);
 587        sysbus_init_mmio(dev, &s->thc8);
 588
 589        s->con = graphic_console_init(DEVICE(dev), &tcx_ops, s);
 590    }
 591
 592    qemu_console_resize(s->con, s->width, s->height);
 593    return 0;
 594}
 595
 596static Property tcx_properties[] = {
 597    DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1),
 598    DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
 599    DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
 600    DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
 601    DEFINE_PROP_END_OF_LIST(),
 602};
 603
 604static void tcx_class_init(ObjectClass *klass, void *data)
 605{
 606    DeviceClass *dc = DEVICE_CLASS(klass);
 607    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 608
 609    k->init = tcx_init1;
 610    dc->reset = tcx_reset;
 611    dc->vmsd = &vmstate_tcx;
 612    dc->props = tcx_properties;
 613}
 614
 615static const TypeInfo tcx_info = {
 616    .name          = TYPE_TCX,
 617    .parent        = TYPE_SYS_BUS_DEVICE,
 618    .instance_size = sizeof(TCXState),
 619    .class_init    = tcx_class_init,
 620};
 621
 622static void tcx_register_types(void)
 623{
 624    type_register_static(&tcx_info);
 625}
 626
 627type_init(tcx_register_types)
 628