diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index a6c0a0cc94..76a10072b4 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -126,6 +126,18 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, } } +static bool fb_use_offsets(BCM2835FBConfig *config) +{ + /* + * Return true if we should use the viewport offsets. + * Experimentally, the hardware seems to do this only if the + * viewport size is larger than the physical screen. (It doesn't + * prevent the guest setting this silly viewport setting, though...) + */ + return config->xres_virtual > config->xres && + config->yres_virtual > config->yres; +} + static void fb_update_display(void *opaque) { BCM2835FBState *s = opaque; @@ -134,12 +146,18 @@ static void fb_update_display(void *opaque) int last = 0; int src_width = 0; int dest_width = 0; + uint32_t xoff = 0, yoff = 0; if (s->lock || !s->config.xres) { return; } src_width = bcm2835_fb_get_pitch(&s->config); + if (fb_use_offsets(&s->config)) { + xoff = s->config.xoffset; + yoff = s->config.yoffset; + } + dest_width = s->config.xres; switch (surface_bits_per_pixel(surface)) { @@ -165,8 +183,9 @@ static void fb_update_display(void *opaque) } if (s->invalidate) { + hwaddr base = s->config.base + xoff + yoff * src_width; framebuffer_update_memory_section(&s->fbsection, s->dma_mr, - s->config.base, + base, s->config.yres, src_width); } @@ -176,7 +195,8 @@ static void fb_update_display(void *opaque) draw_line_src16, s, &first, &last); if (first >= 0) { - dpy_gfx_update(s->con, 0, first, s->config.xres, last - first + 1); + dpy_gfx_update(s->con, 0, first, s->config.xres, + last - first + 1); } s->invalidate = false; @@ -202,8 +222,6 @@ static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) s->config.base = s->vcram_base | (value & 0xc0000000); s->config.base += BCM2835_FB_OFFSET; - /* TODO - Manage properly virtual resolution */ - pitch = bcm2835_fb_get_pitch(&s->config); size = bcm2835_fb_get_size(&s->config); @@ -224,8 +242,6 @@ void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig) s->config = *newconfig; - /* TODO - Manage properly virtual resolution */ - s->invalidate = true; qemu_console_resize(s->con, s->config.xres, s->config.yres); s->lock = false; diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index c8c4979bd2..e3ab677891 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -155,23 +155,32 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) case 0x00040002: /* Blank screen */ resplen = 4; break; - case 0x00040003: /* Get display width/height */ - case 0x00040004: + case 0x00040003: /* Get physical display width/height */ stl_le_phys(&s->dma_as, value + 12, fbconfig.xres); stl_le_phys(&s->dma_as, value + 16, fbconfig.yres); resplen = 8; break; - case 0x00044003: /* Test display width/height */ - case 0x00044004: + case 0x00040004: /* Get virtual display width/height */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual); + stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual); resplen = 8; break; - case 0x00048003: /* Set display width/height */ - case 0x00048004: + case 0x00044003: /* Test physical display width/height */ + case 0x00044004: /* Test virtual display width/height */ + resplen = 8; + break; + case 0x00048003: /* Set physical display width/height */ fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12); fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16); fbconfig_updated = true; resplen = 8; break; + case 0x00048004: /* Set virtual display width/height */ + fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12); + fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16); + fbconfig_updated = true; + resplen = 8; + break; case 0x00040005: /* Get depth */ stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp); resplen = 4; diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h index 95bcec7fe3..d992c60c12 100644 --- a/include/hw/display/bcm2835_fb.h +++ b/include/hw/display/bcm2835_fb.h @@ -62,7 +62,8 @@ void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig); */ static inline uint32_t bcm2835_fb_get_pitch(BCM2835FBConfig *config) { - return config->xres * (config->bpp >> 3); + uint32_t xres = MAX(config->xres, config->xres_virtual); + return xres * (config->bpp >> 3); } /** @@ -71,7 +72,8 @@ static inline uint32_t bcm2835_fb_get_pitch(BCM2835FBConfig *config) */ static inline uint32_t bcm2835_fb_get_size(BCM2835FBConfig *config) { - return config->yres * bcm2835_fb_get_pitch(config); + uint32_t yres = MAX(config->yres, config->yres_virtual); + return yres * bcm2835_fb_get_pitch(config); } #endif