qemu/hw/display/pl110.c
<<
>>
Prefs
   1/*
   2 * Arm PrimeCell PL110 Color LCD Controller
   3 *
   4 * Copyright (c) 2005-2009 CodeSourcery.
   5 * Written by Paul Brook
   6 *
   7 * This code is licensed under the GNU LGPL
   8 */
   9
  10#include "qemu/osdep.h"
  11#include "hw/irq.h"
  12#include "hw/sysbus.h"
  13#include "migration/vmstate.h"
  14#include "ui/console.h"
  15#include "framebuffer.h"
  16#include "ui/pixel_ops.h"
  17#include "qemu/timer.h"
  18#include "qemu/log.h"
  19#include "qemu/module.h"
  20#include "qom/object.h"
  21
  22#define PL110_CR_EN   0x001
  23#define PL110_CR_BGR  0x100
  24#define PL110_CR_BEBO 0x200
  25#define PL110_CR_BEPO 0x400
  26#define PL110_CR_PWR  0x800
  27#define PL110_IE_NB   0x004
  28#define PL110_IE_VC   0x008
  29
  30enum pl110_bppmode
  31{
  32    BPP_1,
  33    BPP_2,
  34    BPP_4,
  35    BPP_8,
  36    BPP_16,
  37    BPP_32,
  38    BPP_16_565, /* PL111 only */
  39    BPP_12      /* PL111 only */
  40};
  41
  42
  43/* The Versatile/PB uses a slightly modified PL110 controller.  */
  44enum pl110_version
  45{
  46    VERSION_PL110,
  47    VERSION_PL110_VERSATILE,
  48    VERSION_PL111
  49};
  50
  51#define TYPE_PL110 "pl110"
  52OBJECT_DECLARE_SIMPLE_TYPE(PL110State, PL110)
  53
  54struct PL110State {
  55    SysBusDevice parent_obj;
  56
  57    MemoryRegion iomem;
  58    MemoryRegionSection fbsection;
  59    QemuConsole *con;
  60    QEMUTimer *vblank_timer;
  61
  62    int version;
  63    uint32_t timing[4];
  64    uint32_t cr;
  65    uint32_t upbase;
  66    uint32_t lpbase;
  67    uint32_t int_status;
  68    uint32_t int_mask;
  69    int cols;
  70    int rows;
  71    enum pl110_bppmode bpp;
  72    int invalidate;
  73    uint32_t mux_ctrl;
  74    uint32_t palette[256];
  75    uint32_t raw_palette[128];
  76    qemu_irq irq;
  77};
  78
  79static int vmstate_pl110_post_load(void *opaque, int version_id);
  80
  81static const VMStateDescription vmstate_pl110 = {
  82    .name = "pl110",
  83    .version_id = 2,
  84    .minimum_version_id = 1,
  85    .post_load = vmstate_pl110_post_load,
  86    .fields = (VMStateField[]) {
  87        VMSTATE_INT32(version, PL110State),
  88        VMSTATE_UINT32_ARRAY(timing, PL110State, 4),
  89        VMSTATE_UINT32(cr, PL110State),
  90        VMSTATE_UINT32(upbase, PL110State),
  91        VMSTATE_UINT32(lpbase, PL110State),
  92        VMSTATE_UINT32(int_status, PL110State),
  93        VMSTATE_UINT32(int_mask, PL110State),
  94        VMSTATE_INT32(cols, PL110State),
  95        VMSTATE_INT32(rows, PL110State),
  96        VMSTATE_UINT32(bpp, PL110State),
  97        VMSTATE_INT32(invalidate, PL110State),
  98        VMSTATE_UINT32_ARRAY(palette, PL110State, 256),
  99        VMSTATE_UINT32_ARRAY(raw_palette, PL110State, 128),
 100        VMSTATE_UINT32_V(mux_ctrl, PL110State, 2),
 101        VMSTATE_END_OF_LIST()
 102    }
 103};
 104
 105static const unsigned char pl110_id[] =
 106{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
 107
 108static const unsigned char pl111_id[] = {
 109    0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
 110};
 111
 112
 113/* Indexed by pl110_version */
 114static const unsigned char *idregs[] = {
 115    pl110_id,
 116    /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board
 117     * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware
 118     * itself has the same ID values as a stock PL110, and guests (in
 119     * particular Linux) rely on this. We emulate what the hardware does,
 120     * rather than what the docs claim it ought to do.
 121     */
 122    pl110_id,
 123    pl111_id
 124};
 125
 126#define COPY_PIXEL(to, from) do { *(uint32_t *)to = from; to += 4; } while (0)
 127
 128#undef RGB
 129#define BORDER bgr
 130#define ORDER 0
 131#include "pl110_template.h"
 132#define ORDER 1
 133#include "pl110_template.h"
 134#define ORDER 2
 135#include "pl110_template.h"
 136#undef BORDER
 137#define RGB
 138#define BORDER rgb
 139#define ORDER 0
 140#include "pl110_template.h"
 141#define ORDER 1
 142#include "pl110_template.h"
 143#define ORDER 2
 144#include "pl110_template.h"
 145#undef BORDER
 146
 147#undef COPY_PIXEL
 148
 149static drawfn pl110_draw_fn_32[48] = {
 150    pl110_draw_line1_lblp_bgr,
 151    pl110_draw_line2_lblp_bgr,
 152    pl110_draw_line4_lblp_bgr,
 153    pl110_draw_line8_lblp_bgr,
 154    pl110_draw_line16_555_lblp_bgr,
 155    pl110_draw_line32_lblp_bgr,
 156    pl110_draw_line16_lblp_bgr,
 157    pl110_draw_line12_lblp_bgr,
 158
 159    pl110_draw_line1_bbbp_bgr,
 160    pl110_draw_line2_bbbp_bgr,
 161    pl110_draw_line4_bbbp_bgr,
 162    pl110_draw_line8_bbbp_bgr,
 163    pl110_draw_line16_555_bbbp_bgr,
 164    pl110_draw_line32_bbbp_bgr,
 165    pl110_draw_line16_bbbp_bgr,
 166    pl110_draw_line12_bbbp_bgr,
 167
 168    pl110_draw_line1_lbbp_bgr,
 169    pl110_draw_line2_lbbp_bgr,
 170    pl110_draw_line4_lbbp_bgr,
 171    pl110_draw_line8_lbbp_bgr,
 172    pl110_draw_line16_555_lbbp_bgr,
 173    pl110_draw_line32_lbbp_bgr,
 174    pl110_draw_line16_lbbp_bgr,
 175    pl110_draw_line12_lbbp_bgr,
 176
 177    pl110_draw_line1_lblp_rgb,
 178    pl110_draw_line2_lblp_rgb,
 179    pl110_draw_line4_lblp_rgb,
 180    pl110_draw_line8_lblp_rgb,
 181    pl110_draw_line16_555_lblp_rgb,
 182    pl110_draw_line32_lblp_rgb,
 183    pl110_draw_line16_lblp_rgb,
 184    pl110_draw_line12_lblp_rgb,
 185
 186    pl110_draw_line1_bbbp_rgb,
 187    pl110_draw_line2_bbbp_rgb,
 188    pl110_draw_line4_bbbp_rgb,
 189    pl110_draw_line8_bbbp_rgb,
 190    pl110_draw_line16_555_bbbp_rgb,
 191    pl110_draw_line32_bbbp_rgb,
 192    pl110_draw_line16_bbbp_rgb,
 193    pl110_draw_line12_bbbp_rgb,
 194
 195    pl110_draw_line1_lbbp_rgb,
 196    pl110_draw_line2_lbbp_rgb,
 197    pl110_draw_line4_lbbp_rgb,
 198    pl110_draw_line8_lbbp_rgb,
 199    pl110_draw_line16_555_lbbp_rgb,
 200    pl110_draw_line32_lbbp_rgb,
 201    pl110_draw_line16_lbbp_rgb,
 202    pl110_draw_line12_lbbp_rgb,
 203};
 204
 205static int pl110_enabled(PL110State *s)
 206{
 207  return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
 208}
 209
 210static void pl110_update_display(void *opaque)
 211{
 212    PL110State *s = (PL110State *)opaque;
 213    SysBusDevice *sbd;
 214    DisplaySurface *surface = qemu_console_surface(s->con);
 215    drawfn fn;
 216    int src_width;
 217    int bpp_offset;
 218    int first;
 219    int last;
 220
 221    if (!pl110_enabled(s)) {
 222        return;
 223    }
 224
 225    sbd = SYS_BUS_DEVICE(s);
 226
 227    if (s->cr & PL110_CR_BGR)
 228        bpp_offset = 0;
 229    else
 230        bpp_offset = 24;
 231
 232    if ((s->version != VERSION_PL111) && (s->bpp == BPP_16)) {
 233        /* The PL110's native 16 bit mode is 5551; however
 234         * most boards with a PL110 implement an external
 235         * mux which allows bits to be reshuffled to give
 236         * 565 format. The mux is typically controlled by
 237         * an external system register.
 238         * This is controlled by a GPIO input pin
 239         * so boards can wire it up to their register.
 240         *
 241         * The PL111 straightforwardly implements both
 242         * 5551 and 565 under control of the bpp field
 243         * in the LCDControl register.
 244         */
 245        switch (s->mux_ctrl) {
 246        case 3: /* 565 BGR */
 247            bpp_offset = (BPP_16_565 - BPP_16);
 248            break;
 249        case 1: /* 5551 */
 250            break;
 251        case 0: /* 888; also if we have loaded vmstate from an old version */
 252        case 2: /* 565 RGB */
 253        default:
 254            /* treat as 565 but honour BGR bit */
 255            bpp_offset += (BPP_16_565 - BPP_16);
 256            break;
 257        }
 258    }
 259
 260    if (s->cr & PL110_CR_BEBO) {
 261        fn = pl110_draw_fn_32[s->bpp + 8 + bpp_offset];
 262    } else if (s->cr & PL110_CR_BEPO) {
 263        fn = pl110_draw_fn_32[s->bpp + 16 + bpp_offset];
 264    } else {
 265        fn = pl110_draw_fn_32[s->bpp + bpp_offset];
 266    }
 267
 268    src_width = s->cols;
 269    switch (s->bpp) {
 270    case BPP_1:
 271        src_width >>= 3;
 272        break;
 273    case BPP_2:
 274        src_width >>= 2;
 275        break;
 276    case BPP_4:
 277        src_width >>= 1;
 278        break;
 279    case BPP_8:
 280        break;
 281    case BPP_16:
 282    case BPP_16_565:
 283    case BPP_12:
 284        src_width <<= 1;
 285        break;
 286    case BPP_32:
 287        src_width <<= 2;
 288        break;
 289    }
 290    first = 0;
 291    if (s->invalidate) {
 292        framebuffer_update_memory_section(&s->fbsection,
 293                                          sysbus_address_space(sbd),
 294                                          s->upbase,
 295                                          s->rows, src_width);
 296    }
 297
 298    framebuffer_update_display(surface, &s->fbsection,
 299                               s->cols, s->rows,
 300                               src_width, s->cols * 4, 0,
 301                               s->invalidate,
 302                               fn, s->palette,
 303                               &first, &last);
 304
 305    if (first >= 0) {
 306        dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
 307    }
 308    s->invalidate = 0;
 309}
 310
 311static void pl110_invalidate_display(void * opaque)
 312{
 313    PL110State *s = (PL110State *)opaque;
 314    s->invalidate = 1;
 315    if (pl110_enabled(s)) {
 316        qemu_console_resize(s->con, s->cols, s->rows);
 317    }
 318}
 319
 320static void pl110_update_palette(PL110State *s, int n)
 321{
 322    DisplaySurface *surface = qemu_console_surface(s->con);
 323    int i;
 324    uint32_t raw;
 325    unsigned int r, g, b;
 326
 327    raw = s->raw_palette[n];
 328    n <<= 1;
 329    for (i = 0; i < 2; i++) {
 330        r = (raw & 0x1f) << 3;
 331        raw >>= 5;
 332        g = (raw & 0x1f) << 3;
 333        raw >>= 5;
 334        b = (raw & 0x1f) << 3;
 335        /* The I bit is ignored.  */
 336        raw >>= 6;
 337        switch (surface_bits_per_pixel(surface)) {
 338        case 8:
 339            s->palette[n] = rgb_to_pixel8(r, g, b);
 340            break;
 341        case 15:
 342            s->palette[n] = rgb_to_pixel15(r, g, b);
 343            break;
 344        case 16:
 345            s->palette[n] = rgb_to_pixel16(r, g, b);
 346            break;
 347        case 24:
 348        case 32:
 349            s->palette[n] = rgb_to_pixel32(r, g, b);
 350            break;
 351        }
 352        n++;
 353    }
 354}
 355
 356static void pl110_resize(PL110State *s, int width, int height)
 357{
 358    if (width != s->cols || height != s->rows) {
 359        if (pl110_enabled(s)) {
 360            qemu_console_resize(s->con, width, height);
 361        }
 362    }
 363    s->cols = width;
 364    s->rows = height;
 365}
 366
 367/* Update interrupts.  */
 368static void pl110_update(PL110State *s)
 369{
 370    /* Raise IRQ if enabled and any status bit is 1 */
 371    if (s->int_status & s->int_mask) {
 372        qemu_irq_raise(s->irq);
 373    } else {
 374        qemu_irq_lower(s->irq);
 375    }
 376}
 377
 378static void pl110_vblank_interrupt(void *opaque)
 379{
 380    PL110State *s = opaque;
 381
 382    /* Fire the vertical compare and next base IRQs and re-arm */
 383    s->int_status |= (PL110_IE_NB | PL110_IE_VC);
 384    timer_mod(s->vblank_timer,
 385              qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
 386                                NANOSECONDS_PER_SECOND / 60);
 387    pl110_update(s);
 388}
 389
 390static uint64_t pl110_read(void *opaque, hwaddr offset,
 391                           unsigned size)
 392{
 393    PL110State *s = (PL110State *)opaque;
 394
 395    if (offset >= 0xfe0 && offset < 0x1000) {
 396        return idregs[s->version][(offset - 0xfe0) >> 2];
 397    }
 398    if (offset >= 0x200 && offset < 0x400) {
 399        return s->raw_palette[(offset - 0x200) >> 2];
 400    }
 401    switch (offset >> 2) {
 402    case 0: /* LCDTiming0 */
 403        return s->timing[0];
 404    case 1: /* LCDTiming1 */
 405        return s->timing[1];
 406    case 2: /* LCDTiming2 */
 407        return s->timing[2];
 408    case 3: /* LCDTiming3 */
 409        return s->timing[3];
 410    case 4: /* LCDUPBASE */
 411        return s->upbase;
 412    case 5: /* LCDLPBASE */
 413        return s->lpbase;
 414    case 6: /* LCDIMSC */
 415        if (s->version != VERSION_PL110) {
 416            return s->cr;
 417        }
 418        return s->int_mask;
 419    case 7: /* LCDControl */
 420        if (s->version != VERSION_PL110) {
 421            return s->int_mask;
 422        }
 423        return s->cr;
 424    case 8: /* LCDRIS */
 425        return s->int_status;
 426    case 9: /* LCDMIS */
 427        return s->int_status & s->int_mask;
 428    case 11: /* LCDUPCURR */
 429        /* TODO: Implement vertical refresh.  */
 430        return s->upbase;
 431    case 12: /* LCDLPCURR */
 432        return s->lpbase;
 433    default:
 434        qemu_log_mask(LOG_GUEST_ERROR,
 435                      "pl110_read: Bad offset %x\n", (int)offset);
 436        return 0;
 437    }
 438}
 439
 440static void pl110_write(void *opaque, hwaddr offset,
 441                        uint64_t val, unsigned size)
 442{
 443    PL110State *s = (PL110State *)opaque;
 444    int n;
 445
 446    /* For simplicity invalidate the display whenever a control register
 447       is written to.  */
 448    s->invalidate = 1;
 449    if (offset >= 0x200 && offset < 0x400) {
 450        /* Palette.  */
 451        n = (offset - 0x200) >> 2;
 452        s->raw_palette[(offset - 0x200) >> 2] = val;
 453        pl110_update_palette(s, n);
 454        return;
 455    }
 456    switch (offset >> 2) {
 457    case 0: /* LCDTiming0 */
 458        s->timing[0] = val;
 459        n = ((val & 0xfc) + 4) * 4;
 460        pl110_resize(s, n, s->rows);
 461        break;
 462    case 1: /* LCDTiming1 */
 463        s->timing[1] = val;
 464        n = (val & 0x3ff) + 1;
 465        pl110_resize(s, s->cols, n);
 466        break;
 467    case 2: /* LCDTiming2 */
 468        s->timing[2] = val;
 469        break;
 470    case 3: /* LCDTiming3 */
 471        s->timing[3] = val;
 472        break;
 473    case 4: /* LCDUPBASE */
 474        s->upbase = val;
 475        break;
 476    case 5: /* LCDLPBASE */
 477        s->lpbase = val;
 478        break;
 479    case 6: /* LCDIMSC */
 480        if (s->version != VERSION_PL110) {
 481            goto control;
 482        }
 483    imsc:
 484        s->int_mask = val;
 485        pl110_update(s);
 486        break;
 487    case 7: /* LCDControl */
 488        if (s->version != VERSION_PL110) {
 489            goto imsc;
 490        }
 491    control:
 492        s->cr = val;
 493        s->bpp = (val >> 1) & 7;
 494        if (pl110_enabled(s)) {
 495            qemu_console_resize(s->con, s->cols, s->rows);
 496            timer_mod(s->vblank_timer,
 497                      qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
 498                                        NANOSECONDS_PER_SECOND / 60);
 499        } else {
 500            timer_del(s->vblank_timer);
 501        }
 502        break;
 503    case 10: /* LCDICR */
 504        s->int_status &= ~val;
 505        pl110_update(s);
 506        break;
 507    default:
 508        qemu_log_mask(LOG_GUEST_ERROR,
 509                      "pl110_write: Bad offset %x\n", (int)offset);
 510    }
 511}
 512
 513static const MemoryRegionOps pl110_ops = {
 514    .read = pl110_read,
 515    .write = pl110_write,
 516    .endianness = DEVICE_NATIVE_ENDIAN,
 517};
 518
 519static void pl110_mux_ctrl_set(void *opaque, int line, int level)
 520{
 521    PL110State *s = (PL110State *)opaque;
 522    s->mux_ctrl = level;
 523}
 524
 525static int vmstate_pl110_post_load(void *opaque, int version_id)
 526{
 527    PL110State *s = opaque;
 528    /* Make sure we redraw, and at the right size */
 529    pl110_invalidate_display(s);
 530    return 0;
 531}
 532
 533static const GraphicHwOps pl110_gfx_ops = {
 534    .invalidate  = pl110_invalidate_display,
 535    .gfx_update  = pl110_update_display,
 536};
 537
 538static void pl110_realize(DeviceState *dev, Error **errp)
 539{
 540    PL110State *s = PL110(dev);
 541    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 542
 543    memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000);
 544    sysbus_init_mmio(sbd, &s->iomem);
 545    sysbus_init_irq(sbd, &s->irq);
 546    s->vblank_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
 547                                   pl110_vblank_interrupt, s);
 548    qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1);
 549    s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s);
 550}
 551
 552static void pl110_init(Object *obj)
 553{
 554    PL110State *s = PL110(obj);
 555
 556    s->version = VERSION_PL110;
 557}
 558
 559static void pl110_versatile_init(Object *obj)
 560{
 561    PL110State *s = PL110(obj);
 562
 563    s->version = VERSION_PL110_VERSATILE;
 564}
 565
 566static void pl111_init(Object *obj)
 567{
 568    PL110State *s = PL110(obj);
 569
 570    s->version = VERSION_PL111;
 571}
 572
 573static void pl110_class_init(ObjectClass *klass, void *data)
 574{
 575    DeviceClass *dc = DEVICE_CLASS(klass);
 576
 577    set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
 578    dc->vmsd = &vmstate_pl110;
 579    dc->realize = pl110_realize;
 580}
 581
 582static const TypeInfo pl110_info = {
 583    .name          = TYPE_PL110,
 584    .parent        = TYPE_SYS_BUS_DEVICE,
 585    .instance_size = sizeof(PL110State),
 586    .instance_init = pl110_init,
 587    .class_init    = pl110_class_init,
 588};
 589
 590static const TypeInfo pl110_versatile_info = {
 591    .name          = "pl110_versatile",
 592    .parent        = TYPE_PL110,
 593    .instance_init = pl110_versatile_init,
 594};
 595
 596static const TypeInfo pl111_info = {
 597    .name          = "pl111",
 598    .parent        = TYPE_PL110,
 599    .instance_init = pl111_init,
 600};
 601
 602static void pl110_register_types(void)
 603{
 604    type_register_static(&pl110_info);
 605    type_register_static(&pl110_versatile_info);
 606    type_register_static(&pl111_info);
 607}
 608
 609type_init(pl110_register_types)
 610