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