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