xref: /openbmc/qemu/hw/display/bcm2835_fb.c (revision e55a8b37904ad8aa4ec4206854c780259fddc15c)
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