15e9c2a8dSGrégory ESTRADE /* 25e9c2a8dSGrégory ESTRADE * Raspberry Pi emulation (c) 2012 Gregory Estrade 35e9c2a8dSGrégory ESTRADE * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann. 45e9c2a8dSGrégory ESTRADE * This code is licensed under the GNU GPLv2 and later. 55e9c2a8dSGrégory ESTRADE * 65e9c2a8dSGrégory ESTRADE * Heavily based on milkymist-vgafb.c, copyright terms below: 75e9c2a8dSGrégory ESTRADE * QEMU model of the Milkymist VGA framebuffer. 85e9c2a8dSGrégory ESTRADE * 95e9c2a8dSGrégory ESTRADE * Copyright (c) 2010-2012 Michael Walle <michael@walle.cc> 105e9c2a8dSGrégory ESTRADE * 115e9c2a8dSGrégory ESTRADE * This library is free software; you can redistribute it and/or 125e9c2a8dSGrégory ESTRADE * modify it under the terms of the GNU Lesser General Public 135e9c2a8dSGrégory ESTRADE * License as published by the Free Software Foundation; either 145e9c2a8dSGrégory ESTRADE * version 2 of the License, or (at your option) any later version. 155e9c2a8dSGrégory ESTRADE * 165e9c2a8dSGrégory ESTRADE * This library is distributed in the hope that it will be useful, 175e9c2a8dSGrégory ESTRADE * but WITHOUT ANY WARRANTY; without even the implied warranty of 185e9c2a8dSGrégory ESTRADE * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 195e9c2a8dSGrégory ESTRADE * Lesser General Public License for more details. 205e9c2a8dSGrégory ESTRADE * 215e9c2a8dSGrégory ESTRADE * You should have received a copy of the GNU Lesser General Public 225e9c2a8dSGrégory ESTRADE * License along with this library; if not, see <http://www.gnu.org/licenses/>. 235e9c2a8dSGrégory ESTRADE * 245e9c2a8dSGrégory ESTRADE */ 255e9c2a8dSGrégory ESTRADE 265e9c2a8dSGrégory ESTRADE #include "qemu/osdep.h" 27da34e65cSMarkus Armbruster #include "qapi/error.h" 285e9c2a8dSGrégory ESTRADE #include "hw/display/bcm2835_fb.h" 29650d103dSMarkus Armbruster #include "hw/hw.h" 3064552b6bSMarkus Armbruster #include "hw/irq.h" 31e921d6efSMichael S. Tsirkin #include "framebuffer.h" 325e9c2a8dSGrégory ESTRADE #include "ui/pixel_ops.h" 335e9c2a8dSGrégory ESTRADE #include "hw/misc/bcm2835_mbox_defs.h" 34a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 35d6454270SMarkus Armbruster #include "migration/vmstate.h" 3603dd024fSPaolo Bonzini #include "qemu/log.h" 370b8fa32fSMarkus Armbruster #include "qemu/module.h" 385e9c2a8dSGrégory ESTRADE 395e9c2a8dSGrégory ESTRADE #define DEFAULT_VCRAM_SIZE 0x4000000 405e9c2a8dSGrégory ESTRADE #define BCM2835_FB_OFFSET 0x00100000 415e9c2a8dSGrégory ESTRADE 42f8add62cSPeter Maydell /* Maximum permitted framebuffer size; experimentally determined on an rpi2 */ 43f8add62cSPeter Maydell #define XRES_MAX 3840 44f8add62cSPeter Maydell #define YRES_MAX 2560 45f8add62cSPeter Maydell /* Framebuffer size used if guest requests zero size */ 46f8add62cSPeter Maydell #define XRES_SMALL 592 47f8add62cSPeter Maydell #define YRES_SMALL 488 48f8add62cSPeter Maydell 495e9c2a8dSGrégory ESTRADE static void fb_invalidate_display(void *opaque) 505e9c2a8dSGrégory ESTRADE { 515e9c2a8dSGrégory ESTRADE BCM2835FBState *s = BCM2835_FB(opaque); 525e9c2a8dSGrégory ESTRADE 535e9c2a8dSGrégory ESTRADE s->invalidate = true; 545e9c2a8dSGrégory ESTRADE } 555e9c2a8dSGrégory ESTRADE 565e9c2a8dSGrégory ESTRADE static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, 575e9c2a8dSGrégory ESTRADE int width, int deststep) 585e9c2a8dSGrégory ESTRADE { 595e9c2a8dSGrégory ESTRADE BCM2835FBState *s = opaque; 605e9c2a8dSGrégory ESTRADE uint16_t rgb565; 615e9c2a8dSGrégory ESTRADE uint32_t rgb888; 625e9c2a8dSGrégory ESTRADE uint8_t r, g, b; 635e9c2a8dSGrégory ESTRADE DisplaySurface *surface = qemu_console_surface(s->con); 645e9c2a8dSGrégory ESTRADE int bpp = surface_bits_per_pixel(surface); 655e9c2a8dSGrégory ESTRADE 665e9c2a8dSGrégory ESTRADE while (width--) { 67a02755ecSPeter Maydell switch (s->config.bpp) { 685e9c2a8dSGrégory ESTRADE case 8: 695e9c2a8dSGrégory ESTRADE /* lookup palette starting at video ram base 705e9c2a8dSGrégory ESTRADE * TODO: cache translation, rather than doing this each time! 715e9c2a8dSGrégory ESTRADE */ 725e9c2a8dSGrégory ESTRADE rgb888 = ldl_le_phys(&s->dma_as, s->vcram_base + (*src << 2)); 735e9c2a8dSGrégory ESTRADE r = (rgb888 >> 0) & 0xff; 745e9c2a8dSGrégory ESTRADE g = (rgb888 >> 8) & 0xff; 755e9c2a8dSGrégory ESTRADE b = (rgb888 >> 16) & 0xff; 765e9c2a8dSGrégory ESTRADE src++; 775e9c2a8dSGrégory ESTRADE break; 785e9c2a8dSGrégory ESTRADE case 16: 795e9c2a8dSGrégory ESTRADE rgb565 = lduw_le_p(src); 805e9c2a8dSGrégory ESTRADE r = ((rgb565 >> 11) & 0x1f) << 3; 815e9c2a8dSGrégory ESTRADE g = ((rgb565 >> 5) & 0x3f) << 2; 825e9c2a8dSGrégory ESTRADE b = ((rgb565 >> 0) & 0x1f) << 3; 835e9c2a8dSGrégory ESTRADE src += 2; 845e9c2a8dSGrégory ESTRADE break; 855e9c2a8dSGrégory ESTRADE case 24: 865e9c2a8dSGrégory ESTRADE rgb888 = ldl_le_p(src); 875e9c2a8dSGrégory ESTRADE r = (rgb888 >> 0) & 0xff; 885e9c2a8dSGrégory ESTRADE g = (rgb888 >> 8) & 0xff; 895e9c2a8dSGrégory ESTRADE b = (rgb888 >> 16) & 0xff; 905e9c2a8dSGrégory ESTRADE src += 3; 915e9c2a8dSGrégory ESTRADE break; 925e9c2a8dSGrégory ESTRADE case 32: 935e9c2a8dSGrégory ESTRADE rgb888 = ldl_le_p(src); 945e9c2a8dSGrégory ESTRADE r = (rgb888 >> 0) & 0xff; 955e9c2a8dSGrégory ESTRADE g = (rgb888 >> 8) & 0xff; 965e9c2a8dSGrégory ESTRADE b = (rgb888 >> 16) & 0xff; 975e9c2a8dSGrégory ESTRADE src += 4; 985e9c2a8dSGrégory ESTRADE break; 995e9c2a8dSGrégory ESTRADE default: 1005e9c2a8dSGrégory ESTRADE r = 0; 1015e9c2a8dSGrégory ESTRADE g = 0; 1025e9c2a8dSGrégory ESTRADE b = 0; 1035e9c2a8dSGrégory ESTRADE break; 1045e9c2a8dSGrégory ESTRADE } 1055e9c2a8dSGrégory ESTRADE 106a02755ecSPeter Maydell if (s->config.pixo == 0) { 1075e9c2a8dSGrégory ESTRADE /* swap to BGR pixel format */ 1085e9c2a8dSGrégory ESTRADE uint8_t tmp = r; 1095e9c2a8dSGrégory ESTRADE r = b; 1105e9c2a8dSGrégory ESTRADE b = tmp; 1115e9c2a8dSGrégory ESTRADE } 1125e9c2a8dSGrégory ESTRADE 1135e9c2a8dSGrégory ESTRADE switch (bpp) { 1145e9c2a8dSGrégory ESTRADE case 8: 1155e9c2a8dSGrégory ESTRADE *dst++ = rgb_to_pixel8(r, g, b); 1165e9c2a8dSGrégory ESTRADE break; 1175e9c2a8dSGrégory ESTRADE case 15: 1185e9c2a8dSGrégory ESTRADE *(uint16_t *)dst = rgb_to_pixel15(r, g, b); 1195e9c2a8dSGrégory ESTRADE dst += 2; 1205e9c2a8dSGrégory ESTRADE break; 1215e9c2a8dSGrégory ESTRADE case 16: 1225e9c2a8dSGrégory ESTRADE *(uint16_t *)dst = rgb_to_pixel16(r, g, b); 1235e9c2a8dSGrégory ESTRADE dst += 2; 1245e9c2a8dSGrégory ESTRADE break; 1255e9c2a8dSGrégory ESTRADE case 24: 1265e9c2a8dSGrégory ESTRADE rgb888 = rgb_to_pixel24(r, g, b); 1275e9c2a8dSGrégory ESTRADE *dst++ = rgb888 & 0xff; 1285e9c2a8dSGrégory ESTRADE *dst++ = (rgb888 >> 8) & 0xff; 1295e9c2a8dSGrégory ESTRADE *dst++ = (rgb888 >> 16) & 0xff; 1305e9c2a8dSGrégory ESTRADE break; 1315e9c2a8dSGrégory ESTRADE case 32: 1325e9c2a8dSGrégory ESTRADE *(uint32_t *)dst = rgb_to_pixel32(r, g, b); 1335e9c2a8dSGrégory ESTRADE dst += 4; 1345e9c2a8dSGrégory ESTRADE break; 1355e9c2a8dSGrégory ESTRADE default: 1365e9c2a8dSGrégory ESTRADE return; 1375e9c2a8dSGrégory ESTRADE } 1385e9c2a8dSGrégory ESTRADE } 1395e9c2a8dSGrégory ESTRADE } 1405e9c2a8dSGrégory ESTRADE 14101f18af9SPeter Maydell static bool fb_use_offsets(BCM2835FBConfig *config) 14201f18af9SPeter Maydell { 14301f18af9SPeter Maydell /* 14401f18af9SPeter Maydell * Return true if we should use the viewport offsets. 14501f18af9SPeter Maydell * Experimentally, the hardware seems to do this only if the 14601f18af9SPeter Maydell * viewport size is larger than the physical screen. (It doesn't 14701f18af9SPeter Maydell * prevent the guest setting this silly viewport setting, though...) 14801f18af9SPeter Maydell */ 14901f18af9SPeter Maydell return config->xres_virtual > config->xres && 15001f18af9SPeter Maydell config->yres_virtual > config->yres; 15101f18af9SPeter Maydell } 15201f18af9SPeter Maydell 1535e9c2a8dSGrégory ESTRADE static void fb_update_display(void *opaque) 1545e9c2a8dSGrégory ESTRADE { 1555e9c2a8dSGrégory ESTRADE BCM2835FBState *s = opaque; 1565e9c2a8dSGrégory ESTRADE DisplaySurface *surface = qemu_console_surface(s->con); 1575e9c2a8dSGrégory ESTRADE int first = 0; 1585e9c2a8dSGrégory ESTRADE int last = 0; 1595e9c2a8dSGrégory ESTRADE int src_width = 0; 1605e9c2a8dSGrégory ESTRADE int dest_width = 0; 16101f18af9SPeter Maydell uint32_t xoff = 0, yoff = 0; 1625e9c2a8dSGrégory ESTRADE 163a02755ecSPeter Maydell if (s->lock || !s->config.xres) { 1645e9c2a8dSGrégory ESTRADE return; 1655e9c2a8dSGrégory ESTRADE } 1665e9c2a8dSGrégory ESTRADE 1679a1f03f4SPeter Maydell src_width = bcm2835_fb_get_pitch(&s->config); 16801f18af9SPeter Maydell if (fb_use_offsets(&s->config)) { 16901f18af9SPeter Maydell xoff = s->config.xoffset; 17001f18af9SPeter Maydell yoff = s->config.yoffset; 17101f18af9SPeter Maydell } 17201f18af9SPeter Maydell 173a02755ecSPeter Maydell dest_width = s->config.xres; 1745e9c2a8dSGrégory ESTRADE 1755e9c2a8dSGrégory ESTRADE switch (surface_bits_per_pixel(surface)) { 1765e9c2a8dSGrégory ESTRADE case 0: 1775e9c2a8dSGrégory ESTRADE return; 1785e9c2a8dSGrégory ESTRADE case 8: 1795e9c2a8dSGrégory ESTRADE break; 1805e9c2a8dSGrégory ESTRADE case 15: 1815e9c2a8dSGrégory ESTRADE dest_width *= 2; 1825e9c2a8dSGrégory ESTRADE break; 1835e9c2a8dSGrégory ESTRADE case 16: 1845e9c2a8dSGrégory ESTRADE dest_width *= 2; 1855e9c2a8dSGrégory ESTRADE break; 1865e9c2a8dSGrégory ESTRADE case 24: 1875e9c2a8dSGrégory ESTRADE dest_width *= 3; 1885e9c2a8dSGrégory ESTRADE break; 1895e9c2a8dSGrégory ESTRADE case 32: 1905e9c2a8dSGrégory ESTRADE dest_width *= 4; 1915e9c2a8dSGrégory ESTRADE break; 1925e9c2a8dSGrégory ESTRADE default: 1935e9c2a8dSGrégory ESTRADE hw_error("bcm2835_fb: bad color depth\n"); 1945e9c2a8dSGrégory ESTRADE break; 1955e9c2a8dSGrégory ESTRADE } 1965e9c2a8dSGrégory ESTRADE 1975e9c2a8dSGrégory ESTRADE if (s->invalidate) { 19874e2e59bSPeter Maydell hwaddr base = s->config.base + xoff + (hwaddr)yoff * src_width; 199a02755ecSPeter Maydell framebuffer_update_memory_section(&s->fbsection, s->dma_mr, 20001f18af9SPeter Maydell base, 201a02755ecSPeter Maydell s->config.yres, src_width); 2025e9c2a8dSGrégory ESTRADE } 2035e9c2a8dSGrégory ESTRADE 204a02755ecSPeter Maydell framebuffer_update_display(surface, &s->fbsection, 205a02755ecSPeter Maydell s->config.xres, s->config.yres, 2065e9c2a8dSGrégory ESTRADE src_width, dest_width, 0, s->invalidate, 2075e9c2a8dSGrégory ESTRADE draw_line_src16, s, &first, &last); 2085e9c2a8dSGrégory ESTRADE 2095e9c2a8dSGrégory ESTRADE if (first >= 0) { 21001f18af9SPeter Maydell dpy_gfx_update(s->con, 0, first, s->config.xres, 21101f18af9SPeter Maydell last - first + 1); 2125e9c2a8dSGrégory ESTRADE } 2135e9c2a8dSGrégory ESTRADE 2145e9c2a8dSGrégory ESTRADE s->invalidate = false; 2155e9c2a8dSGrégory ESTRADE } 2165e9c2a8dSGrégory ESTRADE 217f8add62cSPeter Maydell void bcm2835_fb_validate_config(BCM2835FBConfig *config) 218f8add62cSPeter Maydell { 219f8add62cSPeter Maydell /* 220f8add62cSPeter Maydell * Validate the config, and clip any bogus values into range, 221f8add62cSPeter Maydell * as the hardware does. Note that fb_update_display() relies on 222f8add62cSPeter Maydell * this happening to prevent it from performing out-of-range 223f8add62cSPeter Maydell * accesses on redraw. 224f8add62cSPeter Maydell */ 225f8add62cSPeter Maydell config->xres = MIN(config->xres, XRES_MAX); 226f8add62cSPeter Maydell config->xres_virtual = MIN(config->xres_virtual, XRES_MAX); 227f8add62cSPeter Maydell config->yres = MIN(config->yres, YRES_MAX); 228f8add62cSPeter Maydell config->yres_virtual = MIN(config->yres_virtual, YRES_MAX); 229f8add62cSPeter Maydell 230f8add62cSPeter Maydell /* 231f8add62cSPeter Maydell * These are not minima: a 40x40 framebuffer will be accepted. 232f8add62cSPeter Maydell * They're only used as defaults if the guest asks for zero size. 233f8add62cSPeter Maydell */ 234f8add62cSPeter Maydell if (config->xres == 0) { 235f8add62cSPeter Maydell config->xres = XRES_SMALL; 236f8add62cSPeter Maydell } 237f8add62cSPeter Maydell if (config->yres == 0) { 238f8add62cSPeter Maydell config->yres = YRES_SMALL; 239f8add62cSPeter Maydell } 240f8add62cSPeter Maydell if (config->xres_virtual == 0) { 241f8add62cSPeter Maydell config->xres_virtual = config->xres; 242f8add62cSPeter Maydell } 243f8add62cSPeter Maydell if (config->yres_virtual == 0) { 244f8add62cSPeter Maydell config->yres_virtual = config->yres; 245f8add62cSPeter Maydell } 246f8add62cSPeter Maydell 247f8add62cSPeter Maydell if (fb_use_offsets(config)) { 248f8add62cSPeter Maydell /* Clip the offsets so the viewport is within the physical screen */ 249f8add62cSPeter Maydell config->xoffset = MIN(config->xoffset, 250f8add62cSPeter Maydell config->xres_virtual - config->xres); 251f8add62cSPeter Maydell config->yoffset = MIN(config->yoffset, 252f8add62cSPeter Maydell config->yres_virtual - config->yres); 253f8add62cSPeter Maydell } 254f8add62cSPeter Maydell } 255f8add62cSPeter Maydell 256193100b5SPeter Maydell void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig) 2575e9c2a8dSGrégory ESTRADE { 2585e9c2a8dSGrégory ESTRADE s->lock = true; 2595e9c2a8dSGrégory ESTRADE 260193100b5SPeter Maydell s->config = *newconfig; 2615e9c2a8dSGrégory ESTRADE 2625e9c2a8dSGrégory ESTRADE s->invalidate = true; 263a02755ecSPeter Maydell qemu_console_resize(s->con, s->config.xres, s->config.yres); 2645e9c2a8dSGrégory ESTRADE s->lock = false; 2655e9c2a8dSGrégory ESTRADE } 2665e9c2a8dSGrégory ESTRADE 267cfb7ba98SPeter Maydell static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) 268cfb7ba98SPeter Maydell { 269cfb7ba98SPeter Maydell uint32_t pitch; 270cfb7ba98SPeter Maydell uint32_t size; 271cfb7ba98SPeter Maydell BCM2835FBConfig newconf; 272cfb7ba98SPeter Maydell 273cfb7ba98SPeter Maydell value &= ~0xf; 274cfb7ba98SPeter Maydell 275cfb7ba98SPeter Maydell newconf.xres = ldl_le_phys(&s->dma_as, value); 276cfb7ba98SPeter Maydell newconf.yres = ldl_le_phys(&s->dma_as, value + 4); 277cfb7ba98SPeter Maydell newconf.xres_virtual = ldl_le_phys(&s->dma_as, value + 8); 278cfb7ba98SPeter Maydell newconf.yres_virtual = ldl_le_phys(&s->dma_as, value + 12); 279cfb7ba98SPeter Maydell newconf.bpp = ldl_le_phys(&s->dma_as, value + 20); 280cfb7ba98SPeter Maydell newconf.xoffset = ldl_le_phys(&s->dma_as, value + 24); 281cfb7ba98SPeter Maydell newconf.yoffset = ldl_le_phys(&s->dma_as, value + 28); 282cfb7ba98SPeter Maydell 283cfb7ba98SPeter Maydell newconf.base = s->vcram_base | (value & 0xc0000000); 284cfb7ba98SPeter Maydell newconf.base += BCM2835_FB_OFFSET; 285cfb7ba98SPeter Maydell 286cfb7ba98SPeter Maydell bcm2835_fb_validate_config(&newconf); 287cfb7ba98SPeter Maydell 288cfb7ba98SPeter Maydell pitch = bcm2835_fb_get_pitch(&newconf); 289cfb7ba98SPeter Maydell size = bcm2835_fb_get_size(&newconf); 290cfb7ba98SPeter Maydell 291cfb7ba98SPeter Maydell stl_le_phys(&s->dma_as, value + 16, pitch); 292cfb7ba98SPeter Maydell stl_le_phys(&s->dma_as, value + 32, newconf.base); 293cfb7ba98SPeter Maydell stl_le_phys(&s->dma_as, value + 36, size); 294cfb7ba98SPeter Maydell 295cfb7ba98SPeter Maydell bcm2835_fb_reconfigure(s, &newconf); 296cfb7ba98SPeter Maydell } 297cfb7ba98SPeter Maydell 2985e9c2a8dSGrégory ESTRADE static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size) 2995e9c2a8dSGrégory ESTRADE { 3005e9c2a8dSGrégory ESTRADE BCM2835FBState *s = opaque; 3015e9c2a8dSGrégory ESTRADE uint32_t res = 0; 3025e9c2a8dSGrégory ESTRADE 3035e9c2a8dSGrégory ESTRADE switch (offset) { 3045e9c2a8dSGrégory ESTRADE case MBOX_AS_DATA: 3055e9c2a8dSGrégory ESTRADE res = MBOX_CHAN_FB; 3065e9c2a8dSGrégory ESTRADE s->pending = false; 3075e9c2a8dSGrégory ESTRADE qemu_set_irq(s->mbox_irq, 0); 3085e9c2a8dSGrégory ESTRADE break; 3095e9c2a8dSGrégory ESTRADE 3105e9c2a8dSGrégory ESTRADE case MBOX_AS_PENDING: 3115e9c2a8dSGrégory ESTRADE res = s->pending; 3125e9c2a8dSGrégory ESTRADE break; 3135e9c2a8dSGrégory ESTRADE 3145e9c2a8dSGrégory ESTRADE default: 3155e9c2a8dSGrégory ESTRADE qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", 3165e9c2a8dSGrégory ESTRADE __func__, offset); 3175e9c2a8dSGrégory ESTRADE return 0; 3185e9c2a8dSGrégory ESTRADE } 3195e9c2a8dSGrégory ESTRADE 3205e9c2a8dSGrégory ESTRADE return res; 3215e9c2a8dSGrégory ESTRADE } 3225e9c2a8dSGrégory ESTRADE 3235e9c2a8dSGrégory ESTRADE static void bcm2835_fb_write(void *opaque, hwaddr offset, uint64_t value, 3245e9c2a8dSGrégory ESTRADE unsigned size) 3255e9c2a8dSGrégory ESTRADE { 3265e9c2a8dSGrégory ESTRADE BCM2835FBState *s = opaque; 3275e9c2a8dSGrégory ESTRADE 3285e9c2a8dSGrégory ESTRADE switch (offset) { 3295e9c2a8dSGrégory ESTRADE case MBOX_AS_DATA: 3305e9c2a8dSGrégory ESTRADE /* bcm2835_mbox should check our pending status before pushing */ 3315e9c2a8dSGrégory ESTRADE assert(!s->pending); 3325e9c2a8dSGrégory ESTRADE s->pending = true; 3335e9c2a8dSGrégory ESTRADE bcm2835_fb_mbox_push(s, value); 3345e9c2a8dSGrégory ESTRADE qemu_set_irq(s->mbox_irq, 1); 3355e9c2a8dSGrégory ESTRADE break; 3365e9c2a8dSGrégory ESTRADE 3375e9c2a8dSGrégory ESTRADE default: 3385e9c2a8dSGrégory ESTRADE qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", 3395e9c2a8dSGrégory ESTRADE __func__, offset); 3405e9c2a8dSGrégory ESTRADE return; 3415e9c2a8dSGrégory ESTRADE } 3425e9c2a8dSGrégory ESTRADE } 3435e9c2a8dSGrégory ESTRADE 3445e9c2a8dSGrégory ESTRADE static const MemoryRegionOps bcm2835_fb_ops = { 3455e9c2a8dSGrégory ESTRADE .read = bcm2835_fb_read, 3465e9c2a8dSGrégory ESTRADE .write = bcm2835_fb_write, 3475e9c2a8dSGrégory ESTRADE .endianness = DEVICE_NATIVE_ENDIAN, 3485e9c2a8dSGrégory ESTRADE .valid.min_access_size = 4, 3495e9c2a8dSGrégory ESTRADE .valid.max_access_size = 4, 3505e9c2a8dSGrégory ESTRADE }; 3515e9c2a8dSGrégory ESTRADE 3525e9c2a8dSGrégory ESTRADE static const VMStateDescription vmstate_bcm2835_fb = { 3535e9c2a8dSGrégory ESTRADE .name = TYPE_BCM2835_FB, 3545e9c2a8dSGrégory ESTRADE .version_id = 1, 3555e9c2a8dSGrégory ESTRADE .minimum_version_id = 1, 3565e9c2a8dSGrégory ESTRADE .fields = (VMStateField[]) { 3575e9c2a8dSGrégory ESTRADE VMSTATE_BOOL(lock, BCM2835FBState), 3585e9c2a8dSGrégory ESTRADE VMSTATE_BOOL(invalidate, BCM2835FBState), 3595e9c2a8dSGrégory ESTRADE VMSTATE_BOOL(pending, BCM2835FBState), 360a02755ecSPeter Maydell VMSTATE_UINT32(config.xres, BCM2835FBState), 361a02755ecSPeter Maydell VMSTATE_UINT32(config.yres, BCM2835FBState), 362a02755ecSPeter Maydell VMSTATE_UINT32(config.xres_virtual, BCM2835FBState), 363a02755ecSPeter Maydell VMSTATE_UINT32(config.yres_virtual, BCM2835FBState), 364a02755ecSPeter Maydell VMSTATE_UINT32(config.xoffset, BCM2835FBState), 365a02755ecSPeter Maydell VMSTATE_UINT32(config.yoffset, BCM2835FBState), 366a02755ecSPeter Maydell VMSTATE_UINT32(config.bpp, BCM2835FBState), 367a02755ecSPeter Maydell VMSTATE_UINT32(config.base, BCM2835FBState), 368ea662f7cSPeter Maydell VMSTATE_UNUSED(8), /* Was pitch and size */ 369a02755ecSPeter Maydell VMSTATE_UINT32(config.pixo, BCM2835FBState), 370a02755ecSPeter Maydell VMSTATE_UINT32(config.alpha, BCM2835FBState), 3715e9c2a8dSGrégory ESTRADE VMSTATE_END_OF_LIST() 3725e9c2a8dSGrégory ESTRADE } 3735e9c2a8dSGrégory ESTRADE }; 3745e9c2a8dSGrégory ESTRADE 3755e9c2a8dSGrégory ESTRADE static const GraphicHwOps vgafb_ops = { 3765e9c2a8dSGrégory ESTRADE .invalidate = fb_invalidate_display, 3775e9c2a8dSGrégory ESTRADE .gfx_update = fb_update_display, 3785e9c2a8dSGrégory ESTRADE }; 3795e9c2a8dSGrégory ESTRADE 3805e9c2a8dSGrégory ESTRADE static void bcm2835_fb_init(Object *obj) 3815e9c2a8dSGrégory ESTRADE { 3825e9c2a8dSGrégory ESTRADE BCM2835FBState *s = BCM2835_FB(obj); 3835e9c2a8dSGrégory ESTRADE 3845e9c2a8dSGrégory ESTRADE memory_region_init_io(&s->iomem, obj, &bcm2835_fb_ops, s, TYPE_BCM2835_FB, 3855e9c2a8dSGrégory ESTRADE 0x10); 3865e9c2a8dSGrégory ESTRADE sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); 3875e9c2a8dSGrégory ESTRADE sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq); 3885e9c2a8dSGrégory ESTRADE } 3895e9c2a8dSGrégory ESTRADE 3905e9c2a8dSGrégory ESTRADE static void bcm2835_fb_reset(DeviceState *dev) 3915e9c2a8dSGrégory ESTRADE { 3925e9c2a8dSGrégory ESTRADE BCM2835FBState *s = BCM2835_FB(dev); 3935e9c2a8dSGrégory ESTRADE 3945e9c2a8dSGrégory ESTRADE s->pending = false; 3955e9c2a8dSGrégory ESTRADE 3969e2938a0SPeter Maydell s->config = s->initial_config; 3975e9c2a8dSGrégory ESTRADE 3985e9c2a8dSGrégory ESTRADE s->invalidate = true; 3995e9c2a8dSGrégory ESTRADE s->lock = false; 4005e9c2a8dSGrégory ESTRADE } 4015e9c2a8dSGrégory ESTRADE 4025e9c2a8dSGrégory ESTRADE static void bcm2835_fb_realize(DeviceState *dev, Error **errp) 4035e9c2a8dSGrégory ESTRADE { 4045e9c2a8dSGrégory ESTRADE BCM2835FBState *s = BCM2835_FB(dev); 4055e9c2a8dSGrégory ESTRADE Error *err = NULL; 4065e9c2a8dSGrégory ESTRADE Object *obj; 4075e9c2a8dSGrégory ESTRADE 4085e9c2a8dSGrégory ESTRADE if (s->vcram_base == 0) { 4095e9c2a8dSGrégory ESTRADE error_setg(errp, "%s: required vcram-base property not set", __func__); 4105e9c2a8dSGrégory ESTRADE return; 4115e9c2a8dSGrégory ESTRADE } 4125e9c2a8dSGrégory ESTRADE 4135e9c2a8dSGrégory ESTRADE obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); 4145e9c2a8dSGrégory ESTRADE if (obj == NULL) { 4155e9c2a8dSGrégory ESTRADE error_setg(errp, "%s: required dma-mr link not found: %s", 4165e9c2a8dSGrégory ESTRADE __func__, error_get_pretty(err)); 4175e9c2a8dSGrégory ESTRADE return; 4185e9c2a8dSGrégory ESTRADE } 4195e9c2a8dSGrégory ESTRADE 4209e2938a0SPeter Maydell /* Fill in the parts of initial_config that are not set by QOM properties */ 4219e2938a0SPeter Maydell s->initial_config.xres_virtual = s->initial_config.xres; 4229e2938a0SPeter Maydell s->initial_config.yres_virtual = s->initial_config.yres; 4239e2938a0SPeter Maydell s->initial_config.xoffset = 0; 4249e2938a0SPeter Maydell s->initial_config.yoffset = 0; 4259e2938a0SPeter Maydell s->initial_config.base = s->vcram_base + BCM2835_FB_OFFSET; 4269e2938a0SPeter Maydell 4275e9c2a8dSGrégory ESTRADE s->dma_mr = MEMORY_REGION(obj); 428*e55a8b37SPhilippe Mathieu-Daudé address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_FB "-memory"); 4295e9c2a8dSGrégory ESTRADE 4305e9c2a8dSGrégory ESTRADE bcm2835_fb_reset(dev); 4315e9c2a8dSGrégory ESTRADE 4325e9c2a8dSGrégory ESTRADE s->con = graphic_console_init(dev, 0, &vgafb_ops, s); 433a02755ecSPeter Maydell qemu_console_resize(s->con, s->config.xres, s->config.yres); 4345e9c2a8dSGrégory ESTRADE } 4355e9c2a8dSGrégory ESTRADE 4365e9c2a8dSGrégory ESTRADE static Property bcm2835_fb_props[] = { 4375e9c2a8dSGrégory ESTRADE DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/ 4385e9c2a8dSGrégory ESTRADE DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size, 4395e9c2a8dSGrégory ESTRADE DEFAULT_VCRAM_SIZE), 4409e2938a0SPeter Maydell DEFINE_PROP_UINT32("xres", BCM2835FBState, initial_config.xres, 640), 4419e2938a0SPeter Maydell DEFINE_PROP_UINT32("yres", BCM2835FBState, initial_config.yres, 480), 4429e2938a0SPeter Maydell DEFINE_PROP_UINT32("bpp", BCM2835FBState, initial_config.bpp, 16), 4439e2938a0SPeter Maydell DEFINE_PROP_UINT32("pixo", BCM2835FBState, 4449e2938a0SPeter Maydell initial_config.pixo, 1), /* 1=RGB, 0=BGR */ 4459e2938a0SPeter Maydell DEFINE_PROP_UINT32("alpha", BCM2835FBState, 4469e2938a0SPeter Maydell initial_config.alpha, 2), /* alpha ignored */ 4475e9c2a8dSGrégory ESTRADE DEFINE_PROP_END_OF_LIST() 4485e9c2a8dSGrégory ESTRADE }; 4495e9c2a8dSGrégory ESTRADE 4505e9c2a8dSGrégory ESTRADE static void bcm2835_fb_class_init(ObjectClass *klass, void *data) 4515e9c2a8dSGrégory ESTRADE { 4525e9c2a8dSGrégory ESTRADE DeviceClass *dc = DEVICE_CLASS(klass); 4535e9c2a8dSGrégory ESTRADE 4545e9c2a8dSGrégory ESTRADE dc->props = bcm2835_fb_props; 4555e9c2a8dSGrégory ESTRADE dc->realize = bcm2835_fb_realize; 4565e9c2a8dSGrégory ESTRADE dc->reset = bcm2835_fb_reset; 4575e9c2a8dSGrégory ESTRADE dc->vmsd = &vmstate_bcm2835_fb; 4585e9c2a8dSGrégory ESTRADE } 4595e9c2a8dSGrégory ESTRADE 4605e9c2a8dSGrégory ESTRADE static TypeInfo bcm2835_fb_info = { 4615e9c2a8dSGrégory ESTRADE .name = TYPE_BCM2835_FB, 4625e9c2a8dSGrégory ESTRADE .parent = TYPE_SYS_BUS_DEVICE, 4635e9c2a8dSGrégory ESTRADE .instance_size = sizeof(BCM2835FBState), 4645e9c2a8dSGrégory ESTRADE .class_init = bcm2835_fb_class_init, 4655e9c2a8dSGrégory ESTRADE .instance_init = bcm2835_fb_init, 4665e9c2a8dSGrégory ESTRADE }; 4675e9c2a8dSGrégory ESTRADE 4685e9c2a8dSGrégory ESTRADE static void bcm2835_fb_register_types(void) 4695e9c2a8dSGrégory ESTRADE { 4705e9c2a8dSGrégory ESTRADE type_register_static(&bcm2835_fb_info); 4715e9c2a8dSGrégory ESTRADE } 4725e9c2a8dSGrégory ESTRADE 4735e9c2a8dSGrégory ESTRADE type_init(bcm2835_fb_register_types) 474