Merge remote-tracking branch 'qemu/master'
[qemu/ar7.git] / hw / arm / syborg_fb.c
blob0f05c9480848f246f2a492abf986c6b3b395f9d5
1 /*
2 * Syborg Framebuffer
4 * Copyright (c) 2009 CodeSourcery
5 * Copyright (c) 2010, 2013 Stefan Weil
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
26 #include "qemu/osdep.h"
27 #include "hw/sysbus.h"
28 #include "migration/register.h" /* register_savevm_live */
29 #include "ui/console.h"
30 #include "syborg.h"
31 #include "framebuffer.h"
33 //#define DEBUG_SYBORG_FB
35 #ifdef DEBUG_SYBORG_FB
36 #define DPRINTF(fmt, ...) \
37 do { printf("syborg_fb: " fmt , ## __VA_ARGS__); } while (0)
38 #define BADF(fmt, ...) \
39 do { fprintf(stderr, "syborg_fb: error: " fmt , ## __VA_ARGS__); \
40 exit(1);} while (0)
41 #else
42 #define DPRINTF(fmt, ...) do {} while(0)
43 #define BADF(fmt, ...) \
44 do { fprintf(stderr, "syborg_fb: error: " fmt , ## __VA_ARGS__);} while (0)
45 #endif
47 enum {
48 FB_ID = 0,
49 FB_BASE = 1,
50 FB_HEIGHT = 2,
51 FB_WIDTH = 3,
52 FB_ORIENTATION = 4,
53 FB_BLANK = 5,
54 FB_INT_MASK = 6,
55 FB_INTERRUPT_CAUSE = 7,
56 FB_BPP = 8,
57 FB_COLOR_ORDER = 9,
58 FB_BYTE_ORDER = 10,
59 FB_PIXEL_ORDER = 11,
60 FB_ROW_PITCH = 12,
61 FB_ENABLED = 13,
62 FB_PALETTE_START = 0x400 >> 2,
63 FB_PALETTE_END = FB_PALETTE_START+256-1,
66 #define FB_INT_VSYNC (1U << 0)
67 #define FB_INT_BASE_UPDATE_DONE (1U << 1)
69 typedef struct {
70 SysBusDevice busdev;
71 MemoryRegion iomem;
72 QemuConsole *con;
73 uint32_t need_update : 1;
74 uint32_t need_int : 1;
75 uint32_t enabled : 1;
76 uint32_t int_status;
77 uint32_t int_enable;
78 qemu_irq irq;
80 uint32_t base;
81 uint32_t pitch;
82 uint32_t rows;
83 uint32_t cols;
84 int blank;
85 int bpp;
86 int rgb; /* 0 = BGR, 1 = RGB */
87 int endian; /* 0 = Little, 1 = Big */
88 uint32_t raw_palette[256];
89 uint32_t palette[256];
90 } SyborgFBState;
92 enum {
93 BPP_SRC_1,
94 BPP_SRC_2,
95 BPP_SRC_4,
96 BPP_SRC_8,
97 BPP_SRC_16,
98 BPP_SRC_32,
99 /* TODO: Implement these. */
100 BPP_SRC_15 = -1,
101 BPP_SRC_24 = -2
104 #include "ui/pixel_ops.h"
106 #define BITS 8
107 #include "pl110_template.h"
108 #define BITS 15
109 #include "pl110_template.h"
110 #define BITS 16
111 #include "pl110_template.h"
112 #define BITS 24
113 #include "pl110_template.h"
114 #define BITS 32
115 #include "pl110_template.h"
117 /* Update interrupts. */
118 static void syborg_fb_update(SyborgFBState *s)
120 if ((s->int_status & s->int_enable) != 0) {
121 DPRINTF("Raise IRQ\n");
122 qemu_irq_raise(s->irq);
123 } else {
124 DPRINTF("Lower IRQ\n");
125 qemu_irq_lower(s->irq);
129 static int syborg_fb_enabled(const SyborgFBState *s)
131 return s->enabled;
134 static void syborg_fb_update_palette(SyborgFBState *s)
136 int n, i;
137 uint32_t raw;
138 unsigned int r, g, b;
139 DisplaySurface *surface = qemu_console_surface(s->con);
141 switch (s->bpp) {
142 case BPP_SRC_1: n = 2; break;
143 case BPP_SRC_2: n = 4; break;
144 case BPP_SRC_4: n = 16; break;
145 case BPP_SRC_8: n = 256; break;
146 default: return;
149 for (i = 0; i < n; i++) {
150 raw = s->raw_palette[i];
151 r = (raw >> 16) & 0xff;
152 g = (raw >> 8) & 0xff;
153 b = raw & 0xff;
154 switch (surface_bits_per_pixel(surface)) {
155 case 8:
156 s->palette[i] = rgb_to_pixel8(r, g, b);
157 break;
158 case 15:
159 s->palette[i] = rgb_to_pixel15(r, g, b);
160 break;
161 case 16:
162 s->palette[i] = rgb_to_pixel16(r, g, b);
163 break;
164 case 24:
165 case 32:
166 s->palette[i] = rgb_to_pixel32(r, g, b);
167 break;
168 default:
169 abort();
175 static void syborg_fb_update_display(void *opaque)
177 SyborgFBState *s = (SyborgFBState *)opaque;
178 DisplaySurface *surface = qemu_console_surface(s->con);
179 drawfn* fntable;
180 drawfn fn;
181 int dest_width;
182 int src_width;
183 int bpp_offset;
184 int first;
185 int last;
187 if (!syborg_fb_enabled(s))
188 return;
190 switch (surface_bits_per_pixel(surface)) {
191 case 0:
192 return;
193 case 8:
194 fntable = pl110_draw_fn_8;
195 dest_width = 1;
196 break;
197 case 15:
198 fntable = pl110_draw_fn_15;
199 dest_width = 2;
200 break;
201 case 16:
202 fntable = pl110_draw_fn_16;
203 dest_width = 2;
204 break;
205 case 24:
206 fntable = pl110_draw_fn_24;
207 dest_width = 3;
208 break;
209 case 32:
210 fntable = pl110_draw_fn_32;
211 dest_width = 4;
212 break;
213 default:
214 fprintf(stderr, "syborg_fb: Bad color depth\n");
215 exit(1);
218 if (s->need_int) {
219 s->int_status |= FB_INT_BASE_UPDATE_DONE;
220 syborg_fb_update(s);
221 s->need_int = 0;
224 if (s->rgb) {
225 bpp_offset = 24;
226 } else {
227 bpp_offset = 0;
229 if (s->endian) {
230 bpp_offset += 8;
232 /* Our bpp constants mostly match the PL110/PL111 but
233 * not for the 16 bit case
235 switch (s->bpp) {
236 case BPP_SRC_16:
237 bpp_offset += 6;
238 break;
239 default:
240 bpp_offset += s->bpp;
242 fn = fntable[bpp_offset];
244 if (s->pitch) {
245 src_width = s->pitch;
246 } else {
247 src_width = s->cols;
248 switch (s->bpp) {
249 case BPP_SRC_1:
250 src_width >>= 3;
251 break;
252 case BPP_SRC_2:
253 src_width >>= 2;
254 break;
255 case BPP_SRC_4:
256 src_width >>= 1;
257 break;
258 case BPP_SRC_8:
259 break;
260 case BPP_SRC_15:
261 case BPP_SRC_16:
262 src_width <<= 1;
263 break;
264 case BPP_SRC_24:
265 src_width *= 3;
266 break;
267 case BPP_SRC_32:
268 src_width <<= 2;
269 break;
272 dest_width *= s->cols;
273 first = 0;
274 /* TODO: Implement blanking. */
275 if (!s->blank) {
276 if (s->need_update && s->bpp <= BPP_SRC_8) {
277 syborg_fb_update_palette(s);
279 framebuffer_update_display(surface,
280 sysbus_address_space(&s->busdev),
281 s->base, s->cols, s->rows,
282 src_width, dest_width, 0,
283 s->need_update,
284 fn, s->palette,
285 &first, &last);
286 if (first >= 0) {
287 dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
290 s->int_status |= FB_INT_VSYNC;
291 syborg_fb_update(s);
294 s->need_update = 0;
297 static void syborg_fb_invalidate_display(void * opaque)
299 SyborgFBState *s = (SyborgFBState *)opaque;
300 s->need_update = 1;
303 static uint64_t syborg_fb_read(void *opaque, hwaddr offset,
304 unsigned size)
306 SyborgFBState *s = opaque;
308 DPRINTF("read reg %d\n", (int)offset);
309 offset &= 0xfff;
310 switch (offset >> 2) {
311 case FB_ID:
312 return SYBORG_ID_FRAMEBUFFER;
314 case FB_BASE:
315 return s->base;
317 case FB_HEIGHT:
318 return s->rows;
320 case FB_WIDTH:
321 return s->cols;
323 case FB_ORIENTATION:
324 return 0;
326 case FB_BLANK:
327 return s->blank;
329 case FB_INT_MASK:
330 return s->int_enable;
332 case FB_INTERRUPT_CAUSE:
333 return s->int_status;
335 case FB_BPP:
336 switch (s->bpp) {
337 case BPP_SRC_1: return 1;
338 case BPP_SRC_2: return 2;
339 case BPP_SRC_4: return 4;
340 case BPP_SRC_8: return 8;
341 case BPP_SRC_15: return 15;
342 case BPP_SRC_16: return 16;
343 case BPP_SRC_24: return 24;
344 case BPP_SRC_32: return 32;
345 default: return 0;
348 case FB_COLOR_ORDER:
349 return s->rgb;
351 case FB_BYTE_ORDER:
352 return s->endian;
354 case FB_PIXEL_ORDER:
355 return 0;
357 case FB_ROW_PITCH:
358 return s->pitch;
360 case FB_ENABLED:
361 return s->enabled;
363 default:
364 if ((offset >> 2) >= FB_PALETTE_START
365 && (offset >> 2) <= FB_PALETTE_END) {
366 return s->raw_palette[(offset >> 2) - FB_PALETTE_START];
367 } else {
368 cpu_abort (cpu_single_env, "syborg_fb_read: Bad offset %x\n",
369 (int)offset);
371 return 0;
375 static void syborg_fb_write(void *opaque, hwaddr offset,
376 uint64_t val, unsigned size)
378 SyborgFBState *s = opaque;
380 DPRINTF("write reg %d = %d\n", (int)offset, val);
381 s->need_update = 1;
382 offset &= 0xfff;
383 switch (offset >> 2) {
384 case FB_BASE:
385 s->base = val;
386 s->need_int = 1;
387 s->need_update = 1;
388 syborg_fb_update(s);
389 break;
391 case FB_HEIGHT:
392 s->rows = val;
393 break;
395 case FB_WIDTH:
396 s->cols = val;
397 break;
399 case FB_ORIENTATION:
400 /* TODO: Implement rotation. */
401 break;
403 case FB_BLANK:
404 s->blank = val & 1;
405 break;
407 case FB_INT_MASK:
408 s->int_enable = val;
409 syborg_fb_update(s);
410 break;
412 case FB_INTERRUPT_CAUSE:
413 s->int_status &= ~val;
414 syborg_fb_update(s);
415 break;
417 case FB_BPP:
418 switch (val) {
419 case 1: val = BPP_SRC_1; break;
420 case 2: val = BPP_SRC_2; break;
421 case 4: val = BPP_SRC_4; break;
422 case 8: val = BPP_SRC_8; break;
423 /* case 15: val = BPP_SRC_15; break; */
424 case 16: val = BPP_SRC_16; break;
425 /* case 24: val = BPP_SRC_24; break; */
426 case 32: val = BPP_SRC_32; break;
427 default: val = s->bpp; break;
429 s->bpp = val;
430 break;
432 case FB_COLOR_ORDER:
433 s->rgb = (val != 0);
434 break;
436 case FB_BYTE_ORDER:
437 s->endian = (val != 0);
438 break;
440 case FB_PIXEL_ORDER:
441 /* TODO: Implement this. */
442 break;
444 case FB_ROW_PITCH:
445 s->pitch = val;
446 break;
448 case FB_ENABLED:
449 s->enabled = val;
450 break;
452 default:
453 if ((offset >> 2) >= FB_PALETTE_START
454 && (offset >> 2) <= FB_PALETTE_END) {
455 s->raw_palette[(offset >> 2) - FB_PALETTE_START] = val;
456 } else {
457 cpu_abort (cpu_single_env, "syborg_fb_write: Bad offset %x\n",
458 (int)offset);
460 break;
464 static const MemoryRegionOps syborg_fb_ops = {
465 .read = syborg_fb_read,
466 .write = syborg_fb_write,
467 .endianness = DEVICE_NATIVE_ENDIAN,
470 static void syborg_fb_save(QEMUFile *f, void *opaque)
472 SyborgFBState *s = opaque;
473 int i;
475 qemu_put_be32(f, s->need_int);
476 qemu_put_be32(f, s->int_status);
477 qemu_put_be32(f, s->int_enable);
478 qemu_put_be32(f, s->enabled);
479 qemu_put_be32(f, s->base);
480 qemu_put_be32(f, s->pitch);
481 qemu_put_be32(f, s->rows);
482 qemu_put_be32(f, s->cols);
483 qemu_put_be32(f, s->bpp);
484 qemu_put_be32(f, s->rgb);
485 for (i = 0; i < 256; i++) {
486 qemu_put_be32(f, s->raw_palette[i]);
490 static int syborg_fb_load(QEMUFile *f, void *opaque, int version_id)
492 SyborgFBState *s = opaque;
493 int i;
495 if (version_id != 1)
496 return -EINVAL;
498 s->need_int = qemu_get_be32(f);
499 s->int_status = qemu_get_be32(f);
500 s->int_enable = qemu_get_be32(f);
501 s->enabled = qemu_get_be32(f);
502 s->base = qemu_get_be32(f);
503 s->pitch = qemu_get_be32(f);
504 s->rows = qemu_get_be32(f);
505 s->cols = qemu_get_be32(f);
506 s->bpp = qemu_get_be32(f);
507 s->rgb = qemu_get_be32(f);
508 for (i = 0; i < 256; i++) {
509 s->raw_palette[i] = qemu_get_be32(f);
511 s->need_update = 1;
513 return 0;
516 static SaveVMHandlers savevm_syborg_fb = {
517 .save_state = syborg_fb_save,
518 .load_state = syborg_fb_load
521 static int syborg_fb_init(SysBusDevice *sbd)
523 DeviceState *dev = DEVICE(sbd);
524 SyborgFBState *s = SYBORG_FB(dev);
525 DisplaySurface *surface;
527 sysbus_init_irq(dev, &s->irq);
528 memory_region_init_io(&s->iomem, &syborg_fb_ops, s,
529 "framebuffer", 0x1000);
530 sysbus_init_mmio(sbd, &s->iomem);
532 s->con = graphic_console_init(DEVICE(dev), &syborg_fb_ops, s);
533 //~ syborg_fb_update_display,
534 //~ syborg_fb_invalidate_display,
536 surface = qemu_console_surface(s->con);
538 if (s->cols != 0 && s->rows != 0) {
539 qemu_console_resize(s->con, s->cols, s->rows);
542 if (!s->cols) {
543 s->cols = surface_width(surface);
545 if (!s->rows) {
546 s->rows = surface_height(surface);
549 register_savevm_live(&dev->qdev, "syborg_framebuffer", -1, 1,
550 &savevm_syborg_fb, s);
551 return 0;
554 static Property syborg_fb_properties[] = {
555 DEFINE_PROP_UINT32("width", SyborgFBState, cols, 0),
556 DEFINE_PROP_UINT32("height", SyborgFBState, rows, 0),
557 DEFINE_PROP_END_OF_LIST()
560 static void syborg_fb_class_init(ObjectClass *klass, void *data)
562 DeviceClass *dc = DEVICE_CLASS(klass);
563 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
564 dc->props = syborg_fb_properties;
565 k->init = syborg_fb_init;
568 static const TypeInfo syborg_fb_info = {
569 .name = "syborg,framebuffer",
570 .parent = TYPE_SYS_BUS_DEVICE,
571 .instance_size = sizeof(SyborgFBState),
572 .class_init = syborg_fb_class_init
575 static void syborg_fb_register_types(void)
577 type_register_static(&syborg_fb_info);
580 type_init(syborg_fb_register_types)