bcm2835_fb: (finish) refactor to header file + init/realize methods
[qemu/ar7.git] / hw / display / bcm2835_fb.c
bloba75b418f9d0b85b07cf19f52551a8972aa6af0fb
1 /*
2 * Raspberry Pi emulation (c) 2012 Gregory Estrade
3 * This code is licensed under the GNU GPLv2 and later.
4 */
6 /* Heavily based on milkymist-vgafb.c, copyright terms below. */
8 /*
9 * QEMU model of the Milkymist VGA framebuffer.
11 * Copyright (c) 2010-2012 Michael Walle <michael@walle.cc>
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
28 #include "hw/display/bcm2835_fb.h"
29 #include "hw/display/framebuffer.h"
30 #include "ui/pixel_ops.h"
31 #include "hw/arm/bcm2835_mbox.h"
33 #define DEFAULT_VCRAM_SIZE 0x4000000
34 #define BCM2835_FB_OFFSET 0x00100000
35 #define FRAMESKIP 1
37 static void fb_invalidate_display(void *opaque)
39 Bcm2835FbState *s = BCM2835_FB(opaque);
40 s->invalidate = true;
43 static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src,
44 int width, int deststep)
46 Bcm2835FbState *s = (Bcm2835FbState *)opaque;
47 uint16_t rgb565;
48 uint32_t rgb888;
49 uint8_t r, g, b;
50 DisplaySurface *surface = qemu_console_surface(s->con);
51 int bpp = surface_bits_per_pixel(surface);
53 while (width--) {
54 switch (s->bpp) {
55 case 8:
56 rgb888 = ldl_phys(&s->dma_as, s->vcram_base + (*src << 2));
57 r = (rgb888 >> 0) & 0xff;
58 g = (rgb888 >> 8) & 0xff;
59 b = (rgb888 >> 16) & 0xff;
60 src++;
61 break;
62 case 16:
63 rgb565 = lduw_p(src);
64 r = ((rgb565 >> 11) & 0x1f) << 3;
65 g = ((rgb565 >> 5) & 0x3f) << 2;
66 b = ((rgb565 >> 0) & 0x1f) << 3;
67 src += 2;
68 break;
69 case 24:
70 rgb888 = ldl_p(src);
71 r = (rgb888 >> 0) & 0xff;
72 g = (rgb888 >> 8) & 0xff;
73 b = (rgb888 >> 16) & 0xff;
74 src += 3;
75 break;
76 case 32:
77 rgb888 = ldl_p(src);
78 r = (rgb888 >> 0) & 0xff;
79 g = (rgb888 >> 8) & 0xff;
80 b = (rgb888 >> 16) & 0xff;
81 src += 4;
82 break;
83 default:
84 r = 0;
85 g = 0;
86 b = 0;
87 break;
90 if (s->pixo == 0) {
91 /* swap to BGR pixel format */
92 uint8_t tmp = r;
93 r = b;
94 b = tmp;
97 switch (bpp) {
98 case 8:
99 *dst++ = rgb_to_pixel8(r, g, b);
100 break;
101 case 15:
102 *(uint16_t *)dst = rgb_to_pixel15(r, g, b);
103 dst += 2;
104 break;
105 case 16:
106 *(uint16_t *)dst = rgb_to_pixel16(r, g, b);
107 dst += 2;
108 break;
109 case 24:
110 rgb888 = rgb_to_pixel24(r, g, b);
111 *dst++ = rgb888 & 0xff;
112 *dst++ = (rgb888 >> 8) & 0xff;
113 *dst++ = (rgb888 >> 16) & 0xff;
114 break;
115 case 32:
116 *(uint32_t *)dst = rgb_to_pixel32(r, g, b);
117 dst += 4;
118 break;
119 default:
120 return;
125 static void fb_update_display(void *opaque)
127 Bcm2835FbState *s = (Bcm2835FbState *)opaque;
128 int first = 0;
129 int last = 0;
130 drawfn fn;
131 DisplaySurface *surface = qemu_console_surface(s->con);
133 int src_width = 0;
134 int dest_width = 0;
136 static uint32_t frame; /* 0 */
138 if (++frame < FRAMESKIP) {
139 return;
140 } else {
141 frame = 0;
144 if (s->lock) {
145 return;
148 if (!s->xres) {
149 return;
152 src_width = s->xres * (s->bpp >> 3);
154 dest_width = s->xres;
155 switch (surface_bits_per_pixel(surface)) {
156 case 0:
157 return;
158 case 8:
159 break;
160 case 15:
161 dest_width *= 2;
162 break;
163 case 16:
164 dest_width *= 2;
165 break;
166 case 24:
167 dest_width *= 3;
168 break;
169 case 32:
170 dest_width *= 4;
171 break;
172 default:
173 hw_error("bcm2835_fb: bad color depth\n");
174 break;
179 fn = draw_line_src16;
181 if (s->invalidate) {
182 framebuffer_update_memory_section(&s->fbsection,
183 s->dma_mr,
184 s->base,
185 s->yres,
186 src_width);
189 framebuffer_update_display(surface, &s->fbsection,
190 s->xres,
191 s->yres,
192 src_width,
193 dest_width,
195 s->invalidate,
198 &first, &last);
200 if (first >= 0) {
201 dpy_gfx_update(s->con, 0, first,
202 s->xres, last - first + 1);
205 s->invalidate = false;
208 static void bcm2835_fb_mbox_push(Bcm2835FbState *s, uint32_t value)
210 value &= ~0xf;
212 s->lock = true;
214 s->xres = ldl_phys(&s->dma_as, value);
215 s->yres = ldl_phys(&s->dma_as, value + 4);
216 s->xres_virtual = ldl_phys(&s->dma_as, value + 8);
217 s->yres_virtual = ldl_phys(&s->dma_as, value + 12);
219 s->bpp = ldl_phys(&s->dma_as, value + 20);
220 s->xoffset = ldl_phys(&s->dma_as, value + 24);
221 s->yoffset = ldl_phys(&s->dma_as, value + 28);
223 s->base = s->vcram_base | (value & 0xc0000000);
224 s->base += BCM2835_FB_OFFSET;
226 /* TODO - Manage properly virtual resolution */
228 s->pitch = s->xres * (s->bpp >> 3);
229 s->size = s->yres * s->pitch;
231 stl_phys(&s->dma_as, value + 16, s->pitch);
232 stl_phys(&s->dma_as, value + 32, s->base);
233 stl_phys(&s->dma_as, value + 36, s->size);
235 s->invalidate = true;
236 qemu_console_resize(s->con, s->xres, s->yres);
237 s->lock = false;
240 void bcm2835_fb_reconfigure(Bcm2835FbState *s, uint32_t *xres, uint32_t *yres,
241 uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp,
242 uint32_t *pixo, uint32_t *alpha)
244 s->lock = true;
246 /* TODO: input validation! */
247 if (xres) {
248 s->xres = *xres;
250 if (yres) {
251 s->yres = *yres;
253 if (xoffset) {
254 s->xoffset = *xoffset;
256 if (yoffset) {
257 s->yoffset = *yoffset;
259 if (bpp) {
260 s->bpp = *bpp;
262 if (pixo) {
263 s->pixo = *pixo;
265 if (alpha) {
266 s->alpha = *alpha;
269 /* TODO - Manage properly virtual resolution */
271 s->pitch = s->xres * (s->bpp >> 3);
272 s->size = s->yres * s->pitch;
274 s->invalidate = true;
275 qemu_console_resize(s->con, s->xres, s->yres);
276 s->lock = false;
279 static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset,
280 unsigned size)
282 Bcm2835FbState *s = (Bcm2835FbState *)opaque;
283 uint32_t res = 0;
285 switch (offset) {
286 case 0:
287 res = MBOX_CHAN_FB;
288 s->pending = 0;
289 qemu_set_irq(s->mbox_irq, 0);
290 break;
291 case 4:
292 res = s->pending;
293 break;
294 default:
295 qemu_log_mask(LOG_GUEST_ERROR,
296 "bcm2835_fb_read: Bad offset %x\n", (int)offset);
297 return 0;
299 return res;
302 static void bcm2835_fb_write(void *opaque, hwaddr offset,
303 uint64_t value, unsigned size)
305 Bcm2835FbState *s = (Bcm2835FbState *)opaque;
306 switch (offset) {
307 case 0:
308 if (!s->pending) {
309 s->pending = 1;
310 bcm2835_fb_mbox_push(s, value);
311 qemu_set_irq(s->mbox_irq, 1);
313 break;
314 default:
315 qemu_log_mask(LOG_GUEST_ERROR,
316 "bcm2835_fb_write: Bad offset %x\n", (int)offset);
317 return;
321 static const MemoryRegionOps bcm2835_fb_ops = {
322 .read = bcm2835_fb_read,
323 .write = bcm2835_fb_write,
324 .endianness = DEVICE_NATIVE_ENDIAN,
327 static const VMStateDescription vmstate_bcm2835_fb = {
328 .name = TYPE_BCM2835_FB,
329 .version_id = 1,
330 .minimum_version_id = 1,
331 .minimum_version_id_old = 1,
332 .fields = (VMStateField[]) {
333 VMSTATE_END_OF_LIST()
336 static const GraphicHwOps vgafb_ops = {
337 .invalidate = fb_invalidate_display,
338 .gfx_update = fb_update_display,
341 static void bcm2835_fb_init(Object *obj)
343 Bcm2835FbState *s = BCM2835_FB(obj);
344 memory_region_init_io(&s->iomem, obj, &bcm2835_fb_ops, s, TYPE_BCM2835_FB,
345 0x10);
346 sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
347 sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
350 static void bcm2835_fb_realize(DeviceState *dev, Error **errp)
352 Bcm2835FbState *s = BCM2835_FB(dev);
353 Error *err = NULL;
354 Object *obj;
356 if (s->vcram_base == 0) {
357 error_setg(errp, "bcm2835_fb: required vcram-base property not found");
358 return;
361 obj = object_property_get_link(OBJECT(dev), "dma_mr", &err);
362 if (err || obj == NULL) {
363 error_setg(errp, "bcm2835_fb: required dma_mr property not found");
364 return;
367 s->dma_mr = MEMORY_REGION(obj);
368 address_space_init(&s->dma_as, s->dma_mr, NULL);
370 s->pending = 0;
372 s->xres_virtual = s->xres;
373 s->yres_virtual = s->yres;
374 s->xoffset = 0;
375 s->yoffset = 0;
377 s->base = s->vcram_base + BCM2835_FB_OFFSET;
379 s->pitch = s->xres * (s->bpp >> 3);
380 s->size = s->yres * s->pitch;
382 s->invalidate = true;
383 s->lock = false;
385 s->con = graphic_console_init(dev, 0, &vgafb_ops, s);
386 qemu_console_resize(s->con, s->xres, s->yres);
389 static Property bcm2835_fb_props[] = {
390 DEFINE_PROP_UINT32("vcram-base", Bcm2835FbState, vcram_base, 0),/*required*/
391 DEFINE_PROP_UINT32("vcram-size", Bcm2835FbState, vcram_size,
392 DEFAULT_VCRAM_SIZE),
393 DEFINE_PROP_UINT32("xres", Bcm2835FbState, xres, 640),
394 DEFINE_PROP_UINT32("yres", Bcm2835FbState, yres, 480),
395 DEFINE_PROP_UINT32("bpp", Bcm2835FbState, bpp, 16),
396 DEFINE_PROP_UINT32("pixo", Bcm2835FbState, pixo, 1), /* 1=RGB, 0=BGR */
397 DEFINE_PROP_UINT32("alpha", Bcm2835FbState, alpha, 2), /* alpha ignored */
398 DEFINE_PROP_END_OF_LIST()
401 static void bcm2835_fb_class_init(ObjectClass *klass, void *data)
403 DeviceClass *dc = DEVICE_CLASS(klass);
405 dc->props = bcm2835_fb_props;
406 dc->realize = bcm2835_fb_realize;
407 dc->vmsd = &vmstate_bcm2835_fb;
410 static TypeInfo bcm2835_fb_info = {
411 .name = TYPE_BCM2835_FB,
412 .parent = TYPE_SYS_BUS_DEVICE,
413 .instance_size = sizeof(Bcm2835FbState),
414 .class_init = bcm2835_fb_class_init,
415 .instance_init = bcm2835_fb_init,
418 static void bcm2835_fb_register_types(void)
420 type_register_static(&bcm2835_fb_info);
423 type_init(bcm2835_fb_register_types)