qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

bcm2835_fb.c (14081B)


      1 /*
      2  * Raspberry Pi emulation (c) 2012 Gregory Estrade
      3  * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann.
      4  *
      5  * Heavily based on milkymist-vgafb.c, copyright terms below:
      6  *  QEMU model of the Milkymist VGA framebuffer.
      7  *
      8  *  Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
      9  *
     10  * This library is free software; you can redistribute it and/or
     11  * modify it under the terms of the GNU Lesser General Public
     12  * License as published by the Free Software Foundation; either
     13  * version 2.1 of the License, or (at your option) any later version.
     14  *
     15  * This library is distributed in the hope that it will be useful,
     16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     18  * Lesser General Public License for more details.
     19  *
     20  * You should have received a copy of the GNU Lesser General Public
     21  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
     22  *
     23  */
     24 
     25 #include "qemu/osdep.h"
     26 #include "qapi/error.h"
     27 #include "hw/display/bcm2835_fb.h"
     28 #include "hw/hw.h"
     29 #include "hw/irq.h"
     30 #include "framebuffer.h"
     31 #include "ui/pixel_ops.h"
     32 #include "hw/misc/bcm2835_mbox_defs.h"
     33 #include "hw/qdev-properties.h"
     34 #include "migration/vmstate.h"
     35 #include "qemu/log.h"
     36 #include "qemu/module.h"
     37 
     38 #define DEFAULT_VCRAM_SIZE 0x4000000
     39 #define BCM2835_FB_OFFSET  0x00100000
     40 
     41 /* Maximum permitted framebuffer size; experimentally determined on an rpi2 */
     42 #define XRES_MAX 3840
     43 #define YRES_MAX 2560
     44 /* Framebuffer size used if guest requests zero size */
     45 #define XRES_SMALL 592
     46 #define YRES_SMALL 488
     47 
     48 static void fb_invalidate_display(void *opaque)
     49 {
     50     BCM2835FBState *s = BCM2835_FB(opaque);
     51 
     52     s->invalidate = true;
     53 }
     54 
     55 static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
     56                             int width, int deststep)
     57 {
     58     BCM2835FBState *s = opaque;
     59     uint16_t rgb565;
     60     uint32_t rgb888;
     61     uint8_t r, g, b;
     62     DisplaySurface *surface = qemu_console_surface(s->con);
     63     int bpp = surface_bits_per_pixel(surface);
     64 
     65     while (width--) {
     66         switch (s->config.bpp) {
     67         case 8:
     68             /* lookup palette starting at video ram base
     69              * TODO: cache translation, rather than doing this each time!
     70              */
     71             rgb888 = ldl_le_phys(&s->dma_as, s->vcram_base + (*src << 2));
     72             r = (rgb888 >> 0) & 0xff;
     73             g = (rgb888 >> 8) & 0xff;
     74             b = (rgb888 >> 16) & 0xff;
     75             src++;
     76             break;
     77         case 16:
     78             rgb565 = lduw_le_p(src);
     79             r = ((rgb565 >> 11) & 0x1f) << 3;
     80             g = ((rgb565 >>  5) & 0x3f) << 2;
     81             b = ((rgb565 >>  0) & 0x1f) << 3;
     82             src += 2;
     83             break;
     84         case 24:
     85             rgb888 = ldl_le_p(src);
     86             r = (rgb888 >> 0) & 0xff;
     87             g = (rgb888 >> 8) & 0xff;
     88             b = (rgb888 >> 16) & 0xff;
     89             src += 3;
     90             break;
     91         case 32:
     92             rgb888 = ldl_le_p(src);
     93             r = (rgb888 >> 0) & 0xff;
     94             g = (rgb888 >> 8) & 0xff;
     95             b = (rgb888 >> 16) & 0xff;
     96             src += 4;
     97             break;
     98         default:
     99             r = 0;
    100             g = 0;
    101             b = 0;
    102             break;
    103         }
    104 
    105         if (s->config.pixo == 0) {
    106             /* swap to BGR pixel format */
    107             uint8_t tmp = r;
    108             r = b;
    109             b = tmp;
    110         }
    111 
    112         switch (bpp) {
    113         case 8:
    114             *dst++ = rgb_to_pixel8(r, g, b);
    115             break;
    116         case 15:
    117             *(uint16_t *)dst = rgb_to_pixel15(r, g, b);
    118             dst += 2;
    119             break;
    120         case 16:
    121             *(uint16_t *)dst = rgb_to_pixel16(r, g, b);
    122             dst += 2;
    123             break;
    124         case 24:
    125             rgb888 = rgb_to_pixel24(r, g, b);
    126             *dst++ = rgb888 & 0xff;
    127             *dst++ = (rgb888 >> 8) & 0xff;
    128             *dst++ = (rgb888 >> 16) & 0xff;
    129             break;
    130         case 32:
    131             *(uint32_t *)dst = rgb_to_pixel32(r, g, b);
    132             dst += 4;
    133             break;
    134         default:
    135             return;
    136         }
    137     }
    138 }
    139 
    140 static bool fb_use_offsets(BCM2835FBConfig *config)
    141 {
    142     /*
    143      * Return true if we should use the viewport offsets.
    144      * Experimentally, the hardware seems to do this only if the
    145      * viewport size is larger than the physical screen. (It doesn't
    146      * prevent the guest setting this silly viewport setting, though...)
    147      */
    148     return config->xres_virtual > config->xres &&
    149         config->yres_virtual > config->yres;
    150 }
    151 
    152 static void fb_update_display(void *opaque)
    153 {
    154     BCM2835FBState *s = opaque;
    155     DisplaySurface *surface = qemu_console_surface(s->con);
    156     int first = 0;
    157     int last = 0;
    158     int src_width = 0;
    159     int dest_width = 0;
    160     uint32_t xoff = 0, yoff = 0;
    161 
    162     if (s->lock || !s->config.xres) {
    163         return;
    164     }
    165 
    166     src_width = bcm2835_fb_get_pitch(&s->config);
    167     if (fb_use_offsets(&s->config)) {
    168         xoff = s->config.xoffset;
    169         yoff = s->config.yoffset;
    170     }
    171 
    172     dest_width = s->config.xres;
    173 
    174     switch (surface_bits_per_pixel(surface)) {
    175     case 0:
    176         return;
    177     case 8:
    178         break;
    179     case 15:
    180         dest_width *= 2;
    181         break;
    182     case 16:
    183         dest_width *= 2;
    184         break;
    185     case 24:
    186         dest_width *= 3;
    187         break;
    188     case 32:
    189         dest_width *= 4;
    190         break;
    191     default:
    192         hw_error("bcm2835_fb: bad color depth\n");
    193         break;
    194     }
    195 
    196     if (s->invalidate) {
    197         hwaddr base = s->config.base + xoff + (hwaddr)yoff * src_width;
    198         framebuffer_update_memory_section(&s->fbsection, s->dma_mr,
    199                                           base,
    200                                           s->config.yres, src_width);
    201     }
    202 
    203     framebuffer_update_display(surface, &s->fbsection,
    204                                s->config.xres, s->config.yres,
    205                                src_width, dest_width, 0, s->invalidate,
    206                                draw_line_src16, s, &first, &last);
    207 
    208     if (first >= 0) {
    209         dpy_gfx_update(s->con, 0, first, s->config.xres,
    210                        last - first + 1);
    211     }
    212 
    213     s->invalidate = false;
    214 }
    215 
    216 void bcm2835_fb_validate_config(BCM2835FBConfig *config)
    217 {
    218     /*
    219      * Validate the config, and clip any bogus values into range,
    220      * as the hardware does. Note that fb_update_display() relies on
    221      * this happening to prevent it from performing out-of-range
    222      * accesses on redraw.
    223      */
    224     config->xres = MIN(config->xres, XRES_MAX);
    225     config->xres_virtual = MIN(config->xres_virtual, XRES_MAX);
    226     config->yres = MIN(config->yres, YRES_MAX);
    227     config->yres_virtual = MIN(config->yres_virtual, YRES_MAX);
    228 
    229     /*
    230      * These are not minima: a 40x40 framebuffer will be accepted.
    231      * They're only used as defaults if the guest asks for zero size.
    232      */
    233     if (config->xres == 0) {
    234         config->xres = XRES_SMALL;
    235     }
    236     if (config->yres == 0) {
    237         config->yres = YRES_SMALL;
    238     }
    239     if (config->xres_virtual == 0) {
    240         config->xres_virtual = config->xres;
    241     }
    242     if (config->yres_virtual == 0) {
    243         config->yres_virtual = config->yres;
    244     }
    245 
    246     if (fb_use_offsets(config)) {
    247         /* Clip the offsets so the viewport is within the physical screen */
    248         config->xoffset = MIN(config->xoffset,
    249                               config->xres_virtual - config->xres);
    250         config->yoffset = MIN(config->yoffset,
    251                               config->yres_virtual - config->yres);
    252     }
    253 }
    254 
    255 void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig)
    256 {
    257     s->lock = true;
    258 
    259     s->config = *newconfig;
    260 
    261     s->invalidate = true;
    262     qemu_console_resize(s->con, s->config.xres, s->config.yres);
    263     s->lock = false;
    264 }
    265 
    266 static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value)
    267 {
    268     uint32_t pitch;
    269     uint32_t size;
    270     BCM2835FBConfig newconf;
    271 
    272     value &= ~0xf;
    273 
    274     newconf.xres = ldl_le_phys(&s->dma_as, value);
    275     newconf.yres = ldl_le_phys(&s->dma_as, value + 4);
    276     newconf.xres_virtual = ldl_le_phys(&s->dma_as, value + 8);
    277     newconf.yres_virtual = ldl_le_phys(&s->dma_as, value + 12);
    278     newconf.bpp = ldl_le_phys(&s->dma_as, value + 20);
    279     newconf.xoffset = ldl_le_phys(&s->dma_as, value + 24);
    280     newconf.yoffset = ldl_le_phys(&s->dma_as, value + 28);
    281 
    282     newconf.base = s->vcram_base + BCM2835_FB_OFFSET;
    283 
    284     /* Copy fields which we don't want to change from the existing config */
    285     newconf.pixo = s->config.pixo;
    286     newconf.alpha = s->config.alpha;
    287 
    288     bcm2835_fb_validate_config(&newconf);
    289 
    290     pitch = bcm2835_fb_get_pitch(&newconf);
    291     size = bcm2835_fb_get_size(&newconf);
    292 
    293     stl_le_phys(&s->dma_as, value + 16, pitch);
    294     stl_le_phys(&s->dma_as, value + 32, newconf.base);
    295     stl_le_phys(&s->dma_as, value + 36, size);
    296 
    297     bcm2835_fb_reconfigure(s, &newconf);
    298 }
    299 
    300 static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size)
    301 {
    302     BCM2835FBState *s = opaque;
    303     uint32_t res = 0;
    304 
    305     switch (offset) {
    306     case MBOX_AS_DATA:
    307         res = MBOX_CHAN_FB;
    308         s->pending = false;
    309         qemu_set_irq(s->mbox_irq, 0);
    310         break;
    311 
    312     case MBOX_AS_PENDING:
    313         res = s->pending;
    314         break;
    315 
    316     default:
    317         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
    318                       __func__, offset);
    319         return 0;
    320     }
    321 
    322     return res;
    323 }
    324 
    325 static void bcm2835_fb_write(void *opaque, hwaddr offset, uint64_t value,
    326                              unsigned size)
    327 {
    328     BCM2835FBState *s = opaque;
    329 
    330     switch (offset) {
    331     case MBOX_AS_DATA:
    332         /* bcm2835_mbox should check our pending status before pushing */
    333         assert(!s->pending);
    334         s->pending = true;
    335         bcm2835_fb_mbox_push(s, value);
    336         qemu_set_irq(s->mbox_irq, 1);
    337         break;
    338 
    339     default:
    340         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
    341                       __func__, offset);
    342         return;
    343     }
    344 }
    345 
    346 static const MemoryRegionOps bcm2835_fb_ops = {
    347     .read = bcm2835_fb_read,
    348     .write = bcm2835_fb_write,
    349     .endianness = DEVICE_NATIVE_ENDIAN,
    350     .valid.min_access_size = 4,
    351     .valid.max_access_size = 4,
    352 };
    353 
    354 static const VMStateDescription vmstate_bcm2835_fb = {
    355     .name = TYPE_BCM2835_FB,
    356     .version_id = 1,
    357     .minimum_version_id = 1,
    358     .fields = (VMStateField[]) {
    359         VMSTATE_BOOL(lock, BCM2835FBState),
    360         VMSTATE_BOOL(invalidate, BCM2835FBState),
    361         VMSTATE_BOOL(pending, BCM2835FBState),
    362         VMSTATE_UINT32(config.xres, BCM2835FBState),
    363         VMSTATE_UINT32(config.yres, BCM2835FBState),
    364         VMSTATE_UINT32(config.xres_virtual, BCM2835FBState),
    365         VMSTATE_UINT32(config.yres_virtual, BCM2835FBState),
    366         VMSTATE_UINT32(config.xoffset, BCM2835FBState),
    367         VMSTATE_UINT32(config.yoffset, BCM2835FBState),
    368         VMSTATE_UINT32(config.bpp, BCM2835FBState),
    369         VMSTATE_UINT32(config.base, BCM2835FBState),
    370         VMSTATE_UNUSED(8), /* Was pitch and size */
    371         VMSTATE_UINT32(config.pixo, BCM2835FBState),
    372         VMSTATE_UINT32(config.alpha, BCM2835FBState),
    373         VMSTATE_END_OF_LIST()
    374     }
    375 };
    376 
    377 static const GraphicHwOps vgafb_ops = {
    378     .invalidate  = fb_invalidate_display,
    379     .gfx_update  = fb_update_display,
    380 };
    381 
    382 static void bcm2835_fb_init(Object *obj)
    383 {
    384     BCM2835FBState *s = BCM2835_FB(obj);
    385 
    386     memory_region_init_io(&s->iomem, obj, &bcm2835_fb_ops, s, TYPE_BCM2835_FB,
    387                           0x10);
    388     sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
    389     sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
    390 }
    391 
    392 static void bcm2835_fb_reset(DeviceState *dev)
    393 {
    394     BCM2835FBState *s = BCM2835_FB(dev);
    395 
    396     s->pending = false;
    397 
    398     s->config = s->initial_config;
    399 
    400     s->invalidate = true;
    401     s->lock = false;
    402 }
    403 
    404 static void bcm2835_fb_realize(DeviceState *dev, Error **errp)
    405 {
    406     BCM2835FBState *s = BCM2835_FB(dev);
    407     Object *obj;
    408 
    409     if (s->vcram_base == 0) {
    410         error_setg(errp, "%s: required vcram-base property not set", __func__);
    411         return;
    412     }
    413 
    414     obj = object_property_get_link(OBJECT(dev), "dma-mr", &error_abort);
    415 
    416     /* Fill in the parts of initial_config that are not set by QOM properties */
    417     s->initial_config.xres_virtual = s->initial_config.xres;
    418     s->initial_config.yres_virtual = s->initial_config.yres;
    419     s->initial_config.xoffset = 0;
    420     s->initial_config.yoffset = 0;
    421     s->initial_config.base = s->vcram_base + BCM2835_FB_OFFSET;
    422 
    423     s->dma_mr = MEMORY_REGION(obj);
    424     address_space_init(&s->dma_as, s->dma_mr, TYPE_BCM2835_FB "-memory");
    425 
    426     bcm2835_fb_reset(dev);
    427 
    428     s->con = graphic_console_init(dev, 0, &vgafb_ops, s);
    429     qemu_console_resize(s->con, s->config.xres, s->config.yres);
    430 }
    431 
    432 static Property bcm2835_fb_props[] = {
    433     DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/
    434     DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size,
    435                        DEFAULT_VCRAM_SIZE),
    436     DEFINE_PROP_UINT32("xres", BCM2835FBState, initial_config.xres, 640),
    437     DEFINE_PROP_UINT32("yres", BCM2835FBState, initial_config.yres, 480),
    438     DEFINE_PROP_UINT32("bpp", BCM2835FBState, initial_config.bpp, 16),
    439     DEFINE_PROP_UINT32("pixo", BCM2835FBState,
    440                        initial_config.pixo, 1), /* 1=RGB, 0=BGR */
    441     DEFINE_PROP_UINT32("alpha", BCM2835FBState,
    442                        initial_config.alpha, 2), /* alpha ignored */
    443     DEFINE_PROP_END_OF_LIST()
    444 };
    445 
    446 static void bcm2835_fb_class_init(ObjectClass *klass, void *data)
    447 {
    448     DeviceClass *dc = DEVICE_CLASS(klass);
    449 
    450     device_class_set_props(dc, bcm2835_fb_props);
    451     dc->realize = bcm2835_fb_realize;
    452     dc->reset = bcm2835_fb_reset;
    453     dc->vmsd = &vmstate_bcm2835_fb;
    454 }
    455 
    456 static const TypeInfo bcm2835_fb_info = {
    457     .name          = TYPE_BCM2835_FB,
    458     .parent        = TYPE_SYS_BUS_DEVICE,
    459     .instance_size = sizeof(BCM2835FBState),
    460     .class_init    = bcm2835_fb_class_init,
    461     .instance_init = bcm2835_fb_init,
    462 };
    463 
    464 static void bcm2835_fb_register_types(void)
    465 {
    466     type_register_static(&bcm2835_fb_info);
    467 }
    468 
    469 type_init(bcm2835_fb_register_types)