1bdd5003aSpbrook /* 2bdd5003aSpbrook * Arm PrimeCell PL110 Color LCD Controller 3bdd5003aSpbrook * 42e9bdce5SPaul Brook * Copyright (c) 2005-2009 CodeSourcery. 5bdd5003aSpbrook * Written by Paul Brook 6bdd5003aSpbrook * 78e31bf38SMatthew Fernandez * This code is licensed under the GNU LGPL 8bdd5003aSpbrook */ 9bdd5003aSpbrook 108ef94f0bSPeter Maydell #include "qemu/osdep.h" 1164552b6bSMarkus Armbruster #include "hw/irq.h" 1283c9f4caSPaolo Bonzini #include "hw/sysbus.h" 13*d6454270SMarkus Armbruster #include "migration/vmstate.h" 1428ecbaeeSPaolo Bonzini #include "ui/console.h" 1547b43a1fSPaolo Bonzini #include "framebuffer.h" 1628ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 1724da047aSLinus Walleij #include "qemu/timer.h" 1803dd024fSPaolo Bonzini #include "qemu/log.h" 190b8fa32fSMarkus Armbruster #include "qemu/module.h" 20bdd5003aSpbrook 21bdd5003aSpbrook #define PL110_CR_EN 0x001 22e9c05b42Sbalrog #define PL110_CR_BGR 0x100 23bdd5003aSpbrook #define PL110_CR_BEBO 0x200 24bdd5003aSpbrook #define PL110_CR_BEPO 0x400 25bdd5003aSpbrook #define PL110_CR_PWR 0x800 2624da047aSLinus Walleij #define PL110_IE_NB 0x004 2724da047aSLinus Walleij #define PL110_IE_VC 0x008 28bdd5003aSpbrook 29bdd5003aSpbrook enum pl110_bppmode 30bdd5003aSpbrook { 31bdd5003aSpbrook BPP_1, 32bdd5003aSpbrook BPP_2, 33bdd5003aSpbrook BPP_4, 34bdd5003aSpbrook BPP_8, 35bdd5003aSpbrook BPP_16, 364fbf5556SPeter Maydell BPP_32, 374fbf5556SPeter Maydell BPP_16_565, /* PL111 only */ 384fbf5556SPeter Maydell BPP_12 /* PL111 only */ 394fbf5556SPeter Maydell }; 404fbf5556SPeter Maydell 414fbf5556SPeter Maydell 424fbf5556SPeter Maydell /* The Versatile/PB uses a slightly modified PL110 controller. */ 434fbf5556SPeter Maydell enum pl110_version 444fbf5556SPeter Maydell { 454fbf5556SPeter Maydell PL110, 464fbf5556SPeter Maydell PL110_VERSATILE, 474fbf5556SPeter Maydell PL111 48bdd5003aSpbrook }; 49bdd5003aSpbrook 505d7a11e4SAndreas Färber #define TYPE_PL110 "pl110" 515d7a11e4SAndreas Färber #define PL110(obj) OBJECT_CHECK(PL110State, (obj), TYPE_PL110) 525d7a11e4SAndreas Färber 53513960eaSAndreas Färber typedef struct PL110State { 545d7a11e4SAndreas Färber SysBusDevice parent_obj; 555d7a11e4SAndreas Färber 561a6b31ceSAvi Kivity MemoryRegion iomem; 57c1076c3eSPaolo Bonzini MemoryRegionSection fbsection; 58c78f7137SGerd Hoffmann QemuConsole *con; 5924da047aSLinus Walleij QEMUTimer *vblank_timer; 60c60e08d9Spbrook 614fbf5556SPeter Maydell int version; 62bdd5003aSpbrook uint32_t timing[4]; 63bdd5003aSpbrook uint32_t cr; 64bdd5003aSpbrook uint32_t upbase; 65bdd5003aSpbrook uint32_t lpbase; 66bdd5003aSpbrook uint32_t int_status; 67bdd5003aSpbrook uint32_t int_mask; 68bdd5003aSpbrook int cols; 69bdd5003aSpbrook int rows; 70bdd5003aSpbrook enum pl110_bppmode bpp; 71bdd5003aSpbrook int invalidate; 72242ea2c6SPeter Maydell uint32_t mux_ctrl; 736e4c0d1fSPeter Maydell uint32_t palette[256]; 746e4c0d1fSPeter Maydell uint32_t raw_palette[128]; 75d537cf6cSpbrook qemu_irq irq; 76513960eaSAndreas Färber } PL110State; 77bdd5003aSpbrook 78128939a9SPeter Maydell static int vmstate_pl110_post_load(void *opaque, int version_id); 79128939a9SPeter Maydell 808c60d065SPeter Maydell static const VMStateDescription vmstate_pl110 = { 818c60d065SPeter Maydell .name = "pl110", 82242ea2c6SPeter Maydell .version_id = 2, 838c60d065SPeter Maydell .minimum_version_id = 1, 84128939a9SPeter Maydell .post_load = vmstate_pl110_post_load, 858c60d065SPeter Maydell .fields = (VMStateField[]) { 86513960eaSAndreas Färber VMSTATE_INT32(version, PL110State), 87513960eaSAndreas Färber VMSTATE_UINT32_ARRAY(timing, PL110State, 4), 88513960eaSAndreas Färber VMSTATE_UINT32(cr, PL110State), 89513960eaSAndreas Färber VMSTATE_UINT32(upbase, PL110State), 90513960eaSAndreas Färber VMSTATE_UINT32(lpbase, PL110State), 91513960eaSAndreas Färber VMSTATE_UINT32(int_status, PL110State), 92513960eaSAndreas Färber VMSTATE_UINT32(int_mask, PL110State), 93513960eaSAndreas Färber VMSTATE_INT32(cols, PL110State), 94513960eaSAndreas Färber VMSTATE_INT32(rows, PL110State), 95513960eaSAndreas Färber VMSTATE_UINT32(bpp, PL110State), 96513960eaSAndreas Färber VMSTATE_INT32(invalidate, PL110State), 97513960eaSAndreas Färber VMSTATE_UINT32_ARRAY(palette, PL110State, 256), 98513960eaSAndreas Färber VMSTATE_UINT32_ARRAY(raw_palette, PL110State, 128), 99513960eaSAndreas Färber VMSTATE_UINT32_V(mux_ctrl, PL110State, 2), 1008c60d065SPeter Maydell VMSTATE_END_OF_LIST() 1018c60d065SPeter Maydell } 1028c60d065SPeter Maydell }; 1038c60d065SPeter Maydell 104bdd5003aSpbrook static const unsigned char pl110_id[] = 105bdd5003aSpbrook { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; 106bdd5003aSpbrook 1074fbf5556SPeter Maydell static const unsigned char pl111_id[] = { 1084fbf5556SPeter Maydell 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1 1094fbf5556SPeter Maydell }; 1104fbf5556SPeter Maydell 111031c44e4SPeter Maydell 1124fbf5556SPeter Maydell /* Indexed by pl110_version */ 1134fbf5556SPeter Maydell static const unsigned char *idregs[] = { 1144fbf5556SPeter Maydell pl110_id, 115031c44e4SPeter Maydell /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board 116031c44e4SPeter Maydell * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware 117031c44e4SPeter Maydell * itself has the same ID values as a stock PL110, and guests (in 118031c44e4SPeter Maydell * particular Linux) rely on this. We emulate what the hardware does, 119031c44e4SPeter Maydell * rather than what the docs claim it ought to do. 120031c44e4SPeter Maydell */ 121031c44e4SPeter Maydell pl110_id, 1224fbf5556SPeter Maydell pl111_id 1234fbf5556SPeter Maydell }; 1244fbf5556SPeter Maydell 125bdd5003aSpbrook #define BITS 8 12647b43a1fSPaolo Bonzini #include "pl110_template.h" 127bdd5003aSpbrook #define BITS 15 12847b43a1fSPaolo Bonzini #include "pl110_template.h" 129bdd5003aSpbrook #define BITS 16 13047b43a1fSPaolo Bonzini #include "pl110_template.h" 131bdd5003aSpbrook #define BITS 24 13247b43a1fSPaolo Bonzini #include "pl110_template.h" 133bdd5003aSpbrook #define BITS 32 13447b43a1fSPaolo Bonzini #include "pl110_template.h" 135bdd5003aSpbrook 136513960eaSAndreas Färber static int pl110_enabled(PL110State *s) 137bdd5003aSpbrook { 138bdd5003aSpbrook return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR); 139bdd5003aSpbrook } 140bdd5003aSpbrook 14195219897Spbrook static void pl110_update_display(void *opaque) 142bdd5003aSpbrook { 143513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 1445d7a11e4SAndreas Färber SysBusDevice *sbd; 145c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 146bdd5003aSpbrook drawfn* fntable; 147bdd5003aSpbrook drawfn fn; 148bdd5003aSpbrook int dest_width; 149bdd5003aSpbrook int src_width; 150e9c05b42Sbalrog int bpp_offset; 151714fa308Spbrook int first; 152714fa308Spbrook int last; 153bdd5003aSpbrook 1545d7a11e4SAndreas Färber if (!pl110_enabled(s)) { 155bdd5003aSpbrook return; 1565d7a11e4SAndreas Färber } 1575d7a11e4SAndreas Färber 1585d7a11e4SAndreas Färber sbd = SYS_BUS_DEVICE(s); 159bdd5003aSpbrook 160c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 161af2f6733Spbrook case 0: 162af2f6733Spbrook return; 163bdd5003aSpbrook case 8: 164bdd5003aSpbrook fntable = pl110_draw_fn_8; 165bdd5003aSpbrook dest_width = 1; 166bdd5003aSpbrook break; 167bdd5003aSpbrook case 15: 168bdd5003aSpbrook fntable = pl110_draw_fn_15; 169bdd5003aSpbrook dest_width = 2; 170bdd5003aSpbrook break; 171bdd5003aSpbrook case 16: 172bdd5003aSpbrook fntable = pl110_draw_fn_16; 173bdd5003aSpbrook dest_width = 2; 174bdd5003aSpbrook break; 175bdd5003aSpbrook case 24: 176bdd5003aSpbrook fntable = pl110_draw_fn_24; 177bdd5003aSpbrook dest_width = 3; 178bdd5003aSpbrook break; 179bdd5003aSpbrook case 32: 180bdd5003aSpbrook fntable = pl110_draw_fn_32; 181bdd5003aSpbrook dest_width = 4; 182bdd5003aSpbrook break; 183bdd5003aSpbrook default: 184af2f6733Spbrook fprintf(stderr, "pl110: Bad color depth\n"); 185bdd5003aSpbrook exit(1); 186bdd5003aSpbrook } 187e9c05b42Sbalrog if (s->cr & PL110_CR_BGR) 188e9c05b42Sbalrog bpp_offset = 0; 189bdd5003aSpbrook else 1904fbf5556SPeter Maydell bpp_offset = 24; 1914fbf5556SPeter Maydell 1924fbf5556SPeter Maydell if ((s->version != PL111) && (s->bpp == BPP_16)) { 1934fbf5556SPeter Maydell /* The PL110's native 16 bit mode is 5551; however 1944fbf5556SPeter Maydell * most boards with a PL110 implement an external 1954fbf5556SPeter Maydell * mux which allows bits to be reshuffled to give 1964fbf5556SPeter Maydell * 565 format. The mux is typically controlled by 1974fbf5556SPeter Maydell * an external system register. 198242ea2c6SPeter Maydell * This is controlled by a GPIO input pin 1994fbf5556SPeter Maydell * so boards can wire it up to their register. 2004fbf5556SPeter Maydell * 2014fbf5556SPeter Maydell * The PL111 straightforwardly implements both 2024fbf5556SPeter Maydell * 5551 and 565 under control of the bpp field 2034fbf5556SPeter Maydell * in the LCDControl register. 2044fbf5556SPeter Maydell */ 205242ea2c6SPeter Maydell switch (s->mux_ctrl) { 206242ea2c6SPeter Maydell case 3: /* 565 BGR */ 207242ea2c6SPeter Maydell bpp_offset = (BPP_16_565 - BPP_16); 208242ea2c6SPeter Maydell break; 209242ea2c6SPeter Maydell case 1: /* 5551 */ 210242ea2c6SPeter Maydell break; 211242ea2c6SPeter Maydell case 0: /* 888; also if we have loaded vmstate from an old version */ 212242ea2c6SPeter Maydell case 2: /* 565 RGB */ 213242ea2c6SPeter Maydell default: 214242ea2c6SPeter Maydell /* treat as 565 but honour BGR bit */ 2154fbf5556SPeter Maydell bpp_offset += (BPP_16_565 - BPP_16); 216242ea2c6SPeter Maydell break; 217242ea2c6SPeter Maydell } 2184fbf5556SPeter Maydell } 219e9c05b42Sbalrog 220e9c05b42Sbalrog if (s->cr & PL110_CR_BEBO) 2214fbf5556SPeter Maydell fn = fntable[s->bpp + 8 + bpp_offset]; 222e9c05b42Sbalrog else if (s->cr & PL110_CR_BEPO) 2234fbf5556SPeter Maydell fn = fntable[s->bpp + 16 + bpp_offset]; 224e9c05b42Sbalrog else 225e9c05b42Sbalrog fn = fntable[s->bpp + bpp_offset]; 226bdd5003aSpbrook 227bdd5003aSpbrook src_width = s->cols; 228bdd5003aSpbrook switch (s->bpp) { 229bdd5003aSpbrook case BPP_1: 230bdd5003aSpbrook src_width >>= 3; 231bdd5003aSpbrook break; 232bdd5003aSpbrook case BPP_2: 233bdd5003aSpbrook src_width >>= 2; 234bdd5003aSpbrook break; 235bdd5003aSpbrook case BPP_4: 236bdd5003aSpbrook src_width >>= 1; 237bdd5003aSpbrook break; 238bdd5003aSpbrook case BPP_8: 239bdd5003aSpbrook break; 240bdd5003aSpbrook case BPP_16: 2414fbf5556SPeter Maydell case BPP_16_565: 2424fbf5556SPeter Maydell case BPP_12: 243bdd5003aSpbrook src_width <<= 1; 244bdd5003aSpbrook break; 245bdd5003aSpbrook case BPP_32: 246bdd5003aSpbrook src_width <<= 2; 247bdd5003aSpbrook break; 248bdd5003aSpbrook } 249bdd5003aSpbrook dest_width *= s->cols; 250714fa308Spbrook first = 0; 251c1076c3eSPaolo Bonzini if (s->invalidate) { 252c1076c3eSPaolo Bonzini framebuffer_update_memory_section(&s->fbsection, 253c1076c3eSPaolo Bonzini sysbus_address_space(sbd), 254c1076c3eSPaolo Bonzini s->upbase, 255c1076c3eSPaolo Bonzini s->rows, src_width); 256c1076c3eSPaolo Bonzini } 257c1076c3eSPaolo Bonzini 258c1076c3eSPaolo Bonzini framebuffer_update_display(surface, &s->fbsection, 259c1076c3eSPaolo Bonzini s->cols, s->rows, 260714fa308Spbrook src_width, dest_width, 0, 261714fa308Spbrook s->invalidate, 2626e4c0d1fSPeter Maydell fn, s->palette, 263714fa308Spbrook &first, &last); 264c1076c3eSPaolo Bonzini 265714fa308Spbrook if (first >= 0) { 266c78f7137SGerd Hoffmann dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1); 267bdd5003aSpbrook } 268714fa308Spbrook s->invalidate = 0; 269714fa308Spbrook } 270bdd5003aSpbrook 27195219897Spbrook static void pl110_invalidate_display(void * opaque) 272bdd5003aSpbrook { 273513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 274bdd5003aSpbrook s->invalidate = 1; 275bfdb3629SBlue Swirl if (pl110_enabled(s)) { 276c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->cols, s->rows); 277bfdb3629SBlue Swirl } 278bdd5003aSpbrook } 279bdd5003aSpbrook 280513960eaSAndreas Färber static void pl110_update_palette(PL110State *s, int n) 281bdd5003aSpbrook { 282c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 283bdd5003aSpbrook int i; 284bdd5003aSpbrook uint32_t raw; 285bdd5003aSpbrook unsigned int r, g, b; 286bdd5003aSpbrook 2876e4c0d1fSPeter Maydell raw = s->raw_palette[n]; 288bdd5003aSpbrook n <<= 1; 289bdd5003aSpbrook for (i = 0; i < 2; i++) { 290bdd5003aSpbrook r = (raw & 0x1f) << 3; 291bdd5003aSpbrook raw >>= 5; 292bdd5003aSpbrook g = (raw & 0x1f) << 3; 293bdd5003aSpbrook raw >>= 5; 294bdd5003aSpbrook b = (raw & 0x1f) << 3; 295bdd5003aSpbrook /* The I bit is ignored. */ 296bdd5003aSpbrook raw >>= 6; 297c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 298bdd5003aSpbrook case 8: 2996e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel8(r, g, b); 300bdd5003aSpbrook break; 301bdd5003aSpbrook case 15: 3026e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel15(r, g, b); 303bdd5003aSpbrook break; 304bdd5003aSpbrook case 16: 3056e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel16(r, g, b); 306bdd5003aSpbrook break; 307bdd5003aSpbrook case 24: 308bdd5003aSpbrook case 32: 3096e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel32(r, g, b); 310bdd5003aSpbrook break; 311bdd5003aSpbrook } 312bdd5003aSpbrook n++; 313bdd5003aSpbrook } 314bdd5003aSpbrook } 315bdd5003aSpbrook 316513960eaSAndreas Färber static void pl110_resize(PL110State *s, int width, int height) 317bdd5003aSpbrook { 318bdd5003aSpbrook if (width != s->cols || height != s->rows) { 319bdd5003aSpbrook if (pl110_enabled(s)) { 320c78f7137SGerd Hoffmann qemu_console_resize(s->con, width, height); 321bdd5003aSpbrook } 322bdd5003aSpbrook } 323bdd5003aSpbrook s->cols = width; 324bdd5003aSpbrook s->rows = height; 325bdd5003aSpbrook } 326bdd5003aSpbrook 327bdd5003aSpbrook /* Update interrupts. */ 328513960eaSAndreas Färber static void pl110_update(PL110State *s) 329bdd5003aSpbrook { 33024da047aSLinus Walleij /* Raise IRQ if enabled and any status bit is 1 */ 33124da047aSLinus Walleij if (s->int_status & s->int_mask) { 33224da047aSLinus Walleij qemu_irq_raise(s->irq); 33324da047aSLinus Walleij } else { 33424da047aSLinus Walleij qemu_irq_lower(s->irq); 33524da047aSLinus Walleij } 33624da047aSLinus Walleij } 33724da047aSLinus Walleij 33824da047aSLinus Walleij static void pl110_vblank_interrupt(void *opaque) 33924da047aSLinus Walleij { 34024da047aSLinus Walleij PL110State *s = opaque; 34124da047aSLinus Walleij 34224da047aSLinus Walleij /* Fire the vertical compare and next base IRQs and re-arm */ 34324da047aSLinus Walleij s->int_status |= (PL110_IE_NB | PL110_IE_VC); 34424da047aSLinus Walleij timer_mod(s->vblank_timer, 34524da047aSLinus Walleij qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 34624da047aSLinus Walleij NANOSECONDS_PER_SECOND / 60); 34724da047aSLinus Walleij pl110_update(s); 348bdd5003aSpbrook } 349bdd5003aSpbrook 350a8170e5eSAvi Kivity static uint64_t pl110_read(void *opaque, hwaddr offset, 3511a6b31ceSAvi Kivity unsigned size) 352bdd5003aSpbrook { 353513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 354bdd5003aSpbrook 355bdd5003aSpbrook if (offset >= 0xfe0 && offset < 0x1000) { 3564fbf5556SPeter Maydell return idregs[s->version][(offset - 0xfe0) >> 2]; 357bdd5003aSpbrook } 358bdd5003aSpbrook if (offset >= 0x200 && offset < 0x400) { 3596e4c0d1fSPeter Maydell return s->raw_palette[(offset - 0x200) >> 2]; 360bdd5003aSpbrook } 361bdd5003aSpbrook switch (offset >> 2) { 362bdd5003aSpbrook case 0: /* LCDTiming0 */ 363bdd5003aSpbrook return s->timing[0]; 364bdd5003aSpbrook case 1: /* LCDTiming1 */ 365bdd5003aSpbrook return s->timing[1]; 366bdd5003aSpbrook case 2: /* LCDTiming2 */ 367bdd5003aSpbrook return s->timing[2]; 368bdd5003aSpbrook case 3: /* LCDTiming3 */ 369bdd5003aSpbrook return s->timing[3]; 370bdd5003aSpbrook case 4: /* LCDUPBASE */ 371bdd5003aSpbrook return s->upbase; 372bdd5003aSpbrook case 5: /* LCDLPBASE */ 373bdd5003aSpbrook return s->lpbase; 374bdd5003aSpbrook case 6: /* LCDIMSC */ 3754fbf5556SPeter Maydell if (s->version != PL110) { 37664075cd7Spbrook return s->cr; 3774fbf5556SPeter Maydell } 378bdd5003aSpbrook return s->int_mask; 379bdd5003aSpbrook case 7: /* LCDControl */ 3804fbf5556SPeter Maydell if (s->version != PL110) { 38164075cd7Spbrook return s->int_mask; 3824fbf5556SPeter Maydell } 383bdd5003aSpbrook return s->cr; 384bdd5003aSpbrook case 8: /* LCDRIS */ 385bdd5003aSpbrook return s->int_status; 386bdd5003aSpbrook case 9: /* LCDMIS */ 387bdd5003aSpbrook return s->int_status & s->int_mask; 388bdd5003aSpbrook case 11: /* LCDUPCURR */ 389bdd5003aSpbrook /* TODO: Implement vertical refresh. */ 390bdd5003aSpbrook return s->upbase; 391bdd5003aSpbrook case 12: /* LCDLPCURR */ 392bdd5003aSpbrook return s->lpbase; 393bdd5003aSpbrook default: 394375cb560SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 395375cb560SPeter Maydell "pl110_read: Bad offset %x\n", (int)offset); 396bdd5003aSpbrook return 0; 397bdd5003aSpbrook } 398bdd5003aSpbrook } 399bdd5003aSpbrook 400a8170e5eSAvi Kivity static void pl110_write(void *opaque, hwaddr offset, 4011a6b31ceSAvi Kivity uint64_t val, unsigned size) 402bdd5003aSpbrook { 403513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 404bdd5003aSpbrook int n; 405bdd5003aSpbrook 406bdd5003aSpbrook /* For simplicity invalidate the display whenever a control register 40766a0a2cbSDong Xu Wang is written to. */ 408bdd5003aSpbrook s->invalidate = 1; 409bdd5003aSpbrook if (offset >= 0x200 && offset < 0x400) { 4106e4c0d1fSPeter Maydell /* Palette. */ 411bdd5003aSpbrook n = (offset - 0x200) >> 2; 4126e4c0d1fSPeter Maydell s->raw_palette[(offset - 0x200) >> 2] = val; 4136e4c0d1fSPeter Maydell pl110_update_palette(s, n); 414e10c2bfbSpbrook return; 415bdd5003aSpbrook } 416bdd5003aSpbrook switch (offset >> 2) { 417bdd5003aSpbrook case 0: /* LCDTiming0 */ 418bdd5003aSpbrook s->timing[0] = val; 419bdd5003aSpbrook n = ((val & 0xfc) + 4) * 4; 420bdd5003aSpbrook pl110_resize(s, n, s->rows); 421bdd5003aSpbrook break; 422bdd5003aSpbrook case 1: /* LCDTiming1 */ 423bdd5003aSpbrook s->timing[1] = val; 424bdd5003aSpbrook n = (val & 0x3ff) + 1; 425bdd5003aSpbrook pl110_resize(s, s->cols, n); 426bdd5003aSpbrook break; 427bdd5003aSpbrook case 2: /* LCDTiming2 */ 428bdd5003aSpbrook s->timing[2] = val; 429bdd5003aSpbrook break; 430bdd5003aSpbrook case 3: /* LCDTiming3 */ 431bdd5003aSpbrook s->timing[3] = val; 432bdd5003aSpbrook break; 433bdd5003aSpbrook case 4: /* LCDUPBASE */ 434bdd5003aSpbrook s->upbase = val; 435bdd5003aSpbrook break; 436bdd5003aSpbrook case 5: /* LCDLPBASE */ 437bdd5003aSpbrook s->lpbase = val; 438bdd5003aSpbrook break; 439bdd5003aSpbrook case 6: /* LCDIMSC */ 4404fbf5556SPeter Maydell if (s->version != PL110) { 441cdbdb648Spbrook goto control; 4424fbf5556SPeter Maydell } 443cdbdb648Spbrook imsc: 444bdd5003aSpbrook s->int_mask = val; 445bdd5003aSpbrook pl110_update(s); 446bdd5003aSpbrook break; 447bdd5003aSpbrook case 7: /* LCDControl */ 4484fbf5556SPeter Maydell if (s->version != PL110) { 449cdbdb648Spbrook goto imsc; 4504fbf5556SPeter Maydell } 451cdbdb648Spbrook control: 452bdd5003aSpbrook s->cr = val; 453bdd5003aSpbrook s->bpp = (val >> 1) & 7; 454bdd5003aSpbrook if (pl110_enabled(s)) { 455c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->cols, s->rows); 45624da047aSLinus Walleij timer_mod(s->vblank_timer, 45724da047aSLinus Walleij qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 45824da047aSLinus Walleij NANOSECONDS_PER_SECOND / 60); 45924da047aSLinus Walleij } else { 46024da047aSLinus Walleij timer_del(s->vblank_timer); 461bdd5003aSpbrook } 462bdd5003aSpbrook break; 463bdd5003aSpbrook case 10: /* LCDICR */ 464bdd5003aSpbrook s->int_status &= ~val; 465bdd5003aSpbrook pl110_update(s); 466bdd5003aSpbrook break; 467bdd5003aSpbrook default: 468375cb560SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 469375cb560SPeter Maydell "pl110_write: Bad offset %x\n", (int)offset); 470bdd5003aSpbrook } 471bdd5003aSpbrook } 472bdd5003aSpbrook 4731a6b31ceSAvi Kivity static const MemoryRegionOps pl110_ops = { 4741a6b31ceSAvi Kivity .read = pl110_read, 4751a6b31ceSAvi Kivity .write = pl110_write, 4761a6b31ceSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 477bdd5003aSpbrook }; 478bdd5003aSpbrook 479242ea2c6SPeter Maydell static void pl110_mux_ctrl_set(void *opaque, int line, int level) 480242ea2c6SPeter Maydell { 481513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 482242ea2c6SPeter Maydell s->mux_ctrl = level; 483242ea2c6SPeter Maydell } 484242ea2c6SPeter Maydell 485128939a9SPeter Maydell static int vmstate_pl110_post_load(void *opaque, int version_id) 486128939a9SPeter Maydell { 487513960eaSAndreas Färber PL110State *s = opaque; 488128939a9SPeter Maydell /* Make sure we redraw, and at the right size */ 489128939a9SPeter Maydell pl110_invalidate_display(s); 490128939a9SPeter Maydell return 0; 491128939a9SPeter Maydell } 492128939a9SPeter Maydell 493380cd056SGerd Hoffmann static const GraphicHwOps pl110_gfx_ops = { 494380cd056SGerd Hoffmann .invalidate = pl110_invalidate_display, 495380cd056SGerd Hoffmann .gfx_update = pl110_update_display, 496380cd056SGerd Hoffmann }; 497380cd056SGerd Hoffmann 498caae8032Sxiaoqiang zhao static void pl110_realize(DeviceState *dev, Error **errp) 499bdd5003aSpbrook { 5005d7a11e4SAndreas Färber PL110State *s = PL110(dev); 501caae8032Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 502bdd5003aSpbrook 5033eadad55SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000); 5045d7a11e4SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 5055d7a11e4SAndreas Färber sysbus_init_irq(sbd, &s->irq); 50624da047aSLinus Walleij s->vblank_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 50724da047aSLinus Walleij pl110_vblank_interrupt, s); 5085d7a11e4SAndreas Färber qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1); 5095643706aSGerd Hoffmann s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s); 510bdd5003aSpbrook } 5112e9bdce5SPaul Brook 5125d7a11e4SAndreas Färber static void pl110_init(Object *obj) 5132e9bdce5SPaul Brook { 5145d7a11e4SAndreas Färber PL110State *s = PL110(obj); 5155d7a11e4SAndreas Färber 5165d7a11e4SAndreas Färber s->version = PL110; 5174fbf5556SPeter Maydell } 5184fbf5556SPeter Maydell 5195d7a11e4SAndreas Färber static void pl110_versatile_init(Object *obj) 5204fbf5556SPeter Maydell { 5215d7a11e4SAndreas Färber PL110State *s = PL110(obj); 5225d7a11e4SAndreas Färber 5235d7a11e4SAndreas Färber s->version = PL110_VERSATILE; 5245d7a11e4SAndreas Färber } 5255d7a11e4SAndreas Färber 5265d7a11e4SAndreas Färber static void pl111_init(Object *obj) 5275d7a11e4SAndreas Färber { 5285d7a11e4SAndreas Färber PL110State *s = PL110(obj); 5295d7a11e4SAndreas Färber 5304fbf5556SPeter Maydell s->version = PL111; 5312e9bdce5SPaul Brook } 5322e9bdce5SPaul Brook 533999e12bbSAnthony Liguori static void pl110_class_init(ObjectClass *klass, void *data) 534999e12bbSAnthony Liguori { 53539bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 536999e12bbSAnthony Liguori 537125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); 53839bffca2SAnthony Liguori dc->vmsd = &vmstate_pl110; 539caae8032Sxiaoqiang zhao dc->realize = pl110_realize; 540999e12bbSAnthony Liguori } 541999e12bbSAnthony Liguori 5428c43a6f0SAndreas Färber static const TypeInfo pl110_info = { 5435d7a11e4SAndreas Färber .name = TYPE_PL110, 54439bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 545513960eaSAndreas Färber .instance_size = sizeof(PL110State), 5465d7a11e4SAndreas Färber .instance_init = pl110_init, 547999e12bbSAnthony Liguori .class_init = pl110_class_init, 5488c60d065SPeter Maydell }; 5498c60d065SPeter Maydell 5508c43a6f0SAndreas Färber static const TypeInfo pl110_versatile_info = { 551999e12bbSAnthony Liguori .name = "pl110_versatile", 5525d7a11e4SAndreas Färber .parent = TYPE_PL110, 5535d7a11e4SAndreas Färber .instance_init = pl110_versatile_init, 5548c60d065SPeter Maydell }; 5558c60d065SPeter Maydell 5568c43a6f0SAndreas Färber static const TypeInfo pl111_info = { 557999e12bbSAnthony Liguori .name = "pl111", 5585d7a11e4SAndreas Färber .parent = TYPE_PL110, 5595d7a11e4SAndreas Färber .instance_init = pl111_init, 5604fbf5556SPeter Maydell }; 5614fbf5556SPeter Maydell 56283f7d43aSAndreas Färber static void pl110_register_types(void) 5632e9bdce5SPaul Brook { 56439bffca2SAnthony Liguori type_register_static(&pl110_info); 56539bffca2SAnthony Liguori type_register_static(&pl110_versatile_info); 56639bffca2SAnthony Liguori type_register_static(&pl111_info); 5672e9bdce5SPaul Brook } 5682e9bdce5SPaul Brook 56983f7d43aSAndreas Färber type_init(pl110_register_types) 570