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