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" 13d6454270SMarkus 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" 20db1015e9SEduardo Habkost #include "qom/object.h" 21bdd5003aSpbrook 22bdd5003aSpbrook #define PL110_CR_EN 0x001 23e9c05b42Sbalrog #define PL110_CR_BGR 0x100 24bdd5003aSpbrook #define PL110_CR_BEBO 0x200 25bdd5003aSpbrook #define PL110_CR_BEPO 0x400 26bdd5003aSpbrook #define PL110_CR_PWR 0x800 2724da047aSLinus Walleij #define PL110_IE_NB 0x004 2824da047aSLinus Walleij #define PL110_IE_VC 0x008 29bdd5003aSpbrook 30bdd5003aSpbrook enum pl110_bppmode 31bdd5003aSpbrook { 32bdd5003aSpbrook BPP_1, 33bdd5003aSpbrook BPP_2, 34bdd5003aSpbrook BPP_4, 35bdd5003aSpbrook BPP_8, 36bdd5003aSpbrook BPP_16, 374fbf5556SPeter Maydell BPP_32, 384fbf5556SPeter Maydell BPP_16_565, /* PL111 only */ 394fbf5556SPeter Maydell BPP_12 /* PL111 only */ 404fbf5556SPeter Maydell }; 414fbf5556SPeter Maydell 424fbf5556SPeter Maydell 434fbf5556SPeter Maydell /* The Versatile/PB uses a slightly modified PL110 controller. */ 444fbf5556SPeter Maydell enum pl110_version 454fbf5556SPeter Maydell { 46c7bf3492SEduardo Habkost VERSION_PL110, 47c7bf3492SEduardo Habkost VERSION_PL110_VERSATILE, 48c7bf3492SEduardo Habkost VERSION_PL111 49bdd5003aSpbrook }; 50bdd5003aSpbrook 515d7a11e4SAndreas Färber #define TYPE_PL110 "pl110" 528063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(PL110State, PL110) 535d7a11e4SAndreas Färber 54db1015e9SEduardo Habkost struct PL110State { 555d7a11e4SAndreas Färber SysBusDevice parent_obj; 565d7a11e4SAndreas Färber 571a6b31ceSAvi Kivity MemoryRegion iomem; 58c1076c3eSPaolo Bonzini MemoryRegionSection fbsection; 59c78f7137SGerd Hoffmann QemuConsole *con; 6024da047aSLinus Walleij QEMUTimer *vblank_timer; 61c60e08d9Spbrook 624fbf5556SPeter Maydell int version; 63bdd5003aSpbrook uint32_t timing[4]; 64bdd5003aSpbrook uint32_t cr; 65bdd5003aSpbrook uint32_t upbase; 66bdd5003aSpbrook uint32_t lpbase; 67bdd5003aSpbrook uint32_t int_status; 68bdd5003aSpbrook uint32_t int_mask; 69bdd5003aSpbrook int cols; 70bdd5003aSpbrook int rows; 71bdd5003aSpbrook enum pl110_bppmode bpp; 72bdd5003aSpbrook int invalidate; 73242ea2c6SPeter Maydell uint32_t mux_ctrl; 746e4c0d1fSPeter Maydell uint32_t palette[256]; 756e4c0d1fSPeter Maydell uint32_t raw_palette[128]; 76d537cf6cSpbrook qemu_irq irq; 77db1015e9SEduardo Habkost }; 78bdd5003aSpbrook 79128939a9SPeter Maydell static int vmstate_pl110_post_load(void *opaque, int version_id); 80128939a9SPeter Maydell 818c60d065SPeter Maydell static const VMStateDescription vmstate_pl110 = { 828c60d065SPeter Maydell .name = "pl110", 83242ea2c6SPeter Maydell .version_id = 2, 848c60d065SPeter Maydell .minimum_version_id = 1, 85128939a9SPeter Maydell .post_load = vmstate_pl110_post_load, 868c60d065SPeter Maydell .fields = (VMStateField[]) { 87513960eaSAndreas Färber VMSTATE_INT32(version, PL110State), 88513960eaSAndreas Färber VMSTATE_UINT32_ARRAY(timing, PL110State, 4), 89513960eaSAndreas Färber VMSTATE_UINT32(cr, PL110State), 90513960eaSAndreas Färber VMSTATE_UINT32(upbase, PL110State), 91513960eaSAndreas Färber VMSTATE_UINT32(lpbase, PL110State), 92513960eaSAndreas Färber VMSTATE_UINT32(int_status, PL110State), 93513960eaSAndreas Färber VMSTATE_UINT32(int_mask, PL110State), 94513960eaSAndreas Färber VMSTATE_INT32(cols, PL110State), 95513960eaSAndreas Färber VMSTATE_INT32(rows, PL110State), 96513960eaSAndreas Färber VMSTATE_UINT32(bpp, PL110State), 97513960eaSAndreas Färber VMSTATE_INT32(invalidate, PL110State), 98513960eaSAndreas Färber VMSTATE_UINT32_ARRAY(palette, PL110State, 256), 99513960eaSAndreas Färber VMSTATE_UINT32_ARRAY(raw_palette, PL110State, 128), 100513960eaSAndreas Färber VMSTATE_UINT32_V(mux_ctrl, PL110State, 2), 1018c60d065SPeter Maydell VMSTATE_END_OF_LIST() 1028c60d065SPeter Maydell } 1038c60d065SPeter Maydell }; 1048c60d065SPeter Maydell 105bdd5003aSpbrook static const unsigned char pl110_id[] = 106bdd5003aSpbrook { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; 107bdd5003aSpbrook 1084fbf5556SPeter Maydell static const unsigned char pl111_id[] = { 1094fbf5556SPeter Maydell 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1 1104fbf5556SPeter Maydell }; 1114fbf5556SPeter Maydell 112031c44e4SPeter Maydell 1134fbf5556SPeter Maydell /* Indexed by pl110_version */ 1144fbf5556SPeter Maydell static const unsigned char *idregs[] = { 1154fbf5556SPeter Maydell pl110_id, 116031c44e4SPeter Maydell /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board 117031c44e4SPeter Maydell * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware 118031c44e4SPeter Maydell * itself has the same ID values as a stock PL110, and guests (in 119031c44e4SPeter Maydell * particular Linux) rely on this. We emulate what the hardware does, 120031c44e4SPeter Maydell * rather than what the docs claim it ought to do. 121031c44e4SPeter Maydell */ 122031c44e4SPeter Maydell pl110_id, 1234fbf5556SPeter Maydell pl111_id 1244fbf5556SPeter Maydell }; 1254fbf5556SPeter Maydell 126bdd5003aSpbrook #define BITS 32 127*560ebce6SPeter Maydell #define COPY_PIXEL(to, from) do { *(uint32_t *)to = from; to += 4; } while (0) 128*560ebce6SPeter Maydell 129*560ebce6SPeter Maydell #undef RGB 130*560ebce6SPeter Maydell #define BORDER bgr 131*560ebce6SPeter Maydell #define ORDER 0 13247b43a1fSPaolo Bonzini #include "pl110_template.h" 133*560ebce6SPeter Maydell #define ORDER 1 134*560ebce6SPeter Maydell #include "pl110_template.h" 135*560ebce6SPeter Maydell #define ORDER 2 136*560ebce6SPeter Maydell #include "pl110_template.h" 137*560ebce6SPeter Maydell #undef BORDER 138*560ebce6SPeter Maydell #define RGB 139*560ebce6SPeter Maydell #define BORDER rgb 140*560ebce6SPeter Maydell #define ORDER 0 141*560ebce6SPeter Maydell #include "pl110_template.h" 142*560ebce6SPeter Maydell #define ORDER 1 143*560ebce6SPeter Maydell #include "pl110_template.h" 144*560ebce6SPeter Maydell #define ORDER 2 145*560ebce6SPeter Maydell #include "pl110_template.h" 146*560ebce6SPeter Maydell #undef BORDER 147*560ebce6SPeter Maydell 148*560ebce6SPeter Maydell static drawfn pl110_draw_fn_32[48] = { 149*560ebce6SPeter Maydell pl110_draw_line1_lblp_bgr32, 150*560ebce6SPeter Maydell pl110_draw_line2_lblp_bgr32, 151*560ebce6SPeter Maydell pl110_draw_line4_lblp_bgr32, 152*560ebce6SPeter Maydell pl110_draw_line8_lblp_bgr32, 153*560ebce6SPeter Maydell pl110_draw_line16_555_lblp_bgr32, 154*560ebce6SPeter Maydell pl110_draw_line32_lblp_bgr32, 155*560ebce6SPeter Maydell pl110_draw_line16_lblp_bgr32, 156*560ebce6SPeter Maydell pl110_draw_line12_lblp_bgr32, 157*560ebce6SPeter Maydell 158*560ebce6SPeter Maydell pl110_draw_line1_bbbp_bgr32, 159*560ebce6SPeter Maydell pl110_draw_line2_bbbp_bgr32, 160*560ebce6SPeter Maydell pl110_draw_line4_bbbp_bgr32, 161*560ebce6SPeter Maydell pl110_draw_line8_bbbp_bgr32, 162*560ebce6SPeter Maydell pl110_draw_line16_555_bbbp_bgr32, 163*560ebce6SPeter Maydell pl110_draw_line32_bbbp_bgr32, 164*560ebce6SPeter Maydell pl110_draw_line16_bbbp_bgr32, 165*560ebce6SPeter Maydell pl110_draw_line12_bbbp_bgr32, 166*560ebce6SPeter Maydell 167*560ebce6SPeter Maydell pl110_draw_line1_lbbp_bgr32, 168*560ebce6SPeter Maydell pl110_draw_line2_lbbp_bgr32, 169*560ebce6SPeter Maydell pl110_draw_line4_lbbp_bgr32, 170*560ebce6SPeter Maydell pl110_draw_line8_lbbp_bgr32, 171*560ebce6SPeter Maydell pl110_draw_line16_555_lbbp_bgr32, 172*560ebce6SPeter Maydell pl110_draw_line32_lbbp_bgr32, 173*560ebce6SPeter Maydell pl110_draw_line16_lbbp_bgr32, 174*560ebce6SPeter Maydell pl110_draw_line12_lbbp_bgr32, 175*560ebce6SPeter Maydell 176*560ebce6SPeter Maydell pl110_draw_line1_lblp_rgb32, 177*560ebce6SPeter Maydell pl110_draw_line2_lblp_rgb32, 178*560ebce6SPeter Maydell pl110_draw_line4_lblp_rgb32, 179*560ebce6SPeter Maydell pl110_draw_line8_lblp_rgb32, 180*560ebce6SPeter Maydell pl110_draw_line16_555_lblp_rgb32, 181*560ebce6SPeter Maydell pl110_draw_line32_lblp_rgb32, 182*560ebce6SPeter Maydell pl110_draw_line16_lblp_rgb32, 183*560ebce6SPeter Maydell pl110_draw_line12_lblp_rgb32, 184*560ebce6SPeter Maydell 185*560ebce6SPeter Maydell pl110_draw_line1_bbbp_rgb32, 186*560ebce6SPeter Maydell pl110_draw_line2_bbbp_rgb32, 187*560ebce6SPeter Maydell pl110_draw_line4_bbbp_rgb32, 188*560ebce6SPeter Maydell pl110_draw_line8_bbbp_rgb32, 189*560ebce6SPeter Maydell pl110_draw_line16_555_bbbp_rgb32, 190*560ebce6SPeter Maydell pl110_draw_line32_bbbp_rgb32, 191*560ebce6SPeter Maydell pl110_draw_line16_bbbp_rgb32, 192*560ebce6SPeter Maydell pl110_draw_line12_bbbp_rgb32, 193*560ebce6SPeter Maydell 194*560ebce6SPeter Maydell pl110_draw_line1_lbbp_rgb32, 195*560ebce6SPeter Maydell pl110_draw_line2_lbbp_rgb32, 196*560ebce6SPeter Maydell pl110_draw_line4_lbbp_rgb32, 197*560ebce6SPeter Maydell pl110_draw_line8_lbbp_rgb32, 198*560ebce6SPeter Maydell pl110_draw_line16_555_lbbp_rgb32, 199*560ebce6SPeter Maydell pl110_draw_line32_lbbp_rgb32, 200*560ebce6SPeter Maydell pl110_draw_line16_lbbp_rgb32, 201*560ebce6SPeter Maydell pl110_draw_line12_lbbp_rgb32, 202*560ebce6SPeter Maydell }; 203*560ebce6SPeter Maydell 204*560ebce6SPeter Maydell #undef BITS 205*560ebce6SPeter Maydell #undef COPY_PIXEL 206*560ebce6SPeter Maydell 207bdd5003aSpbrook 208513960eaSAndreas Färber static int pl110_enabled(PL110State *s) 209bdd5003aSpbrook { 210bdd5003aSpbrook return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR); 211bdd5003aSpbrook } 212bdd5003aSpbrook 21395219897Spbrook static void pl110_update_display(void *opaque) 214bdd5003aSpbrook { 215513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 2165d7a11e4SAndreas Färber SysBusDevice *sbd; 217c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 218bdd5003aSpbrook drawfn fn; 219bdd5003aSpbrook int src_width; 220e9c05b42Sbalrog int bpp_offset; 221714fa308Spbrook int first; 222714fa308Spbrook int last; 223bdd5003aSpbrook 2245d7a11e4SAndreas Färber if (!pl110_enabled(s)) { 225bdd5003aSpbrook return; 2265d7a11e4SAndreas Färber } 2275d7a11e4SAndreas Färber 2285d7a11e4SAndreas Färber sbd = SYS_BUS_DEVICE(s); 229bdd5003aSpbrook 230e9c05b42Sbalrog if (s->cr & PL110_CR_BGR) 231e9c05b42Sbalrog bpp_offset = 0; 232bdd5003aSpbrook else 2334fbf5556SPeter Maydell bpp_offset = 24; 2344fbf5556SPeter Maydell 235c7bf3492SEduardo Habkost if ((s->version != VERSION_PL111) && (s->bpp == BPP_16)) { 2364fbf5556SPeter Maydell /* The PL110's native 16 bit mode is 5551; however 2374fbf5556SPeter Maydell * most boards with a PL110 implement an external 2384fbf5556SPeter Maydell * mux which allows bits to be reshuffled to give 2394fbf5556SPeter Maydell * 565 format. The mux is typically controlled by 2404fbf5556SPeter Maydell * an external system register. 241242ea2c6SPeter Maydell * This is controlled by a GPIO input pin 2424fbf5556SPeter Maydell * so boards can wire it up to their register. 2434fbf5556SPeter Maydell * 2444fbf5556SPeter Maydell * The PL111 straightforwardly implements both 2454fbf5556SPeter Maydell * 5551 and 565 under control of the bpp field 2464fbf5556SPeter Maydell * in the LCDControl register. 2474fbf5556SPeter Maydell */ 248242ea2c6SPeter Maydell switch (s->mux_ctrl) { 249242ea2c6SPeter Maydell case 3: /* 565 BGR */ 250242ea2c6SPeter Maydell bpp_offset = (BPP_16_565 - BPP_16); 251242ea2c6SPeter Maydell break; 252242ea2c6SPeter Maydell case 1: /* 5551 */ 253242ea2c6SPeter Maydell break; 254242ea2c6SPeter Maydell case 0: /* 888; also if we have loaded vmstate from an old version */ 255242ea2c6SPeter Maydell case 2: /* 565 RGB */ 256242ea2c6SPeter Maydell default: 257242ea2c6SPeter Maydell /* treat as 565 but honour BGR bit */ 2584fbf5556SPeter Maydell bpp_offset += (BPP_16_565 - BPP_16); 259242ea2c6SPeter Maydell break; 260242ea2c6SPeter Maydell } 2614fbf5556SPeter Maydell } 262e9c05b42Sbalrog 26362bdc8c1SPeter Maydell if (s->cr & PL110_CR_BEBO) { 26462bdc8c1SPeter Maydell fn = pl110_draw_fn_32[s->bpp + 8 + bpp_offset]; 26562bdc8c1SPeter Maydell } else if (s->cr & PL110_CR_BEPO) { 26662bdc8c1SPeter Maydell fn = pl110_draw_fn_32[s->bpp + 16 + bpp_offset]; 26762bdc8c1SPeter Maydell } else { 26862bdc8c1SPeter Maydell fn = pl110_draw_fn_32[s->bpp + bpp_offset]; 26962bdc8c1SPeter Maydell } 270bdd5003aSpbrook 271bdd5003aSpbrook src_width = s->cols; 272bdd5003aSpbrook switch (s->bpp) { 273bdd5003aSpbrook case BPP_1: 274bdd5003aSpbrook src_width >>= 3; 275bdd5003aSpbrook break; 276bdd5003aSpbrook case BPP_2: 277bdd5003aSpbrook src_width >>= 2; 278bdd5003aSpbrook break; 279bdd5003aSpbrook case BPP_4: 280bdd5003aSpbrook src_width >>= 1; 281bdd5003aSpbrook break; 282bdd5003aSpbrook case BPP_8: 283bdd5003aSpbrook break; 284bdd5003aSpbrook case BPP_16: 2854fbf5556SPeter Maydell case BPP_16_565: 2864fbf5556SPeter Maydell case BPP_12: 287bdd5003aSpbrook src_width <<= 1; 288bdd5003aSpbrook break; 289bdd5003aSpbrook case BPP_32: 290bdd5003aSpbrook src_width <<= 2; 291bdd5003aSpbrook break; 292bdd5003aSpbrook } 293714fa308Spbrook first = 0; 294c1076c3eSPaolo Bonzini if (s->invalidate) { 295c1076c3eSPaolo Bonzini framebuffer_update_memory_section(&s->fbsection, 296c1076c3eSPaolo Bonzini sysbus_address_space(sbd), 297c1076c3eSPaolo Bonzini s->upbase, 298c1076c3eSPaolo Bonzini s->rows, src_width); 299c1076c3eSPaolo Bonzini } 300c1076c3eSPaolo Bonzini 301c1076c3eSPaolo Bonzini framebuffer_update_display(surface, &s->fbsection, 302c1076c3eSPaolo Bonzini s->cols, s->rows, 30362bdc8c1SPeter Maydell src_width, s->cols * 4, 0, 304714fa308Spbrook s->invalidate, 3056e4c0d1fSPeter Maydell fn, s->palette, 306714fa308Spbrook &first, &last); 307c1076c3eSPaolo Bonzini 308714fa308Spbrook if (first >= 0) { 309c78f7137SGerd Hoffmann dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1); 310bdd5003aSpbrook } 311714fa308Spbrook s->invalidate = 0; 312714fa308Spbrook } 313bdd5003aSpbrook 31495219897Spbrook static void pl110_invalidate_display(void * opaque) 315bdd5003aSpbrook { 316513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 317bdd5003aSpbrook s->invalidate = 1; 318bfdb3629SBlue Swirl if (pl110_enabled(s)) { 319c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->cols, s->rows); 320bfdb3629SBlue Swirl } 321bdd5003aSpbrook } 322bdd5003aSpbrook 323513960eaSAndreas Färber static void pl110_update_palette(PL110State *s, int n) 324bdd5003aSpbrook { 325c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 326bdd5003aSpbrook int i; 327bdd5003aSpbrook uint32_t raw; 328bdd5003aSpbrook unsigned int r, g, b; 329bdd5003aSpbrook 3306e4c0d1fSPeter Maydell raw = s->raw_palette[n]; 331bdd5003aSpbrook n <<= 1; 332bdd5003aSpbrook for (i = 0; i < 2; i++) { 333bdd5003aSpbrook r = (raw & 0x1f) << 3; 334bdd5003aSpbrook raw >>= 5; 335bdd5003aSpbrook g = (raw & 0x1f) << 3; 336bdd5003aSpbrook raw >>= 5; 337bdd5003aSpbrook b = (raw & 0x1f) << 3; 338bdd5003aSpbrook /* The I bit is ignored. */ 339bdd5003aSpbrook raw >>= 6; 340c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 341bdd5003aSpbrook case 8: 3426e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel8(r, g, b); 343bdd5003aSpbrook break; 344bdd5003aSpbrook case 15: 3456e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel15(r, g, b); 346bdd5003aSpbrook break; 347bdd5003aSpbrook case 16: 3486e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel16(r, g, b); 349bdd5003aSpbrook break; 350bdd5003aSpbrook case 24: 351bdd5003aSpbrook case 32: 3526e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel32(r, g, b); 353bdd5003aSpbrook break; 354bdd5003aSpbrook } 355bdd5003aSpbrook n++; 356bdd5003aSpbrook } 357bdd5003aSpbrook } 358bdd5003aSpbrook 359513960eaSAndreas Färber static void pl110_resize(PL110State *s, int width, int height) 360bdd5003aSpbrook { 361bdd5003aSpbrook if (width != s->cols || height != s->rows) { 362bdd5003aSpbrook if (pl110_enabled(s)) { 363c78f7137SGerd Hoffmann qemu_console_resize(s->con, width, height); 364bdd5003aSpbrook } 365bdd5003aSpbrook } 366bdd5003aSpbrook s->cols = width; 367bdd5003aSpbrook s->rows = height; 368bdd5003aSpbrook } 369bdd5003aSpbrook 370bdd5003aSpbrook /* Update interrupts. */ 371513960eaSAndreas Färber static void pl110_update(PL110State *s) 372bdd5003aSpbrook { 37324da047aSLinus Walleij /* Raise IRQ if enabled and any status bit is 1 */ 37424da047aSLinus Walleij if (s->int_status & s->int_mask) { 37524da047aSLinus Walleij qemu_irq_raise(s->irq); 37624da047aSLinus Walleij } else { 37724da047aSLinus Walleij qemu_irq_lower(s->irq); 37824da047aSLinus Walleij } 37924da047aSLinus Walleij } 38024da047aSLinus Walleij 38124da047aSLinus Walleij static void pl110_vblank_interrupt(void *opaque) 38224da047aSLinus Walleij { 38324da047aSLinus Walleij PL110State *s = opaque; 38424da047aSLinus Walleij 38524da047aSLinus Walleij /* Fire the vertical compare and next base IRQs and re-arm */ 38624da047aSLinus Walleij s->int_status |= (PL110_IE_NB | PL110_IE_VC); 38724da047aSLinus Walleij timer_mod(s->vblank_timer, 38824da047aSLinus Walleij qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 38924da047aSLinus Walleij NANOSECONDS_PER_SECOND / 60); 39024da047aSLinus Walleij pl110_update(s); 391bdd5003aSpbrook } 392bdd5003aSpbrook 393a8170e5eSAvi Kivity static uint64_t pl110_read(void *opaque, hwaddr offset, 3941a6b31ceSAvi Kivity unsigned size) 395bdd5003aSpbrook { 396513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 397bdd5003aSpbrook 398bdd5003aSpbrook if (offset >= 0xfe0 && offset < 0x1000) { 3994fbf5556SPeter Maydell return idregs[s->version][(offset - 0xfe0) >> 2]; 400bdd5003aSpbrook } 401bdd5003aSpbrook if (offset >= 0x200 && offset < 0x400) { 4026e4c0d1fSPeter Maydell return s->raw_palette[(offset - 0x200) >> 2]; 403bdd5003aSpbrook } 404bdd5003aSpbrook switch (offset >> 2) { 405bdd5003aSpbrook case 0: /* LCDTiming0 */ 406bdd5003aSpbrook return s->timing[0]; 407bdd5003aSpbrook case 1: /* LCDTiming1 */ 408bdd5003aSpbrook return s->timing[1]; 409bdd5003aSpbrook case 2: /* LCDTiming2 */ 410bdd5003aSpbrook return s->timing[2]; 411bdd5003aSpbrook case 3: /* LCDTiming3 */ 412bdd5003aSpbrook return s->timing[3]; 413bdd5003aSpbrook case 4: /* LCDUPBASE */ 414bdd5003aSpbrook return s->upbase; 415bdd5003aSpbrook case 5: /* LCDLPBASE */ 416bdd5003aSpbrook return s->lpbase; 417bdd5003aSpbrook case 6: /* LCDIMSC */ 418c7bf3492SEduardo Habkost if (s->version != VERSION_PL110) { 41964075cd7Spbrook return s->cr; 4204fbf5556SPeter Maydell } 421bdd5003aSpbrook return s->int_mask; 422bdd5003aSpbrook case 7: /* LCDControl */ 423c7bf3492SEduardo Habkost if (s->version != VERSION_PL110) { 42464075cd7Spbrook return s->int_mask; 4254fbf5556SPeter Maydell } 426bdd5003aSpbrook return s->cr; 427bdd5003aSpbrook case 8: /* LCDRIS */ 428bdd5003aSpbrook return s->int_status; 429bdd5003aSpbrook case 9: /* LCDMIS */ 430bdd5003aSpbrook return s->int_status & s->int_mask; 431bdd5003aSpbrook case 11: /* LCDUPCURR */ 432bdd5003aSpbrook /* TODO: Implement vertical refresh. */ 433bdd5003aSpbrook return s->upbase; 434bdd5003aSpbrook case 12: /* LCDLPCURR */ 435bdd5003aSpbrook return s->lpbase; 436bdd5003aSpbrook default: 437375cb560SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 438375cb560SPeter Maydell "pl110_read: Bad offset %x\n", (int)offset); 439bdd5003aSpbrook return 0; 440bdd5003aSpbrook } 441bdd5003aSpbrook } 442bdd5003aSpbrook 443a8170e5eSAvi Kivity static void pl110_write(void *opaque, hwaddr offset, 4441a6b31ceSAvi Kivity uint64_t val, unsigned size) 445bdd5003aSpbrook { 446513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 447bdd5003aSpbrook int n; 448bdd5003aSpbrook 449bdd5003aSpbrook /* For simplicity invalidate the display whenever a control register 45066a0a2cbSDong Xu Wang is written to. */ 451bdd5003aSpbrook s->invalidate = 1; 452bdd5003aSpbrook if (offset >= 0x200 && offset < 0x400) { 4536e4c0d1fSPeter Maydell /* Palette. */ 454bdd5003aSpbrook n = (offset - 0x200) >> 2; 4556e4c0d1fSPeter Maydell s->raw_palette[(offset - 0x200) >> 2] = val; 4566e4c0d1fSPeter Maydell pl110_update_palette(s, n); 457e10c2bfbSpbrook return; 458bdd5003aSpbrook } 459bdd5003aSpbrook switch (offset >> 2) { 460bdd5003aSpbrook case 0: /* LCDTiming0 */ 461bdd5003aSpbrook s->timing[0] = val; 462bdd5003aSpbrook n = ((val & 0xfc) + 4) * 4; 463bdd5003aSpbrook pl110_resize(s, n, s->rows); 464bdd5003aSpbrook break; 465bdd5003aSpbrook case 1: /* LCDTiming1 */ 466bdd5003aSpbrook s->timing[1] = val; 467bdd5003aSpbrook n = (val & 0x3ff) + 1; 468bdd5003aSpbrook pl110_resize(s, s->cols, n); 469bdd5003aSpbrook break; 470bdd5003aSpbrook case 2: /* LCDTiming2 */ 471bdd5003aSpbrook s->timing[2] = val; 472bdd5003aSpbrook break; 473bdd5003aSpbrook case 3: /* LCDTiming3 */ 474bdd5003aSpbrook s->timing[3] = val; 475bdd5003aSpbrook break; 476bdd5003aSpbrook case 4: /* LCDUPBASE */ 477bdd5003aSpbrook s->upbase = val; 478bdd5003aSpbrook break; 479bdd5003aSpbrook case 5: /* LCDLPBASE */ 480bdd5003aSpbrook s->lpbase = val; 481bdd5003aSpbrook break; 482bdd5003aSpbrook case 6: /* LCDIMSC */ 483c7bf3492SEduardo Habkost if (s->version != VERSION_PL110) { 484cdbdb648Spbrook goto control; 4854fbf5556SPeter Maydell } 486cdbdb648Spbrook imsc: 487bdd5003aSpbrook s->int_mask = val; 488bdd5003aSpbrook pl110_update(s); 489bdd5003aSpbrook break; 490bdd5003aSpbrook case 7: /* LCDControl */ 491c7bf3492SEduardo Habkost if (s->version != VERSION_PL110) { 492cdbdb648Spbrook goto imsc; 4934fbf5556SPeter Maydell } 494cdbdb648Spbrook control: 495bdd5003aSpbrook s->cr = val; 496bdd5003aSpbrook s->bpp = (val >> 1) & 7; 497bdd5003aSpbrook if (pl110_enabled(s)) { 498c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->cols, s->rows); 49924da047aSLinus Walleij timer_mod(s->vblank_timer, 50024da047aSLinus Walleij qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 50124da047aSLinus Walleij NANOSECONDS_PER_SECOND / 60); 50224da047aSLinus Walleij } else { 50324da047aSLinus Walleij timer_del(s->vblank_timer); 504bdd5003aSpbrook } 505bdd5003aSpbrook break; 506bdd5003aSpbrook case 10: /* LCDICR */ 507bdd5003aSpbrook s->int_status &= ~val; 508bdd5003aSpbrook pl110_update(s); 509bdd5003aSpbrook break; 510bdd5003aSpbrook default: 511375cb560SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 512375cb560SPeter Maydell "pl110_write: Bad offset %x\n", (int)offset); 513bdd5003aSpbrook } 514bdd5003aSpbrook } 515bdd5003aSpbrook 5161a6b31ceSAvi Kivity static const MemoryRegionOps pl110_ops = { 5171a6b31ceSAvi Kivity .read = pl110_read, 5181a6b31ceSAvi Kivity .write = pl110_write, 5191a6b31ceSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 520bdd5003aSpbrook }; 521bdd5003aSpbrook 522242ea2c6SPeter Maydell static void pl110_mux_ctrl_set(void *opaque, int line, int level) 523242ea2c6SPeter Maydell { 524513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 525242ea2c6SPeter Maydell s->mux_ctrl = level; 526242ea2c6SPeter Maydell } 527242ea2c6SPeter Maydell 528128939a9SPeter Maydell static int vmstate_pl110_post_load(void *opaque, int version_id) 529128939a9SPeter Maydell { 530513960eaSAndreas Färber PL110State *s = opaque; 531128939a9SPeter Maydell /* Make sure we redraw, and at the right size */ 532128939a9SPeter Maydell pl110_invalidate_display(s); 533128939a9SPeter Maydell return 0; 534128939a9SPeter Maydell } 535128939a9SPeter Maydell 536380cd056SGerd Hoffmann static const GraphicHwOps pl110_gfx_ops = { 537380cd056SGerd Hoffmann .invalidate = pl110_invalidate_display, 538380cd056SGerd Hoffmann .gfx_update = pl110_update_display, 539380cd056SGerd Hoffmann }; 540380cd056SGerd Hoffmann 541caae8032Sxiaoqiang zhao static void pl110_realize(DeviceState *dev, Error **errp) 542bdd5003aSpbrook { 5435d7a11e4SAndreas Färber PL110State *s = PL110(dev); 544caae8032Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 545bdd5003aSpbrook 5463eadad55SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000); 5475d7a11e4SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 5485d7a11e4SAndreas Färber sysbus_init_irq(sbd, &s->irq); 54924da047aSLinus Walleij s->vblank_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 55024da047aSLinus Walleij pl110_vblank_interrupt, s); 5515d7a11e4SAndreas Färber qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1); 5525643706aSGerd Hoffmann s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s); 553bdd5003aSpbrook } 5542e9bdce5SPaul Brook 5555d7a11e4SAndreas Färber static void pl110_init(Object *obj) 5562e9bdce5SPaul Brook { 5575d7a11e4SAndreas Färber PL110State *s = PL110(obj); 5585d7a11e4SAndreas Färber 559c7bf3492SEduardo Habkost s->version = VERSION_PL110; 5604fbf5556SPeter Maydell } 5614fbf5556SPeter Maydell 5625d7a11e4SAndreas Färber static void pl110_versatile_init(Object *obj) 5634fbf5556SPeter Maydell { 5645d7a11e4SAndreas Färber PL110State *s = PL110(obj); 5655d7a11e4SAndreas Färber 566c7bf3492SEduardo Habkost s->version = VERSION_PL110_VERSATILE; 5675d7a11e4SAndreas Färber } 5685d7a11e4SAndreas Färber 5695d7a11e4SAndreas Färber static void pl111_init(Object *obj) 5705d7a11e4SAndreas Färber { 5715d7a11e4SAndreas Färber PL110State *s = PL110(obj); 5725d7a11e4SAndreas Färber 573c7bf3492SEduardo Habkost s->version = VERSION_PL111; 5742e9bdce5SPaul Brook } 5752e9bdce5SPaul Brook 576999e12bbSAnthony Liguori static void pl110_class_init(ObjectClass *klass, void *data) 577999e12bbSAnthony Liguori { 57839bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 579999e12bbSAnthony Liguori 580125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); 58139bffca2SAnthony Liguori dc->vmsd = &vmstate_pl110; 582caae8032Sxiaoqiang zhao dc->realize = pl110_realize; 583999e12bbSAnthony Liguori } 584999e12bbSAnthony Liguori 5858c43a6f0SAndreas Färber static const TypeInfo pl110_info = { 5865d7a11e4SAndreas Färber .name = TYPE_PL110, 58739bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 588513960eaSAndreas Färber .instance_size = sizeof(PL110State), 5895d7a11e4SAndreas Färber .instance_init = pl110_init, 590999e12bbSAnthony Liguori .class_init = pl110_class_init, 5918c60d065SPeter Maydell }; 5928c60d065SPeter Maydell 5938c43a6f0SAndreas Färber static const TypeInfo pl110_versatile_info = { 594999e12bbSAnthony Liguori .name = "pl110_versatile", 5955d7a11e4SAndreas Färber .parent = TYPE_PL110, 5965d7a11e4SAndreas Färber .instance_init = pl110_versatile_init, 5978c60d065SPeter Maydell }; 5988c60d065SPeter Maydell 5998c43a6f0SAndreas Färber static const TypeInfo pl111_info = { 600999e12bbSAnthony Liguori .name = "pl111", 6015d7a11e4SAndreas Färber .parent = TYPE_PL110, 6025d7a11e4SAndreas Färber .instance_init = pl111_init, 6034fbf5556SPeter Maydell }; 6044fbf5556SPeter Maydell 60583f7d43aSAndreas Färber static void pl110_register_types(void) 6062e9bdce5SPaul Brook { 60739bffca2SAnthony Liguori type_register_static(&pl110_info); 60839bffca2SAnthony Liguori type_register_static(&pl110_versatile_info); 60939bffca2SAnthony Liguori type_register_static(&pl111_info); 6102e9bdce5SPaul Brook } 6112e9bdce5SPaul Brook 61283f7d43aSAndreas Färber type_init(pl110_register_types) 613