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 1083c9f4caSPaolo Bonzini #include "hw/sysbus.h" 1128ecbaeeSPaolo Bonzini #include "ui/console.h" 1247b43a1fSPaolo Bonzini #include "framebuffer.h" 1328ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 14bdd5003aSpbrook 15bdd5003aSpbrook #define PL110_CR_EN 0x001 16e9c05b42Sbalrog #define PL110_CR_BGR 0x100 17bdd5003aSpbrook #define PL110_CR_BEBO 0x200 18bdd5003aSpbrook #define PL110_CR_BEPO 0x400 19bdd5003aSpbrook #define PL110_CR_PWR 0x800 20bdd5003aSpbrook 21bdd5003aSpbrook enum pl110_bppmode 22bdd5003aSpbrook { 23bdd5003aSpbrook BPP_1, 24bdd5003aSpbrook BPP_2, 25bdd5003aSpbrook BPP_4, 26bdd5003aSpbrook BPP_8, 27bdd5003aSpbrook BPP_16, 284fbf5556SPeter Maydell BPP_32, 294fbf5556SPeter Maydell BPP_16_565, /* PL111 only */ 304fbf5556SPeter Maydell BPP_12 /* PL111 only */ 314fbf5556SPeter Maydell }; 324fbf5556SPeter Maydell 334fbf5556SPeter Maydell 344fbf5556SPeter Maydell /* The Versatile/PB uses a slightly modified PL110 controller. */ 354fbf5556SPeter Maydell enum pl110_version 364fbf5556SPeter Maydell { 374fbf5556SPeter Maydell PL110, 384fbf5556SPeter Maydell PL110_VERSATILE, 394fbf5556SPeter Maydell PL111 40bdd5003aSpbrook }; 41bdd5003aSpbrook 425d7a11e4SAndreas Färber #define TYPE_PL110 "pl110" 435d7a11e4SAndreas Färber #define PL110(obj) OBJECT_CHECK(PL110State, (obj), TYPE_PL110) 445d7a11e4SAndreas Färber 45513960eaSAndreas Färber typedef struct PL110State { 465d7a11e4SAndreas Färber SysBusDevice parent_obj; 475d7a11e4SAndreas Färber 481a6b31ceSAvi Kivity MemoryRegion iomem; 49*c1076c3eSPaolo Bonzini MemoryRegionSection fbsection; 50c78f7137SGerd Hoffmann QemuConsole *con; 51c60e08d9Spbrook 524fbf5556SPeter Maydell int version; 53bdd5003aSpbrook uint32_t timing[4]; 54bdd5003aSpbrook uint32_t cr; 55bdd5003aSpbrook uint32_t upbase; 56bdd5003aSpbrook uint32_t lpbase; 57bdd5003aSpbrook uint32_t int_status; 58bdd5003aSpbrook uint32_t int_mask; 59bdd5003aSpbrook int cols; 60bdd5003aSpbrook int rows; 61bdd5003aSpbrook enum pl110_bppmode bpp; 62bdd5003aSpbrook int invalidate; 63242ea2c6SPeter Maydell uint32_t mux_ctrl; 646e4c0d1fSPeter Maydell uint32_t palette[256]; 656e4c0d1fSPeter Maydell uint32_t raw_palette[128]; 66d537cf6cSpbrook qemu_irq irq; 67513960eaSAndreas Färber } PL110State; 68bdd5003aSpbrook 69128939a9SPeter Maydell static int vmstate_pl110_post_load(void *opaque, int version_id); 70128939a9SPeter Maydell 718c60d065SPeter Maydell static const VMStateDescription vmstate_pl110 = { 728c60d065SPeter Maydell .name = "pl110", 73242ea2c6SPeter Maydell .version_id = 2, 748c60d065SPeter Maydell .minimum_version_id = 1, 75128939a9SPeter Maydell .post_load = vmstate_pl110_post_load, 768c60d065SPeter Maydell .fields = (VMStateField[]) { 77513960eaSAndreas Färber VMSTATE_INT32(version, PL110State), 78513960eaSAndreas Färber VMSTATE_UINT32_ARRAY(timing, PL110State, 4), 79513960eaSAndreas Färber VMSTATE_UINT32(cr, PL110State), 80513960eaSAndreas Färber VMSTATE_UINT32(upbase, PL110State), 81513960eaSAndreas Färber VMSTATE_UINT32(lpbase, PL110State), 82513960eaSAndreas Färber VMSTATE_UINT32(int_status, PL110State), 83513960eaSAndreas Färber VMSTATE_UINT32(int_mask, PL110State), 84513960eaSAndreas Färber VMSTATE_INT32(cols, PL110State), 85513960eaSAndreas Färber VMSTATE_INT32(rows, PL110State), 86513960eaSAndreas Färber VMSTATE_UINT32(bpp, PL110State), 87513960eaSAndreas Färber VMSTATE_INT32(invalidate, PL110State), 88513960eaSAndreas Färber VMSTATE_UINT32_ARRAY(palette, PL110State, 256), 89513960eaSAndreas Färber VMSTATE_UINT32_ARRAY(raw_palette, PL110State, 128), 90513960eaSAndreas Färber VMSTATE_UINT32_V(mux_ctrl, PL110State, 2), 918c60d065SPeter Maydell VMSTATE_END_OF_LIST() 928c60d065SPeter Maydell } 938c60d065SPeter Maydell }; 948c60d065SPeter Maydell 95bdd5003aSpbrook static const unsigned char pl110_id[] = 96bdd5003aSpbrook { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; 97bdd5003aSpbrook 984fbf5556SPeter Maydell static const unsigned char pl111_id[] = { 994fbf5556SPeter Maydell 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1 1004fbf5556SPeter Maydell }; 1014fbf5556SPeter Maydell 102031c44e4SPeter Maydell 1034fbf5556SPeter Maydell /* Indexed by pl110_version */ 1044fbf5556SPeter Maydell static const unsigned char *idregs[] = { 1054fbf5556SPeter Maydell pl110_id, 106031c44e4SPeter Maydell /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board 107031c44e4SPeter Maydell * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware 108031c44e4SPeter Maydell * itself has the same ID values as a stock PL110, and guests (in 109031c44e4SPeter Maydell * particular Linux) rely on this. We emulate what the hardware does, 110031c44e4SPeter Maydell * rather than what the docs claim it ought to do. 111031c44e4SPeter Maydell */ 112031c44e4SPeter Maydell pl110_id, 1134fbf5556SPeter Maydell pl111_id 1144fbf5556SPeter Maydell }; 1154fbf5556SPeter Maydell 116bdd5003aSpbrook #define BITS 8 11747b43a1fSPaolo Bonzini #include "pl110_template.h" 118bdd5003aSpbrook #define BITS 15 11947b43a1fSPaolo Bonzini #include "pl110_template.h" 120bdd5003aSpbrook #define BITS 16 12147b43a1fSPaolo Bonzini #include "pl110_template.h" 122bdd5003aSpbrook #define BITS 24 12347b43a1fSPaolo Bonzini #include "pl110_template.h" 124bdd5003aSpbrook #define BITS 32 12547b43a1fSPaolo Bonzini #include "pl110_template.h" 126bdd5003aSpbrook 127513960eaSAndreas Färber static int pl110_enabled(PL110State *s) 128bdd5003aSpbrook { 129bdd5003aSpbrook return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR); 130bdd5003aSpbrook } 131bdd5003aSpbrook 13295219897Spbrook static void pl110_update_display(void *opaque) 133bdd5003aSpbrook { 134513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 1355d7a11e4SAndreas Färber SysBusDevice *sbd; 136c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 137bdd5003aSpbrook drawfn* fntable; 138bdd5003aSpbrook drawfn fn; 139bdd5003aSpbrook int dest_width; 140bdd5003aSpbrook int src_width; 141e9c05b42Sbalrog int bpp_offset; 142714fa308Spbrook int first; 143714fa308Spbrook int last; 144bdd5003aSpbrook 1455d7a11e4SAndreas Färber if (!pl110_enabled(s)) { 146bdd5003aSpbrook return; 1475d7a11e4SAndreas Färber } 1485d7a11e4SAndreas Färber 1495d7a11e4SAndreas Färber sbd = SYS_BUS_DEVICE(s); 150bdd5003aSpbrook 151c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 152af2f6733Spbrook case 0: 153af2f6733Spbrook return; 154bdd5003aSpbrook case 8: 155bdd5003aSpbrook fntable = pl110_draw_fn_8; 156bdd5003aSpbrook dest_width = 1; 157bdd5003aSpbrook break; 158bdd5003aSpbrook case 15: 159bdd5003aSpbrook fntable = pl110_draw_fn_15; 160bdd5003aSpbrook dest_width = 2; 161bdd5003aSpbrook break; 162bdd5003aSpbrook case 16: 163bdd5003aSpbrook fntable = pl110_draw_fn_16; 164bdd5003aSpbrook dest_width = 2; 165bdd5003aSpbrook break; 166bdd5003aSpbrook case 24: 167bdd5003aSpbrook fntable = pl110_draw_fn_24; 168bdd5003aSpbrook dest_width = 3; 169bdd5003aSpbrook break; 170bdd5003aSpbrook case 32: 171bdd5003aSpbrook fntable = pl110_draw_fn_32; 172bdd5003aSpbrook dest_width = 4; 173bdd5003aSpbrook break; 174bdd5003aSpbrook default: 175af2f6733Spbrook fprintf(stderr, "pl110: Bad color depth\n"); 176bdd5003aSpbrook exit(1); 177bdd5003aSpbrook } 178e9c05b42Sbalrog if (s->cr & PL110_CR_BGR) 179e9c05b42Sbalrog bpp_offset = 0; 180bdd5003aSpbrook else 1814fbf5556SPeter Maydell bpp_offset = 24; 1824fbf5556SPeter Maydell 1834fbf5556SPeter Maydell if ((s->version != PL111) && (s->bpp == BPP_16)) { 1844fbf5556SPeter Maydell /* The PL110's native 16 bit mode is 5551; however 1854fbf5556SPeter Maydell * most boards with a PL110 implement an external 1864fbf5556SPeter Maydell * mux which allows bits to be reshuffled to give 1874fbf5556SPeter Maydell * 565 format. The mux is typically controlled by 1884fbf5556SPeter Maydell * an external system register. 189242ea2c6SPeter Maydell * This is controlled by a GPIO input pin 1904fbf5556SPeter Maydell * so boards can wire it up to their register. 1914fbf5556SPeter Maydell * 1924fbf5556SPeter Maydell * The PL111 straightforwardly implements both 1934fbf5556SPeter Maydell * 5551 and 565 under control of the bpp field 1944fbf5556SPeter Maydell * in the LCDControl register. 1954fbf5556SPeter Maydell */ 196242ea2c6SPeter Maydell switch (s->mux_ctrl) { 197242ea2c6SPeter Maydell case 3: /* 565 BGR */ 198242ea2c6SPeter Maydell bpp_offset = (BPP_16_565 - BPP_16); 199242ea2c6SPeter Maydell break; 200242ea2c6SPeter Maydell case 1: /* 5551 */ 201242ea2c6SPeter Maydell break; 202242ea2c6SPeter Maydell case 0: /* 888; also if we have loaded vmstate from an old version */ 203242ea2c6SPeter Maydell case 2: /* 565 RGB */ 204242ea2c6SPeter Maydell default: 205242ea2c6SPeter Maydell /* treat as 565 but honour BGR bit */ 2064fbf5556SPeter Maydell bpp_offset += (BPP_16_565 - BPP_16); 207242ea2c6SPeter Maydell break; 208242ea2c6SPeter Maydell } 2094fbf5556SPeter Maydell } 210e9c05b42Sbalrog 211e9c05b42Sbalrog if (s->cr & PL110_CR_BEBO) 2124fbf5556SPeter Maydell fn = fntable[s->bpp + 8 + bpp_offset]; 213e9c05b42Sbalrog else if (s->cr & PL110_CR_BEPO) 2144fbf5556SPeter Maydell fn = fntable[s->bpp + 16 + bpp_offset]; 215e9c05b42Sbalrog else 216e9c05b42Sbalrog fn = fntable[s->bpp + bpp_offset]; 217bdd5003aSpbrook 218bdd5003aSpbrook src_width = s->cols; 219bdd5003aSpbrook switch (s->bpp) { 220bdd5003aSpbrook case BPP_1: 221bdd5003aSpbrook src_width >>= 3; 222bdd5003aSpbrook break; 223bdd5003aSpbrook case BPP_2: 224bdd5003aSpbrook src_width >>= 2; 225bdd5003aSpbrook break; 226bdd5003aSpbrook case BPP_4: 227bdd5003aSpbrook src_width >>= 1; 228bdd5003aSpbrook break; 229bdd5003aSpbrook case BPP_8: 230bdd5003aSpbrook break; 231bdd5003aSpbrook case BPP_16: 2324fbf5556SPeter Maydell case BPP_16_565: 2334fbf5556SPeter Maydell case BPP_12: 234bdd5003aSpbrook src_width <<= 1; 235bdd5003aSpbrook break; 236bdd5003aSpbrook case BPP_32: 237bdd5003aSpbrook src_width <<= 2; 238bdd5003aSpbrook break; 239bdd5003aSpbrook } 240bdd5003aSpbrook dest_width *= s->cols; 241714fa308Spbrook first = 0; 242*c1076c3eSPaolo Bonzini if (s->invalidate) { 243*c1076c3eSPaolo Bonzini framebuffer_update_memory_section(&s->fbsection, 244*c1076c3eSPaolo Bonzini sysbus_address_space(sbd), 245*c1076c3eSPaolo Bonzini s->upbase, 246*c1076c3eSPaolo Bonzini s->rows, src_width); 247*c1076c3eSPaolo Bonzini } 248*c1076c3eSPaolo Bonzini 249*c1076c3eSPaolo Bonzini framebuffer_update_display(surface, &s->fbsection, 250*c1076c3eSPaolo Bonzini s->cols, s->rows, 251714fa308Spbrook src_width, dest_width, 0, 252714fa308Spbrook s->invalidate, 2536e4c0d1fSPeter Maydell fn, s->palette, 254714fa308Spbrook &first, &last); 255*c1076c3eSPaolo Bonzini 256714fa308Spbrook if (first >= 0) { 257c78f7137SGerd Hoffmann dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1); 258bdd5003aSpbrook } 259714fa308Spbrook s->invalidate = 0; 260714fa308Spbrook } 261bdd5003aSpbrook 26295219897Spbrook static void pl110_invalidate_display(void * opaque) 263bdd5003aSpbrook { 264513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 265bdd5003aSpbrook s->invalidate = 1; 266bfdb3629SBlue Swirl if (pl110_enabled(s)) { 267c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->cols, s->rows); 268bfdb3629SBlue Swirl } 269bdd5003aSpbrook } 270bdd5003aSpbrook 271513960eaSAndreas Färber static void pl110_update_palette(PL110State *s, int n) 272bdd5003aSpbrook { 273c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 274bdd5003aSpbrook int i; 275bdd5003aSpbrook uint32_t raw; 276bdd5003aSpbrook unsigned int r, g, b; 277bdd5003aSpbrook 2786e4c0d1fSPeter Maydell raw = s->raw_palette[n]; 279bdd5003aSpbrook n <<= 1; 280bdd5003aSpbrook for (i = 0; i < 2; i++) { 281bdd5003aSpbrook r = (raw & 0x1f) << 3; 282bdd5003aSpbrook raw >>= 5; 283bdd5003aSpbrook g = (raw & 0x1f) << 3; 284bdd5003aSpbrook raw >>= 5; 285bdd5003aSpbrook b = (raw & 0x1f) << 3; 286bdd5003aSpbrook /* The I bit is ignored. */ 287bdd5003aSpbrook raw >>= 6; 288c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 289bdd5003aSpbrook case 8: 2906e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel8(r, g, b); 291bdd5003aSpbrook break; 292bdd5003aSpbrook case 15: 2936e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel15(r, g, b); 294bdd5003aSpbrook break; 295bdd5003aSpbrook case 16: 2966e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel16(r, g, b); 297bdd5003aSpbrook break; 298bdd5003aSpbrook case 24: 299bdd5003aSpbrook case 32: 3006e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel32(r, g, b); 301bdd5003aSpbrook break; 302bdd5003aSpbrook } 303bdd5003aSpbrook n++; 304bdd5003aSpbrook } 305bdd5003aSpbrook } 306bdd5003aSpbrook 307513960eaSAndreas Färber static void pl110_resize(PL110State *s, int width, int height) 308bdd5003aSpbrook { 309bdd5003aSpbrook if (width != s->cols || height != s->rows) { 310bdd5003aSpbrook if (pl110_enabled(s)) { 311c78f7137SGerd Hoffmann qemu_console_resize(s->con, width, height); 312bdd5003aSpbrook } 313bdd5003aSpbrook } 314bdd5003aSpbrook s->cols = width; 315bdd5003aSpbrook s->rows = height; 316bdd5003aSpbrook } 317bdd5003aSpbrook 318bdd5003aSpbrook /* Update interrupts. */ 319513960eaSAndreas Färber static void pl110_update(PL110State *s) 320bdd5003aSpbrook { 321bdd5003aSpbrook /* TODO: Implement interrupts. */ 322bdd5003aSpbrook } 323bdd5003aSpbrook 324a8170e5eSAvi Kivity static uint64_t pl110_read(void *opaque, hwaddr offset, 3251a6b31ceSAvi Kivity unsigned size) 326bdd5003aSpbrook { 327513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 328bdd5003aSpbrook 329bdd5003aSpbrook if (offset >= 0xfe0 && offset < 0x1000) { 3304fbf5556SPeter Maydell return idregs[s->version][(offset - 0xfe0) >> 2]; 331bdd5003aSpbrook } 332bdd5003aSpbrook if (offset >= 0x200 && offset < 0x400) { 3336e4c0d1fSPeter Maydell return s->raw_palette[(offset - 0x200) >> 2]; 334bdd5003aSpbrook } 335bdd5003aSpbrook switch (offset >> 2) { 336bdd5003aSpbrook case 0: /* LCDTiming0 */ 337bdd5003aSpbrook return s->timing[0]; 338bdd5003aSpbrook case 1: /* LCDTiming1 */ 339bdd5003aSpbrook return s->timing[1]; 340bdd5003aSpbrook case 2: /* LCDTiming2 */ 341bdd5003aSpbrook return s->timing[2]; 342bdd5003aSpbrook case 3: /* LCDTiming3 */ 343bdd5003aSpbrook return s->timing[3]; 344bdd5003aSpbrook case 4: /* LCDUPBASE */ 345bdd5003aSpbrook return s->upbase; 346bdd5003aSpbrook case 5: /* LCDLPBASE */ 347bdd5003aSpbrook return s->lpbase; 348bdd5003aSpbrook case 6: /* LCDIMSC */ 3494fbf5556SPeter Maydell if (s->version != PL110) { 35064075cd7Spbrook return s->cr; 3514fbf5556SPeter Maydell } 352bdd5003aSpbrook return s->int_mask; 353bdd5003aSpbrook case 7: /* LCDControl */ 3544fbf5556SPeter Maydell if (s->version != PL110) { 35564075cd7Spbrook return s->int_mask; 3564fbf5556SPeter Maydell } 357bdd5003aSpbrook return s->cr; 358bdd5003aSpbrook case 8: /* LCDRIS */ 359bdd5003aSpbrook return s->int_status; 360bdd5003aSpbrook case 9: /* LCDMIS */ 361bdd5003aSpbrook return s->int_status & s->int_mask; 362bdd5003aSpbrook case 11: /* LCDUPCURR */ 363bdd5003aSpbrook /* TODO: Implement vertical refresh. */ 364bdd5003aSpbrook return s->upbase; 365bdd5003aSpbrook case 12: /* LCDLPCURR */ 366bdd5003aSpbrook return s->lpbase; 367bdd5003aSpbrook default: 368375cb560SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 369375cb560SPeter Maydell "pl110_read: Bad offset %x\n", (int)offset); 370bdd5003aSpbrook return 0; 371bdd5003aSpbrook } 372bdd5003aSpbrook } 373bdd5003aSpbrook 374a8170e5eSAvi Kivity static void pl110_write(void *opaque, hwaddr offset, 3751a6b31ceSAvi Kivity uint64_t val, unsigned size) 376bdd5003aSpbrook { 377513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 378bdd5003aSpbrook int n; 379bdd5003aSpbrook 380bdd5003aSpbrook /* For simplicity invalidate the display whenever a control register 38166a0a2cbSDong Xu Wang is written to. */ 382bdd5003aSpbrook s->invalidate = 1; 383bdd5003aSpbrook if (offset >= 0x200 && offset < 0x400) { 3846e4c0d1fSPeter Maydell /* Palette. */ 385bdd5003aSpbrook n = (offset - 0x200) >> 2; 3866e4c0d1fSPeter Maydell s->raw_palette[(offset - 0x200) >> 2] = val; 3876e4c0d1fSPeter Maydell pl110_update_palette(s, n); 388e10c2bfbSpbrook return; 389bdd5003aSpbrook } 390bdd5003aSpbrook switch (offset >> 2) { 391bdd5003aSpbrook case 0: /* LCDTiming0 */ 392bdd5003aSpbrook s->timing[0] = val; 393bdd5003aSpbrook n = ((val & 0xfc) + 4) * 4; 394bdd5003aSpbrook pl110_resize(s, n, s->rows); 395bdd5003aSpbrook break; 396bdd5003aSpbrook case 1: /* LCDTiming1 */ 397bdd5003aSpbrook s->timing[1] = val; 398bdd5003aSpbrook n = (val & 0x3ff) + 1; 399bdd5003aSpbrook pl110_resize(s, s->cols, n); 400bdd5003aSpbrook break; 401bdd5003aSpbrook case 2: /* LCDTiming2 */ 402bdd5003aSpbrook s->timing[2] = val; 403bdd5003aSpbrook break; 404bdd5003aSpbrook case 3: /* LCDTiming3 */ 405bdd5003aSpbrook s->timing[3] = val; 406bdd5003aSpbrook break; 407bdd5003aSpbrook case 4: /* LCDUPBASE */ 408bdd5003aSpbrook s->upbase = val; 409bdd5003aSpbrook break; 410bdd5003aSpbrook case 5: /* LCDLPBASE */ 411bdd5003aSpbrook s->lpbase = val; 412bdd5003aSpbrook break; 413bdd5003aSpbrook case 6: /* LCDIMSC */ 4144fbf5556SPeter Maydell if (s->version != PL110) { 415cdbdb648Spbrook goto control; 4164fbf5556SPeter Maydell } 417cdbdb648Spbrook imsc: 418bdd5003aSpbrook s->int_mask = val; 419bdd5003aSpbrook pl110_update(s); 420bdd5003aSpbrook break; 421bdd5003aSpbrook case 7: /* LCDControl */ 4224fbf5556SPeter Maydell if (s->version != PL110) { 423cdbdb648Spbrook goto imsc; 4244fbf5556SPeter Maydell } 425cdbdb648Spbrook control: 426bdd5003aSpbrook s->cr = val; 427bdd5003aSpbrook s->bpp = (val >> 1) & 7; 428bdd5003aSpbrook if (pl110_enabled(s)) { 429c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->cols, s->rows); 430bdd5003aSpbrook } 431bdd5003aSpbrook break; 432bdd5003aSpbrook case 10: /* LCDICR */ 433bdd5003aSpbrook s->int_status &= ~val; 434bdd5003aSpbrook pl110_update(s); 435bdd5003aSpbrook break; 436bdd5003aSpbrook default: 437375cb560SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 438375cb560SPeter Maydell "pl110_write: Bad offset %x\n", (int)offset); 439bdd5003aSpbrook } 440bdd5003aSpbrook } 441bdd5003aSpbrook 4421a6b31ceSAvi Kivity static const MemoryRegionOps pl110_ops = { 4431a6b31ceSAvi Kivity .read = pl110_read, 4441a6b31ceSAvi Kivity .write = pl110_write, 4451a6b31ceSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 446bdd5003aSpbrook }; 447bdd5003aSpbrook 448242ea2c6SPeter Maydell static void pl110_mux_ctrl_set(void *opaque, int line, int level) 449242ea2c6SPeter Maydell { 450513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 451242ea2c6SPeter Maydell s->mux_ctrl = level; 452242ea2c6SPeter Maydell } 453242ea2c6SPeter Maydell 454128939a9SPeter Maydell static int vmstate_pl110_post_load(void *opaque, int version_id) 455128939a9SPeter Maydell { 456513960eaSAndreas Färber PL110State *s = opaque; 457128939a9SPeter Maydell /* Make sure we redraw, and at the right size */ 458128939a9SPeter Maydell pl110_invalidate_display(s); 459128939a9SPeter Maydell return 0; 460128939a9SPeter Maydell } 461128939a9SPeter Maydell 462380cd056SGerd Hoffmann static const GraphicHwOps pl110_gfx_ops = { 463380cd056SGerd Hoffmann .invalidate = pl110_invalidate_display, 464380cd056SGerd Hoffmann .gfx_update = pl110_update_display, 465380cd056SGerd Hoffmann }; 466380cd056SGerd Hoffmann 4675d7a11e4SAndreas Färber static int pl110_initfn(SysBusDevice *sbd) 468bdd5003aSpbrook { 4695d7a11e4SAndreas Färber DeviceState *dev = DEVICE(sbd); 4705d7a11e4SAndreas Färber PL110State *s = PL110(dev); 471bdd5003aSpbrook 4723eadad55SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000); 4735d7a11e4SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 4745d7a11e4SAndreas Färber sysbus_init_irq(sbd, &s->irq); 4755d7a11e4SAndreas Färber qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1); 4765643706aSGerd Hoffmann s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s); 47781a322d4SGerd Hoffmann return 0; 478bdd5003aSpbrook } 4792e9bdce5SPaul Brook 4805d7a11e4SAndreas Färber static void pl110_init(Object *obj) 4812e9bdce5SPaul Brook { 4825d7a11e4SAndreas Färber PL110State *s = PL110(obj); 4835d7a11e4SAndreas Färber 4845d7a11e4SAndreas Färber s->version = PL110; 4854fbf5556SPeter Maydell } 4864fbf5556SPeter Maydell 4875d7a11e4SAndreas Färber static void pl110_versatile_init(Object *obj) 4884fbf5556SPeter Maydell { 4895d7a11e4SAndreas Färber PL110State *s = PL110(obj); 4905d7a11e4SAndreas Färber 4915d7a11e4SAndreas Färber s->version = PL110_VERSATILE; 4925d7a11e4SAndreas Färber } 4935d7a11e4SAndreas Färber 4945d7a11e4SAndreas Färber static void pl111_init(Object *obj) 4955d7a11e4SAndreas Färber { 4965d7a11e4SAndreas Färber PL110State *s = PL110(obj); 4975d7a11e4SAndreas Färber 4984fbf5556SPeter Maydell s->version = PL111; 4992e9bdce5SPaul Brook } 5002e9bdce5SPaul Brook 501999e12bbSAnthony Liguori static void pl110_class_init(ObjectClass *klass, void *data) 502999e12bbSAnthony Liguori { 50339bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 504999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 505999e12bbSAnthony Liguori 5065d7a11e4SAndreas Färber k->init = pl110_initfn; 507125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); 50839bffca2SAnthony Liguori dc->vmsd = &vmstate_pl110; 509999e12bbSAnthony Liguori } 510999e12bbSAnthony Liguori 5118c43a6f0SAndreas Färber static const TypeInfo pl110_info = { 5125d7a11e4SAndreas Färber .name = TYPE_PL110, 51339bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 514513960eaSAndreas Färber .instance_size = sizeof(PL110State), 5155d7a11e4SAndreas Färber .instance_init = pl110_init, 516999e12bbSAnthony Liguori .class_init = pl110_class_init, 5178c60d065SPeter Maydell }; 5188c60d065SPeter Maydell 5198c43a6f0SAndreas Färber static const TypeInfo pl110_versatile_info = { 520999e12bbSAnthony Liguori .name = "pl110_versatile", 5215d7a11e4SAndreas Färber .parent = TYPE_PL110, 5225d7a11e4SAndreas Färber .instance_init = pl110_versatile_init, 5238c60d065SPeter Maydell }; 5248c60d065SPeter Maydell 5258c43a6f0SAndreas Färber static const TypeInfo pl111_info = { 526999e12bbSAnthony Liguori .name = "pl111", 5275d7a11e4SAndreas Färber .parent = TYPE_PL110, 5285d7a11e4SAndreas Färber .instance_init = pl111_init, 5294fbf5556SPeter Maydell }; 5304fbf5556SPeter Maydell 53183f7d43aSAndreas Färber static void pl110_register_types(void) 5322e9bdce5SPaul Brook { 53339bffca2SAnthony Liguori type_register_static(&pl110_info); 53439bffca2SAnthony Liguori type_register_static(&pl110_versatile_info); 53539bffca2SAnthony Liguori type_register_static(&pl111_info); 5362e9bdce5SPaul Brook } 5372e9bdce5SPaul Brook 53883f7d43aSAndreas Färber type_init(pl110_register_types) 539