@@ -345,7 +345,7 @@ obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
obj-arm-y += versatile_pci.o
obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
obj-arm-y += s5pc210.o s5pc210_cmu.o s5pc210_uart.o s5pc210_gic.o \
- s5pc210_combiner.o s5pc210_pwm.o s5pc210_mct.o
+ s5pc210_combiner.o s5pc210_pwm.o s5pc210_mct.o s5pc210_fimd.o
obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
obj-arm-y += pl061.o
obj-arm-y += arm-semi.o
@@ -97,6 +97,10 @@
/* MCT */
#define S5PC210_MCT_BASE_ADDR 0x10050000
+/* Display controllers (FIMD) */
+#define S5PC210_FIMD0_BASE_ADDR 0x11C00000
+#define S5PC210_FIMD1_BASE_ADDR 0x12000000
+
#define S5PC210_BASE_BOOT_ADDR S5PC210_DRAM0_BASE_ADDR
/* Secondary CPU startup code is in IROM memory */
@@ -465,6 +469,13 @@ static void s5pc210_init(ram_addr_t ram_size,
}
}
+ /*** Display controller (FIMD) ***/
+ sysbus_create_varargs("s5pc210.fimd", S5PC210_FIMD0_BASE_ADDR,
+ irq_table[s5pc210_get_irq(11, 0)],
+ irq_table[s5pc210_get_irq(11, 1)],
+ irq_table[s5pc210_get_irq(11, 2)],
+ NULL);
+
/*** Load kernel ***/
s5pc210_binfo.ram_size = ram_size;
new file mode 100644
@@ -0,0 +1,1698 @@
+/*
+ * Samsung s5pc210 Display Controller (FIMD)
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
+ * All rights reserved.
+ * Based on LCD controller for Samsung S5PC1xx-based board emulation
+ * by Kirill Batuzov <batuzovk@ispras.ru>
+ *
+ * Contributed by Mitsyanko Igor <i.mitsyanko@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc., 51
+ * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "qemu-common.h"
+#include "cpu-all.h"
+#include "sysbus.h"
+#include "console.h"
+#include "pixel_ops.h"
+
+/* Debug messages configuration */
+#define S5P_FIMD_DEBUG 0
+#define S5P_FIMD_MODE_TRACE 0
+
+#if S5P_FIMD_DEBUG == 0
+ #define print_debug1(fmt, args...) do { } while (0)
+ #define print_debug2(fmt, args...) do { } while (0)
+ #define print_error(fmt, args...) \
+ do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0)
+#elif S5P_FIMD_DEBUG == 1
+ #define print_debug1(fmt, args...) \
+ do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0)
+ #define print_debug2(fmt, args...) do { } while (0)
+ #define print_error(fmt, args...) \
+ do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0)
+#else
+ #define print_debug1(fmt, args...) \
+ do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0)
+ #define print_debug2(fmt, args...) \
+ do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0)
+ #define print_error(fmt, args...) \
+ do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0)
+#endif
+
+
+#define NUM_OF_WINDOWS 5
+#define FIMD_REGS_SIZE 0x4114
+
+/* Video main control registers */
+#define FIMD_VIDCON0 0x0000
+#define FIMD_VIDCON1 0x0004
+#define FIMD_VIDCON2 0x0008
+#define FIMD_VIDCON3 0x000C
+#define FIMD_VIDCON0_ENVID_F (1 << 0)
+#define FIMD_VIDCON0_ENVID (1 << 1)
+#define FIMD_VIDCON0_ENVID_MASK ((1 << 0) | (1 << 1))
+#define FIMD_VIDCON1_ROMASK 0x07FFE000
+
+/* Video time control registers */
+#define FIMD_VIDTCON2_SIZE_MASK 0x07FF
+#define FIMD_VIDTCON2_HOR_SHIFT 0
+#define FIMD_VIDTCON2_VER_SHIFT 11
+
+/* Window control registers */
+#define FIMD_WINCON0 0x0020
+#define FIMD_WINCON_ROMASK 0x82200000
+#define FIMD_WINCON_ENWIN (1 << 0)
+#define FIMD_WINCON_BLD_PIX (1 << 6)
+#define FIMD_WINCON_ALPHA_SEL (1 << 1)
+#define FIMD_WINCON_ALSEL_SHIFT 1
+#define FIMD_WINCON_SWAP 0x078000
+#define FIMD_WINCON_SWAP_SHIFT 15
+#define FIMD_WINCON_SWAP_WORD 0x1
+#define FIMD_WINCON_SWAP_HWORD 0x2
+#define FIMD_WINCON_SWAP_BYTE 0x4
+#define FIMD_WINCON_SWAP_BITS 0x8
+#define FIMD_WINCON_BPPMODE_SHIFT 2
+#define FIMD_WINCON_BPPMODE 0x0F
+#define FIMD_WINCON_BUFSTATUS_L (1 << 21)
+#define FIMD_WINCON_BUFSTATUS_H (1 << 31)
+#define FIMD_WINCON_BUFSTATUS ((1 << 21) | (1 << 31))
+#define FIMD_WINCON_BUF0_STAT ((0 << 21) | (0 << 31))
+#define FIMD_WINCON_BUF1_STAT ((1 << 21) | (0 << 31))
+#define FIMD_WINCON_BUF2_STAT ((0 << 21) | (1 << 31))
+#define FIMD_WINCON_BUFSELECT ((1 << 20) | (1 << 30))
+#define FIMD_WINCON_BUF0_SEL ((0 << 20) | (0 << 30))
+#define FIMD_WINCON_BUF1_SEL ((1 << 20) | (0 << 30))
+#define FIMD_WINCON_BUF2_SEL ((0 << 20) | (1 << 30))
+#define FIMD_WINCON_BUFMODE (1 << 14)
+
+/* Window position control registers */
+#define FIMD_VIDOSD_COORD_MASK 0x07FF
+#define FIMD_VIDOSD_HOR_SHIFT 11
+#define FIMD_VIDOSD_VER_SHIFT 0
+#define FIMD_VIDOSD_ALPHA_AEN0 0xFFF000
+#define FIMD_VIDOSD_AEN0_SHIFT 12
+#define FIMD_VIDOSD_ALPHA_AEN1 0x000FFF
+
+/* Frame buffer address registers */
+#define FIMD_VIDWADD2_PAGEWIDTH 0x1FFF
+#define FIMD_VIDWADD2_OFFSIZE 0x1FFF
+#define FIMD_VIDWADD2_OFFSIZE_SHIFT 13
+
+/* Window color key registers */
+#define FIMD_WKEYCON0_COMPKEY 0x00FFFFFF
+#define FIMD_WKEYCON0_CTL_SHIFT 24
+#define FIMD_WKEYCON0_CTL_DIRCON 0x1
+#define FIMD_WKEYCON0_CTL_KEYEN 0x2
+#define FIMD_WKEYCON0_CTL_KEYBLEN 0x4
+
+/* Window alpha control registers */
+#define FIMD_VIDALPHA_ALPHA_MASK 0x000F0F0F
+
+/* Window color map registers */
+#define FIMD_WINMAP_EN (1 << 24)
+#define FIMD_WINMAP_COLOR_MASK 0x00FFFFFF
+#define FIMD_WINMAP_NOCOLOR (~FIMD_WINMAP_COLOR_MASK)
+
+/* Window palette control registers */
+#define FIMD_WPAL_W0PAL_L 0x07
+#define FIMD_WPAL_W0PAL_L_SH 0
+#define FIMD_WPAL_W1PAL_L 0x07
+#define FIMD_WPAL_W1PAL_L_SH 3
+#define FIMD_WPAL_W2PAL_L 0x01
+#define FIMD_WPAL_W2PAL_L_SH 6
+#define FIMD_WPAL_W2PAL_H 0x06
+#define FIMD_WPAL_W2PAL_H_SH 8
+#define FIMD_WPAL_W3PAL_L 0x01
+#define FIMD_WPAL_W3PAL_L_SH 7
+#define FIMD_WPAL_W3PAL_H 0x06
+#define FIMD_WPAL_W3PAL_H_SH 12
+#define FIMD_WPAL_W4PAL_L 0x01
+#define FIMD_WPAL_W4PAL_L_SH 8
+#define FIMD_WPAL_W4PAL_H 0x06
+#define FIMD_WPAL_W4PAL_H_SH 16
+
+/* Trigger control registers */
+#define FIMD_TRIGCON 0x01A4
+#define FIMD_TRIGCON_ROMASK 0x00000004
+
+/* Video interrupt control registers */
+#define FIMD_VIDINT_INTFIFOPEND (1 << 0)
+#define FIMD_VIDINT_INTFRMPEND (1 << 1)
+#define FIMD_VIDINT_INTI80PEND (1 << 2)
+#define FIMD_VIDINT_INTEN (1 << 0)
+#define FIMD_VIDINT_INTFIFOEN (1 << 1)
+#define FIMD_VIDINT_INTFRMEN (1 << 12)
+#define FIMD_VIDINT_I80IFDONE (1 << 17)
+
+/* Window blend equation control registers */
+#define FIMD_BLENDEQ_COEF_MASK 0xF
+#define FIMD_BLENDEQ_COEF_Q 18
+#define FIMD_BLENDEQ_COEF_P 12
+#define FIMD_BLENDEQ_COEF_B 6
+#define FIMD_BLENDEQ_COEF_A 0
+
+typedef struct {
+ uint8_t r, g, b;
+ uint32_t a;
+} rgba;
+#define RGBA_SIZE 7
+
+typedef struct DrawConfig DrawConfig;
+
+typedef void pixel_to_rgb_func(uint32_t pixel, rgba *p);
+typedef void draw_line_func(struct DrawConfig *cfg, uint8_t *src,
+ uint8_t *dst, uint8_t *ifb);
+typedef uint32_t coef_func(const struct DrawConfig *cfg, rgba pa, rgba pb);
+
+typedef struct {
+ uint32_t wincon; /* Window control register */
+ uint32_t vidosd[4]; /* Window position control registers A-D */
+ uint32_t buf_start[3]; /* Start address for video frame buffer */
+ uint32_t buf_end[3]; /* End address for video frame buffer */
+ uint32_t buf_size; /* Virtual screen width */
+ uint32_t keycon[2]; /* Window color key registers */
+ uint32_t keyalpha; /* Color key alpha control register */
+ uint32_t winmap; /* Window color map register */
+ uint32_t vidw_alpha[2]; /* Window alpha control registers */
+ uint32_t blendeq; /* Window blending equation control register */
+ uint32_t rtqoscon; /* Window RTQOS Control Registers */
+ uint32_t palette[256]; /* Pallete RAM */
+ uint32_t shadow_buf_start; /* Start address of shadow frame buffer */
+ uint32_t shadow_buf_end; /* End address of shadow frame buffer */
+ uint32_t shadow_buf_size; /* Virtual shadow screen width */
+} S5pc210fimdWindow;
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ DisplayState *console;
+ qemu_irq irq[3];
+
+ uint32_t vidcon[4]; /* Video main control registers 0-3 */
+ uint32_t vidtcon[4]; /* Video time control registers 0-3 */
+ uint32_t shadowcon; /* Window shadow control register */
+ uint32_t winchmap; /* Channel maping control register */
+ uint32_t vidintcon[2]; /* Video interrupt control registers */
+ uint32_t dithmode; /* Dithering control register */
+ uint32_t wpalcon[2]; /* Window pallete control registers */
+ uint32_t trigcon; /* Trigger control register */
+ uint32_t i80ifcon[4]; /* I80 interface control registers */
+ uint32_t colorgaincon; /* Color gain control register */
+ uint32_t ldi_cmdcon[2]; /* LCD I80 interface command control */
+ uint32_t sifccon[3]; /* I80 System Interface Manual Command Control */
+ uint32_t huecoef_cr[4]; /* Hue control registers */
+ uint32_t huecoef_cb[4]; /* Hue control registers */
+ uint32_t hueoffset; /* Hue offset control register */
+ uint32_t blendcon; /* Blending equation control register */
+ uint32_t dualrgb; /* Undocumented register */
+ uint32_t i80ifcmd[12]; /* LCD I80 Interface Command */
+
+ S5pc210fimdWindow window[5]; /* Window-specific registers */
+ uint8_t *ifb; /* Internal frame buffer */
+ bool invalidate; /* Image needs to be redrawn */
+ bool enabled; /* Display controller is enabled */
+} S5pc210fimdState;
+
+struct DrawConfig {
+ pixel_to_rgb_func *pixel_to_rgb;
+ draw_line_func *draw_line;
+ int (*put_pixel)(const rgba p, uint8_t *pixel);
+ int (*get_pixel)(const uint8_t *src, rgba *p);
+ void (*blend)(struct DrawConfig *cfg, rgba p_old, rgba p_new, rgba *p);
+ coef_func *coef_p, *coef_q, *coef_a, *coef_b;
+ uint8_t is_palletized;
+ uint32_t bg_alpha[2], fg_alpha[2];
+ uint32_t color_key, color_mask, color_ctl;
+ uint8_t fg_alpha_pix, bg_alpha_pix;
+ int width;
+ int bpp;
+ uint32_t *palette;
+ uint8_t swap;
+ uint8_t fg_pixel_blending, bg_pixel_blending;
+ uint8_t fg_alpha_sel, bg_alpha_sel;
+ uint32_t map_color;
+};
+
+static inline int s5pc210_buffer_status(S5pc210fimdWindow *w)
+{
+ switch (w->wincon & FIMD_WINCON_BUFSTATUS) {
+ case FIMD_WINCON_BUF0_STAT: default:
+ return 0;
+ case FIMD_WINCON_BUF1_STAT:
+ return 1;
+ case FIMD_WINCON_BUF2_STAT:
+ return 2;
+ }
+}
+
+/* Perform byte/halfword/word swap of data according to config */
+static inline uint64_t swap_data(const DrawConfig *cfg, uint64_t x)
+{
+ int i;
+ uint64_t res;
+
+ if (cfg->swap & FIMD_WINCON_SWAP_BITS) {
+ res = 0;
+ for (i = 0; i < 64; i++) {
+ if (x & (1ULL << (64 - i))) {
+ res |= (1ULL << i);
+ }
+ }
+ x = res;
+ }
+ if (cfg->swap & FIMD_WINCON_SWAP_BYTE) {
+ x = ((x & 0x00000000000000FFULL) << 56) |
+ ((x & 0x000000000000FF00ULL) << 40) |
+ ((x & 0x0000000000FF0000ULL) << 24) |
+ ((x & 0x00000000FF000000ULL) << 8) |
+ ((x & 0x000000FF00000000ULL) >> 8) |
+ ((x & 0x0000FF0000000000ULL) >> 24) |
+ ((x & 0x00FF000000000000ULL) >> 40) |
+ ((x & 0xFF00000000000000ULL) >> 56);
+ }
+ if (cfg->swap & FIMD_WINCON_SWAP_HWORD) {
+ x = ((x & 0x000000000000FFFFULL) << 48) |
+ ((x & 0x00000000FFFF0000ULL) << 16) |
+ ((x & 0x0000FFFF00000000ULL) >> 16) |
+ ((x & 0xFFFF000000000000ULL) >> 48);
+ }
+ if (cfg->swap & FIMD_WINCON_SWAP_WORD) {
+ x = ((x & 0x00000000FFFFFFFFULL) << 32) |
+ ((x & 0xFFFFFFFF00000000ULL) >> 32);
+ }
+ return x;
+}
+
+/* Palette/pixel to RGB conversion */
+
+#define DEF_PIXEL_TO_RGB(N, R, G, B, A) \
+static void N(uint32_t pixel, rgba *p) \
+{ \
+ p->b = (pixel & ((1 << (B)) - 1)) << (8 - (B)); \
+ pixel >>= (B); \
+ p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)); \
+ pixel >>= (G); \
+ p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)); \
+ pixel >>= (R); \
+ if (1 == (A)) { \
+ p->a = pixel & 1; \
+ } else if (8 == (A)) { \
+ p->a = pixel & 0xFF; \
+ p->a = (p->a << 16) | (p->a << 8) | p->a; \
+ } else { \
+ p->a = (pixel & ((1 << (A)) - 1)) << (8 - (A)); \
+ } \
+}
+
+DEF_PIXEL_TO_RGB(pixel_a232_to_rgb, 2, 3, 2, 1)
+DEF_PIXEL_TO_RGB(pixel_a444_to_rgb, 4, 4, 4, 1)
+DEF_PIXEL_TO_RGB(pixel_4444_to_rgb, 4, 4, 4, 4)
+DEF_PIXEL_TO_RGB(pixel_565_to_rgb, 5, 6, 5, 0)
+DEF_PIXEL_TO_RGB(pixel_a555_to_rgb, 5, 5, 5, 1)
+DEF_PIXEL_TO_RGB(pixel_555_to_rgb, 5, 5, 5, 0)
+DEF_PIXEL_TO_RGB(pixel_666_to_rgb, 6, 6, 6, 0)
+DEF_PIXEL_TO_RGB(pixel_a666_to_rgb, 6, 6, 6, 1)
+DEF_PIXEL_TO_RGB(pixel_a665_to_rgb, 6, 6, 5, 1)
+DEF_PIXEL_TO_RGB(pixel_888_to_rgb, 8, 8, 8, 0)
+DEF_PIXEL_TO_RGB(pixel_a888_to_rgb, 8, 8, 8, 1)
+DEF_PIXEL_TO_RGB(pixel_a887_to_rgb, 8, 8, 7, 1)
+DEF_PIXEL_TO_RGB(pixel_8888_to_rgb, 8, 8, 8, 8)
+
+/* Special case for (5+1,5+1,5+1) mode */
+static void pixel_1555_to_rgb(uint32_t pixel, rgba *p)
+{
+ uint8_t u = (pixel >> 15) & 1;
+ p->b = (((pixel & 0x1F) << 1) | u) << 2;
+ pixel >>= 5;
+ p->g = (((pixel & 0x3F) << 1) | u) << 2;
+ pixel >>= 6;
+ p->r = (((pixel & 0x1F) << 1) | u) << 2;
+}
+
+/* Draw line with pallete index in frame buffer data */
+#define DEF_DRAW_LINE_PALLETE(N) \
+static void glue(draw_line_pallete_, N)(DrawConfig *cfg, uint8_t *src, \
+ uint8_t *dst, uint8_t *ifb) \
+{ \
+ int width = cfg->width; \
+ uint64_t data; \
+ rgba p, p_old; \
+ int i; \
+ do { \
+ data = ldq_raw((void *)src); \
+ src += 8; \
+ data = swap_data(cfg, data); \
+ for (i = (64 / (N) - 1); i >= 0; i--) { \
+ cfg->pixel_to_rgb(cfg->palette[(data >> ((N) * i)) & \
+ ((1ULL << (N)) - 1)], &p); \
+ if (cfg->blend) { \
+ ifb += cfg->get_pixel(ifb, &p_old); \
+ cfg->blend(cfg, p_old, p, &p); \
+ } \
+ dst += cfg->put_pixel(p, dst); \
+ } \
+ width -= (64 / (N)); \
+ } while (width > 0); \
+}
+
+/* Draw line with direct color value in frame buffer data */
+#define DEF_DRAW_LINE_NOPALLETE(N) \
+static void glue(draw_line_, N)(DrawConfig *cfg, uint8_t *src, \
+ uint8_t *dst, uint8_t *ifb) \
+{ \
+ int width = cfg->width; \
+ uint64_t data; \
+ rgba p, p_old; \
+ int i; \
+ do { \
+ data = ldq_raw((void *)src); \
+ src += 8; \
+ data = swap_data(cfg, data); \
+ for (i = (64 / (N) - 1); i >= 0; i--) { \
+ cfg->pixel_to_rgb((data >> ((N) * i)) & ((1ULL << (N)) - 1), &p); \
+ if (cfg->blend) { \
+ ifb += cfg->get_pixel(ifb, &p_old); \
+ cfg->blend(cfg, p_old, p, &p); \
+ } \
+ dst += cfg->put_pixel(p, dst); \
+ } \
+ width -= (64 / (N)); \
+ } while (width > 0); \
+}
+
+DEF_DRAW_LINE_PALLETE(1)
+DEF_DRAW_LINE_PALLETE(2)
+DEF_DRAW_LINE_PALLETE(4)
+DEF_DRAW_LINE_PALLETE(8)
+DEF_DRAW_LINE_NOPALLETE(8) /* 8bpp mode has pallete and non-pallete versions */
+DEF_DRAW_LINE_NOPALLETE(16)
+DEF_DRAW_LINE_NOPALLETE(32)
+
+/* Special draw line routine for window color map case */
+static void draw_line_mapcolor(DrawConfig *cfg, uint8_t *src,
+ uint8_t *dst, uint8_t *ifb)
+{
+ rgba p, p_old;
+ int width = cfg->width;
+ uint32_t map_color = cfg->map_color;
+
+ do {
+ pixel_888_to_rgb(map_color, &p);
+ if (cfg->blend) {
+ ifb += cfg->get_pixel(ifb, &p_old);
+ cfg->blend(cfg, p_old, p, &p);
+ }
+ dst += cfg->put_pixel(p, dst);
+ } while (--width);
+}
+
+/* Routine to copy line from internal frame buffer to QEMU display */
+static void draw_line_copy(DrawConfig *cfg, uint8_t *src, uint8_t *dst)
+{
+ rgba p;
+ int width = cfg->width;
+
+ do {
+ src += cfg->get_pixel(src, &p);
+ dst += cfg->put_pixel(p, dst);
+ } while (--width);
+}
+
+/* Parse BPPMODE_F bits and setup known DRAW_CONFIG fields accordingly.
+ BPPMODE_F = WINCON1[5:2] */
+static void s5pc210_parse_win_bppmode(S5pc210fimdWindow *w, DrawConfig *cfg)
+{
+ switch ((w->wincon >> FIMD_WINCON_BPPMODE_SHIFT) & FIMD_WINCON_BPPMODE) {
+ case 0:
+ cfg->draw_line = draw_line_pallete_1;
+ cfg->is_palletized = 1;
+ cfg->bpp = 1;
+ break;
+ case 1:
+ cfg->draw_line = draw_line_pallete_2;
+ cfg->is_palletized = 1;
+ cfg->bpp = 2;
+ break;
+ case 2:
+ cfg->draw_line = draw_line_pallete_4;
+ cfg->is_palletized = 1;
+ cfg->bpp = 4;
+ break;
+ case 3:
+ cfg->draw_line = draw_line_pallete_8;
+ cfg->is_palletized = 1;
+ cfg->bpp = 8;
+ break;
+ case 4:
+ cfg->draw_line = draw_line_8;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_a232_to_rgb;
+ cfg->bpp = 8;
+ break;
+ case 5:
+ cfg->draw_line = draw_line_16;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_565_to_rgb;
+ cfg->bpp = 16;
+ break;
+ case 6:
+ cfg->draw_line = draw_line_16;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_a555_to_rgb;
+ cfg->bpp = 16;
+ break;
+ case 7:
+ cfg->draw_line = draw_line_16;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_1555_to_rgb;
+ cfg->bpp = 16;
+ break;
+ case 8:
+ cfg->draw_line = draw_line_32;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_666_to_rgb;
+ cfg->bpp = 32;
+ break;
+ case 9:
+ cfg->draw_line = draw_line_32;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_a665_to_rgb;
+ cfg->bpp = 32;
+ break;
+ case 10:
+ cfg->draw_line = draw_line_32;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_a666_to_rgb;
+ cfg->bpp = 32;
+ break;
+ case 11:
+ cfg->draw_line = draw_line_32;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_888_to_rgb;
+ cfg->bpp = 32;
+ break;
+ case 12:
+ cfg->draw_line = draw_line_32;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_a887_to_rgb;
+ cfg->bpp = 32;
+ break;
+ case 13:
+ cfg->draw_line = draw_line_32;
+ cfg->is_palletized = 0;
+ if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon &
+ FIMD_WINCON_ALPHA_SEL)) {
+ cfg->pixel_to_rgb = pixel_8888_to_rgb;
+ cfg->fg_alpha_pix = 1;
+ } else {
+ cfg->pixel_to_rgb = pixel_a888_to_rgb;
+ }
+ cfg->bpp = 32;
+ break;
+ case 14:
+ cfg->draw_line = draw_line_16;
+ cfg->is_palletized = 0;
+ if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon &
+ FIMD_WINCON_ALPHA_SEL)) {
+ cfg->pixel_to_rgb = pixel_4444_to_rgb;
+ cfg->fg_alpha_pix = 1;
+ } else {
+ cfg->pixel_to_rgb = pixel_a444_to_rgb;
+ }
+ cfg->bpp = 16;
+ break;
+ case 15:
+ cfg->draw_line = draw_line_16;
+ cfg->is_palletized = 0;
+ cfg->pixel_to_rgb = pixel_555_to_rgb;
+ cfg->bpp = 16;
+ break;
+ }
+}
+
+#if S5P_FIMD_MODE_TRACE > 0
+static const char *s5pc210_fimd_get_bppmode(int mode_code)
+{
+ switch (mode_code) {
+ case 0:
+ return "1 bpp";
+ case 1:
+ return "2 bpp";
+ case 2:
+ return "4 bpp";
+ case 3:
+ return "8 bpp (palletized)";
+ case 4:
+ return "8 bpp (non-palletized, A: 1-R:2-G:3-B:2)";
+ case 5:
+ return "16 bpp (non-palletized, R:5-G:6-B:5)";
+ case 6:
+ return "16 bpp (non-palletized, A:1-R:5-G:5-B:5)";
+ case 7:
+ return "16 bpp (non-palletized, I :1-R:5-G:5-B:5)";
+ case 8:
+ return "Unpacked 18 bpp (non-palletized, R:6-G:6-B:6)";
+ case 9:
+ return "Unpacked 18bpp (non-palletized,A:1-R:6-G:6-B:5)";
+ case 10:
+ return "Unpacked 19bpp (non-palletized,A:1-R:6-G:6-B:6)";
+ case 11:
+ return "Unpacked 24 bpp (non-palletized R:8-G:8-B:8)";
+ case 12:
+ return "Unpacked 24 bpp (non-palletized A:1-R:8-G:8-B:7)";
+ case 13:
+ return "Unpacked 25 bpp (non-palletized A:1-R:8-G:8-B:8)";
+ case 14:
+ return "Unpacked 13 bpp (non-palletized A:1-R:4-G:4-B:4)";
+ case 15:
+ return "Unpacked 15 bpp (non-palletized R:5-G:5-B:5)";
+ default:
+ return "Non-existing bpp mode";
+ }
+}
+#endif
+
+static inline void s5pc210_fimd_trace_bppmode(S5pc210fimdWindow *w,
+ int win_num, uint32_t val)
+{
+#if S5P_FIMD_MODE_TRACE > 0
+ if (((w->wincon >> FIMD_WINCON_BPPMODE_SHIFT) & FIMD_WINCON_BPPMODE) ==
+ ((val >> FIMD_WINCON_BPPMODE_SHIFT) & FIMD_WINCON_BPPMODE)) {
+ return;
+ }
+ printf("QEMU FIMD: Window %d BPP mode changed from %s to %s\n", win_num,
+ s5pc210_fimd_get_bppmode((w->wincon >> FIMD_WINCON_BPPMODE_SHIFT) &
+ FIMD_WINCON_BPPMODE),
+ s5pc210_fimd_get_bppmode((val >> FIMD_WINCON_BPPMODE_SHIFT) &
+ FIMD_WINCON_BPPMODE));
+#endif
+}
+
+static inline void s5pc210_fimd_trace_reset(void)
+{
+#if S5P_FIMD_MODE_TRACE > 0
+ fprintf(stderr, "QEMU FIMD: Display controller reset\n");
+#endif
+}
+
+static inline void s5pc210_fimd_enable(S5pc210fimdState *s, bool enabled)
+{
+ s->enabled = enabled ? true : false;
+#if S5P_FIMD_MODE_TRACE > 0
+ fprintf(stderr, "QEMU FIMD: display controller %s\n",
+ (enabled ? "enabled" : "disabled"));
+#endif
+}
+
+/* Returns WxPAL for given window number WINDOW */
+static uint32_t s5pc210_wxpal(S5pc210fimdState *s, int window)
+{
+ switch (window) {
+ case 0:
+ return (s->wpalcon[1] >> FIMD_WPAL_W0PAL_L_SH) & FIMD_WPAL_W0PAL_L;
+ case 1:
+ return (s->wpalcon[1] >> FIMD_WPAL_W1PAL_L_SH) & FIMD_WPAL_W1PAL_L;
+ case 2:
+ return ((s->wpalcon[0] >> FIMD_WPAL_W2PAL_H_SH) & FIMD_WPAL_W2PAL_H) |
+ ((s->wpalcon[1] >> FIMD_WPAL_W2PAL_L_SH) & FIMD_WPAL_W2PAL_L);
+ case 3:
+ return ((s->wpalcon[0] >> FIMD_WPAL_W3PAL_H_SH) & FIMD_WPAL_W3PAL_H) |
+ ((s->wpalcon[1] >> FIMD_WPAL_W3PAL_L_SH) & FIMD_WPAL_W3PAL_L);
+ case 4:
+ return ((s->wpalcon[0] >> FIMD_WPAL_W4PAL_H_SH) & FIMD_WPAL_W4PAL_H) |
+ ((s->wpalcon[1] >> FIMD_WPAL_W4PAL_L_SH) & FIMD_WPAL_W4PAL_L);
+ }
+ hw_error("s5pc210.fimd: incorrect window number %d\n", window);
+ return 0;
+}
+
+pixel_to_rgb_func *wxpal_to_rgb[8] = {
+ [0] = pixel_565_to_rgb,
+ [1] = pixel_a555_to_rgb,
+ [2] = pixel_666_to_rgb,
+ [3] = pixel_a665_to_rgb,
+ [4] = pixel_a666_to_rgb,
+ [5] = pixel_888_to_rgb,
+ [6] = pixel_a888_to_rgb,
+ [7] = pixel_8888_to_rgb
+};
+
+/* Put/get pixel to/from internal LCD Controller framebuffer */
+
+static int put_rgba(const rgba p, uint8_t *d)
+{
+ *(uint8_t *)d++ = p.r;
+ *(uint8_t *)d++ = p.g;
+ *(uint8_t *)d++ = p.b;
+ *(uint32_t *)d = p.a;
+ return RGBA_SIZE;
+}
+
+static int get_rgba(const uint8_t *s, rgba *p)
+{
+ p->r = *(uint8_t *)s++;
+ p->g = *(uint8_t *)s++;
+ p->b = *(uint8_t *)s++;
+ p->a = *(uint32_t *)s;
+ return RGBA_SIZE;
+}
+
+/* Write RGB to QEMU's GraphicConsole framebuffer */
+
+static int put_pixel8(const rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel8(p.r, p.g, p.b);
+ *(uint8_t *)d = pixel;
+ return 1;
+}
+
+static int put_pixel15(const rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel15(p.r, p.g, p.b);
+ *(uint16_t *)d = pixel;
+ return 2;
+}
+
+static int put_pixel16(const rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel16(p.r, p.g, p.b);
+ *(uint16_t *)d = pixel;
+ return 2;
+}
+
+static int put_pixel24(const rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
+ *(uint8_t *)d++ = (pixel >> 0) & 0xFF;
+ *(uint8_t *)d++ = (pixel >> 8) & 0xFF;
+ *(uint8_t *)d++ = (pixel >> 16) & 0xFF;
+ return 3;
+}
+
+static int put_pixel32(const rgba p, uint8_t *d)
+{
+ uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
+ *(uint32_t *)d = pixel;
+ return 4;
+}
+
+static inline uint32_t unpack_by_4(uint32_t x)
+{
+ return ((x & 0xF00) << 12) | ((x & 0xF0) << 8) | ((x & 0xF) << 4);
+}
+
+
+/* Coefficient extraction functions */
+
+static uint32_t coef_zero(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ return 0;
+}
+
+static uint32_t coef_one(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ return 0xFFFFFF;
+}
+
+static uint32_t coef_alphaa(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ if (!cfg->fg_pixel_blending) {
+ pa.a = cfg->fg_alpha_sel;
+ }
+ if (cfg->fg_alpha_pix) {
+ return pa.a;
+ } else {
+ return cfg->fg_alpha[pa.a];
+ }
+}
+
+static uint32_t coef_one_minus_alphaa(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ if (!cfg->fg_pixel_blending) {
+ pa.a = cfg->fg_alpha_sel;
+ }
+ if (cfg->fg_alpha_pix) {
+ return 0xFFFFFF - pa.a;
+ } else {
+ return 0xFFFFFF - cfg->fg_alpha[pa.a];
+ }
+}
+
+static uint32_t coef_alphab(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ if (!cfg->bg_pixel_blending) {
+ pb.a = cfg->bg_alpha_sel;
+ }
+ if (cfg->bg_alpha_pix) {
+ return pb.a;
+ } else {
+ return cfg->bg_alpha[pb.a];
+ }
+}
+
+static uint32_t coef_one_minus_alphab(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ if (!cfg->bg_pixel_blending) {
+ pb.a = cfg->bg_alpha_sel;
+ }
+ if (cfg->bg_alpha_pix) {
+ return 0xFFFFFF - pb.a;
+ } else {
+ return 0xFFFFFF - cfg->bg_alpha[pb.a];
+ }
+}
+
+static uint32_t coef_a(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ return (pa.r << 16) | (pa.g << 8) | pa.b;
+}
+
+static uint32_t coef_one_minus_a(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ return 0xFFFFFF - ((pa.r << 16) | (pa.g << 8) | pa.b);
+}
+
+static uint32_t coef_b(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ return (pb.r << 16) | (pb.g << 8) | pb.b;
+}
+
+static uint32_t coef_one_minus_b(const DrawConfig *cfg,
+ rgba pa, rgba pb)
+{
+ return 0xFFFFFF - ((pb.r << 16) | (pb.g << 8) | pb.b);
+}
+
+
+static coef_func *coef_decode(uint32_t x)
+{
+ switch (x) {
+ case 0:
+ return coef_zero;
+ case 1:
+ return coef_one;
+ case 2:
+ return coef_alphaa;
+ case 3:
+ return coef_one_minus_alphaa;
+ case 4:
+ return coef_alphab;
+ case 5:
+ return coef_one_minus_alphab;
+ case 10:
+ return coef_a;
+ case 11:
+ return coef_one_minus_a;
+ case 12:
+ return coef_b;
+ case 13:
+ return coef_one_minus_b;
+ default:
+ hw_error("s5pc210.fimd: blend equation coef illegal value\n");
+ return 0;
+ }
+}
+
+static void blend_alpha(const DrawConfig *cfg, rgba p_bg, rgba p_fg, rgba *res)
+{
+ uint32_t pl, ql, al, bl;
+ uint32_t p, q, a, b, fg, bg, fga, bga;
+
+ pl = cfg->coef_p(cfg, p_fg, p_bg);
+ ql = cfg->coef_q(cfg, p_fg, p_bg);
+ al = cfg->coef_a(cfg, p_fg, p_bg);
+ bl = cfg->coef_b(cfg, p_fg, p_bg);
+ res->a = 0;
+ /* B */
+ p = pl & 0xFF;
+ pl >>= 8;
+ q = ql & 0xFF;
+ ql >>= 8;
+ a = al & 0xFF;
+ al >>= 8;
+ b = bl & 0xFF;
+ bl >>= 8;
+ fg = p_fg.b;
+ bg = p_bg.b;
+ if (cfg->fg_pixel_blending) {
+ if (cfg->fg_alpha_pix) {
+ fga = p_fg.a & 0xFF;
+ } else {
+ fga = cfg->fg_alpha[p_fg.a] & 0xFF;
+ }
+ } else {
+ fga = cfg->fg_alpha[cfg->fg_alpha_sel] & 0xFF;
+ }
+ if (cfg->bg_pixel_blending) {
+ if (cfg->bg_alpha_pix) {
+ bga = p_bg.a & 0xFF;
+ } else {
+ bga = cfg->bg_alpha[p_bg.a] & 0xFF;
+ }
+ } else {
+ bga = cfg->bg_alpha[cfg->bg_alpha_sel] & 0xFF;
+ }
+ bg = (bg * b + fg * a) / 0xFF;
+ if (bg > 0xFF) {
+ res->b = 0xFF;
+ } else {
+ res->b = bg;
+ }
+ bga = (bga * p + fga * q) / 0xFF;
+ if (bga > 0xFF) {
+ res->a |= 0xFF;
+ } else {
+ res->a |= bga;
+ }
+ /* G */
+ p = pl & 0xFF;
+ pl >>= 8;
+ q = ql & 0xFF;
+ ql >>= 8;
+ a = al & 0xFF;
+ al >>= 8;
+ b = bl & 0xFF;
+ bl >>= 8;
+ fg = p_fg.g;
+ bg = p_bg.g;
+ if (cfg->fg_pixel_blending) {
+ if (cfg->fg_alpha_pix) {
+ fga = (p_fg.a >> 8) & 0xFF;
+ } else {
+ fga = (cfg->fg_alpha[p_fg.a] >> 8) & 0xFF;
+ }
+ } else {
+ fga = (cfg->fg_alpha[cfg->fg_alpha_sel] >> 8) & 0xFF;
+ }
+ if (cfg->bg_pixel_blending) {
+ if (cfg->bg_alpha_pix) {
+ bga = (p_bg.a >> 8) & 0xFF;
+ } else {
+ bga = (cfg->bg_alpha[p_bg.a] >> 8) & 0xFF;
+ }
+ } else {
+ bga = (cfg->bg_alpha[cfg->bg_alpha_sel] >> 8) & 0xFF;
+ }
+ bg = (bg * b + fg * a) / 0xFF;
+ if (bg > 0xFF) {
+ res->g = 0xFF;
+ } else {
+ res->g = bg;
+ }
+ bga = (bga * p + fga * q) / 0xFF;
+ if (bga > 0xFF) {
+ res->a |= 0xFF << 8;
+ } else {
+ res->a |= bga << 8;
+ }
+ /* R */
+ p = pl & 0xFF;
+ pl >>= 8;
+ q = ql & 0xFF;
+ ql >>= 8;
+ a = al & 0xFF;
+ al >>= 8;
+ b = bl & 0xFF;
+ bl >>= 8;
+ fg = p_fg.r;
+ bg = p_bg.r;
+ if (cfg->fg_pixel_blending) {
+ if (cfg->fg_alpha_pix) {
+ fga = (p_fg.a >> 16) & 0xFF;
+ } else {
+ fga = (cfg->fg_alpha[p_fg.a] >> 16) & 0xFF;
+ }
+ } else {
+ fga = (cfg->fg_alpha[cfg->fg_alpha_sel] >> 16) & 0xFF;
+ }
+ if (cfg->bg_pixel_blending) {
+ if (cfg->bg_alpha_pix) {
+ bga = (p_bg.a >> 16) & 0xFF;
+ } else {
+ bga = (cfg->bg_alpha[p_bg.a] >> 16) & 0xFF;
+ }
+ } else {
+ bga = (cfg->bg_alpha[cfg->bg_alpha_sel] >> 16) & 0xFF;
+ }
+ bg = (bg * b + fg * a) / 0xFF;
+ if (bg > 0xFF) {
+ res->r = 0xFF;
+ } else {
+ res->r = bg;
+ }
+ bga = (bga * p + fga * q) / 0xFF;
+ if (bga > 0xFF) {
+ res->a |= 0xFF << 16;
+ } else {
+ res->a |= bga << 16;
+ }
+}
+
+static void blend_colorkey(DrawConfig *cfg, rgba p_bg, rgba p_fg, rgba *p)
+{
+ uint8_t r, g, b;
+
+ if (cfg->color_ctl & FIMD_WKEYCON0_CTL_KEYEN) {
+ blend_alpha(cfg, p_bg, p_fg, p);
+ return ;
+ }
+ r = ((cfg->color_key & ~cfg->color_mask) >> 16) & 0xFF;
+ g = ((cfg->color_key & ~cfg->color_mask) >> 8) & 0xFF;
+ b = ((cfg->color_key & ~cfg->color_mask) >> 0) & 0xFF;
+ if (cfg->color_ctl & FIMD_WKEYCON0_CTL_DIRCON) {
+ if ((p_fg.r & ~((cfg->color_mask >> 16) & 0xFF)) == r &&
+ (p_fg.g & ~((cfg->color_mask >> 8) & 0xFF)) == g &&
+ (p_fg.b & ~((cfg->color_mask >> 0) & 0xFF)) == b) {
+ if (cfg->color_ctl & FIMD_WKEYCON0_CTL_KEYBLEN) {
+ p_fg.a = 1;
+ cfg->fg_pixel_blending = 0;
+ blend_alpha(cfg, p_bg, p_fg, p);
+ } else {
+ *p = p_bg;
+ }
+ } else {
+ if (cfg->color_ctl & FIMD_WKEYCON0_CTL_KEYBLEN) {
+ p_fg.a = 0;
+ cfg->fg_pixel_blending = 0;
+ blend_alpha(cfg, p_bg, p_fg, p);
+ } else {
+ *p = p_fg;
+ }
+ }
+ } else {
+ if ((p_bg.r & ~((cfg->color_mask >> 16) & 0xFF)) == r &&
+ (p_bg.g & ~((cfg->color_mask >> 8) & 0xFF)) == g &&
+ (p_bg.b & ~((cfg->color_mask >> 0) & 0xFF)) == b) {
+ if (cfg->color_ctl & FIMD_WKEYCON0_CTL_KEYBLEN) {
+ p_fg.a = 1;
+ cfg->fg_pixel_blending = 0;
+ blend_alpha(cfg, p_bg, p_fg, p);
+ } else {
+ *p = p_fg;
+ }
+ } else {
+ if (cfg->color_ctl & FIMD_WKEYCON0_CTL_KEYBLEN) {
+ p_fg.a = 0;
+ cfg->fg_pixel_blending = 0;
+ blend_alpha(cfg, p_bg, p_fg, p);
+ } else {
+ *p = p_bg;
+ }
+ }
+ }
+}
+
+static inline void putpixel_by_bpp(DrawConfig *cfg, int bpp)
+{
+ switch (bpp) {
+ case 8:
+ cfg->put_pixel = put_pixel8;
+ break;
+ case 15:
+ cfg->put_pixel = put_pixel15;
+ break;
+ case 16:
+ cfg->put_pixel = put_pixel16;
+ break;
+ case 24:
+ cfg->put_pixel = put_pixel24;
+ break;
+ case 32:
+ cfg->put_pixel = put_pixel32;
+ break;
+ default:
+ hw_error("s5pc210.fimd: unsupported BPP (%d)", bpp);
+ break;
+ }
+}
+
+static void s5pc210_fimd_update_irq(S5pc210fimdState *s)
+{
+ if (!(s->vidintcon[0] & FIMD_VIDINT_INTEN)) {
+ qemu_irq_lower(s->irq[0]);
+ qemu_irq_lower(s->irq[1]);
+ qemu_irq_lower(s->irq[2]);
+ return;
+ }
+ if ((s->vidintcon[0] & FIMD_VIDINT_INTFIFOEN) &&
+ (s->vidintcon[1] & FIMD_VIDINT_INTFIFOPEND)) {
+ qemu_irq_raise(s->irq[0]);
+ } else {
+ qemu_irq_lower(s->irq[0]);
+ }
+ if ((s->vidintcon[0] & FIMD_VIDINT_INTFRMEN) &&
+ (s->vidintcon[1] & FIMD_VIDINT_INTFRMPEND)) {
+ qemu_irq_raise(s->irq[1]);
+ } else {
+ qemu_irq_lower(s->irq[1]);
+ }
+ if ((s->vidintcon[0] & FIMD_VIDINT_I80IFDONE) &&
+ (s->vidintcon[1] & FIMD_VIDINT_INTI80PEND)) {
+ qemu_irq_raise(s->irq[2]);
+ } else {
+ qemu_irq_lower(s->irq[2]);
+ }
+}
+
+static void s5pc210_update_resolution(S5pc210fimdState *s)
+{
+ /* LCD resolution is stored in VIDEO TIME CONTROL REGISTER 2 */
+ uint32_t width = ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) &
+ FIMD_VIDTCON2_SIZE_MASK) + 1;
+ uint32_t height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) &
+ FIMD_VIDTCON2_SIZE_MASK) + 1;
+
+ if (s->ifb == NULL || ds_get_width(s->console) != width ||
+ ds_get_height(s->console) != height) {
+ print_debug1("Resolution changed from %ux%u to %ux%u\n",
+ ds_get_width(s->console), ds_get_height(s->console), width, height);
+ qemu_console_resize(s->console, width, height);
+ s->ifb = g_realloc(s->ifb, width * height * RGBA_SIZE);
+ memset(s->ifb, 0, width * height * RGBA_SIZE);
+ s->invalidate = true;
+ }
+}
+
+static void s5pc210_fimd_update(void *opaque)
+{
+ S5pc210fimdState *s = (S5pc210fimdState *)opaque;
+ DrawConfig cfg;
+ int i, line, buf_id;
+ target_phys_addr_t fb_start_addr, fb_next_line_addr, inc_size, fb_len, x;
+ int lefttop_x, lefttop_y, rightbottom_x, rightbottom_y;
+ int width, height;
+ int first_line = -1, last_line = -1, fb_line_size;
+ uint8_t is_first_window = 1;
+ uint8_t *host_fb_addr, *host_fb_start_addr;
+ bool is_dirty = false;
+ ram_addr_t pd;
+ const int global_width = (s->vidtcon[2] & FIMD_VIDTCON2_SIZE_MASK) + 1;
+ const int global_height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) &
+ FIMD_VIDTCON2_SIZE_MASK) + 1;
+
+ if (!s || !s->console || !ds_get_bits_per_pixel(s->console)) {
+ return;
+ }
+
+ if (s->enabled == false) {
+ return;
+ }
+
+ memset(&cfg, 0, sizeof(cfg));
+ s5pc210_update_resolution(s);
+
+ for (i = 0; i < NUM_OF_WINDOWS; i++) {
+ if (s->window[i].wincon & FIMD_WINCON_ENWIN) {
+ cfg.fg_alpha_pix = 0;
+ s5pc210_parse_win_bppmode(&s->window[i], &cfg);
+ /* If we have mode with palletized color then we need to parse
+ palette color mode and set pixel-to-rgb conversion function
+ accordingly. */
+ if (cfg.is_palletized) {
+ uint32_t tmp = s5pc210_wxpal(s, i);
+ /* Different windows have different mapping WxPAL to palette
+ pixel format. This transform happens to unify them all. */
+ if (i < 2 && tmp < 7) {
+ tmp = 6 - tmp;
+ }
+ cfg.pixel_to_rgb = wxpal_to_rgb[tmp];
+ if (tmp == 7) {
+ cfg.fg_alpha_pix = 1;
+ }
+ }
+ cfg.put_pixel = put_rgba;
+ cfg.get_pixel = get_rgba;
+ cfg.bg_alpha_pix = 1;
+ cfg.color_mask = s->window[i].keycon[0] & FIMD_WKEYCON0_COMPKEY;
+ cfg.color_key = s->window[i].keycon[1];
+ cfg.color_ctl =
+ (s->window[i].keycon[0] >> FIMD_WKEYCON0_CTL_SHIFT) & 7;
+ if (i == 0) {
+ cfg.fg_alpha[0] = s->window[i].vidw_alpha[0];
+ cfg.fg_alpha[1] = s->window[i].vidw_alpha[1];
+ } else {
+ cfg.fg_alpha[0] =
+ unpack_by_4((s->window[i].vidosd[2] &
+ FIMD_VIDOSD_ALPHA_AEN0) >> FIMD_VIDOSD_AEN0_SHIFT) |
+ (s->window[i].vidw_alpha[0] & FIMD_VIDALPHA_ALPHA_MASK);
+ cfg.fg_alpha[1] =
+ unpack_by_4(s->window[i].vidosd[2] &
+ FIMD_VIDOSD_ALPHA_AEN1) | (s->window[i].vidw_alpha[0]
+ & FIMD_VIDALPHA_ALPHA_MASK);
+ }
+ cfg.bg_pixel_blending = 1;
+ cfg.fg_pixel_blending = s->window[i].wincon & FIMD_WINCON_BLD_PIX;
+ cfg.fg_alpha_sel = (s->window[i].wincon & FIMD_WINCON_ALPHA_SEL) >>
+ FIMD_WINCON_ALSEL_SHIFT;
+ cfg.palette = s->window[i].palette;
+ cfg.swap = (s->window[i].wincon & FIMD_WINCON_SWAP) >>
+ FIMD_WINCON_SWAP_SHIFT;
+ cfg.coef_q = coef_decode((s->window[i].blendeq >>
+ FIMD_BLENDEQ_COEF_Q) & FIMD_BLENDEQ_COEF_MASK);
+ cfg.coef_p = coef_decode((s->window[i].blendeq >>
+ FIMD_BLENDEQ_COEF_P) & FIMD_BLENDEQ_COEF_MASK);
+ cfg.coef_b = coef_decode((s->window[i].blendeq >>
+ FIMD_BLENDEQ_COEF_B) & FIMD_BLENDEQ_COEF_MASK);
+ cfg.coef_a = coef_decode((s->window[i].blendeq >>
+ FIMD_BLENDEQ_COEF_A) & FIMD_BLENDEQ_COEF_MASK);
+ if (is_first_window) {
+ cfg.blend = NULL;
+ } else {
+ cfg.blend = blend_colorkey;
+ }
+ is_first_window = 0;
+ if (s->window[i].winmap & FIMD_WINMAP_EN) {
+ cfg.map_color = s->window[i].winmap & FIMD_WINMAP_COLOR_MASK;
+ cfg.draw_line = draw_line_mapcolor;
+ }
+
+ /* At this point CFG is fully set up except WIDTH. We can proceed
+ with drawing. */
+ lefttop_x = (s->window[i].vidosd[0] >> FIMD_VIDOSD_HOR_SHIFT)
+ & FIMD_VIDOSD_COORD_MASK;
+ lefttop_y = (s->window[i].vidosd[0] >> FIMD_VIDOSD_VER_SHIFT)
+ & FIMD_VIDOSD_COORD_MASK;
+ rightbottom_x = (s->window[i].vidosd[1] >> FIMD_VIDOSD_HOR_SHIFT)
+ & FIMD_VIDOSD_COORD_MASK;
+ rightbottom_y = (s->window[i].vidosd[1] >> FIMD_VIDOSD_VER_SHIFT)
+ & FIMD_VIDOSD_COORD_MASK;
+ height = rightbottom_y - lefttop_y + 1;
+ width = rightbottom_x - lefttop_x + 1;
+ cfg.width = width;
+ buf_id = s5pc210_buffer_status(&s->window[i]);
+
+ fb_line_size = s->window[i].buf_size & FIMD_VIDWADD2_PAGEWIDTH;
+ fb_start_addr = s->window[i].buf_start[buf_id];
+ inc_size = (s->window[i].buf_size & FIMD_VIDWADD2_PAGEWIDTH) +
+ ((s->window[i].buf_size >> FIMD_VIDWADD2_OFFSIZE_SHIFT) &
+ FIMD_VIDWADD2_OFFSIZE);
+ fb_len = inc_size * height;
+
+ cpu_physical_sync_dirty_bitmap(fb_start_addr,
+ fb_start_addr + fb_len);
+ host_fb_addr = cpu_physical_memory_map(fb_start_addr, &fb_len, 0);
+ if (!host_fb_addr) {
+ return;
+ }
+ if (fb_len != inc_size * height) {
+ cpu_physical_memory_unmap(host_fb_addr, fb_len, 0, 0);
+ return;
+ }
+
+ host_fb_start_addr = host_fb_addr;
+ for (line = 0; line < height; line++) {
+ fb_next_line_addr = fb_start_addr + fb_line_size;
+ for (x = fb_start_addr; x < fb_next_line_addr;
+ x += TARGET_PAGE_SIZE) {
+ pd = (cpu_get_physical_page_desc(x) & TARGET_PAGE_MASK) +
+ (x & ~TARGET_PAGE_MASK);
+ is_dirty = is_dirty ||
+ cpu_physical_memory_get_dirty(pd, VGA_DIRTY_FLAG);
+ }
+
+ if (s->invalidate || is_dirty) {
+ if (first_line == -1) {
+ first_line = line;
+ }
+ last_line = line;
+ cfg.draw_line(&cfg, host_fb_addr,
+ s->ifb + lefttop_x * RGBA_SIZE +
+ (lefttop_y + line) * global_width * RGBA_SIZE,
+ s->ifb + lefttop_x * RGBA_SIZE +
+ (lefttop_y + line) * global_width * RGBA_SIZE);
+ }
+ host_fb_addr += inc_size;
+ is_dirty = false;
+ fb_start_addr += inc_size;
+ }
+ cpu_physical_memory_unmap(host_fb_start_addr, fb_len, 0, 0);
+ fb_start_addr = s->window[i].buf_start[buf_id];
+ pd = (cpu_get_physical_page_desc(fb_start_addr) & TARGET_PAGE_MASK)+
+ (fb_start_addr & ~TARGET_PAGE_MASK);
+ cpu_physical_memory_reset_dirty(pd, pd + inc_size * height,
+ VGA_DIRTY_FLAG);
+ }
+ }
+ /* Last pass: copy resulting image to QEMU_CONSOLE. */
+ if (first_line >= 0) {
+ uint8_t *d;
+ int bpp;
+
+ cfg.get_pixel = get_rgba;
+ bpp = ds_get_bits_per_pixel(s->console);
+ putpixel_by_bpp(&cfg, bpp);
+ bpp = (bpp + 1) >> 3;
+ d = ds_get_data(s->console);
+ for (line = first_line; line < last_line; line++) {
+ draw_line_copy(&cfg, s->ifb + global_width * line * RGBA_SIZE,
+ d + global_width * line * bpp);
+ }
+ dpy_update(s->console, 0, 0, global_width, global_height);
+ }
+ s->invalidate = false;
+ s->vidintcon[1] |= FIMD_VIDINT_INTFRMPEND;
+ if ((s->vidcon[0] & FIMD_VIDCON0_ENVID_F) == 0) {
+ s5pc210_fimd_enable(s, false);
+ }
+ s5pc210_fimd_update_irq(s);
+}
+
+static void s5pc210_fimd_invalidate(void *opaque)
+{
+ S5pc210fimdState *s = (S5pc210fimdState *)opaque;
+ s->invalidate = true;
+}
+
+static void s5pc210_fimd_reset(DeviceState *d)
+{
+ S5pc210fimdState *s = container_of(d, S5pc210fimdState, busdev.qdev);
+ int i;
+ unsigned long begin = (unsigned long)s + offsetof(S5pc210fimdState, vidcon);
+ unsigned long len = ((unsigned long)s + offsetof(S5pc210fimdState, window))
+ - begin;
+ s5pc210_fimd_trace_reset();
+
+ /* Set all display controller registers to 0 */
+ memset((void *)begin, 0, len);
+ for (i = 0; i < NUM_OF_WINDOWS; i++) {
+ memset(&s->window[i], 0, sizeof(S5pc210fimdWindow));
+ s->window[i].blendeq = 0xC2;
+ }
+
+ if (s->ifb != NULL) {
+ g_free(s->ifb);
+ }
+ s->ifb = NULL;
+
+ s->invalidate = true;
+ s5pc210_fimd_enable(s, false);
+ /* Some registers have non-zero initial values */
+ s->winchmap = 0x7D517D51;
+ s->colorgaincon = 0x10040100;
+ s->huecoef_cr[0] = s->huecoef_cr[3] = 0x01000100;
+ s->huecoef_cb[0] = s->huecoef_cb[3] = 0x01000100;
+ s->hueoffset = 0x01800080;
+}
+
+static void s5pc210_fimd_write(void *opaque, target_phys_addr_t offset,
+ uint64_t val, unsigned size)
+{
+ S5pc210fimdState *s = (S5pc210fimdState *)opaque;
+ int w, i;
+
+ print_debug2("write offset 0x%08x, value=%ld(0x%08lx)\n", offset, val, val);
+
+ if (offset == FIMD_VIDCON0) {
+ if ((val & FIMD_VIDCON0_ENVID_MASK) == FIMD_VIDCON0_ENVID_MASK) {
+ s5pc210_fimd_enable(s, true);
+ } else {
+ if ((val & FIMD_VIDCON0_ENVID) == 0) {
+ s5pc210_fimd_enable(s, false);
+ }
+ }
+ s->vidcon[0] = val;
+ } else if (offset == FIMD_VIDCON1) {
+ /* Leave read-only bits as is */
+ val = (val & (~FIMD_VIDCON1_ROMASK)) |
+ (s->vidcon[1] & FIMD_VIDCON1_ROMASK);
+ s->vidcon[1] = val;
+ } else if (offset >= FIMD_VIDCON2 && offset <= FIMD_VIDCON3) {
+ s->vidcon[(offset) >> 2] = val;
+ } else if (offset >= 0x010 && offset <= 0x01C) {
+ s->vidtcon[(offset - 0x010) >> 2] = val;
+ } else if (offset >= 0x020 && offset <= 0x030) {
+ w = (offset - 0x020) >> 2;
+ val = (val & ~FIMD_WINCON_ROMASK) |
+ (s->window[w].wincon & FIMD_WINCON_ROMASK);
+ s5pc210_fimd_trace_bppmode(&s->window[w], w, val);
+ switch (val & FIMD_WINCON_BUFSELECT) {
+ case FIMD_WINCON_BUF0_SEL:
+ val &= ~FIMD_WINCON_BUFSTATUS;
+ break;
+ case FIMD_WINCON_BUF1_SEL:
+ val = (val & ~FIMD_WINCON_BUFSTATUS_H) |
+ FIMD_WINCON_BUFSTATUS_L;
+ break;
+ case FIMD_WINCON_BUF2_SEL:
+ if (val & FIMD_WINCON_BUFMODE) {
+ val = (val & ~FIMD_WINCON_BUFSTATUS_L) |
+ FIMD_WINCON_BUFSTATUS_H;
+ }
+ break;
+ default:
+ break;
+ }
+ s->window[w].wincon = val;
+ } else if (offset == 0x034) {
+ s->shadowcon = val;
+ } else if (offset == 0x03C) {
+ s->winchmap = val;
+ } else if (offset >= 0x040 && offset <= 0x088) {
+ w = (offset - 0x040) >> 4;
+ i = ((offset - 0x040) & 0xF) >> 2;
+ if (i == 3 && w != 1 && w != 2) {
+ print_error("Bad write offset 0x%08x\n", offset);
+ }
+ s->window[w].vidosd[i] = val;
+ } else if (offset >= 0x0A0 && offset <= 0x0C4) {
+ w = (offset - 0x0A0) >> 3;
+ i = ((offset - 0x0A0) >> 2) & 1;
+ s->window[w].buf_start[i] = val;
+ } else if (offset >= 0x0D0 && offset <= 0x0F4) {
+ w = (offset - 0x0D0) >> 3;
+ i = ((offset - 0x0D0) >> 2) & 1;
+ s->window[w].buf_end[i] = val;
+ } else if (offset >= 0x100 && offset <= 0x110) {
+ s->window[(offset - 0x100) >> 2].buf_size = val;
+ } else if (offset == 0x130) {
+ s->vidintcon[0] = val;
+ } else if (offset == 0x134) {
+ s->vidintcon[1] &= ~(val & 7);
+ s5pc210_fimd_update_irq(s);
+ } else if (offset >= 0x140 && offset <= 0x15C) {
+ w = ((offset - 0x140) >> 3) + 1;
+ i = ((offset - 0x140) >> 2) & 1;
+ s->window[w].keycon[i] = val;
+ } else if (offset >= 0x160 && offset <= 0x16C) {
+ w = ((offset - 0x160) >> 2) + 1;
+ s->window[w].keyalpha = val;
+ } else if (offset == 0x170) {
+ s->dithmode = val;
+ } else if (offset >= 0x180 && offset <= 0x190) {
+ if (val & FIMD_WINMAP_EN) {
+ s->invalidate = true;
+ s5pc210_fimd_update(s);
+ }
+ s->window[(offset - 0x180) >> 2].winmap = val;
+ } else if (offset >= 0x19C && offset <= 0x1A0) {
+ s->wpalcon[(offset - 0x19C) >> 2] = val;
+ } else if (offset == 0x1A4) {
+ val = (val & ~FIMD_TRIGCON_ROMASK) | (s->trigcon & FIMD_TRIGCON_ROMASK);
+ s->trigcon = val;
+ } else if (offset >= 0x1B0 && offset <= 0x1BC) {
+ s->i80ifcon[(offset - 0x1B0) >> 2] = val;
+ } else if (offset == 0x1C0) {
+ s->colorgaincon = val;
+ } else if (offset >= 0x1D0 && offset <= 0x1D4) {
+ s->ldi_cmdcon[(offset - 0x1D0) >> 2] = val;
+ } else if (offset >= 0x1E0 && offset <= 0x1E8) {
+ i = (offset - 0x1E0) >> 2;
+ if (i != 2) {
+ s->sifccon[i] = val;
+ }
+ } else if (offset >= 0x1EC && offset <= 0x1F8) {
+ i = (offset - 0x1EC) >> 2;
+ s->huecoef_cr[i] = val;
+ } else if (offset >= 0x1FC && offset <= 0x208) {
+ i = (offset - 0x1FC) >> 2;
+ s->huecoef_cb[i] = val;
+ } else if (offset == 0x20C) {
+ s->hueoffset = val;
+ } else if (offset >= 0x21C && offset <= 0x240) {
+ w = ((offset - 0x21C) >> 3);
+ i = ((offset - 0x21C) >> 2) & 1;
+ s->window[w].vidw_alpha[i] = val;
+ } else if (offset >= 0x244 && offset <= 0x250) {
+ s->window[(offset - 0x244) >> 2].blendeq = val;
+ } else if (offset == 0x260) {
+ s->blendcon = val;
+ } else if (offset >= 0x264 && offset <= 0x274) {
+ s->window[(offset - 0x264) >> 2].rtqoscon = val;
+ } else if (offset == 0x27C) {
+ s->dualrgb = val;
+ } else if (offset >= 0x280 && offset <= 0x2AC) {
+ s->i80ifcmd[(offset - 0x280) >> 2] = val;
+ } else if (offset >= 0x40A0 && offset <= 0x40C0) {
+ if (offset & 0x0004) {
+ print_error("bad write offset 0x%08x\n", offset);
+ }
+ s->window[(offset - 0x40A0) >> 3].shadow_buf_start = val;
+ } else if (offset >= 0x40D0 && offset <= 0x40F0) {
+ if (offset & 0x0004) {
+ print_error("bad write offset 0x%08x\n", offset);
+ }
+ s->window[(offset - 0x40D0) >> 3].shadow_buf_end = val;
+ } else if (offset >= 0x4100 && offset <= 0x4110) {
+ s->window[(offset - 0x4100) >> 2].shadow_buf_size = val;
+ } else if (offset >= 0x2400 && offset <= 0x37FC) {
+ w = (offset - 0x2400) >> 10;
+ i = ((offset - 0x2400) >> 2) & 0xFF;
+ s->window[w].palette[i] = val;
+ } else if (offset >= 0x0400 && offset <= 0x0BFC) {
+ w = (offset - 0x0400) >> 10;
+ i = ((offset - 0x0400) >> 2) & 0xFF;
+ s->window[w].palette[i] = val;
+ } else {
+ print_error("bad write offset 0x%08x\n", offset);
+ }
+}
+
+static uint64_t s5pc210_fimd_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ S5pc210fimdState *s = (S5pc210fimdState *)opaque;
+ int w, i;
+
+ print_debug2("read offset 0x%08x\n", offset);
+
+ if (offset <= 0x00C) {
+ return s->vidcon[(offset) >> 2];
+ } else if (offset >= 0x010 && offset <= 0x01C) {
+ return s->vidtcon[(offset - 0x010) >> 2];
+ } else if (offset >= 0x020 && offset <= 0x030) {
+ return s->window[(offset - 0x020) >> 2].wincon;
+ } else if (offset == 0x034) {
+ return s->shadowcon;
+ } else if (offset == 0x03C) {
+ return s->winchmap;
+ } else if (offset >= 0x040 && offset <= 0x088) {
+ w = (offset - 0x040) >> 4;
+ i = ((offset - 0x040) & 0xF) >> 2;
+ if (i == 3 && w != 1 && w != 2) {
+ goto return_error;
+ }
+ return s->window[w].vidosd[i];
+ } else if (offset >= 0x0A0 && offset <= 0x0C4) {
+ w = (offset - 0x0A0) >> 3;
+ i = ((offset - 0x0A0) >> 2) & 1;
+ return s->window[w].buf_start[i];
+ } else if (offset >= 0x0D0 && offset <= 0x0F4) {
+ w = (offset - 0x0D0) >> 3;
+ i = ((offset - 0x0D0) >> 2) & 1;
+ return s->window[w].buf_end[i];
+ } else if (offset >= 0x100 && offset <= 0x110) {
+ return s->window[(offset - 0x100) >> 2].buf_size;
+ } else if (offset >= 0x130 && offset <= 0x134) {
+ return s->vidintcon[(offset - 0x130) >> 2];
+ } else if (offset >= 0x140 && offset <= 0x15C) {
+ w = ((offset - 0x140) >> 3) + 1;
+ i = ((offset - 0x140) >> 2) & 1;
+ return s->window[w].keycon[i];
+ } else if (offset >= 0x160 && offset <= 0x16C) {
+ w = ((offset - 0x160) >> 2) + 1;
+ return s->window[w].keyalpha;
+ } else if (offset == 0x170) {
+ return s->dithmode;
+ } else if (offset >= 0x180 && offset <= 0x190) {
+ return s->window[(offset - 0x180) >> 2].winmap;
+ } else if (offset >= 0x19C && offset <= 0x1A0) {
+ return s->wpalcon[(offset - 0x19C) >> 2];
+ } else if (offset == 0x1A4) {
+ return s->trigcon;
+ } else if (offset >= 0x1B0 && offset <= 0x1BC) {
+ return s->i80ifcon[(offset - 0x1B0) >> 2];
+ } else if (offset == 0x1C0) {
+ return s->colorgaincon;
+ } else if (offset >= 0x1D0 && offset <= 0x1D4) {
+ return s->ldi_cmdcon[(offset - 0x1D0) >> 2];
+ } else if (offset >= 0x1E0 && offset <= 0x1E8) {
+ i = (offset - 0x1E0) >> 2;
+ return s->sifccon[i];
+ } else if (offset >= 0x1EC && offset <= 0x1F8) {
+ i = (offset - 0x1EC) >> 2;
+ return s->huecoef_cr[i];
+ } else if (offset >= 0x1FC && offset <= 0x208) {
+ i = (offset - 0x1FC) >> 2;
+ return s->huecoef_cb[i];
+ } else if (offset == 0x20C) {
+ return s->hueoffset;
+ } else if (offset >= 0x21C && offset <= 0x240) {
+ w = ((offset - 0x21C) >> 3);
+ i = ((offset - 0x21C) >> 2) & 1;
+ return s->window[w].vidw_alpha[i];
+ } else if (offset >= 0x244 && offset <= 0x250) {
+ return s->window[(offset - 0x244) >> 2].blendeq;
+ } else if (offset == 0x260) {
+ return s->blendcon;
+ } else if (offset >= 0x264 && offset <= 0x274) {
+ return s->window[(offset - 0x264) >> 2].rtqoscon;
+ } else if (offset == 0x27C) {
+ return s->dualrgb;
+ } else if (offset >= 0x280 && offset <= 0x2AC) {
+ return s->i80ifcmd[(offset - 0x280) >> 2];
+ } else if (offset >= 0x40A0 && offset <= 0x40C0) {
+ if (offset & 0x0004) {
+ goto return_error;
+ }
+ return s->window[(offset - 0x40A0) >> 3].shadow_buf_start;
+ } else if (offset >= 0x40D0 && offset <= 0x40F0) {
+ if (offset & 0x0004) {
+ goto return_error;
+ }
+ return s->window[(offset - 0x40D0) >> 3].shadow_buf_end;
+ } else if (offset >= 0x4100 && offset <= 0x4110) {
+ return s->window[(offset - 0x4100) >> 2].shadow_buf_size;
+ } else if (offset >= 0x2400 && offset <= 0x37FC) {
+ w = (offset - 0x2400) >> 10;
+ i = ((offset - 0x2400) >> 2) & 0xFF;
+ return s->window[w].palette[i];
+ } else if (offset >= 0x0400 && offset <= 0x0BFC) {
+ /* Palete aliases for win 0,1 */
+ w = (offset - 0x0400) >> 10;
+ i = ((offset - 0x0400) >> 2) & 0xFF;
+ return s->window[w].palette[i];
+ }
+return_error:
+ print_error("bad read offset 0x%08x\n", offset);
+ return 0xBAADBAAD;
+}
+
+static const MemoryRegionOps s5pc210_fimd_mmio_ops = {
+ .read = s5pc210_fimd_read,
+ .write = s5pc210_fimd_write,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int s5pc210_fimd_load(void *opaque, int version_id)
+{
+ S5pc210fimdState *s = (S5pc210fimdState *)opaque;
+
+ if (version_id != 1) {
+ return -EINVAL;
+ }
+
+ /* Redraw the whole screen */
+ s5pc210_update_resolution(s);
+ s5pc210_fimd_invalidate(s);
+ s5pc210_fimd_enable(s, (s->vidcon[0] & FIMD_VIDCON0_ENVID_MASK) ==
+ FIMD_VIDCON0_ENVID_MASK);
+ return 0;
+}
+
+static const VMStateDescription s5pc210_fimd_window_vmstate = {
+ .name = "s5pc210.fimd_window",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(wincon, S5pc210fimdWindow),
+ VMSTATE_UINT32_ARRAY(vidosd, S5pc210fimdWindow, 4),
+ VMSTATE_UINT32_ARRAY(buf_start, S5pc210fimdWindow, 3),
+ VMSTATE_UINT32_ARRAY(buf_end, S5pc210fimdWindow, 3),
+ VMSTATE_UINT32(buf_size, S5pc210fimdWindow),
+ VMSTATE_UINT32_ARRAY(keycon, S5pc210fimdWindow, 2),
+ VMSTATE_UINT32(keyalpha, S5pc210fimdWindow),
+ VMSTATE_UINT32(winmap, S5pc210fimdWindow),
+ VMSTATE_UINT32_ARRAY(vidw_alpha, S5pc210fimdWindow, 2),
+ VMSTATE_UINT32(blendeq, S5pc210fimdWindow),
+ VMSTATE_UINT32(rtqoscon, S5pc210fimdWindow),
+ VMSTATE_UINT32_ARRAY(palette, S5pc210fimdWindow, 256),
+ VMSTATE_UINT32(shadow_buf_start, S5pc210fimdWindow),
+ VMSTATE_UINT32(shadow_buf_end, S5pc210fimdWindow),
+ VMSTATE_UINT32(shadow_buf_size, S5pc210fimdWindow),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription s5pc210_fimd_vmstate = {
+ .name = "s5pc210.fimd",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = s5pc210_fimd_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(vidcon, S5pc210fimdState, 4),
+ VMSTATE_UINT32_ARRAY(vidtcon, S5pc210fimdState, 4),
+ VMSTATE_UINT32(shadowcon, S5pc210fimdState),
+ VMSTATE_UINT32(winchmap, S5pc210fimdState),
+ VMSTATE_UINT32_ARRAY(vidintcon, S5pc210fimdState, 2),
+ VMSTATE_UINT32(dithmode, S5pc210fimdState),
+ VMSTATE_UINT32_ARRAY(wpalcon, S5pc210fimdState, 2),
+ VMSTATE_UINT32(trigcon, S5pc210fimdState),
+ VMSTATE_UINT32_ARRAY(i80ifcon, S5pc210fimdState, 4),
+ VMSTATE_UINT32(colorgaincon, S5pc210fimdState),
+ VMSTATE_UINT32_ARRAY(ldi_cmdcon, S5pc210fimdState, 2),
+ VMSTATE_UINT32_ARRAY(sifccon, S5pc210fimdState, 3),
+ VMSTATE_UINT32_ARRAY(huecoef_cr, S5pc210fimdState, 4),
+ VMSTATE_UINT32_ARRAY(huecoef_cb, S5pc210fimdState, 4),
+ VMSTATE_UINT32(hueoffset, S5pc210fimdState),
+ VMSTATE_UINT32(blendcon, S5pc210fimdState),
+ VMSTATE_UINT32(dualrgb, S5pc210fimdState),
+ VMSTATE_UINT32_ARRAY(i80ifcmd, S5pc210fimdState, 12),
+ VMSTATE_STRUCT_ARRAY(window, S5pc210fimdState, 5, 1,
+ s5pc210_fimd_window_vmstate, S5pc210fimdWindow),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int s5pc210_fimd_init(SysBusDevice *dev)
+{
+ S5pc210fimdState *s = FROM_SYSBUS(S5pc210fimdState, dev);
+
+ s->ifb = NULL;
+
+ sysbus_init_irq(dev, &s->irq[0]);
+ sysbus_init_irq(dev, &s->irq[1]);
+ sysbus_init_irq(dev, &s->irq[2]);
+
+ memory_region_init_io(&s->iomem, &s5pc210_fimd_mmio_ops, s, "s5pc210.fimd",
+ FIMD_REGS_SIZE);
+ sysbus_init_mmio_region(dev, &s->iomem);
+ s->console = graphic_console_init(s5pc210_fimd_update,
+ s5pc210_fimd_invalidate, NULL, NULL, s);
+
+ return 0;
+}
+
+static SysBusDeviceInfo s5pc210_fimd_info = {
+ .init = s5pc210_fimd_init,
+ .qdev.name = "s5pc210.fimd",
+ .qdev.size = sizeof(S5pc210fimdState),
+ .qdev.vmsd = &s5pc210_fimd_vmstate,
+ .qdev.reset = s5pc210_fimd_reset,
+};
+
+static void s5pc210_fimd_register_devices(void)
+{
+ sysbus_register_withprop(&s5pc210_fimd_info);
+}
+
+device_init(s5pc210_fimd_register_devices)