From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([140.186.70.92]:56143) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RYE6n-0001ND-EK for qemu-devel@nongnu.org; Wed, 07 Dec 2011 04:48:19 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RYE6e-00021p-OC for qemu-devel@nongnu.org; Wed, 07 Dec 2011 04:48:13 -0500 Received: from mailout1.w1.samsung.com ([210.118.77.11]:17610) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RYE6e-00021Z-4t for qemu-devel@nongnu.org; Wed, 07 Dec 2011 04:48:04 -0500 Received: from euspt1 (mailout1.w1.samsung.com [210.118.77.11]) by mailout1.w1.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTP id <0LVT00CPYV825J@mailout1.w1.samsung.com> for qemu-devel@nongnu.org; Wed, 07 Dec 2011 09:48:03 +0000 (GMT) Received: from evvoevodinPC.rnd.samsung.ru ([106.109.8.48]) by spt1.w1.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTPA id <0LVT00GNZV7ATA@spt1.w1.samsung.com> for qemu-devel@nongnu.org; Wed, 07 Dec 2011 09:48:02 +0000 (GMT) Date: Wed, 07 Dec 2011 13:47:02 +0400 From: Evgeny Voevodin In-reply-to: <1323251225-1072-1-git-send-email-e.voevodin@samsung.com> Message-id: <1323251225-1072-12-git-send-email-e.voevodin@samsung.com> MIME-version: 1.0 Content-type: TEXT/PLAIN Content-transfer-encoding: 7BIT References: <1323251225-1072-1-git-send-email-e.voevodin@samsung.com> Subject: [Qemu-devel] [PATCH 11/14] ARM: s5pc210: added s5pc210 display controller device (FIMD) List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Mitsyanko Igor , d.solodkiy@samsung.com, Evgeny Voevodin From: Mitsyanko Igor Signed-off-by: Evgeny Voevodin --- Makefile.target | 2 +- hw/s5pc210.c | 11 + hw/s5pc210_fimd.c | 1698 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1710 insertions(+), 1 deletions(-) create mode 100644 hw/s5pc210_fimd.c diff --git a/Makefile.target b/Makefile.target index 805993b..84c4a05 100644 --- a/Makefile.target +++ b/Makefile.target @@ -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 diff --git a/hw/s5pc210.c b/hw/s5pc210.c index 8678b97..c7cba3e 100644 --- a/hw/s5pc210.c +++ b/hw/s5pc210.c @@ -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; diff --git a/hw/s5pc210_fimd.c b/hw/s5pc210_fimd.c new file mode 100644 index 0000000..b3b01f2 --- /dev/null +++ b/hw/s5pc210_fimd.c @@ -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 + * + * Contributed by Mitsyanko Igor + * + * 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) -- 1.7.4.1