From fc97bb5ba3e7239c0b6d24095df6784868dfebbf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 5 Feb 2013 12:59:04 +0100 Subject: hw: move display devices to hw/display/, configure via default-configs/ Signed-off-by: Paolo Bonzini --- default-configs/arm-softmmu.mak | 6 + default-configs/i386-softmmu.mak | 1 + default-configs/lm32-softmmu.mak | 1 + default-configs/sh4-softmmu.mak | 1 + default-configs/sh4eb-softmmu.mak | 1 + default-configs/sparc-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/Makefile.objs | 5 - hw/arm/Makefile.objs | 13 +- hw/blizzard.c | 1004 --------------- hw/blizzard_template.h | 2 +- hw/display/Makefile.objs | 21 + hw/display/blizzard.c | 1004 +++++++++++++++ hw/display/exynos4210_fimd.c | 1930 ++++++++++++++++++++++++++++ hw/display/framebuffer.c | 110 ++ hw/display/milkymist-tmu2.c | 490 +++++++ hw/display/milkymist-vgafb.c | 335 +++++ hw/display/omap_dss.c | 1086 ++++++++++++++++ hw/display/omap_lcdc.c | 493 ++++++++ hw/display/pxa2xx_lcd.c | 1058 ++++++++++++++++ hw/display/qxl-logger.c | 275 ++++ hw/display/qxl-render.c | 280 ++++ hw/display/qxl.c | 2365 ++++++++++++++++++++++++++++++++++ hw/display/sm501.c | 1450 +++++++++++++++++++++ hw/display/tc6393xb.c | 593 +++++++++ hw/display/tcx.c | 739 +++++++++++ hw/display/vga.c | 2457 ++++++++++++++++++++++++++++++++++++ hw/exynos4210_fimd.c | 1931 ---------------------------- hw/framebuffer.c | 110 -- hw/i386/Makefile.objs | 1 - hw/lm32/Makefile.objs | 3 - hw/milkymist-tmu2.c | 490 ------- hw/milkymist-vgafb.c | 335 ----- hw/milkymist-vgafb_template.h | 2 +- hw/omap_dss.c | 1086 ---------------- hw/omap_lcdc.c | 493 -------- hw/pxa2xx_lcd.c | 1058 ---------------- hw/qxl-logger.c | 275 ---- hw/qxl-render.c | 280 ---- hw/qxl.c | 2365 ---------------------------------- hw/sh4/Makefile.objs | 2 +- hw/sm501.c | 1450 --------------------- hw/sparc/Makefile.objs | 2 +- hw/tc6393xb.c | 593 --------- hw/tcx.c | 739 ----------- hw/vga.c | 2457 ------------------------------------ 46 files changed, 14708 insertions(+), 14686 deletions(-) delete mode 100644 hw/blizzard.c create mode 100644 hw/display/blizzard.c create mode 100644 hw/display/exynos4210_fimd.c create mode 100644 hw/display/framebuffer.c create mode 100644 hw/display/milkymist-tmu2.c create mode 100644 hw/display/milkymist-vgafb.c create mode 100644 hw/display/omap_dss.c create mode 100644 hw/display/omap_lcdc.c create mode 100644 hw/display/pxa2xx_lcd.c create mode 100644 hw/display/qxl-logger.c create mode 100644 hw/display/qxl-render.c create mode 100644 hw/display/qxl.c create mode 100644 hw/display/sm501.c create mode 100644 hw/display/tc6393xb.c create mode 100644 hw/display/tcx.c create mode 100644 hw/display/vga.c delete mode 100644 hw/exynos4210_fimd.c delete mode 100644 hw/framebuffer.c delete mode 100644 hw/milkymist-tmu2.c delete mode 100644 hw/milkymist-vgafb.c delete mode 100644 hw/omap_dss.c delete mode 100644 hw/omap_lcdc.c delete mode 100644 hw/pxa2xx_lcd.c delete mode 100644 hw/qxl-logger.c delete mode 100644 hw/qxl-render.c delete mode 100644 hw/qxl.c delete mode 100644 hw/sm501.c delete mode 100644 hw/tc6393xb.c delete mode 100644 hw/tcx.c delete mode 100644 hw/vga.c diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index c0e011084b..a7dd44a432 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -48,8 +48,14 @@ CONFIG_PL310=y CONFIG_PL330=y CONFIG_CADENCE=y CONFIG_XGMAC=y +CONFIG_EXYNOS4=y +CONFIG_PXA2XX=y +CONFIG_FRAMEBUFFER=y CONFIG_MARVELL_88W8618=y +CONFIG_OMAP=y +CONFIG_BLIZZARD=y CONFIG_ONENAND=y +CONFIG_ZAURUS=y CONFIG_VERSATILE_PCI=y CONFIG_VERSATILE_I2C=y diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 4f0b3f346f..48ed8d4413 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -3,6 +3,7 @@ include pci.mak include usb.mak CONFIG_VGA=y +CONFIG_QXL=$(CONFIG_SPICE) CONFIG_VGA_PCI=y CONFIG_VGA_ISA=y CONFIG_VGA_CIRRUS=y diff --git a/default-configs/lm32-softmmu.mak b/default-configs/lm32-softmmu.mak index 2654ad6e44..6b2ee430e5 100644 --- a/default-configs/lm32-softmmu.mak +++ b/default-configs/lm32-softmmu.mak @@ -1,6 +1,7 @@ # Default configuration for lm32-softmmu CONFIG_MILKYMIST=y +CONFIG_FRAMEBUFFER=y CONFIG_PTIMER=y CONFIG_PFLASH_CFI01=y CONFIG_PFLASH_CFI02=y diff --git a/default-configs/sh4-softmmu.mak b/default-configs/sh4-softmmu.mak index 20a05f9f35..f6bf62d1c2 100644 --- a/default-configs/sh4-softmmu.mak +++ b/default-configs/sh4-softmmu.mak @@ -8,3 +8,4 @@ CONFIG_PFLASH_CFI02=y CONFIG_ISA_MMIO=y CONFIG_SH4=y CONFIG_IDE_MMIO=y +CONFIG_SM501=y diff --git a/default-configs/sh4eb-softmmu.mak b/default-configs/sh4eb-softmmu.mak index 875e7e62c2..c1d513d099 100644 --- a/default-configs/sh4eb-softmmu.mak +++ b/default-configs/sh4eb-softmmu.mak @@ -8,3 +8,4 @@ CONFIG_PFLASH_CFI02=y CONFIG_ISA_MMIO=y CONFIG_SH4=y CONFIG_IDE_MMIO=y +CONFIG_SM501=y diff --git a/default-configs/sparc-softmmu.mak b/default-configs/sparc-softmmu.mak index eda8797662..25bcbe38bc 100644 --- a/default-configs/sparc-softmmu.mak +++ b/default-configs/sparc-softmmu.mak @@ -9,4 +9,5 @@ CONFIG_FDC=y CONFIG_EMPTY_SLOT=y CONFIG_PCNET_COMMON=y CONFIG_LANCE=y +CONFIG_TCX=y CONFIG_CS4231=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 6fba70ff73..58b33cfa24 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -3,6 +3,7 @@ include pci.mak include usb.mak CONFIG_VGA=y +CONFIG_QXL=$(CONFIG_SPICE) CONFIG_VGA_PCI=y CONFIG_VGA_ISA=y CONFIG_VGA_CIRRUS=y diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 83a6bf2b18..eef8742af3 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -31,11 +31,6 @@ obj-y += $(devices-dirs-y) ifeq ($(CONFIG_SOFTMMU),y) -# Per-target files -# virtio has to be here due to weird dependency between PCI and virtio-net. -# need to fix this properly -obj-$(CONFIG_VGA) += vga.o - # Inter-VM PCI shared memory & VFIO PCI device assignment ifeq ($(CONFIG_PCI), y) obj-$(CONFIG_KVM) += ivshmem.o diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 6582f5a0aa..3efefe59cf 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -5,22 +5,21 @@ obj-y += a9scu.o obj-y += realview_gic.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-y += exynos4210_gic.o exynos4210_combiner.o obj-y += exynos4210_uart.o exynos4210_pwm.o -obj-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o +obj-y += exynos4210_pmu.o exynos4210_mct.o obj-y += exynos4210_rtc.o exynos4210_i2c.o obj-y += arm_mptimer.o a15mpcore.o obj-y += armv7m_nvic.o obj-y += pxa2xx_timer.o pxa2xx_dma.o -obj-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o -obj-y += zaurus.o tc6393xb.o -obj-y += omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \ +obj-y += pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o +obj-y += zaurus.o +obj-y += omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \ omap_gpio.o omap_intc.o omap_uart.o -obj-y += omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \ +obj-y += soc_dma.o omap_gptimer.o omap_synctimer.o \ omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o obj-y += tsc210x.o -obj-y += blizzard.o cbus.o tusb6010.o +obj-y += cbus.o tusb6010.o obj-y += mst_fpga.o obj-y += bitbang_i2c.o -obj-y += framebuffer.o obj-y += strongarm.o obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o obj-$(CONFIG_KVM) += kvm/arm_gic.o diff --git a/hw/blizzard.c b/hw/blizzard.c deleted file mode 100644 index bdb0b15ddb..0000000000 --- a/hw/blizzard.c +++ /dev/null @@ -1,1004 +0,0 @@ -/* - * Epson S1D13744/S1D13745 (Blizzard/Hailstorm/Tornado) LCD/TV controller. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * 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 or - * (at your option) version 3 of the License. - * - * 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, see . - */ - -#include "qemu-common.h" -#include "ui/console.h" -#include "hw/arm/devices.h" -#include "hw/vga_int.h" -#include "ui/pixel_ops.h" - -typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int); - -typedef struct { - uint8_t reg; - uint32_t addr; - int swallow; - - int pll; - int pll_range; - int pll_ctrl; - uint8_t pll_mode; - uint8_t clksel; - int memenable; - int memrefresh; - uint8_t timing[3]; - int priority; - - uint8_t lcd_config; - int x; - int y; - int skipx; - int skipy; - uint8_t hndp; - uint8_t vndp; - uint8_t hsync; - uint8_t vsync; - uint8_t pclk; - uint8_t u; - uint8_t v; - uint8_t yrc[2]; - int ix[2]; - int iy[2]; - int ox[2]; - int oy[2]; - - int enable; - int blank; - int bpp; - int invalidate; - int mx[2]; - int my[2]; - uint8_t mode; - uint8_t effect; - uint8_t iformat; - uint8_t source; - QemuConsole *con; - blizzard_fn_t *line_fn_tab[2]; - void *fb; - - uint8_t hssi_config[3]; - uint8_t tv_config; - uint8_t tv_timing[4]; - uint8_t vbi; - uint8_t tv_x; - uint8_t tv_y; - uint8_t tv_test; - uint8_t tv_filter_config; - uint8_t tv_filter_idx; - uint8_t tv_filter_coeff[0x20]; - uint8_t border_r; - uint8_t border_g; - uint8_t border_b; - uint8_t gamma_config; - uint8_t gamma_idx; - uint8_t gamma_lut[0x100]; - uint8_t matrix_ena; - uint8_t matrix_coeff[0x12]; - uint8_t matrix_r; - uint8_t matrix_g; - uint8_t matrix_b; - uint8_t pm; - uint8_t status; - uint8_t rgbgpio_dir; - uint8_t rgbgpio; - uint8_t gpio_dir; - uint8_t gpio; - uint8_t gpio_edge[2]; - uint8_t gpio_irq; - uint8_t gpio_pdown; - - struct { - int x; - int y; - int dx; - int dy; - int len; - int buflen; - void *buf; - void *data; - uint16_t *ptr; - int angle; - int pitch; - blizzard_fn_t line_fn; - } data; -} BlizzardState; - -/* Bytes(!) per pixel */ -static const int blizzard_iformat_bpp[0x10] = { - 0, - 2, /* RGB 5:6:5*/ - 3, /* RGB 6:6:6 mode 1 */ - 3, /* RGB 8:8:8 mode 1 */ - 0, 0, - 4, /* RGB 6:6:6 mode 2 */ - 4, /* RGB 8:8:8 mode 2 */ - 0, /* YUV 4:2:2 */ - 0, /* YUV 4:2:0 */ - 0, 0, 0, 0, 0, 0, -}; - -static inline void blizzard_rgb2yuv(int r, int g, int b, - int *y, int *u, int *v) -{ - *y = 0x10 + ((0x838 * r + 0x1022 * g + 0x322 * b) >> 13); - *u = 0x80 + ((0xe0e * b - 0x04c1 * r - 0x94e * g) >> 13); - *v = 0x80 + ((0xe0e * r - 0x0bc7 * g - 0x247 * b) >> 13); -} - -static void blizzard_window(BlizzardState *s) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - uint8_t *src, *dst; - int bypp[2]; - int bypl[3]; - int y; - blizzard_fn_t fn = s->data.line_fn; - - if (!fn) - return; - if (s->mx[0] > s->data.x) - s->mx[0] = s->data.x; - if (s->my[0] > s->data.y) - s->my[0] = s->data.y; - if (s->mx[1] < s->data.x + s->data.dx) - s->mx[1] = s->data.x + s->data.dx; - if (s->my[1] < s->data.y + s->data.dy) - s->my[1] = s->data.y + s->data.dy; - - bypp[0] = s->bpp; - bypp[1] = surface_bytes_per_pixel(surface); - bypl[0] = bypp[0] * s->data.pitch; - bypl[1] = bypp[1] * s->x; - bypl[2] = bypp[0] * s->data.dx; - - src = s->data.data; - dst = s->fb + bypl[1] * s->data.y + bypp[1] * s->data.x; - for (y = s->data.dy; y > 0; y --, src += bypl[0], dst += bypl[1]) - fn(dst, src, bypl[2]); -} - -static int blizzard_transfer_setup(BlizzardState *s) -{ - if (s->source > 3 || !s->bpp || - s->ix[1] < s->ix[0] || s->iy[1] < s->iy[0]) - return 0; - - s->data.angle = s->effect & 3; - s->data.line_fn = s->line_fn_tab[!!s->data.angle][s->iformat]; - s->data.x = s->ix[0]; - s->data.y = s->iy[0]; - s->data.dx = s->ix[1] - s->ix[0] + 1; - s->data.dy = s->iy[1] - s->iy[0] + 1; - s->data.len = s->bpp * s->data.dx * s->data.dy; - s->data.pitch = s->data.dx; - if (s->data.len > s->data.buflen) { - s->data.buf = g_realloc(s->data.buf, s->data.len); - s->data.buflen = s->data.len; - } - s->data.ptr = s->data.buf; - s->data.data = s->data.buf; - s->data.len /= 2; - return 1; -} - -static void blizzard_reset(BlizzardState *s) -{ - s->reg = 0; - s->swallow = 0; - - s->pll = 9; - s->pll_range = 1; - s->pll_ctrl = 0x14; - s->pll_mode = 0x32; - s->clksel = 0x00; - s->memenable = 0; - s->memrefresh = 0x25c; - s->timing[0] = 0x3f; - s->timing[1] = 0x13; - s->timing[2] = 0x21; - s->priority = 0; - - s->lcd_config = 0x74; - s->x = 8; - s->y = 1; - s->skipx = 0; - s->skipy = 0; - s->hndp = 3; - s->vndp = 2; - s->hsync = 1; - s->vsync = 1; - s->pclk = 0x80; - - s->ix[0] = 0; - s->ix[1] = 0; - s->iy[0] = 0; - s->iy[1] = 0; - s->ox[0] = 0; - s->ox[1] = 0; - s->oy[0] = 0; - s->oy[1] = 0; - - s->yrc[0] = 0x00; - s->yrc[1] = 0x30; - s->u = 0; - s->v = 0; - - s->iformat = 3; - s->source = 0; - s->bpp = blizzard_iformat_bpp[s->iformat]; - - s->hssi_config[0] = 0x00; - s->hssi_config[1] = 0x00; - s->hssi_config[2] = 0x01; - s->tv_config = 0x00; - s->tv_timing[0] = 0x00; - s->tv_timing[1] = 0x00; - s->tv_timing[2] = 0x00; - s->tv_timing[3] = 0x00; - s->vbi = 0x10; - s->tv_x = 0x14; - s->tv_y = 0x03; - s->tv_test = 0x00; - s->tv_filter_config = 0x80; - s->tv_filter_idx = 0x00; - s->border_r = 0x10; - s->border_g = 0x80; - s->border_b = 0x80; - s->gamma_config = 0x00; - s->gamma_idx = 0x00; - s->matrix_ena = 0x00; - memset(&s->matrix_coeff, 0, sizeof(s->matrix_coeff)); - s->matrix_r = 0x00; - s->matrix_g = 0x00; - s->matrix_b = 0x00; - s->pm = 0x02; - s->status = 0x00; - s->rgbgpio_dir = 0x00; - s->gpio_dir = 0x00; - s->gpio_edge[0] = 0x00; - s->gpio_edge[1] = 0x00; - s->gpio_irq = 0x00; - s->gpio_pdown = 0xff; -} - -static inline void blizzard_invalidate_display(void *opaque) { - BlizzardState *s = (BlizzardState *) opaque; - - s->invalidate = 1; -} - -static uint16_t blizzard_reg_read(void *opaque, uint8_t reg) -{ - BlizzardState *s = (BlizzardState *) opaque; - - switch (reg) { - case 0x00: /* Revision Code */ - return 0xa5; - - case 0x02: /* Configuration Readback */ - return 0x83; /* Macrovision OK, CNF[2:0] = 3 */ - - case 0x04: /* PLL M-Divider */ - return (s->pll - 1) | (1 << 7); - case 0x06: /* PLL Lock Range Control */ - return s->pll_range; - case 0x08: /* PLL Lock Synthesis Control 0 */ - return s->pll_ctrl & 0xff; - case 0x0a: /* PLL Lock Synthesis Control 1 */ - return s->pll_ctrl >> 8; - case 0x0c: /* PLL Mode Control 0 */ - return s->pll_mode; - - case 0x0e: /* Clock-Source Select */ - return s->clksel; - - case 0x10: /* Memory Controller Activate */ - case 0x14: /* Memory Controller Bank 0 Status Flag */ - return s->memenable; - - case 0x18: /* Auto-Refresh Interval Setting 0 */ - return s->memrefresh & 0xff; - case 0x1a: /* Auto-Refresh Interval Setting 1 */ - return s->memrefresh >> 8; - - case 0x1c: /* Power-On Sequence Timing Control */ - return s->timing[0]; - case 0x1e: /* Timing Control 0 */ - return s->timing[1]; - case 0x20: /* Timing Control 1 */ - return s->timing[2]; - - case 0x24: /* Arbitration Priority Control */ - return s->priority; - - case 0x28: /* LCD Panel Configuration */ - return s->lcd_config; - - case 0x2a: /* LCD Horizontal Display Width */ - return s->x >> 3; - case 0x2c: /* LCD Horizontal Non-display Period */ - return s->hndp; - case 0x2e: /* LCD Vertical Display Height 0 */ - return s->y & 0xff; - case 0x30: /* LCD Vertical Display Height 1 */ - return s->y >> 8; - case 0x32: /* LCD Vertical Non-display Period */ - return s->vndp; - case 0x34: /* LCD HS Pulse-width */ - return s->hsync; - case 0x36: /* LCd HS Pulse Start Position */ - return s->skipx >> 3; - case 0x38: /* LCD VS Pulse-width */ - return s->vsync; - case 0x3a: /* LCD VS Pulse Start Position */ - return s->skipy; - - case 0x3c: /* PCLK Polarity */ - return s->pclk; - - case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */ - return s->hssi_config[0]; - case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */ - return s->hssi_config[1]; - case 0x42: /* High-speed Serial Interface Tx Mode */ - return s->hssi_config[2]; - case 0x44: /* TV Display Configuration */ - return s->tv_config; - case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits */ - return s->tv_timing[(reg - 0x46) >> 1]; - case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ - return s->vbi; - case 0x50: /* TV Horizontal Start Position */ - return s->tv_x; - case 0x52: /* TV Vertical Start Position */ - return s->tv_y; - case 0x54: /* TV Test Pattern Setting */ - return s->tv_test; - case 0x56: /* TV Filter Setting */ - return s->tv_filter_config; - case 0x58: /* TV Filter Coefficient Index */ - return s->tv_filter_idx; - case 0x5a: /* TV Filter Coefficient Data */ - if (s->tv_filter_idx < 0x20) - return s->tv_filter_coeff[s->tv_filter_idx ++]; - return 0; - - case 0x60: /* Input YUV/RGB Translate Mode 0 */ - return s->yrc[0]; - case 0x62: /* Input YUV/RGB Translate Mode 1 */ - return s->yrc[1]; - case 0x64: /* U Data Fix */ - return s->u; - case 0x66: /* V Data Fix */ - return s->v; - - case 0x68: /* Display Mode */ - return s->mode; - - case 0x6a: /* Special Effects */ - return s->effect; - - case 0x6c: /* Input Window X Start Position 0 */ - return s->ix[0] & 0xff; - case 0x6e: /* Input Window X Start Position 1 */ - return s->ix[0] >> 3; - case 0x70: /* Input Window Y Start Position 0 */ - return s->ix[0] & 0xff; - case 0x72: /* Input Window Y Start Position 1 */ - return s->ix[0] >> 3; - case 0x74: /* Input Window X End Position 0 */ - return s->ix[1] & 0xff; - case 0x76: /* Input Window X End Position 1 */ - return s->ix[1] >> 3; - case 0x78: /* Input Window Y End Position 0 */ - return s->ix[1] & 0xff; - case 0x7a: /* Input Window Y End Position 1 */ - return s->ix[1] >> 3; - case 0x7c: /* Output Window X Start Position 0 */ - return s->ox[0] & 0xff; - case 0x7e: /* Output Window X Start Position 1 */ - return s->ox[0] >> 3; - case 0x80: /* Output Window Y Start Position 0 */ - return s->oy[0] & 0xff; - case 0x82: /* Output Window Y Start Position 1 */ - return s->oy[0] >> 3; - case 0x84: /* Output Window X End Position 0 */ - return s->ox[1] & 0xff; - case 0x86: /* Output Window X End Position 1 */ - return s->ox[1] >> 3; - case 0x88: /* Output Window Y End Position 0 */ - return s->oy[1] & 0xff; - case 0x8a: /* Output Window Y End Position 1 */ - return s->oy[1] >> 3; - - case 0x8c: /* Input Data Format */ - return s->iformat; - case 0x8e: /* Data Source Select */ - return s->source; - case 0x90: /* Display Memory Data Port */ - return 0; - - case 0xa8: /* Border Color 0 */ - return s->border_r; - case 0xaa: /* Border Color 1 */ - return s->border_g; - case 0xac: /* Border Color 2 */ - return s->border_b; - - case 0xb4: /* Gamma Correction Enable */ - return s->gamma_config; - case 0xb6: /* Gamma Correction Table Index */ - return s->gamma_idx; - case 0xb8: /* Gamma Correction Table Data */ - return s->gamma_lut[s->gamma_idx ++]; - - case 0xba: /* 3x3 Matrix Enable */ - return s->matrix_ena; - case 0xbc ... 0xde: /* Coefficient Registers */ - return s->matrix_coeff[(reg - 0xbc) >> 1]; - case 0xe0: /* 3x3 Matrix Red Offset */ - return s->matrix_r; - case 0xe2: /* 3x3 Matrix Green Offset */ - return s->matrix_g; - case 0xe4: /* 3x3 Matrix Blue Offset */ - return s->matrix_b; - - case 0xe6: /* Power-save */ - return s->pm; - case 0xe8: /* Non-display Period Control / Status */ - return s->status | (1 << 5); - case 0xea: /* RGB Interface Control */ - return s->rgbgpio_dir; - case 0xec: /* RGB Interface Status */ - return s->rgbgpio; - case 0xee: /* General-purpose IO Pins Configuration */ - return s->gpio_dir; - case 0xf0: /* General-purpose IO Pins Status / Control */ - return s->gpio; - case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ - return s->gpio_edge[0]; - case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ - return s->gpio_edge[1]; - case 0xf6: /* GPIO Interrupt Status */ - return s->gpio_irq; - case 0xf8: /* GPIO Pull-down Control */ - return s->gpio_pdown; - - default: - fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg); - return 0; - } -} - -static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value) -{ - BlizzardState *s = (BlizzardState *) opaque; - - switch (reg) { - case 0x04: /* PLL M-Divider */ - s->pll = (value & 0x3f) + 1; - break; - case 0x06: /* PLL Lock Range Control */ - s->pll_range = value & 3; - break; - case 0x08: /* PLL Lock Synthesis Control 0 */ - s->pll_ctrl &= 0xf00; - s->pll_ctrl |= (value << 0) & 0x0ff; - break; - case 0x0a: /* PLL Lock Synthesis Control 1 */ - s->pll_ctrl &= 0x0ff; - s->pll_ctrl |= (value << 8) & 0xf00; - break; - case 0x0c: /* PLL Mode Control 0 */ - s->pll_mode = value & 0x77; - if ((value & 3) == 0 || (value & 3) == 3) - fprintf(stderr, "%s: wrong PLL Control bits (%i)\n", - __FUNCTION__, value & 3); - break; - - case 0x0e: /* Clock-Source Select */ - s->clksel = value & 0xff; - break; - - case 0x10: /* Memory Controller Activate */ - s->memenable = value & 1; - break; - case 0x14: /* Memory Controller Bank 0 Status Flag */ - break; - - case 0x18: /* Auto-Refresh Interval Setting 0 */ - s->memrefresh &= 0xf00; - s->memrefresh |= (value << 0) & 0x0ff; - break; - case 0x1a: /* Auto-Refresh Interval Setting 1 */ - s->memrefresh &= 0x0ff; - s->memrefresh |= (value << 8) & 0xf00; - break; - - case 0x1c: /* Power-On Sequence Timing Control */ - s->timing[0] = value & 0x7f; - break; - case 0x1e: /* Timing Control 0 */ - s->timing[1] = value & 0x17; - break; - case 0x20: /* Timing Control 1 */ - s->timing[2] = value & 0x35; - break; - - case 0x24: /* Arbitration Priority Control */ - s->priority = value & 1; - break; - - case 0x28: /* LCD Panel Configuration */ - s->lcd_config = value & 0xff; - if (value & (1 << 7)) - fprintf(stderr, "%s: data swap not supported!\n", __FUNCTION__); - break; - - case 0x2a: /* LCD Horizontal Display Width */ - s->x = value << 3; - break; - case 0x2c: /* LCD Horizontal Non-display Period */ - s->hndp = value & 0xff; - break; - case 0x2e: /* LCD Vertical Display Height 0 */ - s->y &= 0x300; - s->y |= (value << 0) & 0x0ff; - break; - case 0x30: /* LCD Vertical Display Height 1 */ - s->y &= 0x0ff; - s->y |= (value << 8) & 0x300; - break; - case 0x32: /* LCD Vertical Non-display Period */ - s->vndp = value & 0xff; - break; - case 0x34: /* LCD HS Pulse-width */ - s->hsync = value & 0xff; - break; - case 0x36: /* LCD HS Pulse Start Position */ - s->skipx = value & 0xff; - break; - case 0x38: /* LCD VS Pulse-width */ - s->vsync = value & 0xbf; - break; - case 0x3a: /* LCD VS Pulse Start Position */ - s->skipy = value & 0xff; - break; - - case 0x3c: /* PCLK Polarity */ - s->pclk = value & 0x82; - /* Affects calculation of s->hndp, s->hsync and s->skipx. */ - break; - - case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */ - s->hssi_config[0] = value; - break; - case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */ - s->hssi_config[1] = value; - if (((value >> 4) & 3) == 3) - fprintf(stderr, "%s: Illegal active-data-links value\n", - __FUNCTION__); - break; - case 0x42: /* High-speed Serial Interface Tx Mode */ - s->hssi_config[2] = value & 0xbd; - break; - - case 0x44: /* TV Display Configuration */ - s->tv_config = value & 0xfe; - break; - case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits 0 */ - s->tv_timing[(reg - 0x46) >> 1] = value; - break; - case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ - s->vbi = value; - break; - case 0x50: /* TV Horizontal Start Position */ - s->tv_x = value; - break; - case 0x52: /* TV Vertical Start Position */ - s->tv_y = value & 0x7f; - break; - case 0x54: /* TV Test Pattern Setting */ - s->tv_test = value; - break; - case 0x56: /* TV Filter Setting */ - s->tv_filter_config = value & 0xbf; - break; - case 0x58: /* TV Filter Coefficient Index */ - s->tv_filter_idx = value & 0x1f; - break; - case 0x5a: /* TV Filter Coefficient Data */ - if (s->tv_filter_idx < 0x20) - s->tv_filter_coeff[s->tv_filter_idx ++] = value; - break; - - case 0x60: /* Input YUV/RGB Translate Mode 0 */ - s->yrc[0] = value & 0xb0; - break; - case 0x62: /* Input YUV/RGB Translate Mode 1 */ - s->yrc[1] = value & 0x30; - break; - case 0x64: /* U Data Fix */ - s->u = value & 0xff; - break; - case 0x66: /* V Data Fix */ - s->v = value & 0xff; - break; - - case 0x68: /* Display Mode */ - if ((s->mode ^ value) & 3) - s->invalidate = 1; - s->mode = value & 0xb7; - s->enable = value & 1; - s->blank = (value >> 1) & 1; - if (value & (1 << 4)) - fprintf(stderr, "%s: Macrovision enable attempt!\n", __FUNCTION__); - break; - - case 0x6a: /* Special Effects */ - s->effect = value & 0xfb; - break; - - case 0x6c: /* Input Window X Start Position 0 */ - s->ix[0] &= 0x300; - s->ix[0] |= (value << 0) & 0x0ff; - break; - case 0x6e: /* Input Window X Start Position 1 */ - s->ix[0] &= 0x0ff; - s->ix[0] |= (value << 8) & 0x300; - break; - case 0x70: /* Input Window Y Start Position 0 */ - s->iy[0] &= 0x300; - s->iy[0] |= (value << 0) & 0x0ff; - break; - case 0x72: /* Input Window Y Start Position 1 */ - s->iy[0] &= 0x0ff; - s->iy[0] |= (value << 8) & 0x300; - break; - case 0x74: /* Input Window X End Position 0 */ - s->ix[1] &= 0x300; - s->ix[1] |= (value << 0) & 0x0ff; - break; - case 0x76: /* Input Window X End Position 1 */ - s->ix[1] &= 0x0ff; - s->ix[1] |= (value << 8) & 0x300; - break; - case 0x78: /* Input Window Y End Position 0 */ - s->iy[1] &= 0x300; - s->iy[1] |= (value << 0) & 0x0ff; - break; - case 0x7a: /* Input Window Y End Position 1 */ - s->iy[1] &= 0x0ff; - s->iy[1] |= (value << 8) & 0x300; - break; - case 0x7c: /* Output Window X Start Position 0 */ - s->ox[0] &= 0x300; - s->ox[0] |= (value << 0) & 0x0ff; - break; - case 0x7e: /* Output Window X Start Position 1 */ - s->ox[0] &= 0x0ff; - s->ox[0] |= (value << 8) & 0x300; - break; - case 0x80: /* Output Window Y Start Position 0 */ - s->oy[0] &= 0x300; - s->oy[0] |= (value << 0) & 0x0ff; - break; - case 0x82: /* Output Window Y Start Position 1 */ - s->oy[0] &= 0x0ff; - s->oy[0] |= (value << 8) & 0x300; - break; - case 0x84: /* Output Window X End Position 0 */ - s->ox[1] &= 0x300; - s->ox[1] |= (value << 0) & 0x0ff; - break; - case 0x86: /* Output Window X End Position 1 */ - s->ox[1] &= 0x0ff; - s->ox[1] |= (value << 8) & 0x300; - break; - case 0x88: /* Output Window Y End Position 0 */ - s->oy[1] &= 0x300; - s->oy[1] |= (value << 0) & 0x0ff; - break; - case 0x8a: /* Output Window Y End Position 1 */ - s->oy[1] &= 0x0ff; - s->oy[1] |= (value << 8) & 0x300; - break; - - case 0x8c: /* Input Data Format */ - s->iformat = value & 0xf; - s->bpp = blizzard_iformat_bpp[s->iformat]; - if (!s->bpp) - fprintf(stderr, "%s: Illegal or unsupported input format %x\n", - __FUNCTION__, s->iformat); - break; - case 0x8e: /* Data Source Select */ - s->source = value & 7; - /* Currently all windows will be "destructive overlays". */ - if ((!(s->effect & (1 << 3)) && (s->ix[0] != s->ox[0] || - s->iy[0] != s->oy[0] || - s->ix[1] != s->ox[1] || - s->iy[1] != s->oy[1])) || - !((s->ix[1] - s->ix[0]) & (s->iy[1] - s->iy[0]) & - (s->ox[1] - s->ox[0]) & (s->oy[1] - s->oy[0]) & 1)) - fprintf(stderr, "%s: Illegal input/output window positions\n", - __FUNCTION__); - - blizzard_transfer_setup(s); - break; - - case 0x90: /* Display Memory Data Port */ - if (!s->data.len && !blizzard_transfer_setup(s)) - break; - - *s->data.ptr ++ = value; - if (-- s->data.len == 0) - blizzard_window(s); - break; - - case 0xa8: /* Border Color 0 */ - s->border_r = value; - break; - case 0xaa: /* Border Color 1 */ - s->border_g = value; - break; - case 0xac: /* Border Color 2 */ - s->border_b = value; - break; - - case 0xb4: /* Gamma Correction Enable */ - s->gamma_config = value & 0x87; - break; - case 0xb6: /* Gamma Correction Table Index */ - s->gamma_idx = value; - break; - case 0xb8: /* Gamma Correction Table Data */ - s->gamma_lut[s->gamma_idx ++] = value; - break; - - case 0xba: /* 3x3 Matrix Enable */ - s->matrix_ena = value & 1; - break; - case 0xbc ... 0xde: /* Coefficient Registers */ - s->matrix_coeff[(reg - 0xbc) >> 1] = value & ((reg & 2) ? 0x80 : 0xff); - break; - case 0xe0: /* 3x3 Matrix Red Offset */ - s->matrix_r = value; - break; - case 0xe2: /* 3x3 Matrix Green Offset */ - s->matrix_g = value; - break; - case 0xe4: /* 3x3 Matrix Blue Offset */ - s->matrix_b = value; - break; - - case 0xe6: /* Power-save */ - s->pm = value & 0x83; - if (value & s->mode & 1) - fprintf(stderr, "%s: The display must be disabled before entering " - "Standby Mode\n", __FUNCTION__); - break; - case 0xe8: /* Non-display Period Control / Status */ - s->status = value & 0x1b; - break; - case 0xea: /* RGB Interface Control */ - s->rgbgpio_dir = value & 0x8f; - break; - case 0xec: /* RGB Interface Status */ - s->rgbgpio = value & 0xcf; - break; - case 0xee: /* General-purpose IO Pins Configuration */ - s->gpio_dir = value; - break; - case 0xf0: /* General-purpose IO Pins Status / Control */ - s->gpio = value; - break; - case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ - s->gpio_edge[0] = value; - break; - case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ - s->gpio_edge[1] = value; - break; - case 0xf6: /* GPIO Interrupt Status */ - s->gpio_irq &= value; - break; - case 0xf8: /* GPIO Pull-down Control */ - s->gpio_pdown = value; - break; - - default: - fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg); - break; - } -} - -uint16_t s1d13745_read(void *opaque, int dc) -{ - BlizzardState *s = (BlizzardState *) opaque; - uint16_t value = blizzard_reg_read(s, s->reg); - - if (s->swallow -- > 0) - return 0; - if (dc) - s->reg ++; - - return value; -} - -void s1d13745_write(void *opaque, int dc, uint16_t value) -{ - BlizzardState *s = (BlizzardState *) opaque; - - if (s->swallow -- > 0) - return; - if (dc) { - blizzard_reg_write(s, s->reg, value); - - if (s->reg != 0x90 && s->reg != 0x5a && s->reg != 0xb8) - s->reg += 2; - } else - s->reg = value & 0xff; -} - -void s1d13745_write_block(void *opaque, int dc, - void *buf, size_t len, int pitch) -{ - BlizzardState *s = (BlizzardState *) opaque; - - while (len > 0) { - if (s->reg == 0x90 && dc && - (s->data.len || blizzard_transfer_setup(s)) && - len >= (s->data.len << 1)) { - len -= s->data.len << 1; - s->data.len = 0; - s->data.data = buf; - if (pitch) - s->data.pitch = pitch; - blizzard_window(s); - s->data.data = s->data.buf; - continue; - } - - s1d13745_write(opaque, dc, *(uint16_t *) buf); - len -= 2; - buf += 2; - } -} - -static void blizzard_update_display(void *opaque) -{ - BlizzardState *s = (BlizzardState *) opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - int y, bypp, bypl, bwidth; - uint8_t *src, *dst; - - if (!s->enable) - return; - - if (s->x != surface_width(surface) || s->y != surface_height(surface)) { - s->invalidate = 1; - qemu_console_resize(s->con, s->x, s->y); - surface = qemu_console_surface(s->con); - } - - if (s->invalidate) { - s->invalidate = 0; - - if (s->blank) { - bypp = surface_bytes_per_pixel(surface); - memset(surface_data(surface), 0, bypp * s->x * s->y); - return; - } - - s->mx[0] = 0; - s->mx[1] = s->x; - s->my[0] = 0; - s->my[1] = s->y; - } - - if (s->mx[1] <= s->mx[0]) - return; - - bypp = surface_bytes_per_pixel(surface); - bypl = bypp * s->x; - bwidth = bypp * (s->mx[1] - s->mx[0]); - y = s->my[0]; - src = s->fb + bypl * y + bypp * s->mx[0]; - dst = surface_data(surface) + bypl * y + bypp * s->mx[0]; - for (; y < s->my[1]; y ++, src += bypl, dst += bypl) - memcpy(dst, src, bwidth); - - dpy_gfx_update(s->con, s->mx[0], s->my[0], - s->mx[1] - s->mx[0], y - s->my[0]); - - s->mx[0] = s->x; - s->mx[1] = 0; - s->my[0] = s->y; - s->my[1] = 0; -} - -static void blizzard_screen_dump(void *opaque, const char *filename, - bool cswitch, Error **errp) -{ - BlizzardState *s = (BlizzardState *) opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - - blizzard_update_display(opaque); - if (s && surface_data(surface)) { - ppm_save(filename, surface, errp); - } -} - -#define DEPTH 8 -#include "hw/blizzard_template.h" -#define DEPTH 15 -#include "hw/blizzard_template.h" -#define DEPTH 16 -#include "hw/blizzard_template.h" -#define DEPTH 24 -#include "hw/blizzard_template.h" -#define DEPTH 32 -#include "hw/blizzard_template.h" - -void *s1d13745_init(qemu_irq gpio_int) -{ - BlizzardState *s = (BlizzardState *) g_malloc0(sizeof(*s)); - DisplaySurface *surface; - - s->fb = g_malloc(0x180000); - - s->con = graphic_console_init(blizzard_update_display, - blizzard_invalidate_display, - blizzard_screen_dump, NULL, s); - surface = qemu_console_surface(s->con); - - switch (surface_bits_per_pixel(surface)) { - case 0: - s->line_fn_tab[0] = s->line_fn_tab[1] = - g_malloc0(sizeof(blizzard_fn_t) * 0x10); - break; - case 8: - s->line_fn_tab[0] = blizzard_draw_fn_8; - s->line_fn_tab[1] = blizzard_draw_fn_r_8; - break; - case 15: - s->line_fn_tab[0] = blizzard_draw_fn_15; - s->line_fn_tab[1] = blizzard_draw_fn_r_15; - break; - case 16: - s->line_fn_tab[0] = blizzard_draw_fn_16; - s->line_fn_tab[1] = blizzard_draw_fn_r_16; - break; - case 24: - s->line_fn_tab[0] = blizzard_draw_fn_24; - s->line_fn_tab[1] = blizzard_draw_fn_r_24; - break; - case 32: - s->line_fn_tab[0] = blizzard_draw_fn_32; - s->line_fn_tab[1] = blizzard_draw_fn_r_32; - break; - default: - fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__); - exit(1); - } - - blizzard_reset(s); - - return s; -} diff --git a/hw/blizzard_template.h b/hw/blizzard_template.h index 42f4e90b04..a8a8899478 100644 --- a/hw/blizzard_template.h +++ b/hw/blizzard_template.h @@ -55,7 +55,7 @@ static void glue(blizzard_draw_line16_, DEPTH)(PIXEL_TYPE *dest, unsigned int r, g, b; const uint16_t *end = (const void *) src + width; while (src < end) { - data = lduw_raw(src ++); + data = *src ++; b = (data & 0x1f) << 3; data >>= 5; g = (data & 0x3f) << 2; diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 3ac154d708..3f7027d46b 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -11,3 +11,24 @@ common-obj-$(CONFIG_VGA_PCI) += vga-pci.o common-obj-$(CONFIG_VGA_ISA) += vga-isa.o common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o + +common-obj-$(CONFIG_BLIZZARD) += blizzard.o +common-obj-$(CONFIG_EXYNOS4) += exynos4210_fimd.o +common-obj-$(CONFIG_FRAMEBUFFER) += framebuffer.o +common-obj-$(CONFIG_MILKYMIST) += milkymist-vgafb.o +common-obj-$(CONFIG_ZAURUS) += tc6393xb.o + +ifeq ($(CONFIG_GLX),y) +common-obj-$(CONFIG_MILKYMIST) += milkymist-tmu2.o +endif + +obj-$(CONFIG_OMAP) += omap_dss.o +obj-$(CONFIG_OMAP) += omap_lcdc.o +obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o +obj-$(CONFIG_SM501) += sm501.o +obj-$(CONFIG_TCX) += tcx.o + +obj-$(CONFIG_VGA) += vga.o + +common-obj-$(CONFIG_QXL) += qxl-logger.o qxl-render.o +obj-$(CONFIG_QXL) += qxl.o diff --git a/hw/display/blizzard.c b/hw/display/blizzard.c new file mode 100644 index 0000000000..bdb0b15ddb --- /dev/null +++ b/hw/display/blizzard.c @@ -0,0 +1,1004 @@ +/* + * Epson S1D13744/S1D13745 (Blizzard/Hailstorm/Tornado) LCD/TV controller. + * + * Copyright (C) 2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include "qemu-common.h" +#include "ui/console.h" +#include "hw/arm/devices.h" +#include "hw/vga_int.h" +#include "ui/pixel_ops.h" + +typedef void (*blizzard_fn_t)(uint8_t *, const uint8_t *, unsigned int); + +typedef struct { + uint8_t reg; + uint32_t addr; + int swallow; + + int pll; + int pll_range; + int pll_ctrl; + uint8_t pll_mode; + uint8_t clksel; + int memenable; + int memrefresh; + uint8_t timing[3]; + int priority; + + uint8_t lcd_config; + int x; + int y; + int skipx; + int skipy; + uint8_t hndp; + uint8_t vndp; + uint8_t hsync; + uint8_t vsync; + uint8_t pclk; + uint8_t u; + uint8_t v; + uint8_t yrc[2]; + int ix[2]; + int iy[2]; + int ox[2]; + int oy[2]; + + int enable; + int blank; + int bpp; + int invalidate; + int mx[2]; + int my[2]; + uint8_t mode; + uint8_t effect; + uint8_t iformat; + uint8_t source; + QemuConsole *con; + blizzard_fn_t *line_fn_tab[2]; + void *fb; + + uint8_t hssi_config[3]; + uint8_t tv_config; + uint8_t tv_timing[4]; + uint8_t vbi; + uint8_t tv_x; + uint8_t tv_y; + uint8_t tv_test; + uint8_t tv_filter_config; + uint8_t tv_filter_idx; + uint8_t tv_filter_coeff[0x20]; + uint8_t border_r; + uint8_t border_g; + uint8_t border_b; + uint8_t gamma_config; + uint8_t gamma_idx; + uint8_t gamma_lut[0x100]; + uint8_t matrix_ena; + uint8_t matrix_coeff[0x12]; + uint8_t matrix_r; + uint8_t matrix_g; + uint8_t matrix_b; + uint8_t pm; + uint8_t status; + uint8_t rgbgpio_dir; + uint8_t rgbgpio; + uint8_t gpio_dir; + uint8_t gpio; + uint8_t gpio_edge[2]; + uint8_t gpio_irq; + uint8_t gpio_pdown; + + struct { + int x; + int y; + int dx; + int dy; + int len; + int buflen; + void *buf; + void *data; + uint16_t *ptr; + int angle; + int pitch; + blizzard_fn_t line_fn; + } data; +} BlizzardState; + +/* Bytes(!) per pixel */ +static const int blizzard_iformat_bpp[0x10] = { + 0, + 2, /* RGB 5:6:5*/ + 3, /* RGB 6:6:6 mode 1 */ + 3, /* RGB 8:8:8 mode 1 */ + 0, 0, + 4, /* RGB 6:6:6 mode 2 */ + 4, /* RGB 8:8:8 mode 2 */ + 0, /* YUV 4:2:2 */ + 0, /* YUV 4:2:0 */ + 0, 0, 0, 0, 0, 0, +}; + +static inline void blizzard_rgb2yuv(int r, int g, int b, + int *y, int *u, int *v) +{ + *y = 0x10 + ((0x838 * r + 0x1022 * g + 0x322 * b) >> 13); + *u = 0x80 + ((0xe0e * b - 0x04c1 * r - 0x94e * g) >> 13); + *v = 0x80 + ((0xe0e * r - 0x0bc7 * g - 0x247 * b) >> 13); +} + +static void blizzard_window(BlizzardState *s) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + uint8_t *src, *dst; + int bypp[2]; + int bypl[3]; + int y; + blizzard_fn_t fn = s->data.line_fn; + + if (!fn) + return; + if (s->mx[0] > s->data.x) + s->mx[0] = s->data.x; + if (s->my[0] > s->data.y) + s->my[0] = s->data.y; + if (s->mx[1] < s->data.x + s->data.dx) + s->mx[1] = s->data.x + s->data.dx; + if (s->my[1] < s->data.y + s->data.dy) + s->my[1] = s->data.y + s->data.dy; + + bypp[0] = s->bpp; + bypp[1] = surface_bytes_per_pixel(surface); + bypl[0] = bypp[0] * s->data.pitch; + bypl[1] = bypp[1] * s->x; + bypl[2] = bypp[0] * s->data.dx; + + src = s->data.data; + dst = s->fb + bypl[1] * s->data.y + bypp[1] * s->data.x; + for (y = s->data.dy; y > 0; y --, src += bypl[0], dst += bypl[1]) + fn(dst, src, bypl[2]); +} + +static int blizzard_transfer_setup(BlizzardState *s) +{ + if (s->source > 3 || !s->bpp || + s->ix[1] < s->ix[0] || s->iy[1] < s->iy[0]) + return 0; + + s->data.angle = s->effect & 3; + s->data.line_fn = s->line_fn_tab[!!s->data.angle][s->iformat]; + s->data.x = s->ix[0]; + s->data.y = s->iy[0]; + s->data.dx = s->ix[1] - s->ix[0] + 1; + s->data.dy = s->iy[1] - s->iy[0] + 1; + s->data.len = s->bpp * s->data.dx * s->data.dy; + s->data.pitch = s->data.dx; + if (s->data.len > s->data.buflen) { + s->data.buf = g_realloc(s->data.buf, s->data.len); + s->data.buflen = s->data.len; + } + s->data.ptr = s->data.buf; + s->data.data = s->data.buf; + s->data.len /= 2; + return 1; +} + +static void blizzard_reset(BlizzardState *s) +{ + s->reg = 0; + s->swallow = 0; + + s->pll = 9; + s->pll_range = 1; + s->pll_ctrl = 0x14; + s->pll_mode = 0x32; + s->clksel = 0x00; + s->memenable = 0; + s->memrefresh = 0x25c; + s->timing[0] = 0x3f; + s->timing[1] = 0x13; + s->timing[2] = 0x21; + s->priority = 0; + + s->lcd_config = 0x74; + s->x = 8; + s->y = 1; + s->skipx = 0; + s->skipy = 0; + s->hndp = 3; + s->vndp = 2; + s->hsync = 1; + s->vsync = 1; + s->pclk = 0x80; + + s->ix[0] = 0; + s->ix[1] = 0; + s->iy[0] = 0; + s->iy[1] = 0; + s->ox[0] = 0; + s->ox[1] = 0; + s->oy[0] = 0; + s->oy[1] = 0; + + s->yrc[0] = 0x00; + s->yrc[1] = 0x30; + s->u = 0; + s->v = 0; + + s->iformat = 3; + s->source = 0; + s->bpp = blizzard_iformat_bpp[s->iformat]; + + s->hssi_config[0] = 0x00; + s->hssi_config[1] = 0x00; + s->hssi_config[2] = 0x01; + s->tv_config = 0x00; + s->tv_timing[0] = 0x00; + s->tv_timing[1] = 0x00; + s->tv_timing[2] = 0x00; + s->tv_timing[3] = 0x00; + s->vbi = 0x10; + s->tv_x = 0x14; + s->tv_y = 0x03; + s->tv_test = 0x00; + s->tv_filter_config = 0x80; + s->tv_filter_idx = 0x00; + s->border_r = 0x10; + s->border_g = 0x80; + s->border_b = 0x80; + s->gamma_config = 0x00; + s->gamma_idx = 0x00; + s->matrix_ena = 0x00; + memset(&s->matrix_coeff, 0, sizeof(s->matrix_coeff)); + s->matrix_r = 0x00; + s->matrix_g = 0x00; + s->matrix_b = 0x00; + s->pm = 0x02; + s->status = 0x00; + s->rgbgpio_dir = 0x00; + s->gpio_dir = 0x00; + s->gpio_edge[0] = 0x00; + s->gpio_edge[1] = 0x00; + s->gpio_irq = 0x00; + s->gpio_pdown = 0xff; +} + +static inline void blizzard_invalidate_display(void *opaque) { + BlizzardState *s = (BlizzardState *) opaque; + + s->invalidate = 1; +} + +static uint16_t blizzard_reg_read(void *opaque, uint8_t reg) +{ + BlizzardState *s = (BlizzardState *) opaque; + + switch (reg) { + case 0x00: /* Revision Code */ + return 0xa5; + + case 0x02: /* Configuration Readback */ + return 0x83; /* Macrovision OK, CNF[2:0] = 3 */ + + case 0x04: /* PLL M-Divider */ + return (s->pll - 1) | (1 << 7); + case 0x06: /* PLL Lock Range Control */ + return s->pll_range; + case 0x08: /* PLL Lock Synthesis Control 0 */ + return s->pll_ctrl & 0xff; + case 0x0a: /* PLL Lock Synthesis Control 1 */ + return s->pll_ctrl >> 8; + case 0x0c: /* PLL Mode Control 0 */ + return s->pll_mode; + + case 0x0e: /* Clock-Source Select */ + return s->clksel; + + case 0x10: /* Memory Controller Activate */ + case 0x14: /* Memory Controller Bank 0 Status Flag */ + return s->memenable; + + case 0x18: /* Auto-Refresh Interval Setting 0 */ + return s->memrefresh & 0xff; + case 0x1a: /* Auto-Refresh Interval Setting 1 */ + return s->memrefresh >> 8; + + case 0x1c: /* Power-On Sequence Timing Control */ + return s->timing[0]; + case 0x1e: /* Timing Control 0 */ + return s->timing[1]; + case 0x20: /* Timing Control 1 */ + return s->timing[2]; + + case 0x24: /* Arbitration Priority Control */ + return s->priority; + + case 0x28: /* LCD Panel Configuration */ + return s->lcd_config; + + case 0x2a: /* LCD Horizontal Display Width */ + return s->x >> 3; + case 0x2c: /* LCD Horizontal Non-display Period */ + return s->hndp; + case 0x2e: /* LCD Vertical Display Height 0 */ + return s->y & 0xff; + case 0x30: /* LCD Vertical Display Height 1 */ + return s->y >> 8; + case 0x32: /* LCD Vertical Non-display Period */ + return s->vndp; + case 0x34: /* LCD HS Pulse-width */ + return s->hsync; + case 0x36: /* LCd HS Pulse Start Position */ + return s->skipx >> 3; + case 0x38: /* LCD VS Pulse-width */ + return s->vsync; + case 0x3a: /* LCD VS Pulse Start Position */ + return s->skipy; + + case 0x3c: /* PCLK Polarity */ + return s->pclk; + + case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */ + return s->hssi_config[0]; + case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */ + return s->hssi_config[1]; + case 0x42: /* High-speed Serial Interface Tx Mode */ + return s->hssi_config[2]; + case 0x44: /* TV Display Configuration */ + return s->tv_config; + case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits */ + return s->tv_timing[(reg - 0x46) >> 1]; + case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ + return s->vbi; + case 0x50: /* TV Horizontal Start Position */ + return s->tv_x; + case 0x52: /* TV Vertical Start Position */ + return s->tv_y; + case 0x54: /* TV Test Pattern Setting */ + return s->tv_test; + case 0x56: /* TV Filter Setting */ + return s->tv_filter_config; + case 0x58: /* TV Filter Coefficient Index */ + return s->tv_filter_idx; + case 0x5a: /* TV Filter Coefficient Data */ + if (s->tv_filter_idx < 0x20) + return s->tv_filter_coeff[s->tv_filter_idx ++]; + return 0; + + case 0x60: /* Input YUV/RGB Translate Mode 0 */ + return s->yrc[0]; + case 0x62: /* Input YUV/RGB Translate Mode 1 */ + return s->yrc[1]; + case 0x64: /* U Data Fix */ + return s->u; + case 0x66: /* V Data Fix */ + return s->v; + + case 0x68: /* Display Mode */ + return s->mode; + + case 0x6a: /* Special Effects */ + return s->effect; + + case 0x6c: /* Input Window X Start Position 0 */ + return s->ix[0] & 0xff; + case 0x6e: /* Input Window X Start Position 1 */ + return s->ix[0] >> 3; + case 0x70: /* Input Window Y Start Position 0 */ + return s->ix[0] & 0xff; + case 0x72: /* Input Window Y Start Position 1 */ + return s->ix[0] >> 3; + case 0x74: /* Input Window X End Position 0 */ + return s->ix[1] & 0xff; + case 0x76: /* Input Window X End Position 1 */ + return s->ix[1] >> 3; + case 0x78: /* Input Window Y End Position 0 */ + return s->ix[1] & 0xff; + case 0x7a: /* Input Window Y End Position 1 */ + return s->ix[1] >> 3; + case 0x7c: /* Output Window X Start Position 0 */ + return s->ox[0] & 0xff; + case 0x7e: /* Output Window X Start Position 1 */ + return s->ox[0] >> 3; + case 0x80: /* Output Window Y Start Position 0 */ + return s->oy[0] & 0xff; + case 0x82: /* Output Window Y Start Position 1 */ + return s->oy[0] >> 3; + case 0x84: /* Output Window X End Position 0 */ + return s->ox[1] & 0xff; + case 0x86: /* Output Window X End Position 1 */ + return s->ox[1] >> 3; + case 0x88: /* Output Window Y End Position 0 */ + return s->oy[1] & 0xff; + case 0x8a: /* Output Window Y End Position 1 */ + return s->oy[1] >> 3; + + case 0x8c: /* Input Data Format */ + return s->iformat; + case 0x8e: /* Data Source Select */ + return s->source; + case 0x90: /* Display Memory Data Port */ + return 0; + + case 0xa8: /* Border Color 0 */ + return s->border_r; + case 0xaa: /* Border Color 1 */ + return s->border_g; + case 0xac: /* Border Color 2 */ + return s->border_b; + + case 0xb4: /* Gamma Correction Enable */ + return s->gamma_config; + case 0xb6: /* Gamma Correction Table Index */ + return s->gamma_idx; + case 0xb8: /* Gamma Correction Table Data */ + return s->gamma_lut[s->gamma_idx ++]; + + case 0xba: /* 3x3 Matrix Enable */ + return s->matrix_ena; + case 0xbc ... 0xde: /* Coefficient Registers */ + return s->matrix_coeff[(reg - 0xbc) >> 1]; + case 0xe0: /* 3x3 Matrix Red Offset */ + return s->matrix_r; + case 0xe2: /* 3x3 Matrix Green Offset */ + return s->matrix_g; + case 0xe4: /* 3x3 Matrix Blue Offset */ + return s->matrix_b; + + case 0xe6: /* Power-save */ + return s->pm; + case 0xe8: /* Non-display Period Control / Status */ + return s->status | (1 << 5); + case 0xea: /* RGB Interface Control */ + return s->rgbgpio_dir; + case 0xec: /* RGB Interface Status */ + return s->rgbgpio; + case 0xee: /* General-purpose IO Pins Configuration */ + return s->gpio_dir; + case 0xf0: /* General-purpose IO Pins Status / Control */ + return s->gpio; + case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ + return s->gpio_edge[0]; + case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ + return s->gpio_edge[1]; + case 0xf6: /* GPIO Interrupt Status */ + return s->gpio_irq; + case 0xf8: /* GPIO Pull-down Control */ + return s->gpio_pdown; + + default: + fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg); + return 0; + } +} + +static void blizzard_reg_write(void *opaque, uint8_t reg, uint16_t value) +{ + BlizzardState *s = (BlizzardState *) opaque; + + switch (reg) { + case 0x04: /* PLL M-Divider */ + s->pll = (value & 0x3f) + 1; + break; + case 0x06: /* PLL Lock Range Control */ + s->pll_range = value & 3; + break; + case 0x08: /* PLL Lock Synthesis Control 0 */ + s->pll_ctrl &= 0xf00; + s->pll_ctrl |= (value << 0) & 0x0ff; + break; + case 0x0a: /* PLL Lock Synthesis Control 1 */ + s->pll_ctrl &= 0x0ff; + s->pll_ctrl |= (value << 8) & 0xf00; + break; + case 0x0c: /* PLL Mode Control 0 */ + s->pll_mode = value & 0x77; + if ((value & 3) == 0 || (value & 3) == 3) + fprintf(stderr, "%s: wrong PLL Control bits (%i)\n", + __FUNCTION__, value & 3); + break; + + case 0x0e: /* Clock-Source Select */ + s->clksel = value & 0xff; + break; + + case 0x10: /* Memory Controller Activate */ + s->memenable = value & 1; + break; + case 0x14: /* Memory Controller Bank 0 Status Flag */ + break; + + case 0x18: /* Auto-Refresh Interval Setting 0 */ + s->memrefresh &= 0xf00; + s->memrefresh |= (value << 0) & 0x0ff; + break; + case 0x1a: /* Auto-Refresh Interval Setting 1 */ + s->memrefresh &= 0x0ff; + s->memrefresh |= (value << 8) & 0xf00; + break; + + case 0x1c: /* Power-On Sequence Timing Control */ + s->timing[0] = value & 0x7f; + break; + case 0x1e: /* Timing Control 0 */ + s->timing[1] = value & 0x17; + break; + case 0x20: /* Timing Control 1 */ + s->timing[2] = value & 0x35; + break; + + case 0x24: /* Arbitration Priority Control */ + s->priority = value & 1; + break; + + case 0x28: /* LCD Panel Configuration */ + s->lcd_config = value & 0xff; + if (value & (1 << 7)) + fprintf(stderr, "%s: data swap not supported!\n", __FUNCTION__); + break; + + case 0x2a: /* LCD Horizontal Display Width */ + s->x = value << 3; + break; + case 0x2c: /* LCD Horizontal Non-display Period */ + s->hndp = value & 0xff; + break; + case 0x2e: /* LCD Vertical Display Height 0 */ + s->y &= 0x300; + s->y |= (value << 0) & 0x0ff; + break; + case 0x30: /* LCD Vertical Display Height 1 */ + s->y &= 0x0ff; + s->y |= (value << 8) & 0x300; + break; + case 0x32: /* LCD Vertical Non-display Period */ + s->vndp = value & 0xff; + break; + case 0x34: /* LCD HS Pulse-width */ + s->hsync = value & 0xff; + break; + case 0x36: /* LCD HS Pulse Start Position */ + s->skipx = value & 0xff; + break; + case 0x38: /* LCD VS Pulse-width */ + s->vsync = value & 0xbf; + break; + case 0x3a: /* LCD VS Pulse Start Position */ + s->skipy = value & 0xff; + break; + + case 0x3c: /* PCLK Polarity */ + s->pclk = value & 0x82; + /* Affects calculation of s->hndp, s->hsync and s->skipx. */ + break; + + case 0x3e: /* High-speed Serial Interface Tx Configuration Port 0 */ + s->hssi_config[0] = value; + break; + case 0x40: /* High-speed Serial Interface Tx Configuration Port 1 */ + s->hssi_config[1] = value; + if (((value >> 4) & 3) == 3) + fprintf(stderr, "%s: Illegal active-data-links value\n", + __FUNCTION__); + break; + case 0x42: /* High-speed Serial Interface Tx Mode */ + s->hssi_config[2] = value & 0xbd; + break; + + case 0x44: /* TV Display Configuration */ + s->tv_config = value & 0xfe; + break; + case 0x46 ... 0x4c: /* TV Vertical Blanking Interval Data bits 0 */ + s->tv_timing[(reg - 0x46) >> 1] = value; + break; + case 0x4e: /* VBI: Closed Caption / XDS Control / Status */ + s->vbi = value; + break; + case 0x50: /* TV Horizontal Start Position */ + s->tv_x = value; + break; + case 0x52: /* TV Vertical Start Position */ + s->tv_y = value & 0x7f; + break; + case 0x54: /* TV Test Pattern Setting */ + s->tv_test = value; + break; + case 0x56: /* TV Filter Setting */ + s->tv_filter_config = value & 0xbf; + break; + case 0x58: /* TV Filter Coefficient Index */ + s->tv_filter_idx = value & 0x1f; + break; + case 0x5a: /* TV Filter Coefficient Data */ + if (s->tv_filter_idx < 0x20) + s->tv_filter_coeff[s->tv_filter_idx ++] = value; + break; + + case 0x60: /* Input YUV/RGB Translate Mode 0 */ + s->yrc[0] = value & 0xb0; + break; + case 0x62: /* Input YUV/RGB Translate Mode 1 */ + s->yrc[1] = value & 0x30; + break; + case 0x64: /* U Data Fix */ + s->u = value & 0xff; + break; + case 0x66: /* V Data Fix */ + s->v = value & 0xff; + break; + + case 0x68: /* Display Mode */ + if ((s->mode ^ value) & 3) + s->invalidate = 1; + s->mode = value & 0xb7; + s->enable = value & 1; + s->blank = (value >> 1) & 1; + if (value & (1 << 4)) + fprintf(stderr, "%s: Macrovision enable attempt!\n", __FUNCTION__); + break; + + case 0x6a: /* Special Effects */ + s->effect = value & 0xfb; + break; + + case 0x6c: /* Input Window X Start Position 0 */ + s->ix[0] &= 0x300; + s->ix[0] |= (value << 0) & 0x0ff; + break; + case 0x6e: /* Input Window X Start Position 1 */ + s->ix[0] &= 0x0ff; + s->ix[0] |= (value << 8) & 0x300; + break; + case 0x70: /* Input Window Y Start Position 0 */ + s->iy[0] &= 0x300; + s->iy[0] |= (value << 0) & 0x0ff; + break; + case 0x72: /* Input Window Y Start Position 1 */ + s->iy[0] &= 0x0ff; + s->iy[0] |= (value << 8) & 0x300; + break; + case 0x74: /* Input Window X End Position 0 */ + s->ix[1] &= 0x300; + s->ix[1] |= (value << 0) & 0x0ff; + break; + case 0x76: /* Input Window X End Position 1 */ + s->ix[1] &= 0x0ff; + s->ix[1] |= (value << 8) & 0x300; + break; + case 0x78: /* Input Window Y End Position 0 */ + s->iy[1] &= 0x300; + s->iy[1] |= (value << 0) & 0x0ff; + break; + case 0x7a: /* Input Window Y End Position 1 */ + s->iy[1] &= 0x0ff; + s->iy[1] |= (value << 8) & 0x300; + break; + case 0x7c: /* Output Window X Start Position 0 */ + s->ox[0] &= 0x300; + s->ox[0] |= (value << 0) & 0x0ff; + break; + case 0x7e: /* Output Window X Start Position 1 */ + s->ox[0] &= 0x0ff; + s->ox[0] |= (value << 8) & 0x300; + break; + case 0x80: /* Output Window Y Start Position 0 */ + s->oy[0] &= 0x300; + s->oy[0] |= (value << 0) & 0x0ff; + break; + case 0x82: /* Output Window Y Start Position 1 */ + s->oy[0] &= 0x0ff; + s->oy[0] |= (value << 8) & 0x300; + break; + case 0x84: /* Output Window X End Position 0 */ + s->ox[1] &= 0x300; + s->ox[1] |= (value << 0) & 0x0ff; + break; + case 0x86: /* Output Window X End Position 1 */ + s->ox[1] &= 0x0ff; + s->ox[1] |= (value << 8) & 0x300; + break; + case 0x88: /* Output Window Y End Position 0 */ + s->oy[1] &= 0x300; + s->oy[1] |= (value << 0) & 0x0ff; + break; + case 0x8a: /* Output Window Y End Position 1 */ + s->oy[1] &= 0x0ff; + s->oy[1] |= (value << 8) & 0x300; + break; + + case 0x8c: /* Input Data Format */ + s->iformat = value & 0xf; + s->bpp = blizzard_iformat_bpp[s->iformat]; + if (!s->bpp) + fprintf(stderr, "%s: Illegal or unsupported input format %x\n", + __FUNCTION__, s->iformat); + break; + case 0x8e: /* Data Source Select */ + s->source = value & 7; + /* Currently all windows will be "destructive overlays". */ + if ((!(s->effect & (1 << 3)) && (s->ix[0] != s->ox[0] || + s->iy[0] != s->oy[0] || + s->ix[1] != s->ox[1] || + s->iy[1] != s->oy[1])) || + !((s->ix[1] - s->ix[0]) & (s->iy[1] - s->iy[0]) & + (s->ox[1] - s->ox[0]) & (s->oy[1] - s->oy[0]) & 1)) + fprintf(stderr, "%s: Illegal input/output window positions\n", + __FUNCTION__); + + blizzard_transfer_setup(s); + break; + + case 0x90: /* Display Memory Data Port */ + if (!s->data.len && !blizzard_transfer_setup(s)) + break; + + *s->data.ptr ++ = value; + if (-- s->data.len == 0) + blizzard_window(s); + break; + + case 0xa8: /* Border Color 0 */ + s->border_r = value; + break; + case 0xaa: /* Border Color 1 */ + s->border_g = value; + break; + case 0xac: /* Border Color 2 */ + s->border_b = value; + break; + + case 0xb4: /* Gamma Correction Enable */ + s->gamma_config = value & 0x87; + break; + case 0xb6: /* Gamma Correction Table Index */ + s->gamma_idx = value; + break; + case 0xb8: /* Gamma Correction Table Data */ + s->gamma_lut[s->gamma_idx ++] = value; + break; + + case 0xba: /* 3x3 Matrix Enable */ + s->matrix_ena = value & 1; + break; + case 0xbc ... 0xde: /* Coefficient Registers */ + s->matrix_coeff[(reg - 0xbc) >> 1] = value & ((reg & 2) ? 0x80 : 0xff); + break; + case 0xe0: /* 3x3 Matrix Red Offset */ + s->matrix_r = value; + break; + case 0xe2: /* 3x3 Matrix Green Offset */ + s->matrix_g = value; + break; + case 0xe4: /* 3x3 Matrix Blue Offset */ + s->matrix_b = value; + break; + + case 0xe6: /* Power-save */ + s->pm = value & 0x83; + if (value & s->mode & 1) + fprintf(stderr, "%s: The display must be disabled before entering " + "Standby Mode\n", __FUNCTION__); + break; + case 0xe8: /* Non-display Period Control / Status */ + s->status = value & 0x1b; + break; + case 0xea: /* RGB Interface Control */ + s->rgbgpio_dir = value & 0x8f; + break; + case 0xec: /* RGB Interface Status */ + s->rgbgpio = value & 0xcf; + break; + case 0xee: /* General-purpose IO Pins Configuration */ + s->gpio_dir = value; + break; + case 0xf0: /* General-purpose IO Pins Status / Control */ + s->gpio = value; + break; + case 0xf2: /* GPIO Positive Edge Interrupt Trigger */ + s->gpio_edge[0] = value; + break; + case 0xf4: /* GPIO Negative Edge Interrupt Trigger */ + s->gpio_edge[1] = value; + break; + case 0xf6: /* GPIO Interrupt Status */ + s->gpio_irq &= value; + break; + case 0xf8: /* GPIO Pull-down Control */ + s->gpio_pdown = value; + break; + + default: + fprintf(stderr, "%s: unknown register %02x\n", __FUNCTION__, reg); + break; + } +} + +uint16_t s1d13745_read(void *opaque, int dc) +{ + BlizzardState *s = (BlizzardState *) opaque; + uint16_t value = blizzard_reg_read(s, s->reg); + + if (s->swallow -- > 0) + return 0; + if (dc) + s->reg ++; + + return value; +} + +void s1d13745_write(void *opaque, int dc, uint16_t value) +{ + BlizzardState *s = (BlizzardState *) opaque; + + if (s->swallow -- > 0) + return; + if (dc) { + blizzard_reg_write(s, s->reg, value); + + if (s->reg != 0x90 && s->reg != 0x5a && s->reg != 0xb8) + s->reg += 2; + } else + s->reg = value & 0xff; +} + +void s1d13745_write_block(void *opaque, int dc, + void *buf, size_t len, int pitch) +{ + BlizzardState *s = (BlizzardState *) opaque; + + while (len > 0) { + if (s->reg == 0x90 && dc && + (s->data.len || blizzard_transfer_setup(s)) && + len >= (s->data.len << 1)) { + len -= s->data.len << 1; + s->data.len = 0; + s->data.data = buf; + if (pitch) + s->data.pitch = pitch; + blizzard_window(s); + s->data.data = s->data.buf; + continue; + } + + s1d13745_write(opaque, dc, *(uint16_t *) buf); + len -= 2; + buf += 2; + } +} + +static void blizzard_update_display(void *opaque) +{ + BlizzardState *s = (BlizzardState *) opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + int y, bypp, bypl, bwidth; + uint8_t *src, *dst; + + if (!s->enable) + return; + + if (s->x != surface_width(surface) || s->y != surface_height(surface)) { + s->invalidate = 1; + qemu_console_resize(s->con, s->x, s->y); + surface = qemu_console_surface(s->con); + } + + if (s->invalidate) { + s->invalidate = 0; + + if (s->blank) { + bypp = surface_bytes_per_pixel(surface); + memset(surface_data(surface), 0, bypp * s->x * s->y); + return; + } + + s->mx[0] = 0; + s->mx[1] = s->x; + s->my[0] = 0; + s->my[1] = s->y; + } + + if (s->mx[1] <= s->mx[0]) + return; + + bypp = surface_bytes_per_pixel(surface); + bypl = bypp * s->x; + bwidth = bypp * (s->mx[1] - s->mx[0]); + y = s->my[0]; + src = s->fb + bypl * y + bypp * s->mx[0]; + dst = surface_data(surface) + bypl * y + bypp * s->mx[0]; + for (; y < s->my[1]; y ++, src += bypl, dst += bypl) + memcpy(dst, src, bwidth); + + dpy_gfx_update(s->con, s->mx[0], s->my[0], + s->mx[1] - s->mx[0], y - s->my[0]); + + s->mx[0] = s->x; + s->mx[1] = 0; + s->my[0] = s->y; + s->my[1] = 0; +} + +static void blizzard_screen_dump(void *opaque, const char *filename, + bool cswitch, Error **errp) +{ + BlizzardState *s = (BlizzardState *) opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + + blizzard_update_display(opaque); + if (s && surface_data(surface)) { + ppm_save(filename, surface, errp); + } +} + +#define DEPTH 8 +#include "hw/blizzard_template.h" +#define DEPTH 15 +#include "hw/blizzard_template.h" +#define DEPTH 16 +#include "hw/blizzard_template.h" +#define DEPTH 24 +#include "hw/blizzard_template.h" +#define DEPTH 32 +#include "hw/blizzard_template.h" + +void *s1d13745_init(qemu_irq gpio_int) +{ + BlizzardState *s = (BlizzardState *) g_malloc0(sizeof(*s)); + DisplaySurface *surface; + + s->fb = g_malloc(0x180000); + + s->con = graphic_console_init(blizzard_update_display, + blizzard_invalidate_display, + blizzard_screen_dump, NULL, s); + surface = qemu_console_surface(s->con); + + switch (surface_bits_per_pixel(surface)) { + case 0: + s->line_fn_tab[0] = s->line_fn_tab[1] = + g_malloc0(sizeof(blizzard_fn_t) * 0x10); + break; + case 8: + s->line_fn_tab[0] = blizzard_draw_fn_8; + s->line_fn_tab[1] = blizzard_draw_fn_r_8; + break; + case 15: + s->line_fn_tab[0] = blizzard_draw_fn_15; + s->line_fn_tab[1] = blizzard_draw_fn_r_15; + break; + case 16: + s->line_fn_tab[0] = blizzard_draw_fn_16; + s->line_fn_tab[1] = blizzard_draw_fn_r_16; + break; + case 24: + s->line_fn_tab[0] = blizzard_draw_fn_24; + s->line_fn_tab[1] = blizzard_draw_fn_r_24; + break; + case 32: + s->line_fn_tab[0] = blizzard_draw_fn_32; + s->line_fn_tab[1] = blizzard_draw_fn_r_32; + break; + default: + fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__); + exit(1); + } + + blizzard_reset(s); + + return s; +} diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c new file mode 100644 index 0000000000..49cca4bf94 --- /dev/null +++ b/hw/display/exynos4210_fimd.c @@ -0,0 +1,1930 @@ +/* + * Samsung exynos4210 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, see . + */ + +#include "qemu-common.h" +#include "hw/sysbus.h" +#include "ui/console.h" +#include "ui/pixel_ops.h" +#include "qemu/bswap.h" + +/* Debug messages configuration */ +#define EXYNOS4210_FIMD_DEBUG 0 +#define EXYNOS4210_FIMD_MODE_TRACE 0 + +#if EXYNOS4210_FIMD_DEBUG == 0 + #define DPRINT_L1(fmt, args...) do { } while (0) + #define DPRINT_L2(fmt, args...) do { } while (0) + #define DPRINT_ERROR(fmt, args...) do { } while (0) +#elif EXYNOS4210_FIMD_DEBUG == 1 + #define DPRINT_L1(fmt, args...) \ + do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) + #define DPRINT_L2(fmt, args...) do { } while (0) + #define DPRINT_ERROR(fmt, args...) \ + do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0) +#else + #define DPRINT_L1(fmt, args...) \ + do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) + #define DPRINT_L2(fmt, args...) \ + do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) + #define DPRINT_ERROR(fmt, args...) \ + do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0) +#endif + +#if EXYNOS4210_FIMD_MODE_TRACE == 0 + #define DPRINT_TRACE(fmt, args...) do { } while (0) +#else + #define DPRINT_TRACE(fmt, args...) \ + do {fprintf(stderr, "QEMU FIMD: "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_VIDTCON_START 0x10 +#define FIMD_VIDTCON_END 0x1C +#define FIMD_VIDTCON2_SIZE_MASK 0x07FF +#define FIMD_VIDTCON2_HOR_SHIFT 0 +#define FIMD_VIDTCON2_VER_SHIFT 11 + +/* Window control registers */ +#define FIMD_WINCON_START 0x0020 +#define FIMD_WINCON_END 0x0030 +#define FIMD_WINCON_ROMASK 0x82200000 +#define FIMD_WINCON_ENWIN (1 << 0) +#define FIMD_WINCON_BLD_PIX (1 << 6) +#define FIMD_WINCON_ALPHA_MUL (1 << 7) +#define FIMD_WINCON_ALPHA_SEL (1 << 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_BUFSTAT_L (1 << 21) +#define FIMD_WINCON_BUFSTAT_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) +#define IS_PALETTIZED_MODE(w) (w->wincon & 0xC) +#define PAL_MODE_WITH_ALPHA(x) ((x) == 7) +#define WIN_BPP_MODE(w) ((w->wincon >> 2) & 0xF) +#define WIN_BPP_MODE_WITH_ALPHA(w) \ + (WIN_BPP_MODE(w) == 0xD || WIN_BPP_MODE(w) == 0xE) + +/* Shadow control register */ +#define FIMD_SHADOWCON 0x0034 +#define FIMD_WINDOW_PROTECTED(s, w) ((s) & (1 << (10 + (w)))) +/* Channel mapping control register */ +#define FIMD_WINCHMAP 0x003C + +/* Window position control registers */ +#define FIMD_VIDOSD_START 0x0040 +#define FIMD_VIDOSD_END 0x0088 +#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_VIDWADD0_START 0x00A0 +#define FIMD_VIDWADD0_END 0x00C4 +#define FIMD_VIDWADD0_END 0x00C4 +#define FIMD_VIDWADD1_START 0x00D0 +#define FIMD_VIDWADD1_END 0x00F4 +#define FIMD_VIDWADD2_START 0x0100 +#define FIMD_VIDWADD2_END 0x0110 +#define FIMD_VIDWADD2_PAGEWIDTH 0x1FFF +#define FIMD_VIDWADD2_OFFSIZE 0x1FFF +#define FIMD_VIDWADD2_OFFSIZE_SHIFT 13 +#define FIMD_VIDW0ADD0_B2 0x20A0 +#define FIMD_VIDW4ADD0_B2 0x20C0 + +/* Video interrupt control registers */ +#define FIMD_VIDINTCON0 0x130 +#define FIMD_VIDINTCON1 0x134 + +/* Window color key registers */ +#define FIMD_WKEYCON_START 0x140 +#define FIMD_WKEYCON_END 0x15C +#define FIMD_WKEYCON0_COMPKEY 0x00FFFFFF +#define FIMD_WKEYCON0_CTL_SHIFT 24 +#define FIMD_WKEYCON0_DIRCON (1 << 24) +#define FIMD_WKEYCON0_KEYEN (1 << 25) +#define FIMD_WKEYCON0_KEYBLEN (1 << 26) +/* Window color key alpha control register */ +#define FIMD_WKEYALPHA_START 0x160 +#define FIMD_WKEYALPHA_END 0x16C + +/* Dithering control register */ +#define FIMD_DITHMODE 0x170 + +/* Window alpha control registers */ +#define FIMD_VIDALPHA_ALPHA_LOWER 0x000F0F0F +#define FIMD_VIDALPHA_ALPHA_UPPER 0x00F0F0F0 +#define FIMD_VIDWALPHA_START 0x21C +#define FIMD_VIDWALPHA_END 0x240 + +/* Window color map registers */ +#define FIMD_WINMAP_START 0x180 +#define FIMD_WINMAP_END 0x190 +#define FIMD_WINMAP_EN (1 << 24) +#define FIMD_WINMAP_COLOR_MASK 0x00FFFFFF + +/* Window palette control registers */ +#define FIMD_WPALCON_HIGH 0x019C +#define FIMD_WPALCON_LOW 0x01A0 +#define FIMD_WPALCON_UPDATEEN (1 << 9) +#define FIMD_WPAL_W0PAL_L 0x07 +#define FIMD_WPAL_W0PAL_L_SHT 0 +#define FIMD_WPAL_W1PAL_L 0x07 +#define FIMD_WPAL_W1PAL_L_SHT 3 +#define FIMD_WPAL_W2PAL_L 0x01 +#define FIMD_WPAL_W2PAL_L_SHT 6 +#define FIMD_WPAL_W2PAL_H 0x06 +#define FIMD_WPAL_W2PAL_H_SHT 8 +#define FIMD_WPAL_W3PAL_L 0x01 +#define FIMD_WPAL_W3PAL_L_SHT 7 +#define FIMD_WPAL_W3PAL_H 0x06 +#define FIMD_WPAL_W3PAL_H_SHT 12 +#define FIMD_WPAL_W4PAL_L 0x01 +#define FIMD_WPAL_W4PAL_L_SHT 8 +#define FIMD_WPAL_W4PAL_H 0x06 +#define FIMD_WPAL_W4PAL_H_SHT 16 + +/* Trigger control registers */ +#define FIMD_TRIGCON 0x01A4 +#define FIMD_TRIGCON_ROMASK 0x00000004 + +/* LCD I80 Interface Control */ +#define FIMD_I80IFCON_START 0x01B0 +#define FIMD_I80IFCON_END 0x01BC +/* Color gain control register */ +#define FIMD_COLORGAINCON 0x01C0 +/* LCD i80 Interface Command Control */ +#define FIMD_LDI_CMDCON0 0x01D0 +#define FIMD_LDI_CMDCON1 0x01D4 +/* I80 System Interface Manual Command Control */ +#define FIMD_SIFCCON0 0x01E0 +#define FIMD_SIFCCON2 0x01E8 + +/* Hue Control Registers */ +#define FIMD_HUECOEFCR_START 0x01EC +#define FIMD_HUECOEFCR_END 0x01F4 +#define FIMD_HUECOEFCB_START 0x01FC +#define FIMD_HUECOEFCB_END 0x0208 +#define FIMD_HUEOFFSET 0x020C + +/* 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_START 0x0244 +#define FIMD_BLENDEQ_END 0x0250 +#define FIMD_BLENDCON 0x0260 +#define FIMD_ALPHA_8BIT (1 << 0) +#define FIMD_BLENDEQ_COEF_MASK 0xF + +/* Window RTQOS Control Registers */ +#define FIMD_WRTQOSCON_START 0x0264 +#define FIMD_WRTQOSCON_END 0x0274 + +/* LCD I80 Interface Command */ +#define FIMD_I80IFCMD_START 0x0280 +#define FIMD_I80IFCMD_END 0x02AC + +/* Shadow windows control registers */ +#define FIMD_SHD_ADD0_START 0x40A0 +#define FIMD_SHD_ADD0_END 0x40C0 +#define FIMD_SHD_ADD1_START 0x40D0 +#define FIMD_SHD_ADD1_END 0x40F0 +#define FIMD_SHD_ADD2_START 0x4100 +#define FIMD_SHD_ADD2_END 0x4110 + +/* Palette memory */ +#define FIMD_PAL_MEM_START 0x2400 +#define FIMD_PAL_MEM_END 0x37FC +/* Palette memory aliases for windows 0 and 1 */ +#define FIMD_PALMEM_AL_START 0x0400 +#define FIMD_PALMEM_AL_END 0x0BFC + +typedef struct { + uint8_t r, g, b; + /* D[31..24]dummy, D[23..16]rAlpha, D[15..8]gAlpha, D[7..0]bAlpha */ + uint32_t a; +} rgba; +#define RGBA_SIZE 7 + +typedef void pixel_to_rgb_func(uint32_t pixel, rgba *p); +typedef struct Exynos4210fimdWindow Exynos4210fimdWindow; + +struct Exynos4210fimdWindow { + uint32_t wincon; /* Window control register */ + uint32_t buf_start[3]; /* Start address for video frame buffer */ + uint32_t buf_end[3]; /* End address for video frame buffer */ + 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 blendeq; /* Window blending equation control register */ + uint32_t rtqoscon; /* Window RTQOS Control Registers */ + uint32_t palette[256]; /* Palette 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 */ + + pixel_to_rgb_func *pixel_to_rgb; + void (*draw_line)(Exynos4210fimdWindow *w, uint8_t *src, uint8_t *dst, + bool blend); + uint32_t (*get_alpha)(Exynos4210fimdWindow *w, uint32_t pix_a); + uint16_t lefttop_x, lefttop_y; /* VIDOSD0 register */ + uint16_t rightbot_x, rightbot_y; /* VIDOSD1 register */ + uint32_t osdsize; /* VIDOSD2&3 register */ + uint32_t alpha_val[2]; /* VIDOSD2&3, VIDWALPHA registers */ + uint16_t virtpage_width; /* VIDWADD2 register */ + uint16_t virtpage_offsize; /* VIDWADD2 register */ + MemoryRegionSection mem_section; /* RAM fragment containing framebuffer */ + uint8_t *host_fb_addr; /* Host pointer to window's framebuffer */ + hwaddr fb_len; /* Framebuffer length */ +}; + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + QemuConsole *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 mapping control register */ + uint32_t vidintcon[2]; /* Video interrupt control registers */ + uint32_t dithmode; /* Dithering control register */ + uint32_t wpalcon[2]; /* Window palette 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 control register */ + uint32_t i80ifcmd[12]; /* LCD I80 Interface Command */ + + Exynos4210fimdWindow window[5]; /* Window-specific registers */ + uint8_t *ifb; /* Internal frame buffer */ + bool invalidate; /* Image needs to be redrawn */ + bool enabled; /* Display controller is enabled */ +} Exynos4210fimdState; + +/* Perform byte/halfword/word swap of data according to WINCON */ +static inline void fimd_swap_data(unsigned int swap_ctl, uint64_t *data) +{ + int i; + uint64_t res; + uint64_t x = *data; + + if (swap_ctl & FIMD_WINCON_SWAP_BITS) { + res = 0; + for (i = 0; i < 64; i++) { + if (x & (1ULL << (64 - i))) { + res |= (1ULL << i); + } + } + x = res; + } + + if (swap_ctl & FIMD_WINCON_SWAP_BYTE) { + x = bswap64(x); + } + + if (swap_ctl & FIMD_WINCON_SWAP_HWORD) { + x = ((x & 0x000000000000FFFFULL) << 48) | + ((x & 0x00000000FFFF0000ULL) << 16) | + ((x & 0x0000FFFF00000000ULL) >> 16) | + ((x & 0xFFFF000000000000ULL) >> 48); + } + + if (swap_ctl & FIMD_WINCON_SWAP_WORD) { + x = ((x & 0x00000000FFFFFFFFULL) << 32) | + ((x & 0xFFFFFFFF00000000ULL) >> 32); + } + + *data = x; +} + +/* Conversion routines of Pixel data from frame buffer area to internal RGBA + * pixel representation. + * Every color component internally represented as 8-bit value. If original + * data has less than 8 bit for component, data is extended to 8 bit. For + * example, if blue component has only two possible values 0 and 1 it will be + * extended to 0 and 0xFF */ + +/* One bit for alpha representation */ +#define DEF_PIXEL_TO_RGB_A1(N, R, G, B) \ +static void N(uint32_t pixel, rgba *p) \ +{ \ + p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \ + ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \ + pixel >>= (B); \ + p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \ + ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \ + pixel >>= (G); \ + p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \ + ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \ + pixel >>= (R); \ + p->a = (pixel & 0x1); \ +} + +DEF_PIXEL_TO_RGB_A1(pixel_a444_to_rgb, 4, 4, 4) +DEF_PIXEL_TO_RGB_A1(pixel_a555_to_rgb, 5, 5, 5) +DEF_PIXEL_TO_RGB_A1(pixel_a666_to_rgb, 6, 6, 6) +DEF_PIXEL_TO_RGB_A1(pixel_a665_to_rgb, 6, 6, 5) +DEF_PIXEL_TO_RGB_A1(pixel_a888_to_rgb, 8, 8, 8) +DEF_PIXEL_TO_RGB_A1(pixel_a887_to_rgb, 8, 8, 7) + +/* Alpha component is always zero */ +#define DEF_PIXEL_TO_RGB_A0(N, R, G, B) \ +static void N(uint32_t pixel, rgba *p) \ +{ \ + p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \ + ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \ + pixel >>= (B); \ + p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \ + ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \ + pixel >>= (G); \ + p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \ + ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \ + p->a = 0x0; \ +} + +DEF_PIXEL_TO_RGB_A0(pixel_565_to_rgb, 5, 6, 5) +DEF_PIXEL_TO_RGB_A0(pixel_555_to_rgb, 5, 5, 5) +DEF_PIXEL_TO_RGB_A0(pixel_666_to_rgb, 6, 6, 6) +DEF_PIXEL_TO_RGB_A0(pixel_888_to_rgb, 8, 8, 8) + +/* Alpha component has some meaningful value */ +#define DEF_PIXEL_TO_RGB_A(N, R, G, B, A) \ +static void N(uint32_t pixel, rgba *p) \ +{ \ + p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \ + ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \ + pixel >>= (B); \ + p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \ + ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \ + pixel >>= (G); \ + p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \ + ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \ + pixel >>= (R); \ + p->a = (pixel & ((1 << (A)) - 1)) << (8 - (A)) | \ + ((pixel >> (2 * (A) - 8)) & ((1 << (8 - (A))) - 1)); \ + p->a = p->a | (p->a << 8) | (p->a << 16); \ +} + +DEF_PIXEL_TO_RGB_A(pixel_4444_to_rgb, 4, 4, 4, 4) +DEF_PIXEL_TO_RGB_A(pixel_8888_to_rgb, 8, 8, 8, 8) + +/* Lookup table to extent 2-bit color component to 8 bit */ +static const uint8_t pixel_lutable_2b[4] = { + 0x0, 0x55, 0xAA, 0xFF +}; +/* Lookup table to extent 3-bit color component to 8 bit */ +static const uint8_t pixel_lutable_3b[8] = { + 0x0, 0x24, 0x49, 0x6D, 0x92, 0xB6, 0xDB, 0xFF +}; +/* Special case for a232 bpp mode */ +static void pixel_a232_to_rgb(uint32_t pixel, rgba *p) +{ + p->b = pixel_lutable_2b[(pixel & 0x3)]; + pixel >>= 2; + p->g = pixel_lutable_3b[(pixel & 0x7)]; + pixel >>= 3; + p->r = pixel_lutable_2b[(pixel & 0x3)]; + pixel >>= 2; + p->a = (pixel & 0x1); +} + +/* Special case for (5+1, 5+1, 5+1) mode. Data bit 15 is common LSB + * for all three color components */ +static void pixel_1555_to_rgb(uint32_t pixel, rgba *p) +{ + uint8_t comm = (pixel >> 15) & 1; + p->b = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3); + pixel >>= 5; + p->g = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3); + pixel >>= 5; + p->r = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3); + p->a = 0x0; +} + +/* Put/get pixel to/from internal LCD Controller framebuffer */ + +static int put_pixel_ifb(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_pixel_ifb(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) & 0x00FFFFFF; + return RGBA_SIZE; +} + +static pixel_to_rgb_func *palette_data_format[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 +}; + +/* Returns Index in palette data formats table for given window number WINDOW */ +static uint32_t +exynos4210_fimd_palette_format(Exynos4210fimdState *s, int window) +{ + uint32_t ret; + + switch (window) { + case 0: + ret = (s->wpalcon[1] >> FIMD_WPAL_W0PAL_L_SHT) & FIMD_WPAL_W0PAL_L; + if (ret != 7) { + ret = 6 - ret; + } + break; + case 1: + ret = (s->wpalcon[1] >> FIMD_WPAL_W1PAL_L_SHT) & FIMD_WPAL_W1PAL_L; + if (ret != 7) { + ret = 6 - ret; + } + break; + case 2: + ret = ((s->wpalcon[0] >> FIMD_WPAL_W2PAL_H_SHT) & FIMD_WPAL_W2PAL_H) | + ((s->wpalcon[1] >> FIMD_WPAL_W2PAL_L_SHT) & FIMD_WPAL_W2PAL_L); + break; + case 3: + ret = ((s->wpalcon[0] >> FIMD_WPAL_W3PAL_H_SHT) & FIMD_WPAL_W3PAL_H) | + ((s->wpalcon[1] >> FIMD_WPAL_W3PAL_L_SHT) & FIMD_WPAL_W3PAL_L); + break; + case 4: + ret = ((s->wpalcon[0] >> FIMD_WPAL_W4PAL_H_SHT) & FIMD_WPAL_W4PAL_H) | + ((s->wpalcon[1] >> FIMD_WPAL_W4PAL_L_SHT) & FIMD_WPAL_W4PAL_L); + break; + default: + hw_error("exynos4210.fimd: incorrect window number %d\n", window); + ret = 0; + break; + } + return ret; +} + +#define FIMD_1_MINUS_COLOR(x) \ + ((0xFF - ((x) & 0xFF)) | (0xFF00 - ((x) & 0xFF00)) | \ + (0xFF0000 - ((x) & 0xFF0000))) +#define EXTEND_LOWER_HALFBYTE(x) (((x) & 0xF0F0F) | (((x) << 4) & 0xF0F0F0)) +#define EXTEND_UPPER_HALFBYTE(x) (((x) & 0xF0F0F0) | (((x) >> 4) & 0xF0F0F)) + +/* Multiply three lower bytes of two 32-bit words with each other. + * Each byte with values 0-255 is considered as a number with possible values + * in a range [0 - 1] */ +static inline uint32_t fimd_mult_each_byte(uint32_t a, uint32_t b) +{ + uint32_t tmp; + uint32_t ret; + + ret = ((tmp = (((a & 0xFF) * (b & 0xFF)) / 0xFF)) > 0xFF) ? 0xFF : tmp; + ret |= ((tmp = ((((a >> 8) & 0xFF) * ((b >> 8) & 0xFF)) / 0xFF)) > 0xFF) ? + 0xFF00 : tmp << 8; + ret |= ((tmp = ((((a >> 16) & 0xFF) * ((b >> 16) & 0xFF)) / 0xFF)) > 0xFF) ? + 0xFF0000 : tmp << 16; + return ret; +} + +/* For each corresponding bytes of two 32-bit words: (a*b + c*d) + * Byte values 0-255 are mapped to a range [0 .. 1] */ +static inline uint32_t +fimd_mult_and_sum_each_byte(uint32_t a, uint32_t b, uint32_t c, uint32_t d) +{ + uint32_t tmp; + uint32_t ret; + + ret = ((tmp = (((a & 0xFF) * (b & 0xFF) + (c & 0xFF) * (d & 0xFF)) / 0xFF)) + > 0xFF) ? 0xFF : tmp; + ret |= ((tmp = ((((a >> 8) & 0xFF) * ((b >> 8) & 0xFF) + ((c >> 8) & 0xFF) * + ((d >> 8) & 0xFF)) / 0xFF)) > 0xFF) ? 0xFF00 : tmp << 8; + ret |= ((tmp = ((((a >> 16) & 0xFF) * ((b >> 16) & 0xFF) + + ((c >> 16) & 0xFF) * ((d >> 16) & 0xFF)) / 0xFF)) > 0xFF) ? + 0xFF0000 : tmp << 16; + return ret; +} + +/* These routines cover all possible sources of window's transparent factor + * used in blending equation. Choice of routine is affected by WPALCON + * registers, BLENDCON register and window's WINCON register */ + +static uint32_t fimd_get_alpha_pix(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return pix_a; +} + +static uint32_t +fimd_get_alpha_pix_extlow(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return EXTEND_LOWER_HALFBYTE(pix_a); +} + +static uint32_t +fimd_get_alpha_pix_exthigh(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return EXTEND_UPPER_HALFBYTE(pix_a); +} + +static uint32_t fimd_get_alpha_mult(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return fimd_mult_each_byte(pix_a, w->alpha_val[0]); +} + +static uint32_t fimd_get_alpha_mult_ext(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return fimd_mult_each_byte(EXTEND_LOWER_HALFBYTE(pix_a), + EXTEND_UPPER_HALFBYTE(w->alpha_val[0])); +} + +static uint32_t fimd_get_alpha_aen(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return w->alpha_val[pix_a]; +} + +static uint32_t fimd_get_alpha_aen_ext(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return EXTEND_UPPER_HALFBYTE(w->alpha_val[pix_a]); +} + +static uint32_t fimd_get_alpha_sel(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return w->alpha_val[(w->wincon & FIMD_WINCON_ALPHA_SEL) ? 1 : 0]; +} + +static uint32_t fimd_get_alpha_sel_ext(Exynos4210fimdWindow *w, uint32_t pix_a) +{ + return EXTEND_UPPER_HALFBYTE(w->alpha_val[(w->wincon & + FIMD_WINCON_ALPHA_SEL) ? 1 : 0]); +} + +/* Updates currently active alpha value get function for specified window */ +static void fimd_update_get_alpha(Exynos4210fimdState *s, int win) +{ + Exynos4210fimdWindow *w = &s->window[win]; + const bool alpha_is_8bit = s->blendcon & FIMD_ALPHA_8BIT; + + if (w->wincon & FIMD_WINCON_BLD_PIX) { + if ((w->wincon & FIMD_WINCON_ALPHA_SEL) && WIN_BPP_MODE_WITH_ALPHA(w)) { + /* In this case, alpha component contains meaningful value */ + if (w->wincon & FIMD_WINCON_ALPHA_MUL) { + w->get_alpha = alpha_is_8bit ? + fimd_get_alpha_mult : fimd_get_alpha_mult_ext; + } else { + w->get_alpha = alpha_is_8bit ? + fimd_get_alpha_pix : fimd_get_alpha_pix_extlow; + } + } else { + if (IS_PALETTIZED_MODE(w) && + PAL_MODE_WITH_ALPHA(exynos4210_fimd_palette_format(s, win))) { + /* Alpha component has 8-bit numeric value */ + w->get_alpha = alpha_is_8bit ? + fimd_get_alpha_pix : fimd_get_alpha_pix_exthigh; + } else { + /* Alpha has only two possible values (AEN) */ + w->get_alpha = alpha_is_8bit ? + fimd_get_alpha_aen : fimd_get_alpha_aen_ext; + } + } + } else { + w->get_alpha = alpha_is_8bit ? fimd_get_alpha_sel : + fimd_get_alpha_sel_ext; + } +} + +/* Blends current window's (w) pixel (foreground pixel *ret) with background + * window (w_blend) pixel p_bg according to formula: + * NEW_COLOR = a_coef x FG_PIXEL_COLOR + b_coef x BG_PIXEL_COLOR + * NEW_ALPHA = p_coef x FG_ALPHA + q_coef x BG_ALPHA + */ +static void +exynos4210_fimd_blend_pixel(Exynos4210fimdWindow *w, rgba p_bg, rgba *ret) +{ + rgba p_fg = *ret; + uint32_t bg_color = ((p_bg.r & 0xFF) << 16) | ((p_bg.g & 0xFF) << 8) | + (p_bg.b & 0xFF); + uint32_t fg_color = ((p_fg.r & 0xFF) << 16) | ((p_fg.g & 0xFF) << 8) | + (p_fg.b & 0xFF); + uint32_t alpha_fg = p_fg.a; + int i; + /* It is possible that blending equation parameters a and b do not + * depend on window BLENEQ register. Account for this with first_coef */ + enum { A_COEF = 0, B_COEF = 1, P_COEF = 2, Q_COEF = 3, COEF_NUM = 4}; + uint32_t first_coef = A_COEF; + uint32_t blend_param[COEF_NUM]; + + if (w->keycon[0] & FIMD_WKEYCON0_KEYEN) { + uint32_t colorkey = (w->keycon[1] & + ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) & FIMD_WKEYCON0_COMPKEY; + + if ((w->keycon[0] & FIMD_WKEYCON0_DIRCON) && + (bg_color & ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) == colorkey) { + /* Foreground pixel is displayed */ + if (w->keycon[0] & FIMD_WKEYCON0_KEYBLEN) { + alpha_fg = w->keyalpha; + blend_param[A_COEF] = alpha_fg; + blend_param[B_COEF] = FIMD_1_MINUS_COLOR(alpha_fg); + } else { + alpha_fg = 0; + blend_param[A_COEF] = 0xFFFFFF; + blend_param[B_COEF] = 0x0; + } + first_coef = P_COEF; + } else if ((w->keycon[0] & FIMD_WKEYCON0_DIRCON) == 0 && + (fg_color & ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) == colorkey) { + /* Background pixel is displayed */ + if (w->keycon[0] & FIMD_WKEYCON0_KEYBLEN) { + alpha_fg = w->keyalpha; + blend_param[A_COEF] = alpha_fg; + blend_param[B_COEF] = FIMD_1_MINUS_COLOR(alpha_fg); + } else { + alpha_fg = 0; + blend_param[A_COEF] = 0x0; + blend_param[B_COEF] = 0xFFFFFF; + } + first_coef = P_COEF; + } + } + + for (i = first_coef; i < COEF_NUM; i++) { + switch ((w->blendeq >> i * 6) & FIMD_BLENDEQ_COEF_MASK) { + case 0: + blend_param[i] = 0; + break; + case 1: + blend_param[i] = 0xFFFFFF; + break; + case 2: + blend_param[i] = alpha_fg; + break; + case 3: + blend_param[i] = FIMD_1_MINUS_COLOR(alpha_fg); + break; + case 4: + blend_param[i] = p_bg.a; + break; + case 5: + blend_param[i] = FIMD_1_MINUS_COLOR(p_bg.a); + break; + case 6: + blend_param[i] = w->alpha_val[0]; + break; + case 10: + blend_param[i] = fg_color; + break; + case 11: + blend_param[i] = FIMD_1_MINUS_COLOR(fg_color); + break; + case 12: + blend_param[i] = bg_color; + break; + case 13: + blend_param[i] = FIMD_1_MINUS_COLOR(bg_color); + break; + default: + hw_error("exynos4210.fimd: blend equation coef illegal value\n"); + break; + } + } + + fg_color = fimd_mult_and_sum_each_byte(bg_color, blend_param[B_COEF], + fg_color, blend_param[A_COEF]); + ret->b = fg_color & 0xFF; + fg_color >>= 8; + ret->g = fg_color & 0xFF; + fg_color >>= 8; + ret->r = fg_color & 0xFF; + ret->a = fimd_mult_and_sum_each_byte(alpha_fg, blend_param[P_COEF], + p_bg.a, blend_param[Q_COEF]); +} + +/* These routines read data from video frame buffer in system RAM, convert + * this data to display controller internal representation, if necessary, + * perform pixel blending with data, currently presented in internal buffer. + * Result is stored in display controller internal frame buffer. */ + +/* Draw line with index in palette table in RAM frame buffer data */ +#define DEF_DRAW_LINE_PALETTE(N) \ +static void glue(draw_line_palette_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ + uint8_t *dst, bool blend) \ +{ \ + int width = w->rightbot_x - w->lefttop_x + 1; \ + uint8_t *ifb = dst; \ + uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ + uint64_t data; \ + rgba p, p_old; \ + int i; \ + do { \ + memcpy(&data, src, sizeof(data)); \ + src += 8; \ + fimd_swap_data(swap, &data); \ + for (i = (64 / (N) - 1); i >= 0; i--) { \ + w->pixel_to_rgb(w->palette[(data >> ((N) * i)) & \ + ((1ULL << (N)) - 1)], &p); \ + p.a = w->get_alpha(w, p.a); \ + if (blend) { \ + ifb += get_pixel_ifb(ifb, &p_old); \ + exynos4210_fimd_blend_pixel(w, p_old, &p); \ + } \ + dst += put_pixel_ifb(p, dst); \ + } \ + width -= (64 / (N)); \ + } while (width > 0); \ +} + +/* Draw line with direct color value in RAM frame buffer data */ +#define DEF_DRAW_LINE_NOPALETTE(N) \ +static void glue(draw_line_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ + uint8_t *dst, bool blend) \ +{ \ + int width = w->rightbot_x - w->lefttop_x + 1; \ + uint8_t *ifb = dst; \ + uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ + uint64_t data; \ + rgba p, p_old; \ + int i; \ + do { \ + memcpy(&data, src, sizeof(data)); \ + src += 8; \ + fimd_swap_data(swap, &data); \ + for (i = (64 / (N) - 1); i >= 0; i--) { \ + w->pixel_to_rgb((data >> ((N) * i)) & ((1ULL << (N)) - 1), &p); \ + p.a = w->get_alpha(w, p.a); \ + if (blend) { \ + ifb += get_pixel_ifb(ifb, &p_old); \ + exynos4210_fimd_blend_pixel(w, p_old, &p); \ + } \ + dst += put_pixel_ifb(p, dst); \ + } \ + width -= (64 / (N)); \ + } while (width > 0); \ +} + +DEF_DRAW_LINE_PALETTE(1) +DEF_DRAW_LINE_PALETTE(2) +DEF_DRAW_LINE_PALETTE(4) +DEF_DRAW_LINE_PALETTE(8) +DEF_DRAW_LINE_NOPALETTE(8) /* 8bpp mode has palette and non-palette versions */ +DEF_DRAW_LINE_NOPALETTE(16) +DEF_DRAW_LINE_NOPALETTE(32) + +/* Special draw line routine for window color map case */ +static void draw_line_mapcolor(Exynos4210fimdWindow *w, uint8_t *src, + uint8_t *dst, bool blend) +{ + rgba p, p_old; + uint8_t *ifb = dst; + int width = w->rightbot_x - w->lefttop_x + 1; + uint32_t map_color = w->winmap & FIMD_WINMAP_COLOR_MASK; + + do { + pixel_888_to_rgb(map_color, &p); + p.a = w->get_alpha(w, p.a); + if (blend) { + ifb += get_pixel_ifb(ifb, &p_old); + exynos4210_fimd_blend_pixel(w, p_old, &p); + } + dst += put_pixel_ifb(p, dst); + } while (--width); +} + +/* Write RGB to QEMU's GraphicConsole framebuffer */ + +static int put_to_qemufb_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_to_qemufb_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_to_qemufb_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_to_qemufb_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_to_qemufb_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; +} + +/* Routine to copy pixel from internal buffer to QEMU buffer */ +static int (*put_pixel_toqemu)(const rgba p, uint8_t *pixel); +static inline void fimd_update_putpix_qemu(int bpp) +{ + switch (bpp) { + case 8: + put_pixel_toqemu = put_to_qemufb_pixel8; + break; + case 15: + put_pixel_toqemu = put_to_qemufb_pixel15; + break; + case 16: + put_pixel_toqemu = put_to_qemufb_pixel16; + break; + case 24: + put_pixel_toqemu = put_to_qemufb_pixel24; + break; + case 32: + put_pixel_toqemu = put_to_qemufb_pixel32; + break; + default: + hw_error("exynos4210.fimd: unsupported BPP (%d)", bpp); + break; + } +} + +/* Routine to copy a line from internal frame buffer to QEMU display */ +static void fimd_copy_line_toqemu(int width, uint8_t *src, uint8_t *dst) +{ + rgba p; + + do { + src += get_pixel_ifb(src, &p); + dst += put_pixel_toqemu(p, dst); + } while (--width); +} + +/* Parse BPPMODE_F = WINCON1[5:2] bits */ +static void exynos4210_fimd_update_win_bppmode(Exynos4210fimdState *s, int win) +{ + Exynos4210fimdWindow *w = &s->window[win]; + + if (w->winmap & FIMD_WINMAP_EN) { + w->draw_line = draw_line_mapcolor; + return; + } + + switch (WIN_BPP_MODE(w)) { + case 0: + w->draw_line = draw_line_palette_1; + w->pixel_to_rgb = + palette_data_format[exynos4210_fimd_palette_format(s, win)]; + break; + case 1: + w->draw_line = draw_line_palette_2; + w->pixel_to_rgb = + palette_data_format[exynos4210_fimd_palette_format(s, win)]; + break; + case 2: + w->draw_line = draw_line_palette_4; + w->pixel_to_rgb = + palette_data_format[exynos4210_fimd_palette_format(s, win)]; + break; + case 3: + w->draw_line = draw_line_palette_8; + w->pixel_to_rgb = + palette_data_format[exynos4210_fimd_palette_format(s, win)]; + break; + case 4: + w->draw_line = draw_line_8; + w->pixel_to_rgb = pixel_a232_to_rgb; + break; + case 5: + w->draw_line = draw_line_16; + w->pixel_to_rgb = pixel_565_to_rgb; + break; + case 6: + w->draw_line = draw_line_16; + w->pixel_to_rgb = pixel_a555_to_rgb; + break; + case 7: + w->draw_line = draw_line_16; + w->pixel_to_rgb = pixel_1555_to_rgb; + break; + case 8: + w->draw_line = draw_line_32; + w->pixel_to_rgb = pixel_666_to_rgb; + break; + case 9: + w->draw_line = draw_line_32; + w->pixel_to_rgb = pixel_a665_to_rgb; + break; + case 10: + w->draw_line = draw_line_32; + w->pixel_to_rgb = pixel_a666_to_rgb; + break; + case 11: + w->draw_line = draw_line_32; + w->pixel_to_rgb = pixel_888_to_rgb; + break; + case 12: + w->draw_line = draw_line_32; + w->pixel_to_rgb = pixel_a887_to_rgb; + break; + case 13: + w->draw_line = draw_line_32; + if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon & + FIMD_WINCON_ALPHA_SEL)) { + w->pixel_to_rgb = pixel_8888_to_rgb; + } else { + w->pixel_to_rgb = pixel_a888_to_rgb; + } + break; + case 14: + w->draw_line = draw_line_16; + if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon & + FIMD_WINCON_ALPHA_SEL)) { + w->pixel_to_rgb = pixel_4444_to_rgb; + } else { + w->pixel_to_rgb = pixel_a444_to_rgb; + } + break; + case 15: + w->draw_line = draw_line_16; + w->pixel_to_rgb = pixel_555_to_rgb; + break; + } +} + +#if EXYNOS4210_FIMD_MODE_TRACE > 0 +static const char *exynos4210_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 (palettized)"; + case 4: + return "8 bpp (non-palettized, A: 1-R:2-G:3-B:2)"; + case 5: + return "16 bpp (non-palettized, R:5-G:6-B:5)"; + case 6: + return "16 bpp (non-palettized, A:1-R:5-G:5-B:5)"; + case 7: + return "16 bpp (non-palettized, I :1-R:5-G:5-B:5)"; + case 8: + return "Unpacked 18 bpp (non-palettized, R:6-G:6-B:6)"; + case 9: + return "Unpacked 18bpp (non-palettized,A:1-R:6-G:6-B:5)"; + case 10: + return "Unpacked 19bpp (non-palettized,A:1-R:6-G:6-B:6)"; + case 11: + return "Unpacked 24 bpp (non-palettized R:8-G:8-B:8)"; + case 12: + return "Unpacked 24 bpp (non-palettized A:1-R:8-G:8-B:7)"; + case 13: + return "Unpacked 25 bpp (non-palettized A:1-R:8-G:8-B:8)"; + case 14: + return "Unpacked 13 bpp (non-palettized A:1-R:4-G:4-B:4)"; + case 15: + return "Unpacked 15 bpp (non-palettized R:5-G:5-B:5)"; + default: + return "Non-existing bpp mode"; + } +} + +static inline void exynos4210_fimd_trace_bppmode(Exynos4210fimdState *s, + int win_num, uint32_t val) +{ + Exynos4210fimdWindow *w = &s->window[win_num]; + + if (w->winmap & FIMD_WINMAP_EN) { + printf("QEMU FIMD: Window %d is mapped with MAPCOLOR=0x%x\n", + win_num, w->winmap & 0xFFFFFF); + return; + } + + if ((val != 0xFFFFFFFF) && ((w->wincon >> 2) & 0xF) == ((val >> 2) & 0xF)) { + return; + } + printf("QEMU FIMD: Window %d BPP mode set to %s\n", win_num, + exynos4210_fimd_get_bppmode((val >> 2) & 0xF)); +} +#else +static inline void exynos4210_fimd_trace_bppmode(Exynos4210fimdState *s, + int win_num, uint32_t val) +{ + +} +#endif + +static inline int fimd_get_buffer_id(Exynos4210fimdWindow *w) +{ + switch (w->wincon & FIMD_WINCON_BUFSTATUS) { + case FIMD_WINCON_BUF0_STAT: + return 0; + case FIMD_WINCON_BUF1_STAT: + return 1; + case FIMD_WINCON_BUF2_STAT: + return 2; + default: + DPRINT_ERROR("Non-existent buffer index\n"); + return 0; + } +} + +/* Updates specified window's MemorySection based on values of WINCON, + * VIDOSDA, VIDOSDB, VIDWADDx and SHADOWCON registers */ +static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win) +{ + Exynos4210fimdWindow *w = &s->window[win]; + hwaddr fb_start_addr, fb_mapped_len; + + if (!s->enabled || !(w->wincon & FIMD_WINCON_ENWIN) || + FIMD_WINDOW_PROTECTED(s->shadowcon, win)) { + return; + } + + if (w->host_fb_addr) { + cpu_physical_memory_unmap(w->host_fb_addr, w->fb_len, 0, 0); + w->host_fb_addr = NULL; + w->fb_len = 0; + } + + fb_start_addr = w->buf_start[fimd_get_buffer_id(w)]; + /* Total number of bytes of virtual screen used by current window */ + w->fb_len = fb_mapped_len = (w->virtpage_width + w->virtpage_offsize) * + (w->rightbot_y - w->lefttop_y + 1); + w->mem_section = memory_region_find(sysbus_address_space(&s->busdev), + fb_start_addr, w->fb_len); + assert(w->mem_section.mr); + assert(w->mem_section.offset_within_address_space == fb_start_addr); + DPRINT_TRACE("Window %u framebuffer changed: address=0x%08x, len=0x%x\n", + win, fb_start_addr, w->fb_len); + + if (w->mem_section.size != w->fb_len || + !memory_region_is_ram(w->mem_section.mr)) { + DPRINT_ERROR("Failed to find window %u framebuffer region\n", win); + goto error_return; + } + + w->host_fb_addr = cpu_physical_memory_map(fb_start_addr, &fb_mapped_len, 0); + if (!w->host_fb_addr) { + DPRINT_ERROR("Failed to map window %u framebuffer\n", win); + goto error_return; + } + + if (fb_mapped_len != w->fb_len) { + DPRINT_ERROR("Window %u mapped framebuffer length is less then " + "expected\n", win); + cpu_physical_memory_unmap(w->host_fb_addr, fb_mapped_len, 0, 0); + goto error_return; + } + return; + +error_return: + w->mem_section.mr = NULL; + w->mem_section.size = 0; + w->host_fb_addr = NULL; + w->fb_len = 0; +} + +static void exynos4210_fimd_enable(Exynos4210fimdState *s, bool enabled) +{ + if (enabled && !s->enabled) { + unsigned w; + s->enabled = true; + for (w = 0; w < NUM_OF_WINDOWS; w++) { + fimd_update_memory_section(s, w); + } + } + s->enabled = enabled; + DPRINT_TRACE("display controller %s\n", enabled ? "enabled" : "disabled"); +} + +static inline uint32_t unpack_upper_4(uint32_t x) +{ + return ((x & 0xF00) << 12) | ((x & 0xF0) << 8) | ((x & 0xF) << 4); +} + +static inline uint32_t pack_upper_4(uint32_t x) +{ + return (((x & 0xF00000) >> 12) | ((x & 0xF000) >> 8) | + ((x & 0xF0) >> 4)) & 0xFFF; +} + +static void exynos4210_fimd_update_irq(Exynos4210fimdState *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 exynos4210_fimd_invalidate(void *opaque) +{ + Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; + s->invalidate = true; +} + +static void exynos4210_update_resolution(Exynos4210fimdState *s) +{ + DisplaySurface *surface = qemu_console_surface(s->console); + + /* 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 || surface_width(surface) != width || + surface_height(surface) != height) { + DPRINT_L1("Resolution changed from %ux%u to %ux%u\n", + surface_width(surface), surface_height(surface), width, height); + qemu_console_resize(s->console, width, height); + s->ifb = g_realloc(s->ifb, width * height * RGBA_SIZE + 1); + memset(s->ifb, 0, width * height * RGBA_SIZE + 1); + exynos4210_fimd_invalidate(s); + } +} + +static void exynos4210_fimd_update(void *opaque) +{ + Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; + DisplaySurface *surface = qemu_console_surface(s->console); + Exynos4210fimdWindow *w; + int i, line; + hwaddr fb_line_addr, inc_size; + int scrn_height; + int first_line = -1, last_line = -1, scrn_width; + bool blend = false; + uint8_t *host_fb_addr; + bool is_dirty = false; + 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 || !surface_bits_per_pixel(surface) || + !s->enabled) { + return; + } + exynos4210_update_resolution(s); + + for (i = 0; i < NUM_OF_WINDOWS; i++) { + w = &s->window[i]; + if ((w->wincon & FIMD_WINCON_ENWIN) && w->host_fb_addr) { + scrn_height = w->rightbot_y - w->lefttop_y + 1; + scrn_width = w->virtpage_width; + /* Total width of virtual screen page in bytes */ + inc_size = scrn_width + w->virtpage_offsize; + memory_region_sync_dirty_bitmap(w->mem_section.mr); + host_fb_addr = w->host_fb_addr; + fb_line_addr = w->mem_section.offset_within_region; + + for (line = 0; line < scrn_height; line++) { + is_dirty = memory_region_get_dirty(w->mem_section.mr, + fb_line_addr, scrn_width, DIRTY_MEMORY_VGA); + + if (s->invalidate || is_dirty) { + if (first_line == -1) { + first_line = line; + } + last_line = line; + w->draw_line(w, host_fb_addr, s->ifb + + w->lefttop_x * RGBA_SIZE + (w->lefttop_y + line) * + global_width * RGBA_SIZE, blend); + } + host_fb_addr += inc_size; + fb_line_addr += inc_size; + is_dirty = false; + } + memory_region_reset_dirty(w->mem_section.mr, + w->mem_section.offset_within_region, + w->fb_len, DIRTY_MEMORY_VGA); + blend = true; + } + } + + /* Copy resulting image to QEMU_CONSOLE. */ + if (first_line >= 0) { + uint8_t *d; + int bpp; + + bpp = surface_bits_per_pixel(surface); + fimd_update_putpix_qemu(bpp); + bpp = (bpp + 1) >> 3; + d = surface_data(surface); + for (line = first_line; line <= last_line; line++) { + fimd_copy_line_toqemu(global_width, s->ifb + global_width * line * + RGBA_SIZE, d + global_width * line * bpp); + } + dpy_gfx_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) { + exynos4210_fimd_enable(s, false); + } + exynos4210_fimd_update_irq(s); +} + +static void exynos4210_fimd_reset(DeviceState *d) +{ + Exynos4210fimdState *s = DO_UPCAST(Exynos4210fimdState, busdev.qdev, d); + unsigned w; + + DPRINT_TRACE("Display controller reset\n"); + /* Set all display controller registers to 0 */ + memset(&s->vidcon, 0, (uint8_t *)&s->window - (uint8_t *)&s->vidcon); + for (w = 0; w < NUM_OF_WINDOWS; w++) { + memset(&s->window[w], 0, sizeof(Exynos4210fimdWindow)); + s->window[w].blendeq = 0xC2; + exynos4210_fimd_update_win_bppmode(s, w); + exynos4210_fimd_trace_bppmode(s, w, 0xFFFFFFFF); + fimd_update_get_alpha(s, w); + } + + if (s->ifb != NULL) { + g_free(s->ifb); + } + s->ifb = NULL; + + exynos4210_fimd_invalidate(s); + exynos4210_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 exynos4210_fimd_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; + unsigned w, i; + uint32_t old_value; + + DPRINT_L2("write offset 0x%08x, value=%llu(0x%08llx)\n", offset, + (long long unsigned int)val, (long long unsigned int)val); + + switch (offset) { + case FIMD_VIDCON0: + if ((val & FIMD_VIDCON0_ENVID_MASK) == FIMD_VIDCON0_ENVID_MASK) { + exynos4210_fimd_enable(s, true); + } else { + if ((val & FIMD_VIDCON0_ENVID) == 0) { + exynos4210_fimd_enable(s, false); + } + } + s->vidcon[0] = val; + break; + case FIMD_VIDCON1: + /* Leave read-only bits as is */ + val = (val & (~FIMD_VIDCON1_ROMASK)) | + (s->vidcon[1] & FIMD_VIDCON1_ROMASK); + s->vidcon[1] = val; + break; + case FIMD_VIDCON2 ... FIMD_VIDCON3: + s->vidcon[(offset) >> 2] = val; + break; + case FIMD_VIDTCON_START ... FIMD_VIDTCON_END: + s->vidtcon[(offset - FIMD_VIDTCON_START) >> 2] = val; + break; + case FIMD_WINCON_START ... FIMD_WINCON_END: + w = (offset - FIMD_WINCON_START) >> 2; + /* Window's current buffer ID */ + i = fimd_get_buffer_id(&s->window[w]); + old_value = s->window[w].wincon; + val = (val & ~FIMD_WINCON_ROMASK) | + (s->window[w].wincon & FIMD_WINCON_ROMASK); + if (w == 0) { + /* Window 0 wincon ALPHA_MUL bit must always be 0 */ + val &= ~FIMD_WINCON_ALPHA_MUL; + } + exynos4210_fimd_trace_bppmode(s, 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_BUFSTAT_H) | FIMD_WINCON_BUFSTAT_L; + break; + case FIMD_WINCON_BUF2_SEL: + if (val & FIMD_WINCON_BUFMODE) { + val = (val & ~FIMD_WINCON_BUFSTAT_L) | FIMD_WINCON_BUFSTAT_H; + } + break; + default: + break; + } + s->window[w].wincon = val; + exynos4210_fimd_update_win_bppmode(s, w); + fimd_update_get_alpha(s, w); + if ((i != fimd_get_buffer_id(&s->window[w])) || + (!(old_value & FIMD_WINCON_ENWIN) && (s->window[w].wincon & + FIMD_WINCON_ENWIN))) { + fimd_update_memory_section(s, w); + } + break; + case FIMD_SHADOWCON: + old_value = s->shadowcon; + s->shadowcon = val; + for (w = 0; w < NUM_OF_WINDOWS; w++) { + if (FIMD_WINDOW_PROTECTED(old_value, w) && + !FIMD_WINDOW_PROTECTED(s->shadowcon, w)) { + fimd_update_memory_section(s, w); + } + } + break; + case FIMD_WINCHMAP: + s->winchmap = val; + break; + case FIMD_VIDOSD_START ... FIMD_VIDOSD_END: + w = (offset - FIMD_VIDOSD_START) >> 4; + i = ((offset - FIMD_VIDOSD_START) & 0xF) >> 2; + switch (i) { + case 0: + old_value = s->window[w].lefttop_y; + s->window[w].lefttop_x = (val >> FIMD_VIDOSD_HOR_SHIFT) & + FIMD_VIDOSD_COORD_MASK; + s->window[w].lefttop_y = (val >> FIMD_VIDOSD_VER_SHIFT) & + FIMD_VIDOSD_COORD_MASK; + if (s->window[w].lefttop_y != old_value) { + fimd_update_memory_section(s, w); + } + break; + case 1: + old_value = s->window[w].rightbot_y; + s->window[w].rightbot_x = (val >> FIMD_VIDOSD_HOR_SHIFT) & + FIMD_VIDOSD_COORD_MASK; + s->window[w].rightbot_y = (val >> FIMD_VIDOSD_VER_SHIFT) & + FIMD_VIDOSD_COORD_MASK; + if (s->window[w].rightbot_y != old_value) { + fimd_update_memory_section(s, w); + } + break; + case 2: + if (w == 0) { + s->window[w].osdsize = val; + } else { + s->window[w].alpha_val[0] = + unpack_upper_4((val & FIMD_VIDOSD_ALPHA_AEN0) >> + FIMD_VIDOSD_AEN0_SHIFT) | + (s->window[w].alpha_val[0] & FIMD_VIDALPHA_ALPHA_LOWER); + s->window[w].alpha_val[1] = + unpack_upper_4(val & FIMD_VIDOSD_ALPHA_AEN1) | + (s->window[w].alpha_val[1] & FIMD_VIDALPHA_ALPHA_LOWER); + } + break; + case 3: + if (w != 1 && w != 2) { + DPRINT_ERROR("Bad write offset 0x%08x\n", offset); + return; + } + s->window[w].osdsize = val; + break; + } + break; + case FIMD_VIDWADD0_START ... FIMD_VIDWADD0_END: + w = (offset - FIMD_VIDWADD0_START) >> 3; + i = ((offset - FIMD_VIDWADD0_START) >> 2) & 1; + if (i == fimd_get_buffer_id(&s->window[w]) && + s->window[w].buf_start[i] != val) { + s->window[w].buf_start[i] = val; + fimd_update_memory_section(s, w); + break; + } + s->window[w].buf_start[i] = val; + break; + case FIMD_VIDWADD1_START ... FIMD_VIDWADD1_END: + w = (offset - FIMD_VIDWADD1_START) >> 3; + i = ((offset - FIMD_VIDWADD1_START) >> 2) & 1; + s->window[w].buf_end[i] = val; + break; + case FIMD_VIDWADD2_START ... FIMD_VIDWADD2_END: + w = (offset - FIMD_VIDWADD2_START) >> 2; + if (((val & FIMD_VIDWADD2_PAGEWIDTH) != s->window[w].virtpage_width) || + (((val >> FIMD_VIDWADD2_OFFSIZE_SHIFT) & FIMD_VIDWADD2_OFFSIZE) != + s->window[w].virtpage_offsize)) { + s->window[w].virtpage_width = val & FIMD_VIDWADD2_PAGEWIDTH; + s->window[w].virtpage_offsize = + (val >> FIMD_VIDWADD2_OFFSIZE_SHIFT) & FIMD_VIDWADD2_OFFSIZE; + fimd_update_memory_section(s, w); + } + break; + case FIMD_VIDINTCON0: + s->vidintcon[0] = val; + break; + case FIMD_VIDINTCON1: + s->vidintcon[1] &= ~(val & 7); + exynos4210_fimd_update_irq(s); + break; + case FIMD_WKEYCON_START ... FIMD_WKEYCON_END: + w = ((offset - FIMD_WKEYCON_START) >> 3) + 1; + i = ((offset - FIMD_WKEYCON_START) >> 2) & 1; + s->window[w].keycon[i] = val; + break; + case FIMD_WKEYALPHA_START ... FIMD_WKEYALPHA_END: + w = ((offset - FIMD_WKEYALPHA_START) >> 2) + 1; + s->window[w].keyalpha = val; + break; + case FIMD_DITHMODE: + s->dithmode = val; + break; + case FIMD_WINMAP_START ... FIMD_WINMAP_END: + w = (offset - FIMD_WINMAP_START) >> 2; + old_value = s->window[w].winmap; + s->window[w].winmap = val; + if ((val & FIMD_WINMAP_EN) ^ (old_value & FIMD_WINMAP_EN)) { + exynos4210_fimd_invalidate(s); + exynos4210_fimd_update_win_bppmode(s, w); + exynos4210_fimd_trace_bppmode(s, w, 0xFFFFFFFF); + exynos4210_fimd_update(s); + } + break; + case FIMD_WPALCON_HIGH ... FIMD_WPALCON_LOW: + i = (offset - FIMD_WPALCON_HIGH) >> 2; + s->wpalcon[i] = val; + if (s->wpalcon[1] & FIMD_WPALCON_UPDATEEN) { + for (w = 0; w < NUM_OF_WINDOWS; w++) { + exynos4210_fimd_update_win_bppmode(s, w); + fimd_update_get_alpha(s, w); + } + } + break; + case FIMD_TRIGCON: + val = (val & ~FIMD_TRIGCON_ROMASK) | (s->trigcon & FIMD_TRIGCON_ROMASK); + s->trigcon = val; + break; + case FIMD_I80IFCON_START ... FIMD_I80IFCON_END: + s->i80ifcon[(offset - FIMD_I80IFCON_START) >> 2] = val; + break; + case FIMD_COLORGAINCON: + s->colorgaincon = val; + break; + case FIMD_LDI_CMDCON0 ... FIMD_LDI_CMDCON1: + s->ldi_cmdcon[(offset - FIMD_LDI_CMDCON0) >> 2] = val; + break; + case FIMD_SIFCCON0 ... FIMD_SIFCCON2: + i = (offset - FIMD_SIFCCON0) >> 2; + if (i != 2) { + s->sifccon[i] = val; + } + break; + case FIMD_HUECOEFCR_START ... FIMD_HUECOEFCR_END: + i = (offset - FIMD_HUECOEFCR_START) >> 2; + s->huecoef_cr[i] = val; + break; + case FIMD_HUECOEFCB_START ... FIMD_HUECOEFCB_END: + i = (offset - FIMD_HUECOEFCB_START) >> 2; + s->huecoef_cb[i] = val; + break; + case FIMD_HUEOFFSET: + s->hueoffset = val; + break; + case FIMD_VIDWALPHA_START ... FIMD_VIDWALPHA_END: + w = ((offset - FIMD_VIDWALPHA_START) >> 3); + i = ((offset - FIMD_VIDWALPHA_START) >> 2) & 1; + if (w == 0) { + s->window[w].alpha_val[i] = val; + } else { + s->window[w].alpha_val[i] = (val & FIMD_VIDALPHA_ALPHA_LOWER) | + (s->window[w].alpha_val[i] & FIMD_VIDALPHA_ALPHA_UPPER); + } + break; + case FIMD_BLENDEQ_START ... FIMD_BLENDEQ_END: + s->window[(offset - FIMD_BLENDEQ_START) >> 2].blendeq = val; + break; + case FIMD_BLENDCON: + old_value = s->blendcon; + s->blendcon = val; + if ((s->blendcon & FIMD_ALPHA_8BIT) != (old_value & FIMD_ALPHA_8BIT)) { + for (w = 0; w < NUM_OF_WINDOWS; w++) { + fimd_update_get_alpha(s, w); + } + } + break; + case FIMD_WRTQOSCON_START ... FIMD_WRTQOSCON_END: + s->window[(offset - FIMD_WRTQOSCON_START) >> 2].rtqoscon = val; + break; + case FIMD_I80IFCMD_START ... FIMD_I80IFCMD_END: + s->i80ifcmd[(offset - FIMD_I80IFCMD_START) >> 2] = val; + break; + case FIMD_VIDW0ADD0_B2 ... FIMD_VIDW4ADD0_B2: + if (offset & 0x0004) { + DPRINT_ERROR("bad write offset 0x%08x\n", offset); + break; + } + w = (offset - FIMD_VIDW0ADD0_B2) >> 3; + if (fimd_get_buffer_id(&s->window[w]) == 2 && + s->window[w].buf_start[2] != val) { + s->window[w].buf_start[2] = val; + fimd_update_memory_section(s, w); + break; + } + s->window[w].buf_start[2] = val; + break; + case FIMD_SHD_ADD0_START ... FIMD_SHD_ADD0_END: + if (offset & 0x0004) { + DPRINT_ERROR("bad write offset 0x%08x\n", offset); + break; + } + s->window[(offset - FIMD_SHD_ADD0_START) >> 3].shadow_buf_start = val; + break; + case FIMD_SHD_ADD1_START ... FIMD_SHD_ADD1_END: + if (offset & 0x0004) { + DPRINT_ERROR("bad write offset 0x%08x\n", offset); + break; + } + s->window[(offset - FIMD_SHD_ADD1_START) >> 3].shadow_buf_end = val; + break; + case FIMD_SHD_ADD2_START ... FIMD_SHD_ADD2_END: + s->window[(offset - FIMD_SHD_ADD2_START) >> 2].shadow_buf_size = val; + break; + case FIMD_PAL_MEM_START ... FIMD_PAL_MEM_END: + w = (offset - FIMD_PAL_MEM_START) >> 10; + i = ((offset - FIMD_PAL_MEM_START) >> 2) & 0xFF; + s->window[w].palette[i] = val; + break; + case FIMD_PALMEM_AL_START ... FIMD_PALMEM_AL_END: + /* Palette memory aliases for windows 0 and 1 */ + w = (offset - FIMD_PALMEM_AL_START) >> 10; + i = ((offset - FIMD_PALMEM_AL_START) >> 2) & 0xFF; + s->window[w].palette[i] = val; + break; + default: + DPRINT_ERROR("bad write offset 0x%08x\n", offset); + break; + } +} + +static uint64_t exynos4210_fimd_read(void *opaque, hwaddr offset, + unsigned size) +{ + Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; + int w, i; + uint32_t ret = 0; + + DPRINT_L2("read offset 0x%08x\n", offset); + + switch (offset) { + case FIMD_VIDCON0 ... FIMD_VIDCON3: + return s->vidcon[(offset - FIMD_VIDCON0) >> 2]; + case FIMD_VIDTCON_START ... FIMD_VIDTCON_END: + return s->vidtcon[(offset - FIMD_VIDTCON_START) >> 2]; + case FIMD_WINCON_START ... FIMD_WINCON_END: + return s->window[(offset - FIMD_WINCON_START) >> 2].wincon; + case FIMD_SHADOWCON: + return s->shadowcon; + case FIMD_WINCHMAP: + return s->winchmap; + case FIMD_VIDOSD_START ... FIMD_VIDOSD_END: + w = (offset - FIMD_VIDOSD_START) >> 4; + i = ((offset - FIMD_VIDOSD_START) & 0xF) >> 2; + switch (i) { + case 0: + ret = ((s->window[w].lefttop_x & FIMD_VIDOSD_COORD_MASK) << + FIMD_VIDOSD_HOR_SHIFT) | + (s->window[w].lefttop_y & FIMD_VIDOSD_COORD_MASK); + break; + case 1: + ret = ((s->window[w].rightbot_x & FIMD_VIDOSD_COORD_MASK) << + FIMD_VIDOSD_HOR_SHIFT) | + (s->window[w].rightbot_y & FIMD_VIDOSD_COORD_MASK); + break; + case 2: + if (w == 0) { + ret = s->window[w].osdsize; + } else { + ret = (pack_upper_4(s->window[w].alpha_val[0]) << + FIMD_VIDOSD_AEN0_SHIFT) | + pack_upper_4(s->window[w].alpha_val[1]); + } + break; + case 3: + if (w != 1 && w != 2) { + DPRINT_ERROR("bad read offset 0x%08x\n", offset); + return 0xBAADBAAD; + } + ret = s->window[w].osdsize; + break; + } + return ret; + case FIMD_VIDWADD0_START ... FIMD_VIDWADD0_END: + w = (offset - FIMD_VIDWADD0_START) >> 3; + i = ((offset - FIMD_VIDWADD0_START) >> 2) & 1; + return s->window[w].buf_start[i]; + case FIMD_VIDWADD1_START ... FIMD_VIDWADD1_END: + w = (offset - FIMD_VIDWADD1_START) >> 3; + i = ((offset - FIMD_VIDWADD1_START) >> 2) & 1; + return s->window[w].buf_end[i]; + case FIMD_VIDWADD2_START ... FIMD_VIDWADD2_END: + w = (offset - FIMD_VIDWADD2_START) >> 2; + return s->window[w].virtpage_width | (s->window[w].virtpage_offsize << + FIMD_VIDWADD2_OFFSIZE_SHIFT); + case FIMD_VIDINTCON0 ... FIMD_VIDINTCON1: + return s->vidintcon[(offset - FIMD_VIDINTCON0) >> 2]; + case FIMD_WKEYCON_START ... FIMD_WKEYCON_END: + w = ((offset - FIMD_WKEYCON_START) >> 3) + 1; + i = ((offset - FIMD_WKEYCON_START) >> 2) & 1; + return s->window[w].keycon[i]; + case FIMD_WKEYALPHA_START ... FIMD_WKEYALPHA_END: + w = ((offset - FIMD_WKEYALPHA_START) >> 2) + 1; + return s->window[w].keyalpha; + case FIMD_DITHMODE: + return s->dithmode; + case FIMD_WINMAP_START ... FIMD_WINMAP_END: + return s->window[(offset - FIMD_WINMAP_START) >> 2].winmap; + case FIMD_WPALCON_HIGH ... FIMD_WPALCON_LOW: + return s->wpalcon[(offset - FIMD_WPALCON_HIGH) >> 2]; + case FIMD_TRIGCON: + return s->trigcon; + case FIMD_I80IFCON_START ... FIMD_I80IFCON_END: + return s->i80ifcon[(offset - FIMD_I80IFCON_START) >> 2]; + case FIMD_COLORGAINCON: + return s->colorgaincon; + case FIMD_LDI_CMDCON0 ... FIMD_LDI_CMDCON1: + return s->ldi_cmdcon[(offset - FIMD_LDI_CMDCON0) >> 2]; + case FIMD_SIFCCON0 ... FIMD_SIFCCON2: + i = (offset - FIMD_SIFCCON0) >> 2; + return s->sifccon[i]; + case FIMD_HUECOEFCR_START ... FIMD_HUECOEFCR_END: + i = (offset - FIMD_HUECOEFCR_START) >> 2; + return s->huecoef_cr[i]; + case FIMD_HUECOEFCB_START ... FIMD_HUECOEFCB_END: + i = (offset - FIMD_HUECOEFCB_START) >> 2; + return s->huecoef_cb[i]; + case FIMD_HUEOFFSET: + return s->hueoffset; + case FIMD_VIDWALPHA_START ... FIMD_VIDWALPHA_END: + w = ((offset - FIMD_VIDWALPHA_START) >> 3); + i = ((offset - FIMD_VIDWALPHA_START) >> 2) & 1; + return s->window[w].alpha_val[i] & + (w == 0 ? 0xFFFFFF : FIMD_VIDALPHA_ALPHA_LOWER); + case FIMD_BLENDEQ_START ... FIMD_BLENDEQ_END: + return s->window[(offset - FIMD_BLENDEQ_START) >> 2].blendeq; + case FIMD_BLENDCON: + return s->blendcon; + case FIMD_WRTQOSCON_START ... FIMD_WRTQOSCON_END: + return s->window[(offset - FIMD_WRTQOSCON_START) >> 2].rtqoscon; + case FIMD_I80IFCMD_START ... FIMD_I80IFCMD_END: + return s->i80ifcmd[(offset - FIMD_I80IFCMD_START) >> 2]; + case FIMD_VIDW0ADD0_B2 ... FIMD_VIDW4ADD0_B2: + if (offset & 0x0004) { + break; + } + return s->window[(offset - FIMD_VIDW0ADD0_B2) >> 3].buf_start[2]; + case FIMD_SHD_ADD0_START ... FIMD_SHD_ADD0_END: + if (offset & 0x0004) { + break; + } + return s->window[(offset - FIMD_SHD_ADD0_START) >> 3].shadow_buf_start; + case FIMD_SHD_ADD1_START ... FIMD_SHD_ADD1_END: + if (offset & 0x0004) { + break; + } + return s->window[(offset - FIMD_SHD_ADD1_START) >> 3].shadow_buf_end; + case FIMD_SHD_ADD2_START ... FIMD_SHD_ADD2_END: + return s->window[(offset - FIMD_SHD_ADD2_START) >> 2].shadow_buf_size; + case FIMD_PAL_MEM_START ... FIMD_PAL_MEM_END: + w = (offset - FIMD_PAL_MEM_START) >> 10; + i = ((offset - FIMD_PAL_MEM_START) >> 2) & 0xFF; + return s->window[w].palette[i]; + case FIMD_PALMEM_AL_START ... FIMD_PALMEM_AL_END: + /* Palette aliases for win 0,1 */ + w = (offset - FIMD_PALMEM_AL_START) >> 10; + i = ((offset - FIMD_PALMEM_AL_START) >> 2) & 0xFF; + return s->window[w].palette[i]; + } + + DPRINT_ERROR("bad read offset 0x%08x\n", offset); + return 0xBAADBAAD; +} + +static const MemoryRegionOps exynos4210_fimd_mmio_ops = { + .read = exynos4210_fimd_read, + .write = exynos4210_fimd_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int exynos4210_fimd_load(void *opaque, int version_id) +{ + Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; + int w; + + if (version_id != 1) { + return -EINVAL; + } + + for (w = 0; w < NUM_OF_WINDOWS; w++) { + exynos4210_fimd_update_win_bppmode(s, w); + fimd_update_get_alpha(s, w); + fimd_update_memory_section(s, w); + } + + /* Redraw the whole screen */ + exynos4210_update_resolution(s); + exynos4210_fimd_invalidate(s); + exynos4210_fimd_enable(s, (s->vidcon[0] & FIMD_VIDCON0_ENVID_MASK) == + FIMD_VIDCON0_ENVID_MASK); + return 0; +} + +static const VMStateDescription exynos4210_fimd_window_vmstate = { + .name = "exynos4210.fimd_window", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(wincon, Exynos4210fimdWindow), + VMSTATE_UINT32_ARRAY(buf_start, Exynos4210fimdWindow, 3), + VMSTATE_UINT32_ARRAY(buf_end, Exynos4210fimdWindow, 3), + VMSTATE_UINT32_ARRAY(keycon, Exynos4210fimdWindow, 2), + VMSTATE_UINT32(keyalpha, Exynos4210fimdWindow), + VMSTATE_UINT32(winmap, Exynos4210fimdWindow), + VMSTATE_UINT32(blendeq, Exynos4210fimdWindow), + VMSTATE_UINT32(rtqoscon, Exynos4210fimdWindow), + VMSTATE_UINT32_ARRAY(palette, Exynos4210fimdWindow, 256), + VMSTATE_UINT32(shadow_buf_start, Exynos4210fimdWindow), + VMSTATE_UINT32(shadow_buf_end, Exynos4210fimdWindow), + VMSTATE_UINT32(shadow_buf_size, Exynos4210fimdWindow), + VMSTATE_UINT16(lefttop_x, Exynos4210fimdWindow), + VMSTATE_UINT16(lefttop_y, Exynos4210fimdWindow), + VMSTATE_UINT16(rightbot_x, Exynos4210fimdWindow), + VMSTATE_UINT16(rightbot_y, Exynos4210fimdWindow), + VMSTATE_UINT32(osdsize, Exynos4210fimdWindow), + VMSTATE_UINT32_ARRAY(alpha_val, Exynos4210fimdWindow, 2), + VMSTATE_UINT16(virtpage_width, Exynos4210fimdWindow), + VMSTATE_UINT16(virtpage_offsize, Exynos4210fimdWindow), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription exynos4210_fimd_vmstate = { + .name = "exynos4210.fimd", + .version_id = 1, + .minimum_version_id = 1, + .post_load = exynos4210_fimd_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(vidcon, Exynos4210fimdState, 4), + VMSTATE_UINT32_ARRAY(vidtcon, Exynos4210fimdState, 4), + VMSTATE_UINT32(shadowcon, Exynos4210fimdState), + VMSTATE_UINT32(winchmap, Exynos4210fimdState), + VMSTATE_UINT32_ARRAY(vidintcon, Exynos4210fimdState, 2), + VMSTATE_UINT32(dithmode, Exynos4210fimdState), + VMSTATE_UINT32_ARRAY(wpalcon, Exynos4210fimdState, 2), + VMSTATE_UINT32(trigcon, Exynos4210fimdState), + VMSTATE_UINT32_ARRAY(i80ifcon, Exynos4210fimdState, 4), + VMSTATE_UINT32(colorgaincon, Exynos4210fimdState), + VMSTATE_UINT32_ARRAY(ldi_cmdcon, Exynos4210fimdState, 2), + VMSTATE_UINT32_ARRAY(sifccon, Exynos4210fimdState, 3), + VMSTATE_UINT32_ARRAY(huecoef_cr, Exynos4210fimdState, 4), + VMSTATE_UINT32_ARRAY(huecoef_cb, Exynos4210fimdState, 4), + VMSTATE_UINT32(hueoffset, Exynos4210fimdState), + VMSTATE_UINT32_ARRAY(i80ifcmd, Exynos4210fimdState, 12), + VMSTATE_UINT32(blendcon, Exynos4210fimdState), + VMSTATE_STRUCT_ARRAY(window, Exynos4210fimdState, 5, 1, + exynos4210_fimd_window_vmstate, Exynos4210fimdWindow), + VMSTATE_END_OF_LIST() + } +}; + +static int exynos4210_fimd_init(SysBusDevice *dev) +{ + Exynos4210fimdState *s = FROM_SYSBUS(Exynos4210fimdState, 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, &exynos4210_fimd_mmio_ops, s, + "exynos4210.fimd", FIMD_REGS_SIZE); + sysbus_init_mmio(dev, &s->iomem); + s->console = graphic_console_init(exynos4210_fimd_update, + exynos4210_fimd_invalidate, NULL, NULL, s); + + return 0; +} + +static void exynos4210_fimd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + dc->vmsd = &exynos4210_fimd_vmstate; + dc->reset = exynos4210_fimd_reset; + k->init = exynos4210_fimd_init; +} + +static const TypeInfo exynos4210_fimd_info = { + .name = "exynos4210.fimd", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210fimdState), + .class_init = exynos4210_fimd_class_init, +}; + +static void exynos4210_fimd_register_types(void) +{ + type_register_static(&exynos4210_fimd_info); +} + +type_init(exynos4210_fimd_register_types) diff --git a/hw/display/framebuffer.c b/hw/display/framebuffer.c new file mode 100644 index 0000000000..7326a98a41 --- /dev/null +++ b/hw/display/framebuffer.c @@ -0,0 +1,110 @@ +/* + * Framebuffer device helper routines + * + * Copyright (c) 2009 CodeSourcery + * Written by Paul Brook + * + * This code is licensed under the GNU GPLv2. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +/* TODO: + - Do something similar for framebuffers with local ram + - Handle rotation here instead of hacking dest_pitch + - Use common pixel conversion routines instead of per-device drawfn + - Remove all DisplayState knowledge from devices. + */ + +#include "hw/hw.h" +#include "ui/console.h" +#include "hw/framebuffer.h" + +/* Render an image from a shared memory framebuffer. */ + +void framebuffer_update_display( + DisplaySurface *ds, + MemoryRegion *address_space, + hwaddr base, + int cols, /* Width in pixels. */ + int rows, /* Height in pixels. */ + int src_width, /* Length of source line, in bytes. */ + int dest_row_pitch, /* Bytes between adjacent horizontal output pixels. */ + int dest_col_pitch, /* Bytes between adjacent vertical output pixels. */ + int invalidate, /* nonzero to redraw the whole image. */ + drawfn fn, + void *opaque, + int *first_row, /* Input and output. */ + int *last_row /* Output only */) +{ + hwaddr src_len; + uint8_t *dest; + uint8_t *src; + uint8_t *src_base; + int first, last = 0; + int dirty; + int i; + ram_addr_t addr; + MemoryRegionSection mem_section; + MemoryRegion *mem; + + i = *first_row; + *first_row = -1; + src_len = src_width * rows; + + mem_section = memory_region_find(address_space, base, src_len); + if (mem_section.size != src_len || !memory_region_is_ram(mem_section.mr)) { + return; + } + mem = mem_section.mr; + assert(mem); + assert(mem_section.offset_within_address_space == base); + + memory_region_sync_dirty_bitmap(mem); + src_base = cpu_physical_memory_map(base, &src_len, 0); + /* If we can't map the framebuffer then bail. We could try harder, + but it's not really worth it as dirty flag tracking will probably + already have failed above. */ + if (!src_base) + return; + if (src_len != src_width * rows) { + cpu_physical_memory_unmap(src_base, src_len, 0, 0); + return; + } + src = src_base; + dest = surface_data(ds); + if (dest_col_pitch < 0) + dest -= dest_col_pitch * (cols - 1); + if (dest_row_pitch < 0) { + dest -= dest_row_pitch * (rows - 1); + } + first = -1; + addr = mem_section.offset_within_region; + + addr += i * src_width; + src += i * src_width; + dest += i * dest_row_pitch; + + for (; i < rows; i++) { + dirty = memory_region_get_dirty(mem, addr, src_width, + DIRTY_MEMORY_VGA); + if (dirty || invalidate) { + fn(opaque, dest, src, cols, dest_col_pitch); + if (first == -1) + first = i; + last = i; + } + addr += src_width; + src += src_width; + dest += dest_row_pitch; + } + cpu_physical_memory_unmap(src_base, src_len, 0, 0); + if (first < 0) { + return; + } + memory_region_reset_dirty(mem, mem_section.offset_within_region, src_len, + DIRTY_MEMORY_VGA); + *first_row = first; + *last_row = last; +} diff --git a/hw/display/milkymist-tmu2.c b/hw/display/milkymist-tmu2.c new file mode 100644 index 0000000000..b723a04cc9 --- /dev/null +++ b/hw/display/milkymist-tmu2.c @@ -0,0 +1,490 @@ +/* + * QEMU model of the Milkymist texture mapping unit. + * + * Copyright (c) 2010 Michael Walle + * Copyright (c) 2010 Sebastien Bourdeauducq + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * + * Specification available at: + * http://www.milkymist.org/socdoc/tmu2.pdf + * + */ + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "trace.h" +#include "qemu/error-report.h" + +#include +#include +#include + +enum { + R_CTL = 0, + R_HMESHLAST, + R_VMESHLAST, + R_BRIGHTNESS, + R_CHROMAKEY, + R_VERTICESADDR, + R_TEXFBUF, + R_TEXHRES, + R_TEXVRES, + R_TEXHMASK, + R_TEXVMASK, + R_DSTFBUF, + R_DSTHRES, + R_DSTVRES, + R_DSTHOFFSET, + R_DSTVOFFSET, + R_DSTSQUAREW, + R_DSTSQUAREH, + R_ALPHA, + R_MAX +}; + +enum { + CTL_START_BUSY = (1<<0), + CTL_CHROMAKEY = (1<<1), +}; + +enum { + MAX_BRIGHTNESS = 63, + MAX_ALPHA = 63, +}; + +enum { + MESH_MAXSIZE = 128, +}; + +struct vertex { + int x; + int y; +} QEMU_PACKED; + +struct MilkymistTMU2State { + SysBusDevice busdev; + MemoryRegion regs_region; + CharDriverState *chr; + qemu_irq irq; + + uint32_t regs[R_MAX]; + + Display *dpy; + GLXFBConfig glx_fb_config; + GLXContext glx_context; +}; +typedef struct MilkymistTMU2State MilkymistTMU2State; + +static const int glx_fbconfig_attr[] = { + GLX_GREEN_SIZE, 5, + GLX_GREEN_SIZE, 6, + GLX_BLUE_SIZE, 5, + None +}; + +static int tmu2_glx_init(MilkymistTMU2State *s) +{ + GLXFBConfig *configs; + int nelements; + + s->dpy = XOpenDisplay(NULL); /* FIXME: call XCloseDisplay() */ + if (s->dpy == NULL) { + return 1; + } + + configs = glXChooseFBConfig(s->dpy, 0, glx_fbconfig_attr, &nelements); + if (configs == NULL) { + return 1; + } + + s->glx_fb_config = *configs; + XFree(configs); + + /* FIXME: call glXDestroyContext() */ + s->glx_context = glXCreateNewContext(s->dpy, s->glx_fb_config, + GLX_RGBA_TYPE, NULL, 1); + if (s->glx_context == NULL) { + return 1; + } + + return 0; +} + +static void tmu2_gl_map(struct vertex *mesh, int texhres, int texvres, + int hmeshlast, int vmeshlast, int ho, int vo, int sw, int sh) +{ + int x, y; + int x0, y0, x1, y1; + int u0, v0, u1, v1, u2, v2, u3, v3; + double xscale = 1.0 / ((double)(64 * texhres)); + double yscale = 1.0 / ((double)(64 * texvres)); + + glLoadIdentity(); + glTranslatef(ho, vo, 0); + glEnable(GL_TEXTURE_2D); + glBegin(GL_QUADS); + + for (y = 0; y < vmeshlast; y++) { + y0 = y * sh; + y1 = y0 + sh; + for (x = 0; x < hmeshlast; x++) { + x0 = x * sw; + x1 = x0 + sw; + + u0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].x); + v0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].y); + u1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].x); + v1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].y); + u2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].x); + v2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].y); + u3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].x); + v3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].y); + + glTexCoord2d(((double)u0) * xscale, ((double)v0) * yscale); + glVertex3i(x0, y0, 0); + glTexCoord2d(((double)u1) * xscale, ((double)v1) * yscale); + glVertex3i(x1, y0, 0); + glTexCoord2d(((double)u2) * xscale, ((double)v2) * yscale); + glVertex3i(x1, y1, 0); + glTexCoord2d(((double)u3) * xscale, ((double)v3) * yscale); + glVertex3i(x0, y1, 0); + } + } + + glEnd(); +} + +static void tmu2_start(MilkymistTMU2State *s) +{ + int pbuffer_attrib[6] = { + GLX_PBUFFER_WIDTH, + 0, + GLX_PBUFFER_HEIGHT, + 0, + GLX_PRESERVED_CONTENTS, + True + }; + + GLXPbuffer pbuffer; + GLuint texture; + void *fb; + hwaddr fb_len; + void *mesh; + hwaddr mesh_len; + float m; + + trace_milkymist_tmu2_start(); + + /* Create and set up a suitable OpenGL context */ + pbuffer_attrib[1] = s->regs[R_DSTHRES]; + pbuffer_attrib[3] = s->regs[R_DSTVRES]; + pbuffer = glXCreatePbuffer(s->dpy, s->glx_fb_config, pbuffer_attrib); + glXMakeContextCurrent(s->dpy, pbuffer, pbuffer, s->glx_context); + + /* Fixup endianness. TODO: would it work on BE hosts? */ + glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); + glPixelStorei(GL_PACK_SWAP_BYTES, 1); + + /* Row alignment */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 2); + glPixelStorei(GL_PACK_ALIGNMENT, 2); + + /* Read the QEMU source framebuffer into an OpenGL texture */ + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + fb_len = 2*s->regs[R_TEXHRES]*s->regs[R_TEXVRES]; + fb = cpu_physical_memory_map(s->regs[R_TEXFBUF], &fb_len, 0); + if (fb == NULL) { + glDeleteTextures(1, &texture); + glXMakeContextCurrent(s->dpy, None, None, NULL); + glXDestroyPbuffer(s->dpy, pbuffer); + return; + } + glTexImage2D(GL_TEXTURE_2D, 0, 3, s->regs[R_TEXHRES], s->regs[R_TEXVRES], + 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, fb); + cpu_physical_memory_unmap(fb, fb_len, 0, fb_len); + + /* Set up texturing options */ + /* WARNING: + * Many cases of TMU2 masking are not supported by OpenGL. + * We only implement the most common ones: + * - full bilinear filtering vs. nearest texel + * - texture clamping vs. texture wrapping + */ + if ((s->regs[R_TEXHMASK] & 0x3f) > 0x20) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + if ((s->regs[R_TEXHMASK] >> 6) & s->regs[R_TEXHRES]) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + } + if ((s->regs[R_TEXVMASK] >> 6) & s->regs[R_TEXVRES]) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + /* Translucency and decay */ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + m = (float)(s->regs[R_BRIGHTNESS] + 1) / 64.0f; + glColor4f(m, m, m, (float)(s->regs[R_ALPHA] + 1) / 64.0f); + + /* Read the QEMU dest. framebuffer into the OpenGL framebuffer */ + fb_len = 2 * s->regs[R_DSTHRES] * s->regs[R_DSTVRES]; + fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 0); + if (fb == NULL) { + glDeleteTextures(1, &texture); + glXMakeContextCurrent(s->dpy, None, None, NULL); + glXDestroyPbuffer(s->dpy, pbuffer); + return; + } + + glDrawPixels(s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB, + GL_UNSIGNED_SHORT_5_6_5, fb); + cpu_physical_memory_unmap(fb, fb_len, 0, fb_len); + glViewport(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES]); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, s->regs[R_DSTHRES], 0.0, s->regs[R_DSTVRES], -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + + /* Map the texture */ + mesh_len = MESH_MAXSIZE*MESH_MAXSIZE*sizeof(struct vertex); + mesh = cpu_physical_memory_map(s->regs[R_VERTICESADDR], &mesh_len, 0); + if (mesh == NULL) { + glDeleteTextures(1, &texture); + glXMakeContextCurrent(s->dpy, None, None, NULL); + glXDestroyPbuffer(s->dpy, pbuffer); + return; + } + + tmu2_gl_map((struct vertex *)mesh, + s->regs[R_TEXHRES], s->regs[R_TEXVRES], + s->regs[R_HMESHLAST], s->regs[R_VMESHLAST], + s->regs[R_DSTHOFFSET], s->regs[R_DSTVOFFSET], + s->regs[R_DSTSQUAREW], s->regs[R_DSTSQUAREH]); + cpu_physical_memory_unmap(mesh, mesh_len, 0, mesh_len); + + /* Write back the OpenGL framebuffer to the QEMU framebuffer */ + fb_len = 2 * s->regs[R_DSTHRES] * s->regs[R_DSTVRES]; + fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 1); + if (fb == NULL) { + glDeleteTextures(1, &texture); + glXMakeContextCurrent(s->dpy, None, None, NULL); + glXDestroyPbuffer(s->dpy, pbuffer); + return; + } + + glReadPixels(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB, + GL_UNSIGNED_SHORT_5_6_5, fb); + cpu_physical_memory_unmap(fb, fb_len, 1, fb_len); + + /* Free OpenGL allocs */ + glDeleteTextures(1, &texture); + glXMakeContextCurrent(s->dpy, None, None, NULL); + glXDestroyPbuffer(s->dpy, pbuffer); + + s->regs[R_CTL] &= ~CTL_START_BUSY; + + trace_milkymist_tmu2_pulse_irq(); + qemu_irq_pulse(s->irq); +} + +static uint64_t tmu2_read(void *opaque, hwaddr addr, + unsigned size) +{ + MilkymistTMU2State *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_CTL: + case R_HMESHLAST: + case R_VMESHLAST: + case R_BRIGHTNESS: + case R_CHROMAKEY: + case R_VERTICESADDR: + case R_TEXFBUF: + case R_TEXHRES: + case R_TEXVRES: + case R_TEXHMASK: + case R_TEXVMASK: + case R_DSTFBUF: + case R_DSTHRES: + case R_DSTVRES: + case R_DSTHOFFSET: + case R_DSTVOFFSET: + case R_DSTSQUAREW: + case R_DSTSQUAREH: + case R_ALPHA: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_tmu2: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_tmu2_memory_read(addr << 2, r); + + return r; +} + +static void tmu2_check_registers(MilkymistTMU2State *s) +{ + if (s->regs[R_BRIGHTNESS] > MAX_BRIGHTNESS) { + error_report("milkymist_tmu2: max brightness is %d", MAX_BRIGHTNESS); + } + + if (s->regs[R_ALPHA] > MAX_ALPHA) { + error_report("milkymist_tmu2: max alpha is %d", MAX_ALPHA); + } + + if (s->regs[R_VERTICESADDR] & 0x07) { + error_report("milkymist_tmu2: vertex mesh address has to be 64-bit " + "aligned"); + } + + if (s->regs[R_TEXFBUF] & 0x01) { + error_report("milkymist_tmu2: texture buffer address has to be " + "16-bit aligned"); + } +} + +static void tmu2_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + MilkymistTMU2State *s = opaque; + + trace_milkymist_tmu2_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_CTL: + s->regs[addr] = value; + if (value & CTL_START_BUSY) { + tmu2_start(s); + } + break; + case R_BRIGHTNESS: + case R_HMESHLAST: + case R_VMESHLAST: + case R_CHROMAKEY: + case R_VERTICESADDR: + case R_TEXFBUF: + case R_TEXHRES: + case R_TEXVRES: + case R_TEXHMASK: + case R_TEXVMASK: + case R_DSTFBUF: + case R_DSTHRES: + case R_DSTVRES: + case R_DSTHOFFSET: + case R_DSTVOFFSET: + case R_DSTSQUAREW: + case R_DSTSQUAREH: + case R_ALPHA: + s->regs[addr] = value; + break; + + default: + error_report("milkymist_tmu2: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + tmu2_check_registers(s); +} + +static const MemoryRegionOps tmu2_mmio_ops = { + .read = tmu2_read, + .write = tmu2_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void milkymist_tmu2_reset(DeviceState *d) +{ + MilkymistTMU2State *s = container_of(d, MilkymistTMU2State, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } +} + +static int milkymist_tmu2_init(SysBusDevice *dev) +{ + MilkymistTMU2State *s = FROM_SYSBUS(typeof(*s), dev); + + if (tmu2_glx_init(s)) { + return 1; + } + + sysbus_init_irq(dev, &s->irq); + + memory_region_init_io(&s->regs_region, &tmu2_mmio_ops, s, + "milkymist-tmu2", R_MAX * 4); + sysbus_init_mmio(dev, &s->regs_region); + + return 0; +} + +static const VMStateDescription vmstate_milkymist_tmu2 = { + .name = "milkymist-tmu2", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistTMU2State, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static void milkymist_tmu2_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = milkymist_tmu2_init; + dc->reset = milkymist_tmu2_reset; + dc->vmsd = &vmstate_milkymist_tmu2; +} + +static const TypeInfo milkymist_tmu2_info = { + .name = "milkymist-tmu2", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MilkymistTMU2State), + .class_init = milkymist_tmu2_class_init, +}; + +static void milkymist_tmu2_register_types(void) +{ + type_register_static(&milkymist_tmu2_info); +} + +type_init(milkymist_tmu2_register_types) diff --git a/hw/display/milkymist-vgafb.c b/hw/display/milkymist-vgafb.c new file mode 100644 index 0000000000..98762ecd21 --- /dev/null +++ b/hw/display/milkymist-vgafb.c @@ -0,0 +1,335 @@ + +/* + * QEMU model of the Milkymist VGA framebuffer. + * + * Copyright (c) 2010-2012 Michael Walle + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * + * Specification available at: + * http://www.milkymist.org/socdoc/vgafb.pdf + */ + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "trace.h" +#include "ui/console.h" +#include "hw/framebuffer.h" +#include "ui/pixel_ops.h" +#include "qemu/error-report.h" + +#define BITS 8 +#include "hw/milkymist-vgafb_template.h" +#define BITS 15 +#include "hw/milkymist-vgafb_template.h" +#define BITS 16 +#include "hw/milkymist-vgafb_template.h" +#define BITS 24 +#include "hw/milkymist-vgafb_template.h" +#define BITS 32 +#include "hw/milkymist-vgafb_template.h" + +enum { + R_CTRL = 0, + R_HRES, + R_HSYNC_START, + R_HSYNC_END, + R_HSCAN, + R_VRES, + R_VSYNC_START, + R_VSYNC_END, + R_VSCAN, + R_BASEADDRESS, + R_BASEADDRESS_ACT, + R_BURST_COUNT, + R_DDC, + R_SOURCE_CLOCK, + R_MAX +}; + +enum { + CTRL_RESET = (1<<0), +}; + +struct MilkymistVgafbState { + SysBusDevice busdev; + MemoryRegion regs_region; + QemuConsole *con; + + int invalidate; + uint32_t fb_offset; + uint32_t fb_mask; + + uint32_t regs[R_MAX]; +}; +typedef struct MilkymistVgafbState MilkymistVgafbState; + +static int vgafb_enabled(MilkymistVgafbState *s) +{ + return !(s->regs[R_CTRL] & CTRL_RESET); +} + +static void vgafb_update_display(void *opaque) +{ + MilkymistVgafbState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + int first = 0; + int last = 0; + drawfn fn; + + if (!vgafb_enabled(s)) { + return; + } + + int dest_width = s->regs[R_HRES]; + + switch (surface_bits_per_pixel(surface)) { + case 0: + return; + case 8: + fn = draw_line_8; + break; + case 15: + fn = draw_line_15; + dest_width *= 2; + break; + case 16: + fn = draw_line_16; + dest_width *= 2; + break; + case 24: + fn = draw_line_24; + dest_width *= 3; + break; + case 32: + fn = draw_line_32; + dest_width *= 4; + break; + default: + hw_error("milkymist_vgafb: bad color depth\n"); + break; + } + + framebuffer_update_display(surface, sysbus_address_space(&s->busdev), + s->regs[R_BASEADDRESS] + s->fb_offset, + s->regs[R_HRES], + s->regs[R_VRES], + s->regs[R_HRES] * 2, + dest_width, + 0, + s->invalidate, + fn, + NULL, + &first, &last); + + if (first >= 0) { + dpy_gfx_update(s->con, 0, first, s->regs[R_HRES], last - first + 1); + } + s->invalidate = 0; +} + +static void vgafb_invalidate_display(void *opaque) +{ + MilkymistVgafbState *s = opaque; + s->invalidate = 1; +} + +static void vgafb_resize(MilkymistVgafbState *s) +{ + if (!vgafb_enabled(s)) { + return; + } + + qemu_console_resize(s->con, s->regs[R_HRES], s->regs[R_VRES]); + s->invalidate = 1; +} + +static uint64_t vgafb_read(void *opaque, hwaddr addr, + unsigned size) +{ + MilkymistVgafbState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_CTRL: + case R_HRES: + case R_HSYNC_START: + case R_HSYNC_END: + case R_HSCAN: + case R_VRES: + case R_VSYNC_START: + case R_VSYNC_END: + case R_VSCAN: + case R_BASEADDRESS: + case R_BURST_COUNT: + case R_DDC: + case R_SOURCE_CLOCK: + r = s->regs[addr]; + break; + case R_BASEADDRESS_ACT: + r = s->regs[R_BASEADDRESS]; + break; + + default: + error_report("milkymist_vgafb: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_vgafb_memory_read(addr << 2, r); + + return r; +} + +static void vgafb_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + MilkymistVgafbState *s = opaque; + + trace_milkymist_vgafb_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_CTRL: + s->regs[addr] = value; + vgafb_resize(s); + break; + case R_HSYNC_START: + case R_HSYNC_END: + case R_HSCAN: + case R_VSYNC_START: + case R_VSYNC_END: + case R_VSCAN: + case R_BURST_COUNT: + case R_DDC: + case R_SOURCE_CLOCK: + s->regs[addr] = value; + break; + case R_BASEADDRESS: + if (value & 0x1f) { + error_report("milkymist_vgafb: framebuffer base address have to " + "be 32 byte aligned"); + break; + } + s->regs[addr] = value & s->fb_mask; + s->invalidate = 1; + break; + case R_HRES: + case R_VRES: + s->regs[addr] = value; + vgafb_resize(s); + break; + case R_BASEADDRESS_ACT: + error_report("milkymist_vgafb: write to read-only register 0x" + TARGET_FMT_plx, addr << 2); + break; + + default: + error_report("milkymist_vgafb: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } +} + +static const MemoryRegionOps vgafb_mmio_ops = { + .read = vgafb_read, + .write = vgafb_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void milkymist_vgafb_reset(DeviceState *d) +{ + MilkymistVgafbState *s = container_of(d, MilkymistVgafbState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + + /* defaults */ + s->regs[R_CTRL] = CTRL_RESET; + s->regs[R_HRES] = 640; + s->regs[R_VRES] = 480; + s->regs[R_BASEADDRESS] = 0; +} + +static int milkymist_vgafb_init(SysBusDevice *dev) +{ + MilkymistVgafbState *s = FROM_SYSBUS(typeof(*s), dev); + + memory_region_init_io(&s->regs_region, &vgafb_mmio_ops, s, + "milkymist-vgafb", R_MAX * 4); + sysbus_init_mmio(dev, &s->regs_region); + + s->con = graphic_console_init(vgafb_update_display, + vgafb_invalidate_display, + NULL, NULL, s); + + return 0; +} + +static int vgafb_post_load(void *opaque, int version_id) +{ + vgafb_invalidate_display(opaque); + return 0; +} + +static const VMStateDescription vmstate_milkymist_vgafb = { + .name = "milkymist-vgafb", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = vgafb_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistVgafbState, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static Property milkymist_vgafb_properties[] = { + DEFINE_PROP_UINT32("fb_offset", MilkymistVgafbState, fb_offset, 0x0), + DEFINE_PROP_UINT32("fb_mask", MilkymistVgafbState, fb_mask, 0xffffffff), + DEFINE_PROP_END_OF_LIST(), +}; + +static void milkymist_vgafb_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = milkymist_vgafb_init; + dc->reset = milkymist_vgafb_reset; + dc->vmsd = &vmstate_milkymist_vgafb; + dc->props = milkymist_vgafb_properties; +} + +static const TypeInfo milkymist_vgafb_info = { + .name = "milkymist-vgafb", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MilkymistVgafbState), + .class_init = milkymist_vgafb_class_init, +}; + +static void milkymist_vgafb_register_types(void) +{ + type_register_static(&milkymist_vgafb_info); +} + +type_init(milkymist_vgafb_register_types) diff --git a/hw/display/omap_dss.c b/hw/display/omap_dss.c new file mode 100644 index 0000000000..ea3afcef5e --- /dev/null +++ b/hw/display/omap_dss.c @@ -0,0 +1,1086 @@ +/* + * OMAP2 Display Subsystem. + * + * Copyright (C) 2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ +#include "hw/hw.h" +#include "ui/console.h" +#include "hw/arm/omap.h" + +struct omap_dss_s { + qemu_irq irq; + qemu_irq drq; + DisplayState *state; + MemoryRegion iomem_diss1, iomem_disc1, iomem_rfbi1, iomem_venc1, iomem_im3; + + int autoidle; + int control; + int enable; + + struct omap_dss_panel_s { + int enable; + int nx; + int ny; + + int x; + int y; + } dig, lcd; + + struct { + uint32_t idlemode; + uint32_t irqst; + uint32_t irqen; + uint32_t control; + uint32_t config; + uint32_t capable; + uint32_t timing[4]; + int line; + uint32_t bg[2]; + uint32_t trans[2]; + + struct omap_dss_plane_s { + int enable; + int bpp; + int posx; + int posy; + int nx; + int ny; + + hwaddr addr[3]; + + uint32_t attr; + uint32_t tresh; + int rowinc; + int colinc; + int wininc; + } l[3]; + + int invalidate; + uint16_t palette[256]; + } dispc; + + struct { + int idlemode; + uint32_t control; + int enable; + int pixels; + int busy; + int skiplines; + uint16_t rxbuf; + uint32_t config[2]; + uint32_t time[4]; + uint32_t data[6]; + uint16_t vsync; + uint16_t hsync; + struct rfbi_chip_s *chip[2]; + } rfbi; +}; + +static void omap_dispc_interrupt_update(struct omap_dss_s *s) +{ + qemu_set_irq(s->irq, s->dispc.irqst & s->dispc.irqen); +} + +static void omap_rfbi_reset(struct omap_dss_s *s) +{ + s->rfbi.idlemode = 0; + s->rfbi.control = 2; + s->rfbi.enable = 0; + s->rfbi.pixels = 0; + s->rfbi.skiplines = 0; + s->rfbi.busy = 0; + s->rfbi.config[0] = 0x00310000; + s->rfbi.config[1] = 0x00310000; + s->rfbi.time[0] = 0; + s->rfbi.time[1] = 0; + s->rfbi.time[2] = 0; + s->rfbi.time[3] = 0; + s->rfbi.data[0] = 0; + s->rfbi.data[1] = 0; + s->rfbi.data[2] = 0; + s->rfbi.data[3] = 0; + s->rfbi.data[4] = 0; + s->rfbi.data[5] = 0; + s->rfbi.vsync = 0; + s->rfbi.hsync = 0; +} + +void omap_dss_reset(struct omap_dss_s *s) +{ + s->autoidle = 0; + s->control = 0; + s->enable = 0; + + s->dig.enable = 0; + s->dig.nx = 1; + s->dig.ny = 1; + + s->lcd.enable = 0; + s->lcd.nx = 1; + s->lcd.ny = 1; + + s->dispc.idlemode = 0; + s->dispc.irqst = 0; + s->dispc.irqen = 0; + s->dispc.control = 0; + s->dispc.config = 0; + s->dispc.capable = 0x161; + s->dispc.timing[0] = 0; + s->dispc.timing[1] = 0; + s->dispc.timing[2] = 0; + s->dispc.timing[3] = 0; + s->dispc.line = 0; + s->dispc.bg[0] = 0; + s->dispc.bg[1] = 0; + s->dispc.trans[0] = 0; + s->dispc.trans[1] = 0; + + s->dispc.l[0].enable = 0; + s->dispc.l[0].bpp = 0; + s->dispc.l[0].addr[0] = 0; + s->dispc.l[0].addr[1] = 0; + s->dispc.l[0].addr[2] = 0; + s->dispc.l[0].posx = 0; + s->dispc.l[0].posy = 0; + s->dispc.l[0].nx = 1; + s->dispc.l[0].ny = 1; + s->dispc.l[0].attr = 0; + s->dispc.l[0].tresh = 0; + s->dispc.l[0].rowinc = 1; + s->dispc.l[0].colinc = 1; + s->dispc.l[0].wininc = 0; + + omap_rfbi_reset(s); + omap_dispc_interrupt_update(s); +} + +static uint64_t omap_diss_read(void *opaque, hwaddr addr, + unsigned size) +{ + struct omap_dss_s *s = (struct omap_dss_s *) opaque; + + if (size != 4) { + return omap_badwidth_read32(opaque, addr); + } + + switch (addr) { + case 0x00: /* DSS_REVISIONNUMBER */ + return 0x20; + + case 0x10: /* DSS_SYSCONFIG */ + return s->autoidle; + + case 0x14: /* DSS_SYSSTATUS */ + return 1; /* RESETDONE */ + + case 0x40: /* DSS_CONTROL */ + return s->control; + + case 0x50: /* DSS_PSA_LCD_REG_1 */ + case 0x54: /* DSS_PSA_LCD_REG_2 */ + case 0x58: /* DSS_PSA_VIDEO_REG */ + /* TODO: fake some values when appropriate s->control bits are set */ + return 0; + + case 0x5c: /* DSS_STATUS */ + return 1 + (s->control & 1); + + default: + break; + } + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_diss_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + struct omap_dss_s *s = (struct omap_dss_s *) opaque; + + if (size != 4) { + return omap_badwidth_write32(opaque, addr, value); + } + + switch (addr) { + case 0x00: /* DSS_REVISIONNUMBER */ + case 0x14: /* DSS_SYSSTATUS */ + case 0x50: /* DSS_PSA_LCD_REG_1 */ + case 0x54: /* DSS_PSA_LCD_REG_2 */ + case 0x58: /* DSS_PSA_VIDEO_REG */ + case 0x5c: /* DSS_STATUS */ + OMAP_RO_REG(addr); + break; + + case 0x10: /* DSS_SYSCONFIG */ + if (value & 2) /* SOFTRESET */ + omap_dss_reset(s); + s->autoidle = value & 1; + break; + + case 0x40: /* DSS_CONTROL */ + s->control = value & 0x3dd; + break; + + default: + OMAP_BAD_REG(addr); + } +} + +static const MemoryRegionOps omap_diss_ops = { + .read = omap_diss_read, + .write = omap_diss_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static uint64_t omap_disc_read(void *opaque, hwaddr addr, + unsigned size) +{ + struct omap_dss_s *s = (struct omap_dss_s *) opaque; + + if (size != 4) { + return omap_badwidth_read32(opaque, addr); + } + + switch (addr) { + case 0x000: /* DISPC_REVISION */ + return 0x20; + + case 0x010: /* DISPC_SYSCONFIG */ + return s->dispc.idlemode; + + case 0x014: /* DISPC_SYSSTATUS */ + return 1; /* RESETDONE */ + + case 0x018: /* DISPC_IRQSTATUS */ + return s->dispc.irqst; + + case 0x01c: /* DISPC_IRQENABLE */ + return s->dispc.irqen; + + case 0x040: /* DISPC_CONTROL */ + return s->dispc.control; + + case 0x044: /* DISPC_CONFIG */ + return s->dispc.config; + + case 0x048: /* DISPC_CAPABLE */ + return s->dispc.capable; + + case 0x04c: /* DISPC_DEFAULT_COLOR0 */ + return s->dispc.bg[0]; + case 0x050: /* DISPC_DEFAULT_COLOR1 */ + return s->dispc.bg[1]; + case 0x054: /* DISPC_TRANS_COLOR0 */ + return s->dispc.trans[0]; + case 0x058: /* DISPC_TRANS_COLOR1 */ + return s->dispc.trans[1]; + + case 0x05c: /* DISPC_LINE_STATUS */ + return 0x7ff; + case 0x060: /* DISPC_LINE_NUMBER */ + return s->dispc.line; + + case 0x064: /* DISPC_TIMING_H */ + return s->dispc.timing[0]; + case 0x068: /* DISPC_TIMING_V */ + return s->dispc.timing[1]; + case 0x06c: /* DISPC_POL_FREQ */ + return s->dispc.timing[2]; + case 0x070: /* DISPC_DIVISOR */ + return s->dispc.timing[3]; + + case 0x078: /* DISPC_SIZE_DIG */ + return ((s->dig.ny - 1) << 16) | (s->dig.nx - 1); + case 0x07c: /* DISPC_SIZE_LCD */ + return ((s->lcd.ny - 1) << 16) | (s->lcd.nx - 1); + + case 0x080: /* DISPC_GFX_BA0 */ + return s->dispc.l[0].addr[0]; + case 0x084: /* DISPC_GFX_BA1 */ + return s->dispc.l[0].addr[1]; + case 0x088: /* DISPC_GFX_POSITION */ + return (s->dispc.l[0].posy << 16) | s->dispc.l[0].posx; + case 0x08c: /* DISPC_GFX_SIZE */ + return ((s->dispc.l[0].ny - 1) << 16) | (s->dispc.l[0].nx - 1); + case 0x0a0: /* DISPC_GFX_ATTRIBUTES */ + return s->dispc.l[0].attr; + case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */ + return s->dispc.l[0].tresh; + case 0x0a8: /* DISPC_GFX_FIFO_SIZE_STATUS */ + return 256; + case 0x0ac: /* DISPC_GFX_ROW_INC */ + return s->dispc.l[0].rowinc; + case 0x0b0: /* DISPC_GFX_PIXEL_INC */ + return s->dispc.l[0].colinc; + case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */ + return s->dispc.l[0].wininc; + case 0x0b8: /* DISPC_GFX_TABLE_BA */ + return s->dispc.l[0].addr[2]; + + case 0x0bc: /* DISPC_VID1_BA0 */ + case 0x0c0: /* DISPC_VID1_BA1 */ + case 0x0c4: /* DISPC_VID1_POSITION */ + case 0x0c8: /* DISPC_VID1_SIZE */ + case 0x0cc: /* DISPC_VID1_ATTRIBUTES */ + case 0x0d0: /* DISPC_VID1_FIFO_TRESHOLD */ + case 0x0d4: /* DISPC_VID1_FIFO_SIZE_STATUS */ + case 0x0d8: /* DISPC_VID1_ROW_INC */ + case 0x0dc: /* DISPC_VID1_PIXEL_INC */ + case 0x0e0: /* DISPC_VID1_FIR */ + case 0x0e4: /* DISPC_VID1_PICTURE_SIZE */ + case 0x0e8: /* DISPC_VID1_ACCU0 */ + case 0x0ec: /* DISPC_VID1_ACCU1 */ + case 0x0f0 ... 0x140: /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */ + case 0x14c: /* DISPC_VID2_BA0 */ + case 0x150: /* DISPC_VID2_BA1 */ + case 0x154: /* DISPC_VID2_POSITION */ + case 0x158: /* DISPC_VID2_SIZE */ + case 0x15c: /* DISPC_VID2_ATTRIBUTES */ + case 0x160: /* DISPC_VID2_FIFO_TRESHOLD */ + case 0x164: /* DISPC_VID2_FIFO_SIZE_STATUS */ + case 0x168: /* DISPC_VID2_ROW_INC */ + case 0x16c: /* DISPC_VID2_PIXEL_INC */ + case 0x170: /* DISPC_VID2_FIR */ + case 0x174: /* DISPC_VID2_PICTURE_SIZE */ + case 0x178: /* DISPC_VID2_ACCU0 */ + case 0x17c: /* DISPC_VID2_ACCU1 */ + case 0x180 ... 0x1d0: /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */ + case 0x1d4: /* DISPC_DATA_CYCLE1 */ + case 0x1d8: /* DISPC_DATA_CYCLE2 */ + case 0x1dc: /* DISPC_DATA_CYCLE3 */ + return 0; + + default: + break; + } + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_disc_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + struct omap_dss_s *s = (struct omap_dss_s *) opaque; + + if (size != 4) { + return omap_badwidth_write32(opaque, addr, value); + } + + switch (addr) { + case 0x010: /* DISPC_SYSCONFIG */ + if (value & 2) /* SOFTRESET */ + omap_dss_reset(s); + s->dispc.idlemode = value & 0x301b; + break; + + case 0x018: /* DISPC_IRQSTATUS */ + s->dispc.irqst &= ~value; + omap_dispc_interrupt_update(s); + break; + + case 0x01c: /* DISPC_IRQENABLE */ + s->dispc.irqen = value & 0xffff; + omap_dispc_interrupt_update(s); + break; + + case 0x040: /* DISPC_CONTROL */ + s->dispc.control = value & 0x07ff9fff; + s->dig.enable = (value >> 1) & 1; + s->lcd.enable = (value >> 0) & 1; + if (value & (1 << 12)) /* OVERLAY_OPTIMIZATION */ + if (!((s->dispc.l[1].attr | s->dispc.l[2].attr) & 1)) { + fprintf(stderr, "%s: Overlay Optimization when no overlay " + "region effectively exists leads to " + "unpredictable behaviour!\n", __func__); + } + if (value & (1 << 6)) { /* GODIGITAL */ + /* XXX: Shadowed fields are: + * s->dispc.config + * s->dispc.capable + * s->dispc.bg[0] + * s->dispc.bg[1] + * s->dispc.trans[0] + * s->dispc.trans[1] + * s->dispc.line + * s->dispc.timing[0] + * s->dispc.timing[1] + * s->dispc.timing[2] + * s->dispc.timing[3] + * s->lcd.nx + * s->lcd.ny + * s->dig.nx + * s->dig.ny + * s->dispc.l[0].addr[0] + * s->dispc.l[0].addr[1] + * s->dispc.l[0].addr[2] + * s->dispc.l[0].posx + * s->dispc.l[0].posy + * s->dispc.l[0].nx + * s->dispc.l[0].ny + * s->dispc.l[0].tresh + * s->dispc.l[0].rowinc + * s->dispc.l[0].colinc + * s->dispc.l[0].wininc + * All they need to be loaded here from their shadow registers. + */ + } + if (value & (1 << 5)) { /* GOLCD */ + /* XXX: Likewise for LCD here. */ + } + s->dispc.invalidate = 1; + break; + + case 0x044: /* DISPC_CONFIG */ + s->dispc.config = value & 0x3fff; + /* XXX: + * bits 2:1 (LOADMODE) reset to 0 after set to 1 and palette loaded + * bits 2:1 (LOADMODE) reset to 2 after set to 3 and palette loaded + */ + s->dispc.invalidate = 1; + break; + + case 0x048: /* DISPC_CAPABLE */ + s->dispc.capable = value & 0x3ff; + break; + + case 0x04c: /* DISPC_DEFAULT_COLOR0 */ + s->dispc.bg[0] = value & 0xffffff; + s->dispc.invalidate = 1; + break; + case 0x050: /* DISPC_DEFAULT_COLOR1 */ + s->dispc.bg[1] = value & 0xffffff; + s->dispc.invalidate = 1; + break; + case 0x054: /* DISPC_TRANS_COLOR0 */ + s->dispc.trans[0] = value & 0xffffff; + s->dispc.invalidate = 1; + break; + case 0x058: /* DISPC_TRANS_COLOR1 */ + s->dispc.trans[1] = value & 0xffffff; + s->dispc.invalidate = 1; + break; + + case 0x060: /* DISPC_LINE_NUMBER */ + s->dispc.line = value & 0x7ff; + break; + + case 0x064: /* DISPC_TIMING_H */ + s->dispc.timing[0] = value & 0x0ff0ff3f; + break; + case 0x068: /* DISPC_TIMING_V */ + s->dispc.timing[1] = value & 0x0ff0ff3f; + break; + case 0x06c: /* DISPC_POL_FREQ */ + s->dispc.timing[2] = value & 0x0003ffff; + break; + case 0x070: /* DISPC_DIVISOR */ + s->dispc.timing[3] = value & 0x00ff00ff; + break; + + case 0x078: /* DISPC_SIZE_DIG */ + s->dig.nx = ((value >> 0) & 0x7ff) + 1; /* PPL */ + s->dig.ny = ((value >> 16) & 0x7ff) + 1; /* LPP */ + s->dispc.invalidate = 1; + break; + case 0x07c: /* DISPC_SIZE_LCD */ + s->lcd.nx = ((value >> 0) & 0x7ff) + 1; /* PPL */ + s->lcd.ny = ((value >> 16) & 0x7ff) + 1; /* LPP */ + s->dispc.invalidate = 1; + break; + case 0x080: /* DISPC_GFX_BA0 */ + s->dispc.l[0].addr[0] = (hwaddr) value; + s->dispc.invalidate = 1; + break; + case 0x084: /* DISPC_GFX_BA1 */ + s->dispc.l[0].addr[1] = (hwaddr) value; + s->dispc.invalidate = 1; + break; + case 0x088: /* DISPC_GFX_POSITION */ + s->dispc.l[0].posx = ((value >> 0) & 0x7ff); /* GFXPOSX */ + s->dispc.l[0].posy = ((value >> 16) & 0x7ff); /* GFXPOSY */ + s->dispc.invalidate = 1; + break; + case 0x08c: /* DISPC_GFX_SIZE */ + s->dispc.l[0].nx = ((value >> 0) & 0x7ff) + 1; /* GFXSIZEX */ + s->dispc.l[0].ny = ((value >> 16) & 0x7ff) + 1; /* GFXSIZEY */ + s->dispc.invalidate = 1; + break; + case 0x0a0: /* DISPC_GFX_ATTRIBUTES */ + s->dispc.l[0].attr = value & 0x7ff; + if (value & (3 << 9)) + fprintf(stderr, "%s: Big-endian pixel format not supported\n", + __FUNCTION__); + s->dispc.l[0].enable = value & 1; + s->dispc.l[0].bpp = (value >> 1) & 0xf; + s->dispc.invalidate = 1; + break; + case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */ + s->dispc.l[0].tresh = value & 0x01ff01ff; + break; + case 0x0ac: /* DISPC_GFX_ROW_INC */ + s->dispc.l[0].rowinc = value; + s->dispc.invalidate = 1; + break; + case 0x0b0: /* DISPC_GFX_PIXEL_INC */ + s->dispc.l[0].colinc = value; + s->dispc.invalidate = 1; + break; + case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */ + s->dispc.l[0].wininc = value; + break; + case 0x0b8: /* DISPC_GFX_TABLE_BA */ + s->dispc.l[0].addr[2] = (hwaddr) value; + s->dispc.invalidate = 1; + break; + + case 0x0bc: /* DISPC_VID1_BA0 */ + case 0x0c0: /* DISPC_VID1_BA1 */ + case 0x0c4: /* DISPC_VID1_POSITION */ + case 0x0c8: /* DISPC_VID1_SIZE */ + case 0x0cc: /* DISPC_VID1_ATTRIBUTES */ + case 0x0d0: /* DISPC_VID1_FIFO_TRESHOLD */ + case 0x0d8: /* DISPC_VID1_ROW_INC */ + case 0x0dc: /* DISPC_VID1_PIXEL_INC */ + case 0x0e0: /* DISPC_VID1_FIR */ + case 0x0e4: /* DISPC_VID1_PICTURE_SIZE */ + case 0x0e8: /* DISPC_VID1_ACCU0 */ + case 0x0ec: /* DISPC_VID1_ACCU1 */ + case 0x0f0 ... 0x140: /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */ + case 0x14c: /* DISPC_VID2_BA0 */ + case 0x150: /* DISPC_VID2_BA1 */ + case 0x154: /* DISPC_VID2_POSITION */ + case 0x158: /* DISPC_VID2_SIZE */ + case 0x15c: /* DISPC_VID2_ATTRIBUTES */ + case 0x160: /* DISPC_VID2_FIFO_TRESHOLD */ + case 0x168: /* DISPC_VID2_ROW_INC */ + case 0x16c: /* DISPC_VID2_PIXEL_INC */ + case 0x170: /* DISPC_VID2_FIR */ + case 0x174: /* DISPC_VID2_PICTURE_SIZE */ + case 0x178: /* DISPC_VID2_ACCU0 */ + case 0x17c: /* DISPC_VID2_ACCU1 */ + case 0x180 ... 0x1d0: /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */ + case 0x1d4: /* DISPC_DATA_CYCLE1 */ + case 0x1d8: /* DISPC_DATA_CYCLE2 */ + case 0x1dc: /* DISPC_DATA_CYCLE3 */ + break; + + default: + OMAP_BAD_REG(addr); + } +} + +static const MemoryRegionOps omap_disc_ops = { + .read = omap_disc_read, + .write = omap_disc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void omap_rfbi_transfer_stop(struct omap_dss_s *s) +{ + if (!s->rfbi.busy) + return; + + /* TODO: in non-Bypass mode we probably need to just deassert the DRQ. */ + + s->rfbi.busy = 0; +} + +static void omap_rfbi_transfer_start(struct omap_dss_s *s) +{ + void *data; + hwaddr len; + hwaddr data_addr; + int pitch; + static void *bounce_buffer; + static hwaddr bounce_len; + + if (!s->rfbi.enable || s->rfbi.busy) + return; + + if (s->rfbi.control & (1 << 1)) { /* BYPASS */ + /* TODO: in non-Bypass mode we probably need to just assert the + * DRQ and wait for DMA to write the pixels. */ + fprintf(stderr, "%s: Bypass mode unimplemented\n", __FUNCTION__); + return; + } + + if (!(s->dispc.control & (1 << 11))) /* RFBIMODE */ + return; + /* TODO: check that LCD output is enabled in DISPC. */ + + s->rfbi.busy = 1; + + len = s->rfbi.pixels * 2; + + data_addr = s->dispc.l[0].addr[0]; + data = cpu_physical_memory_map(data_addr, &len, 0); + if (data && len != s->rfbi.pixels * 2) { + cpu_physical_memory_unmap(data, len, 0, 0); + data = NULL; + len = s->rfbi.pixels * 2; + } + if (!data) { + if (len > bounce_len) { + bounce_buffer = g_realloc(bounce_buffer, len); + } + data = bounce_buffer; + cpu_physical_memory_read(data_addr, data, len); + } + + /* TODO bpp */ + s->rfbi.pixels = 0; + + /* TODO: negative values */ + pitch = s->dispc.l[0].nx + (s->dispc.l[0].rowinc - 1) / 2; + + if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) + s->rfbi.chip[0]->block(s->rfbi.chip[0]->opaque, 1, data, len, pitch); + if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) + s->rfbi.chip[1]->block(s->rfbi.chip[1]->opaque, 1, data, len, pitch); + + if (data != bounce_buffer) { + cpu_physical_memory_unmap(data, len, 0, len); + } + + omap_rfbi_transfer_stop(s); + + /* TODO */ + s->dispc.irqst |= 1; /* FRAMEDONE */ + omap_dispc_interrupt_update(s); +} + +static uint64_t omap_rfbi_read(void *opaque, hwaddr addr, + unsigned size) +{ + struct omap_dss_s *s = (struct omap_dss_s *) opaque; + + if (size != 4) { + return omap_badwidth_read32(opaque, addr); + } + + switch (addr) { + case 0x00: /* RFBI_REVISION */ + return 0x10; + + case 0x10: /* RFBI_SYSCONFIG */ + return s->rfbi.idlemode; + + case 0x14: /* RFBI_SYSSTATUS */ + return 1 | (s->rfbi.busy << 8); /* RESETDONE */ + + case 0x40: /* RFBI_CONTROL */ + return s->rfbi.control; + + case 0x44: /* RFBI_PIXELCNT */ + return s->rfbi.pixels; + + case 0x48: /* RFBI_LINE_NUMBER */ + return s->rfbi.skiplines; + + case 0x58: /* RFBI_READ */ + case 0x5c: /* RFBI_STATUS */ + return s->rfbi.rxbuf; + + case 0x60: /* RFBI_CONFIG0 */ + return s->rfbi.config[0]; + case 0x64: /* RFBI_ONOFF_TIME0 */ + return s->rfbi.time[0]; + case 0x68: /* RFBI_CYCLE_TIME0 */ + return s->rfbi.time[1]; + case 0x6c: /* RFBI_DATA_CYCLE1_0 */ + return s->rfbi.data[0]; + case 0x70: /* RFBI_DATA_CYCLE2_0 */ + return s->rfbi.data[1]; + case 0x74: /* RFBI_DATA_CYCLE3_0 */ + return s->rfbi.data[2]; + + case 0x78: /* RFBI_CONFIG1 */ + return s->rfbi.config[1]; + case 0x7c: /* RFBI_ONOFF_TIME1 */ + return s->rfbi.time[2]; + case 0x80: /* RFBI_CYCLE_TIME1 */ + return s->rfbi.time[3]; + case 0x84: /* RFBI_DATA_CYCLE1_1 */ + return s->rfbi.data[3]; + case 0x88: /* RFBI_DATA_CYCLE2_1 */ + return s->rfbi.data[4]; + case 0x8c: /* RFBI_DATA_CYCLE3_1 */ + return s->rfbi.data[5]; + + case 0x90: /* RFBI_VSYNC_WIDTH */ + return s->rfbi.vsync; + case 0x94: /* RFBI_HSYNC_WIDTH */ + return s->rfbi.hsync; + } + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_rfbi_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + struct omap_dss_s *s = (struct omap_dss_s *) opaque; + + if (size != 4) { + return omap_badwidth_write32(opaque, addr, value); + } + + switch (addr) { + case 0x10: /* RFBI_SYSCONFIG */ + if (value & 2) /* SOFTRESET */ + omap_rfbi_reset(s); + s->rfbi.idlemode = value & 0x19; + break; + + case 0x40: /* RFBI_CONTROL */ + s->rfbi.control = value & 0xf; + s->rfbi.enable = value & 1; + if (value & (1 << 4) && /* ITE */ + !(s->rfbi.config[0] & s->rfbi.config[1] & 0xc)) + omap_rfbi_transfer_start(s); + break; + + case 0x44: /* RFBI_PIXELCNT */ + s->rfbi.pixels = value; + break; + + case 0x48: /* RFBI_LINE_NUMBER */ + s->rfbi.skiplines = value & 0x7ff; + break; + + case 0x4c: /* RFBI_CMD */ + if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) + s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 0, value & 0xffff); + if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) + s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 0, value & 0xffff); + break; + case 0x50: /* RFBI_PARAM */ + if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) + s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value & 0xffff); + if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) + s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value & 0xffff); + break; + case 0x54: /* RFBI_DATA */ + /* TODO: take into account the format set up in s->rfbi.config[?] and + * s->rfbi.data[?], but special-case the most usual scenario so that + * speed doesn't suffer. */ + if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) { + s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value & 0xffff); + s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value >> 16); + } + if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) { + s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value & 0xffff); + s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value >> 16); + } + if (!-- s->rfbi.pixels) + omap_rfbi_transfer_stop(s); + break; + case 0x58: /* RFBI_READ */ + if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) + s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 1); + else if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) + s->rfbi.rxbuf = s->rfbi.chip[1]->read(s->rfbi.chip[1]->opaque, 1); + if (!-- s->rfbi.pixels) + omap_rfbi_transfer_stop(s); + break; + + case 0x5c: /* RFBI_STATUS */ + if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) + s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 0); + else if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) + s->rfbi.rxbuf = s->rfbi.chip[1]->read(s->rfbi.chip[1]->opaque, 0); + if (!-- s->rfbi.pixels) + omap_rfbi_transfer_stop(s); + break; + + case 0x60: /* RFBI_CONFIG0 */ + s->rfbi.config[0] = value & 0x003f1fff; + break; + + case 0x64: /* RFBI_ONOFF_TIME0 */ + s->rfbi.time[0] = value & 0x3fffffff; + break; + case 0x68: /* RFBI_CYCLE_TIME0 */ + s->rfbi.time[1] = value & 0x0fffffff; + break; + case 0x6c: /* RFBI_DATA_CYCLE1_0 */ + s->rfbi.data[0] = value & 0x0f1f0f1f; + break; + case 0x70: /* RFBI_DATA_CYCLE2_0 */ + s->rfbi.data[1] = value & 0x0f1f0f1f; + break; + case 0x74: /* RFBI_DATA_CYCLE3_0 */ + s->rfbi.data[2] = value & 0x0f1f0f1f; + break; + case 0x78: /* RFBI_CONFIG1 */ + s->rfbi.config[1] = value & 0x003f1fff; + break; + + case 0x7c: /* RFBI_ONOFF_TIME1 */ + s->rfbi.time[2] = value & 0x3fffffff; + break; + case 0x80: /* RFBI_CYCLE_TIME1 */ + s->rfbi.time[3] = value & 0x0fffffff; + break; + case 0x84: /* RFBI_DATA_CYCLE1_1 */ + s->rfbi.data[3] = value & 0x0f1f0f1f; + break; + case 0x88: /* RFBI_DATA_CYCLE2_1 */ + s->rfbi.data[4] = value & 0x0f1f0f1f; + break; + case 0x8c: /* RFBI_DATA_CYCLE3_1 */ + s->rfbi.data[5] = value & 0x0f1f0f1f; + break; + + case 0x90: /* RFBI_VSYNC_WIDTH */ + s->rfbi.vsync = value & 0xffff; + break; + case 0x94: /* RFBI_HSYNC_WIDTH */ + s->rfbi.hsync = value & 0xffff; + break; + + default: + OMAP_BAD_REG(addr); + } +} + +static const MemoryRegionOps omap_rfbi_ops = { + .read = omap_rfbi_read, + .write = omap_rfbi_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static uint64_t omap_venc_read(void *opaque, hwaddr addr, + unsigned size) +{ + if (size != 4) { + return omap_badwidth_read32(opaque, addr); + } + + switch (addr) { + case 0x00: /* REV_ID */ + case 0x04: /* STATUS */ + case 0x08: /* F_CONTROL */ + case 0x10: /* VIDOUT_CTRL */ + case 0x14: /* SYNC_CTRL */ + case 0x1c: /* LLEN */ + case 0x20: /* FLENS */ + case 0x24: /* HFLTR_CTRL */ + case 0x28: /* CC_CARR_WSS_CARR */ + case 0x2c: /* C_PHASE */ + case 0x30: /* GAIN_U */ + case 0x34: /* GAIN_V */ + case 0x38: /* GAIN_Y */ + case 0x3c: /* BLACK_LEVEL */ + case 0x40: /* BLANK_LEVEL */ + case 0x44: /* X_COLOR */ + case 0x48: /* M_CONTROL */ + case 0x4c: /* BSTAMP_WSS_DATA */ + case 0x50: /* S_CARR */ + case 0x54: /* LINE21 */ + case 0x58: /* LN_SEL */ + case 0x5c: /* L21__WC_CTL */ + case 0x60: /* HTRIGGER_VTRIGGER */ + case 0x64: /* SAVID__EAVID */ + case 0x68: /* FLEN__FAL */ + case 0x6c: /* LAL__PHASE_RESET */ + case 0x70: /* HS_INT_START_STOP_X */ + case 0x74: /* HS_EXT_START_STOP_X */ + case 0x78: /* VS_INT_START_X */ + case 0x7c: /* VS_INT_STOP_X__VS_INT_START_Y */ + case 0x80: /* VS_INT_STOP_Y__VS_INT_START_X */ + case 0x84: /* VS_EXT_STOP_X__VS_EXT_START_Y */ + case 0x88: /* VS_EXT_STOP_Y */ + case 0x90: /* AVID_START_STOP_X */ + case 0x94: /* AVID_START_STOP_Y */ + case 0xa0: /* FID_INT_START_X__FID_INT_START_Y */ + case 0xa4: /* FID_INT_OFFSET_Y__FID_EXT_START_X */ + case 0xa8: /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */ + case 0xb0: /* TVDETGP_INT_START_STOP_X */ + case 0xb4: /* TVDETGP_INT_START_STOP_Y */ + case 0xb8: /* GEN_CTRL */ + case 0xc4: /* DAC_TST__DAC_A */ + case 0xc8: /* DAC_B__DAC_C */ + return 0; + + default: + break; + } + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_venc_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + if (size != 4) { + return omap_badwidth_write32(opaque, addr, size); + } + + switch (addr) { + case 0x08: /* F_CONTROL */ + case 0x10: /* VIDOUT_CTRL */ + case 0x14: /* SYNC_CTRL */ + case 0x1c: /* LLEN */ + case 0x20: /* FLENS */ + case 0x24: /* HFLTR_CTRL */ + case 0x28: /* CC_CARR_WSS_CARR */ + case 0x2c: /* C_PHASE */ + case 0x30: /* GAIN_U */ + case 0x34: /* GAIN_V */ + case 0x38: /* GAIN_Y */ + case 0x3c: /* BLACK_LEVEL */ + case 0x40: /* BLANK_LEVEL */ + case 0x44: /* X_COLOR */ + case 0x48: /* M_CONTROL */ + case 0x4c: /* BSTAMP_WSS_DATA */ + case 0x50: /* S_CARR */ + case 0x54: /* LINE21 */ + case 0x58: /* LN_SEL */ + case 0x5c: /* L21__WC_CTL */ + case 0x60: /* HTRIGGER_VTRIGGER */ + case 0x64: /* SAVID__EAVID */ + case 0x68: /* FLEN__FAL */ + case 0x6c: /* LAL__PHASE_RESET */ + case 0x70: /* HS_INT_START_STOP_X */ + case 0x74: /* HS_EXT_START_STOP_X */ + case 0x78: /* VS_INT_START_X */ + case 0x7c: /* VS_INT_STOP_X__VS_INT_START_Y */ + case 0x80: /* VS_INT_STOP_Y__VS_INT_START_X */ + case 0x84: /* VS_EXT_STOP_X__VS_EXT_START_Y */ + case 0x88: /* VS_EXT_STOP_Y */ + case 0x90: /* AVID_START_STOP_X */ + case 0x94: /* AVID_START_STOP_Y */ + case 0xa0: /* FID_INT_START_X__FID_INT_START_Y */ + case 0xa4: /* FID_INT_OFFSET_Y__FID_EXT_START_X */ + case 0xa8: /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */ + case 0xb0: /* TVDETGP_INT_START_STOP_X */ + case 0xb4: /* TVDETGP_INT_START_STOP_Y */ + case 0xb8: /* GEN_CTRL */ + case 0xc4: /* DAC_TST__DAC_A */ + case 0xc8: /* DAC_B__DAC_C */ + break; + + default: + OMAP_BAD_REG(addr); + } +} + +static const MemoryRegionOps omap_venc_ops = { + .read = omap_venc_read, + .write = omap_venc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static uint64_t omap_im3_read(void *opaque, hwaddr addr, + unsigned size) +{ + if (size != 4) { + return omap_badwidth_read32(opaque, addr); + } + + switch (addr) { + case 0x0a8: /* SBIMERRLOGA */ + case 0x0b0: /* SBIMERRLOG */ + case 0x190: /* SBIMSTATE */ + case 0x198: /* SBTMSTATE_L */ + case 0x19c: /* SBTMSTATE_H */ + case 0x1a8: /* SBIMCONFIG_L */ + case 0x1ac: /* SBIMCONFIG_H */ + case 0x1f8: /* SBID_L */ + case 0x1fc: /* SBID_H */ + return 0; + + default: + break; + } + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_im3_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + if (size != 4) { + return omap_badwidth_write32(opaque, addr, value); + } + + switch (addr) { + case 0x0b0: /* SBIMERRLOG */ + case 0x190: /* SBIMSTATE */ + case 0x198: /* SBTMSTATE_L */ + case 0x19c: /* SBTMSTATE_H */ + case 0x1a8: /* SBIMCONFIG_L */ + case 0x1ac: /* SBIMCONFIG_H */ + break; + + default: + OMAP_BAD_REG(addr); + } +} + +static const MemoryRegionOps omap_im3_ops = { + .read = omap_im3_read, + .write = omap_im3_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta, + MemoryRegion *sysmem, + hwaddr l3_base, + qemu_irq irq, qemu_irq drq, + omap_clk fck1, omap_clk fck2, omap_clk ck54m, + omap_clk ick1, omap_clk ick2) +{ + struct omap_dss_s *s = (struct omap_dss_s *) + g_malloc0(sizeof(struct omap_dss_s)); + + s->irq = irq; + s->drq = drq; + omap_dss_reset(s); + + memory_region_init_io(&s->iomem_diss1, &omap_diss_ops, s, "omap.diss1", + omap_l4_region_size(ta, 0)); + memory_region_init_io(&s->iomem_disc1, &omap_disc_ops, s, "omap.disc1", + omap_l4_region_size(ta, 1)); + memory_region_init_io(&s->iomem_rfbi1, &omap_rfbi_ops, s, "omap.rfbi1", + omap_l4_region_size(ta, 2)); + memory_region_init_io(&s->iomem_venc1, &omap_venc_ops, s, "omap.venc1", + omap_l4_region_size(ta, 3)); + memory_region_init_io(&s->iomem_im3, &omap_im3_ops, s, + "omap.im3", 0x1000); + + omap_l4_attach(ta, 0, &s->iomem_diss1); + omap_l4_attach(ta, 1, &s->iomem_disc1); + omap_l4_attach(ta, 2, &s->iomem_rfbi1); + omap_l4_attach(ta, 3, &s->iomem_venc1); + memory_region_add_subregion(sysmem, l3_base, &s->iomem_im3); + +#if 0 + s->state = graphic_console_init(omap_update_display, + omap_invalidate_display, omap_screen_dump, s); +#endif + + return s; +} + +void omap_rfbi_attach(struct omap_dss_s *s, int cs, struct rfbi_chip_s *chip) +{ + if (cs < 0 || cs > 1) + hw_error("%s: wrong CS %i\n", __FUNCTION__, cs); + s->rfbi.chip[cs] = chip; +} diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c new file mode 100644 index 0000000000..4048cc102d --- /dev/null +++ b/hw/display/omap_lcdc.c @@ -0,0 +1,493 @@ +/* + * OMAP LCD controller. + * + * Copyright (C) 2006-2007 Andrzej Zaborowski + * + * 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, see . + */ +#include "hw/hw.h" +#include "ui/console.h" +#include "hw/arm/omap.h" +#include "hw/framebuffer.h" +#include "ui/pixel_ops.h" + +struct omap_lcd_panel_s { + MemoryRegion *sysmem; + MemoryRegion iomem; + qemu_irq irq; + QemuConsole *con; + + int plm; + int tft; + int mono; + int enable; + int width; + int height; + int interrupts; + uint32_t timing[3]; + uint32_t subpanel; + uint32_t ctrl; + + struct omap_dma_lcd_channel_s *dma; + uint16_t palette[256]; + int palette_done; + int frame_done; + int invalidate; + int sync_error; +}; + +static void omap_lcd_interrupts(struct omap_lcd_panel_s *s) +{ + if (s->frame_done && (s->interrupts & 1)) { + qemu_irq_raise(s->irq); + return; + } + + if (s->palette_done && (s->interrupts & 2)) { + qemu_irq_raise(s->irq); + return; + } + + if (s->sync_error) { + qemu_irq_raise(s->irq); + return; + } + + qemu_irq_lower(s->irq); +} + +#define draw_line_func drawfn + +#define DEPTH 8 +#include "hw/omap_lcd_template.h" +#define DEPTH 15 +#include "hw/omap_lcd_template.h" +#define DEPTH 16 +#include "hw/omap_lcd_template.h" +#define DEPTH 32 +#include "hw/omap_lcd_template.h" + +static draw_line_func draw_line_table2[33] = { + [0 ... 32] = NULL, + [8] = draw_line2_8, + [15] = draw_line2_15, + [16] = draw_line2_16, + [32] = draw_line2_32, +}, draw_line_table4[33] = { + [0 ... 32] = NULL, + [8] = draw_line4_8, + [15] = draw_line4_15, + [16] = draw_line4_16, + [32] = draw_line4_32, +}, draw_line_table8[33] = { + [0 ... 32] = NULL, + [8] = draw_line8_8, + [15] = draw_line8_15, + [16] = draw_line8_16, + [32] = draw_line8_32, +}, draw_line_table12[33] = { + [0 ... 32] = NULL, + [8] = draw_line12_8, + [15] = draw_line12_15, + [16] = draw_line12_16, + [32] = draw_line12_32, +}, draw_line_table16[33] = { + [0 ... 32] = NULL, + [8] = draw_line16_8, + [15] = draw_line16_15, + [16] = draw_line16_16, + [32] = draw_line16_32, +}; + +static void omap_update_display(void *opaque) +{ + struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque; + DisplaySurface *surface = qemu_console_surface(omap_lcd->con); + draw_line_func draw_line; + int size, height, first, last; + int width, linesize, step, bpp, frame_offset; + hwaddr frame_base; + + if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable || + !surface_bits_per_pixel(surface)) { + return; + } + + frame_offset = 0; + if (omap_lcd->plm != 2) { + cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[ + omap_lcd->dma->current_frame], + (void *)omap_lcd->palette, 0x200); + switch (omap_lcd->palette[0] >> 12 & 7) { + case 3 ... 7: + frame_offset += 0x200; + break; + default: + frame_offset += 0x20; + } + } + + /* Colour depth */ + switch ((omap_lcd->palette[0] >> 12) & 7) { + case 1: + draw_line = draw_line_table2[surface_bits_per_pixel(surface)]; + bpp = 2; + break; + + case 2: + draw_line = draw_line_table4[surface_bits_per_pixel(surface)]; + bpp = 4; + break; + + case 3: + draw_line = draw_line_table8[surface_bits_per_pixel(surface)]; + bpp = 8; + break; + + case 4 ... 7: + if (!omap_lcd->tft) + draw_line = draw_line_table12[surface_bits_per_pixel(surface)]; + else + draw_line = draw_line_table16[surface_bits_per_pixel(surface)]; + bpp = 16; + break; + + default: + /* Unsupported at the moment. */ + return; + } + + /* Resolution */ + width = omap_lcd->width; + if (width != surface_width(surface) || + omap_lcd->height != surface_height(surface)) { + qemu_console_resize(omap_lcd->con, + omap_lcd->width, omap_lcd->height); + surface = qemu_console_surface(omap_lcd->con); + omap_lcd->invalidate = 1; + } + + if (omap_lcd->dma->current_frame == 0) + size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top; + else + size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top; + + if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) { + omap_lcd->sync_error = 1; + omap_lcd_interrupts(omap_lcd); + omap_lcd->enable = 0; + return; + } + + /* Content */ + frame_base = omap_lcd->dma->phys_framebuffer[ + omap_lcd->dma->current_frame] + frame_offset; + omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame; + if (omap_lcd->dma->interrupts & 1) + qemu_irq_raise(omap_lcd->dma->irq); + if (omap_lcd->dma->dual) + omap_lcd->dma->current_frame ^= 1; + + if (!surface_bits_per_pixel(surface)) { + return; + } + + first = 0; + height = omap_lcd->height; + if (omap_lcd->subpanel & (1 << 31)) { + if (omap_lcd->subpanel & (1 << 29)) + first = (omap_lcd->subpanel >> 16) & 0x3ff; + else + height = (omap_lcd->subpanel >> 16) & 0x3ff; + /* TODO: fill the rest of the panel with DPD */ + } + + step = width * bpp >> 3; + linesize = surface_stride(surface); + framebuffer_update_display(surface, omap_lcd->sysmem, + frame_base, width, height, + step, linesize, 0, + omap_lcd->invalidate, + draw_line, omap_lcd->palette, + &first, &last); + if (first >= 0) { + dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1); + } + omap_lcd->invalidate = 0; +} + +static void omap_ppm_save(const char *filename, uint8_t *data, + int w, int h, int linesize, Error **errp) +{ + FILE *f; + uint8_t *d, *d1; + unsigned int v; + int ret, y, x, bpp; + + f = fopen(filename, "wb"); + if (!f) { + error_setg(errp, "failed to open file '%s': %s", filename, + strerror(errno)); + return; + } + ret = fprintf(f, "P6\n%d %d\n%d\n", w, h, 255); + if (ret < 0) { + goto write_err; + } + d1 = data; + bpp = linesize / w; + for (y = 0; y < h; y ++) { + d = d1; + for (x = 0; x < w; x ++) { + v = *(uint32_t *) d; + switch (bpp) { + case 2: + ret = fputc((v >> 8) & 0xf8, f); + if (ret == EOF) { + goto write_err; + } + ret = fputc((v >> 3) & 0xfc, f); + if (ret == EOF) { + goto write_err; + } + ret = fputc((v << 3) & 0xf8, f); + if (ret == EOF) { + goto write_err; + } + break; + case 3: + case 4: + default: + ret = fputc((v >> 16) & 0xff, f); + if (ret == EOF) { + goto write_err; + } + ret = fputc((v >> 8) & 0xff, f); + if (ret == EOF) { + goto write_err; + } + ret = fputc((v) & 0xff, f); + if (ret == EOF) { + goto write_err; + } + break; + } + d += bpp; + } + d1 += linesize; + } +out: + fclose(f); + return; + +write_err: + error_setg(errp, "failed to write to file '%s': %s", filename, + strerror(errno)); + unlink(filename); + goto out; +} + +static void omap_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp) +{ + struct omap_lcd_panel_s *omap_lcd = opaque; + DisplaySurface *surface = qemu_console_surface(omap_lcd->con); + + omap_update_display(opaque); + if (omap_lcd && surface_data(surface)) + omap_ppm_save(filename, surface_data(surface), + omap_lcd->width, omap_lcd->height, + surface_stride(surface), errp); +} + +static void omap_invalidate_display(void *opaque) { + struct omap_lcd_panel_s *omap_lcd = opaque; + omap_lcd->invalidate = 1; +} + +static void omap_lcd_update(struct omap_lcd_panel_s *s) { + if (!s->enable) { + s->dma->current_frame = -1; + s->sync_error = 0; + if (s->plm != 1) + s->frame_done = 1; + omap_lcd_interrupts(s); + return; + } + + if (s->dma->current_frame == -1) { + s->frame_done = 0; + s->palette_done = 0; + s->dma->current_frame = 0; + } + + if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu, + s->dma->src_f1_top) || + !s->dma->mpu->port[ + s->dma->src].addr_valid(s->dma->mpu, + s->dma->src_f1_bottom) || + (s->dma->dual && + (!s->dma->mpu->port[ + s->dma->src].addr_valid(s->dma->mpu, + s->dma->src_f2_top) || + !s->dma->mpu->port[ + s->dma->src].addr_valid(s->dma->mpu, + s->dma->src_f2_bottom)))) { + s->dma->condition |= 1 << 2; + if (s->dma->interrupts & (1 << 1)) + qemu_irq_raise(s->dma->irq); + s->enable = 0; + return; + } + + s->dma->phys_framebuffer[0] = s->dma->src_f1_top; + s->dma->phys_framebuffer[1] = s->dma->src_f2_top; + + if (s->plm != 2 && !s->palette_done) { + cpu_physical_memory_read( + s->dma->phys_framebuffer[s->dma->current_frame], + (void *)s->palette, 0x200); + s->palette_done = 1; + omap_lcd_interrupts(s); + } +} + +static uint64_t omap_lcdc_read(void *opaque, hwaddr addr, + unsigned size) +{ + struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; + + switch (addr) { + case 0x00: /* LCD_CONTROL */ + return (s->tft << 23) | (s->plm << 20) | + (s->tft << 7) | (s->interrupts << 3) | + (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34; + + case 0x04: /* LCD_TIMING0 */ + return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f; + + case 0x08: /* LCD_TIMING1 */ + return (s->timing[1] << 10) | (s->height - 1); + + case 0x0c: /* LCD_TIMING2 */ + return s->timing[2] | 0xfc000000; + + case 0x10: /* LCD_STATUS */ + return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done; + + case 0x14: /* LCD_SUBPANEL */ + return s->subpanel; + + default: + break; + } + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_lcdc_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; + + switch (addr) { + case 0x00: /* LCD_CONTROL */ + s->plm = (value >> 20) & 3; + s->tft = (value >> 7) & 1; + s->interrupts = (value >> 3) & 3; + s->mono = (value >> 1) & 1; + s->ctrl = value & 0x01cff300; + if (s->enable != (value & 1)) { + s->enable = value & 1; + omap_lcd_update(s); + } + break; + + case 0x04: /* LCD_TIMING0 */ + s->timing[0] = value >> 10; + s->width = (value & 0x3ff) + 1; + break; + + case 0x08: /* LCD_TIMING1 */ + s->timing[1] = value >> 10; + s->height = (value & 0x3ff) + 1; + break; + + case 0x0c: /* LCD_TIMING2 */ + s->timing[2] = value; + break; + + case 0x10: /* LCD_STATUS */ + break; + + case 0x14: /* LCD_SUBPANEL */ + s->subpanel = value & 0xa1ffffff; + break; + + default: + OMAP_BAD_REG(addr); + } +} + +static const MemoryRegionOps omap_lcdc_ops = { + .read = omap_lcdc_read, + .write = omap_lcdc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +void omap_lcdc_reset(struct omap_lcd_panel_s *s) +{ + s->dma->current_frame = -1; + s->plm = 0; + s->tft = 0; + s->mono = 0; + s->enable = 0; + s->width = 0; + s->height = 0; + s->interrupts = 0; + s->timing[0] = 0; + s->timing[1] = 0; + s->timing[2] = 0; + s->subpanel = 0; + s->palette_done = 0; + s->frame_done = 0; + s->sync_error = 0; + s->invalidate = 1; + s->subpanel = 0; + s->ctrl = 0; +} + +struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, + hwaddr base, + qemu_irq irq, + struct omap_dma_lcd_channel_s *dma, + omap_clk clk) +{ + struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) + g_malloc0(sizeof(struct omap_lcd_panel_s)); + + s->irq = irq; + s->dma = dma; + s->sysmem = sysmem; + omap_lcdc_reset(s); + + memory_region_init_io(&s->iomem, &omap_lcdc_ops, s, "omap.lcdc", 0x100); + memory_region_add_subregion(sysmem, base, &s->iomem); + + s->con = graphic_console_init(omap_update_display, + omap_invalidate_display, + omap_screen_dump, NULL, s); + + return s; +} diff --git a/hw/display/pxa2xx_lcd.c b/hw/display/pxa2xx_lcd.c new file mode 100644 index 0000000000..ee59bc2de3 --- /dev/null +++ b/hw/display/pxa2xx_lcd.c @@ -0,0 +1,1058 @@ +/* + * Intel XScale PXA255/270 LCDC emulation. + * + * Copyright (c) 2006 Openedhand Ltd. + * Written by Andrzej Zaborowski + * + * This code is licensed under the GPLv2. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "hw/hw.h" +#include "ui/console.h" +#include "hw/arm/pxa.h" +#include "ui/pixel_ops.h" +/* FIXME: For graphic_rotate. Should probably be done in common code. */ +#include "sysemu/sysemu.h" +#include "hw/framebuffer.h" + +struct DMAChannel { + uint32_t branch; + uint8_t up; + uint8_t palette[1024]; + uint8_t pbuffer[1024]; + void (*redraw)(PXA2xxLCDState *s, hwaddr addr, + int *miny, int *maxy); + + uint32_t descriptor; + uint32_t source; + uint32_t id; + uint32_t command; +}; + +struct PXA2xxLCDState { + MemoryRegion *sysmem; + MemoryRegion iomem; + qemu_irq irq; + int irqlevel; + + int invalidated; + QemuConsole *con; + drawfn *line_fn[2]; + int dest_width; + int xres, yres; + int pal_for; + int transp; + enum { + pxa_lcdc_2bpp = 1, + pxa_lcdc_4bpp = 2, + pxa_lcdc_8bpp = 3, + pxa_lcdc_16bpp = 4, + pxa_lcdc_18bpp = 5, + pxa_lcdc_18pbpp = 6, + pxa_lcdc_19bpp = 7, + pxa_lcdc_19pbpp = 8, + pxa_lcdc_24bpp = 9, + pxa_lcdc_25bpp = 10, + } bpp; + + uint32_t control[6]; + uint32_t status[2]; + uint32_t ovl1c[2]; + uint32_t ovl2c[2]; + uint32_t ccr; + uint32_t cmdcr; + uint32_t trgbr; + uint32_t tcr; + uint32_t liidr; + uint8_t bscntr; + + struct DMAChannel dma_ch[7]; + + qemu_irq vsync_cb; + int orientation; +}; + +typedef struct QEMU_PACKED { + uint32_t fdaddr; + uint32_t fsaddr; + uint32_t fidr; + uint32_t ldcmd; +} PXAFrameDescriptor; + +#define LCCR0 0x000 /* LCD Controller Control register 0 */ +#define LCCR1 0x004 /* LCD Controller Control register 1 */ +#define LCCR2 0x008 /* LCD Controller Control register 2 */ +#define LCCR3 0x00c /* LCD Controller Control register 3 */ +#define LCCR4 0x010 /* LCD Controller Control register 4 */ +#define LCCR5 0x014 /* LCD Controller Control register 5 */ + +#define FBR0 0x020 /* DMA Channel 0 Frame Branch register */ +#define FBR1 0x024 /* DMA Channel 1 Frame Branch register */ +#define FBR2 0x028 /* DMA Channel 2 Frame Branch register */ +#define FBR3 0x02c /* DMA Channel 3 Frame Branch register */ +#define FBR4 0x030 /* DMA Channel 4 Frame Branch register */ +#define FBR5 0x110 /* DMA Channel 5 Frame Branch register */ +#define FBR6 0x114 /* DMA Channel 6 Frame Branch register */ + +#define LCSR1 0x034 /* LCD Controller Status register 1 */ +#define LCSR0 0x038 /* LCD Controller Status register 0 */ +#define LIIDR 0x03c /* LCD Controller Interrupt ID register */ + +#define TRGBR 0x040 /* TMED RGB Seed register */ +#define TCR 0x044 /* TMED Control register */ + +#define OVL1C1 0x050 /* Overlay 1 Control register 1 */ +#define OVL1C2 0x060 /* Overlay 1 Control register 2 */ +#define OVL2C1 0x070 /* Overlay 2 Control register 1 */ +#define OVL2C2 0x080 /* Overlay 2 Control register 2 */ +#define CCR 0x090 /* Cursor Control register */ + +#define CMDCR 0x100 /* Command Control register */ +#define PRSR 0x104 /* Panel Read Status register */ + +#define PXA_LCDDMA_CHANS 7 +#define DMA_FDADR 0x00 /* Frame Descriptor Address register */ +#define DMA_FSADR 0x04 /* Frame Source Address register */ +#define DMA_FIDR 0x08 /* Frame ID register */ +#define DMA_LDCMD 0x0c /* Command register */ + +/* LCD Buffer Strength Control register */ +#define BSCNTR 0x04000054 + +/* Bitfield masks */ +#define LCCR0_ENB (1 << 0) +#define LCCR0_CMS (1 << 1) +#define LCCR0_SDS (1 << 2) +#define LCCR0_LDM (1 << 3) +#define LCCR0_SOFM0 (1 << 4) +#define LCCR0_IUM (1 << 5) +#define LCCR0_EOFM0 (1 << 6) +#define LCCR0_PAS (1 << 7) +#define LCCR0_DPD (1 << 9) +#define LCCR0_DIS (1 << 10) +#define LCCR0_QDM (1 << 11) +#define LCCR0_PDD (0xff << 12) +#define LCCR0_BSM0 (1 << 20) +#define LCCR0_OUM (1 << 21) +#define LCCR0_LCDT (1 << 22) +#define LCCR0_RDSTM (1 << 23) +#define LCCR0_CMDIM (1 << 24) +#define LCCR0_OUC (1 << 25) +#define LCCR0_LDDALT (1 << 26) +#define LCCR1_PPL(x) ((x) & 0x3ff) +#define LCCR2_LPP(x) ((x) & 0x3ff) +#define LCCR3_API (15 << 16) +#define LCCR3_BPP(x) ((((x) >> 24) & 7) | (((x) >> 26) & 8)) +#define LCCR3_PDFOR(x) (((x) >> 30) & 3) +#define LCCR4_K1(x) (((x) >> 0) & 7) +#define LCCR4_K2(x) (((x) >> 3) & 7) +#define LCCR4_K3(x) (((x) >> 6) & 7) +#define LCCR4_PALFOR(x) (((x) >> 15) & 3) +#define LCCR5_SOFM(ch) (1 << (ch - 1)) +#define LCCR5_EOFM(ch) (1 << (ch + 7)) +#define LCCR5_BSM(ch) (1 << (ch + 15)) +#define LCCR5_IUM(ch) (1 << (ch + 23)) +#define OVLC1_EN (1 << 31) +#define CCR_CEN (1 << 31) +#define FBR_BRA (1 << 0) +#define FBR_BINT (1 << 1) +#define FBR_SRCADDR (0xfffffff << 4) +#define LCSR0_LDD (1 << 0) +#define LCSR0_SOF0 (1 << 1) +#define LCSR0_BER (1 << 2) +#define LCSR0_ABC (1 << 3) +#define LCSR0_IU0 (1 << 4) +#define LCSR0_IU1 (1 << 5) +#define LCSR0_OU (1 << 6) +#define LCSR0_QD (1 << 7) +#define LCSR0_EOF0 (1 << 8) +#define LCSR0_BS0 (1 << 9) +#define LCSR0_SINT (1 << 10) +#define LCSR0_RDST (1 << 11) +#define LCSR0_CMDINT (1 << 12) +#define LCSR0_BERCH(x) (((x) & 7) << 28) +#define LCSR1_SOF(ch) (1 << (ch - 1)) +#define LCSR1_EOF(ch) (1 << (ch + 7)) +#define LCSR1_BS(ch) (1 << (ch + 15)) +#define LCSR1_IU(ch) (1 << (ch + 23)) +#define LDCMD_LENGTH(x) ((x) & 0x001ffffc) +#define LDCMD_EOFINT (1 << 21) +#define LDCMD_SOFINT (1 << 22) +#define LDCMD_PAL (1 << 26) + +/* Route internal interrupt lines to the global IC */ +static void pxa2xx_lcdc_int_update(PXA2xxLCDState *s) +{ + int level = 0; + level |= (s->status[0] & LCSR0_LDD) && !(s->control[0] & LCCR0_LDM); + level |= (s->status[0] & LCSR0_SOF0) && !(s->control[0] & LCCR0_SOFM0); + level |= (s->status[0] & LCSR0_IU0) && !(s->control[0] & LCCR0_IUM); + level |= (s->status[0] & LCSR0_IU1) && !(s->control[5] & LCCR5_IUM(1)); + level |= (s->status[0] & LCSR0_OU) && !(s->control[0] & LCCR0_OUM); + level |= (s->status[0] & LCSR0_QD) && !(s->control[0] & LCCR0_QDM); + level |= (s->status[0] & LCSR0_EOF0) && !(s->control[0] & LCCR0_EOFM0); + level |= (s->status[0] & LCSR0_BS0) && !(s->control[0] & LCCR0_BSM0); + level |= (s->status[0] & LCSR0_RDST) && !(s->control[0] & LCCR0_RDSTM); + level |= (s->status[0] & LCSR0_CMDINT) && !(s->control[0] & LCCR0_CMDIM); + level |= (s->status[1] & ~s->control[5]); + + qemu_set_irq(s->irq, !!level); + s->irqlevel = level; +} + +/* Set Branch Status interrupt high and poke associated registers */ +static inline void pxa2xx_dma_bs_set(PXA2xxLCDState *s, int ch) +{ + int unmasked; + if (ch == 0) { + s->status[0] |= LCSR0_BS0; + unmasked = !(s->control[0] & LCCR0_BSM0); + } else { + s->status[1] |= LCSR1_BS(ch); + unmasked = !(s->control[5] & LCCR5_BSM(ch)); + } + + if (unmasked) { + if (s->irqlevel) + s->status[0] |= LCSR0_SINT; + else + s->liidr = s->dma_ch[ch].id; + } +} + +/* Set Start Of Frame Status interrupt high and poke associated registers */ +static inline void pxa2xx_dma_sof_set(PXA2xxLCDState *s, int ch) +{ + int unmasked; + if (!(s->dma_ch[ch].command & LDCMD_SOFINT)) + return; + + if (ch == 0) { + s->status[0] |= LCSR0_SOF0; + unmasked = !(s->control[0] & LCCR0_SOFM0); + } else { + s->status[1] |= LCSR1_SOF(ch); + unmasked = !(s->control[5] & LCCR5_SOFM(ch)); + } + + if (unmasked) { + if (s->irqlevel) + s->status[0] |= LCSR0_SINT; + else + s->liidr = s->dma_ch[ch].id; + } +} + +/* Set End Of Frame Status interrupt high and poke associated registers */ +static inline void pxa2xx_dma_eof_set(PXA2xxLCDState *s, int ch) +{ + int unmasked; + if (!(s->dma_ch[ch].command & LDCMD_EOFINT)) + return; + + if (ch == 0) { + s->status[0] |= LCSR0_EOF0; + unmasked = !(s->control[0] & LCCR0_EOFM0); + } else { + s->status[1] |= LCSR1_EOF(ch); + unmasked = !(s->control[5] & LCCR5_EOFM(ch)); + } + + if (unmasked) { + if (s->irqlevel) + s->status[0] |= LCSR0_SINT; + else + s->liidr = s->dma_ch[ch].id; + } +} + +/* Set Bus Error Status interrupt high and poke associated registers */ +static inline void pxa2xx_dma_ber_set(PXA2xxLCDState *s, int ch) +{ + s->status[0] |= LCSR0_BERCH(ch) | LCSR0_BER; + if (s->irqlevel) + s->status[0] |= LCSR0_SINT; + else + s->liidr = s->dma_ch[ch].id; +} + +/* Set Read Status interrupt high and poke associated registers */ +static inline void pxa2xx_dma_rdst_set(PXA2xxLCDState *s) +{ + s->status[0] |= LCSR0_RDST; + if (s->irqlevel && !(s->control[0] & LCCR0_RDSTM)) + s->status[0] |= LCSR0_SINT; +} + +/* Load new Frame Descriptors from DMA */ +static void pxa2xx_descriptor_load(PXA2xxLCDState *s) +{ + PXAFrameDescriptor desc; + hwaddr descptr; + int i; + + for (i = 0; i < PXA_LCDDMA_CHANS; i ++) { + s->dma_ch[i].source = 0; + + if (!s->dma_ch[i].up) + continue; + + if (s->dma_ch[i].branch & FBR_BRA) { + descptr = s->dma_ch[i].branch & FBR_SRCADDR; + if (s->dma_ch[i].branch & FBR_BINT) + pxa2xx_dma_bs_set(s, i); + s->dma_ch[i].branch &= ~FBR_BRA; + } else + descptr = s->dma_ch[i].descriptor; + + if (!((descptr >= PXA2XX_SDRAM_BASE && descptr + + sizeof(desc) <= PXA2XX_SDRAM_BASE + ram_size) || + (descptr >= PXA2XX_INTERNAL_BASE && descptr + sizeof(desc) <= + PXA2XX_INTERNAL_BASE + PXA2XX_INTERNAL_SIZE))) { + continue; + } + + cpu_physical_memory_read(descptr, (void *)&desc, sizeof(desc)); + s->dma_ch[i].descriptor = tswap32(desc.fdaddr); + s->dma_ch[i].source = tswap32(desc.fsaddr); + s->dma_ch[i].id = tswap32(desc.fidr); + s->dma_ch[i].command = tswap32(desc.ldcmd); + } +} + +static uint64_t pxa2xx_lcdc_read(void *opaque, hwaddr offset, + unsigned size) +{ + PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; + int ch; + + switch (offset) { + case LCCR0: + return s->control[0]; + case LCCR1: + return s->control[1]; + case LCCR2: + return s->control[2]; + case LCCR3: + return s->control[3]; + case LCCR4: + return s->control[4]; + case LCCR5: + return s->control[5]; + + case OVL1C1: + return s->ovl1c[0]; + case OVL1C2: + return s->ovl1c[1]; + case OVL2C1: + return s->ovl2c[0]; + case OVL2C2: + return s->ovl2c[1]; + + case CCR: + return s->ccr; + + case CMDCR: + return s->cmdcr; + + case TRGBR: + return s->trgbr; + case TCR: + return s->tcr; + + case 0x200 ... 0x1000: /* DMA per-channel registers */ + ch = (offset - 0x200) >> 4; + if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS)) + goto fail; + + switch (offset & 0xf) { + case DMA_FDADR: + return s->dma_ch[ch].descriptor; + case DMA_FSADR: + return s->dma_ch[ch].source; + case DMA_FIDR: + return s->dma_ch[ch].id; + case DMA_LDCMD: + return s->dma_ch[ch].command; + default: + goto fail; + } + + case FBR0: + return s->dma_ch[0].branch; + case FBR1: + return s->dma_ch[1].branch; + case FBR2: + return s->dma_ch[2].branch; + case FBR3: + return s->dma_ch[3].branch; + case FBR4: + return s->dma_ch[4].branch; + case FBR5: + return s->dma_ch[5].branch; + case FBR6: + return s->dma_ch[6].branch; + + case BSCNTR: + return s->bscntr; + + case PRSR: + return 0; + + case LCSR0: + return s->status[0]; + case LCSR1: + return s->status[1]; + case LIIDR: + return s->liidr; + + default: + fail: + hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); + } + + return 0; +} + +static void pxa2xx_lcdc_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; + int ch; + + switch (offset) { + case LCCR0: + /* ACK Quick Disable done */ + if ((s->control[0] & LCCR0_ENB) && !(value & LCCR0_ENB)) + s->status[0] |= LCSR0_QD; + + if (!(s->control[0] & LCCR0_LCDT) && (value & LCCR0_LCDT)) + printf("%s: internal frame buffer unsupported\n", __FUNCTION__); + + if ((s->control[3] & LCCR3_API) && + (value & LCCR0_ENB) && !(value & LCCR0_LCDT)) + s->status[0] |= LCSR0_ABC; + + s->control[0] = value & 0x07ffffff; + pxa2xx_lcdc_int_update(s); + + s->dma_ch[0].up = !!(value & LCCR0_ENB); + s->dma_ch[1].up = (s->ovl1c[0] & OVLC1_EN) || (value & LCCR0_SDS); + break; + + case LCCR1: + s->control[1] = value; + break; + + case LCCR2: + s->control[2] = value; + break; + + case LCCR3: + s->control[3] = value & 0xefffffff; + s->bpp = LCCR3_BPP(value); + break; + + case LCCR4: + s->control[4] = value & 0x83ff81ff; + break; + + case LCCR5: + s->control[5] = value & 0x3f3f3f3f; + break; + + case OVL1C1: + if (!(s->ovl1c[0] & OVLC1_EN) && (value & OVLC1_EN)) + printf("%s: Overlay 1 not supported\n", __FUNCTION__); + + s->ovl1c[0] = value & 0x80ffffff; + s->dma_ch[1].up = (value & OVLC1_EN) || (s->control[0] & LCCR0_SDS); + break; + + case OVL1C2: + s->ovl1c[1] = value & 0x000fffff; + break; + + case OVL2C1: + if (!(s->ovl2c[0] & OVLC1_EN) && (value & OVLC1_EN)) + printf("%s: Overlay 2 not supported\n", __FUNCTION__); + + s->ovl2c[0] = value & 0x80ffffff; + s->dma_ch[2].up = !!(value & OVLC1_EN); + s->dma_ch[3].up = !!(value & OVLC1_EN); + s->dma_ch[4].up = !!(value & OVLC1_EN); + break; + + case OVL2C2: + s->ovl2c[1] = value & 0x007fffff; + break; + + case CCR: + if (!(s->ccr & CCR_CEN) && (value & CCR_CEN)) + printf("%s: Hardware cursor unimplemented\n", __FUNCTION__); + + s->ccr = value & 0x81ffffe7; + s->dma_ch[5].up = !!(value & CCR_CEN); + break; + + case CMDCR: + s->cmdcr = value & 0xff; + break; + + case TRGBR: + s->trgbr = value & 0x00ffffff; + break; + + case TCR: + s->tcr = value & 0x7fff; + break; + + case 0x200 ... 0x1000: /* DMA per-channel registers */ + ch = (offset - 0x200) >> 4; + if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS)) + goto fail; + + switch (offset & 0xf) { + case DMA_FDADR: + s->dma_ch[ch].descriptor = value & 0xfffffff0; + break; + + default: + goto fail; + } + break; + + case FBR0: + s->dma_ch[0].branch = value & 0xfffffff3; + break; + case FBR1: + s->dma_ch[1].branch = value & 0xfffffff3; + break; + case FBR2: + s->dma_ch[2].branch = value & 0xfffffff3; + break; + case FBR3: + s->dma_ch[3].branch = value & 0xfffffff3; + break; + case FBR4: + s->dma_ch[4].branch = value & 0xfffffff3; + break; + case FBR5: + s->dma_ch[5].branch = value & 0xfffffff3; + break; + case FBR6: + s->dma_ch[6].branch = value & 0xfffffff3; + break; + + case BSCNTR: + s->bscntr = value & 0xf; + break; + + case PRSR: + break; + + case LCSR0: + s->status[0] &= ~(value & 0xfff); + if (value & LCSR0_BER) + s->status[0] &= ~LCSR0_BERCH(7); + break; + + case LCSR1: + s->status[1] &= ~(value & 0x3e3f3f); + break; + + default: + fail: + hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); + } +} + +static const MemoryRegionOps pxa2xx_lcdc_ops = { + .read = pxa2xx_lcdc_read, + .write = pxa2xx_lcdc_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/* Load new palette for a given DMA channel, convert to internal format */ +static void pxa2xx_palette_parse(PXA2xxLCDState *s, int ch, int bpp) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int i, n, format, r, g, b, alpha; + uint32_t *dest; + uint8_t *src; + s->pal_for = LCCR4_PALFOR(s->control[4]); + format = s->pal_for; + + switch (bpp) { + case pxa_lcdc_2bpp: + n = 4; + break; + case pxa_lcdc_4bpp: + n = 16; + break; + case pxa_lcdc_8bpp: + n = 256; + break; + default: + format = 0; + return; + } + + src = (uint8_t *) s->dma_ch[ch].pbuffer; + dest = (uint32_t *) s->dma_ch[ch].palette; + alpha = r = g = b = 0; + + for (i = 0; i < n; i ++) { + switch (format) { + case 0: /* 16 bpp, no transparency */ + alpha = 0; + if (s->control[0] & LCCR0_CMS) { + r = g = b = *(uint16_t *) src & 0xff; + } + else { + r = (*(uint16_t *) src & 0xf800) >> 8; + g = (*(uint16_t *) src & 0x07e0) >> 3; + b = (*(uint16_t *) src & 0x001f) << 3; + } + src += 2; + break; + case 1: /* 16 bpp plus transparency */ + alpha = *(uint16_t *) src & (1 << 24); + if (s->control[0] & LCCR0_CMS) + r = g = b = *(uint16_t *) src & 0xff; + else { + r = (*(uint16_t *) src & 0xf800) >> 8; + g = (*(uint16_t *) src & 0x07e0) >> 3; + b = (*(uint16_t *) src & 0x001f) << 3; + } + src += 2; + break; + case 2: /* 18 bpp plus transparency */ + alpha = *(uint32_t *) src & (1 << 24); + if (s->control[0] & LCCR0_CMS) + r = g = b = *(uint32_t *) src & 0xff; + else { + r = (*(uint32_t *) src & 0xf80000) >> 16; + g = (*(uint32_t *) src & 0x00fc00) >> 8; + b = (*(uint32_t *) src & 0x0000f8); + } + src += 4; + break; + case 3: /* 24 bpp plus transparency */ + alpha = *(uint32_t *) src & (1 << 24); + if (s->control[0] & LCCR0_CMS) + r = g = b = *(uint32_t *) src & 0xff; + else { + r = (*(uint32_t *) src & 0xff0000) >> 16; + g = (*(uint32_t *) src & 0x00ff00) >> 8; + b = (*(uint32_t *) src & 0x0000ff); + } + src += 4; + break; + } + switch (surface_bits_per_pixel(surface)) { + case 8: + *dest = rgb_to_pixel8(r, g, b) | alpha; + break; + case 15: + *dest = rgb_to_pixel15(r, g, b) | alpha; + break; + case 16: + *dest = rgb_to_pixel16(r, g, b) | alpha; + break; + case 24: + *dest = rgb_to_pixel24(r, g, b) | alpha; + break; + case 32: + *dest = rgb_to_pixel32(r, g, b) | alpha; + break; + } + dest ++; + } +} + +static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s, + hwaddr addr, int *miny, int *maxy) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int src_width, dest_width; + drawfn fn = NULL; + if (s->dest_width) + fn = s->line_fn[s->transp][s->bpp]; + if (!fn) + return; + + src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ + if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) + src_width *= 3; + else if (s->bpp > pxa_lcdc_16bpp) + src_width *= 4; + else if (s->bpp > pxa_lcdc_8bpp) + src_width *= 2; + + dest_width = s->xres * s->dest_width; + *miny = 0; + framebuffer_update_display(surface, s->sysmem, + addr, s->xres, s->yres, + src_width, dest_width, s->dest_width, + s->invalidated, + fn, s->dma_ch[0].palette, miny, maxy); +} + +static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s, + hwaddr addr, int *miny, int *maxy) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int src_width, dest_width; + drawfn fn = NULL; + if (s->dest_width) + fn = s->line_fn[s->transp][s->bpp]; + if (!fn) + return; + + src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ + if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) + src_width *= 3; + else if (s->bpp > pxa_lcdc_16bpp) + src_width *= 4; + else if (s->bpp > pxa_lcdc_8bpp) + src_width *= 2; + + dest_width = s->yres * s->dest_width; + *miny = 0; + framebuffer_update_display(surface, s->sysmem, + addr, s->xres, s->yres, + src_width, s->dest_width, -dest_width, + s->invalidated, + fn, s->dma_ch[0].palette, + miny, maxy); +} + +static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s, + hwaddr addr, int *miny, int *maxy) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int src_width, dest_width; + drawfn fn = NULL; + if (s->dest_width) { + fn = s->line_fn[s->transp][s->bpp]; + } + if (!fn) { + return; + } + + src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ + if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) { + src_width *= 3; + } else if (s->bpp > pxa_lcdc_16bpp) { + src_width *= 4; + } else if (s->bpp > pxa_lcdc_8bpp) { + src_width *= 2; + } + + dest_width = s->xres * s->dest_width; + *miny = 0; + framebuffer_update_display(surface, s->sysmem, + addr, s->xres, s->yres, + src_width, -dest_width, -s->dest_width, + s->invalidated, + fn, s->dma_ch[0].palette, miny, maxy); +} + +static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s, + hwaddr addr, int *miny, int *maxy) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int src_width, dest_width; + drawfn fn = NULL; + if (s->dest_width) { + fn = s->line_fn[s->transp][s->bpp]; + } + if (!fn) { + return; + } + + src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ + if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) { + src_width *= 3; + } else if (s->bpp > pxa_lcdc_16bpp) { + src_width *= 4; + } else if (s->bpp > pxa_lcdc_8bpp) { + src_width *= 2; + } + + dest_width = s->yres * s->dest_width; + *miny = 0; + framebuffer_update_display(surface, s->sysmem, + addr, s->xres, s->yres, + src_width, -s->dest_width, dest_width, + s->invalidated, + fn, s->dma_ch[0].palette, + miny, maxy); +} + +static void pxa2xx_lcdc_resize(PXA2xxLCDState *s) +{ + int width, height; + if (!(s->control[0] & LCCR0_ENB)) + return; + + width = LCCR1_PPL(s->control[1]) + 1; + height = LCCR2_LPP(s->control[2]) + 1; + + if (width != s->xres || height != s->yres) { + if (s->orientation == 90 || s->orientation == 270) { + qemu_console_resize(s->con, height, width); + } else { + qemu_console_resize(s->con, width, height); + } + s->invalidated = 1; + s->xres = width; + s->yres = height; + } +} + +static void pxa2xx_update_display(void *opaque) +{ + PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; + hwaddr fbptr; + int miny, maxy; + int ch; + if (!(s->control[0] & LCCR0_ENB)) + return; + + pxa2xx_descriptor_load(s); + + pxa2xx_lcdc_resize(s); + miny = s->yres; + maxy = 0; + s->transp = s->dma_ch[2].up || s->dma_ch[3].up; + /* Note: With overlay planes the order depends on LCCR0 bit 25. */ + for (ch = 0; ch < PXA_LCDDMA_CHANS; ch ++) + if (s->dma_ch[ch].up) { + if (!s->dma_ch[ch].source) { + pxa2xx_dma_ber_set(s, ch); + continue; + } + fbptr = s->dma_ch[ch].source; + if (!((fbptr >= PXA2XX_SDRAM_BASE && + fbptr <= PXA2XX_SDRAM_BASE + ram_size) || + (fbptr >= PXA2XX_INTERNAL_BASE && + fbptr <= PXA2XX_INTERNAL_BASE + PXA2XX_INTERNAL_SIZE))) { + pxa2xx_dma_ber_set(s, ch); + continue; + } + + if (s->dma_ch[ch].command & LDCMD_PAL) { + cpu_physical_memory_read(fbptr, s->dma_ch[ch].pbuffer, + MAX(LDCMD_LENGTH(s->dma_ch[ch].command), + sizeof(s->dma_ch[ch].pbuffer))); + pxa2xx_palette_parse(s, ch, s->bpp); + } else { + /* Do we need to reparse palette */ + if (LCCR4_PALFOR(s->control[4]) != s->pal_for) + pxa2xx_palette_parse(s, ch, s->bpp); + + /* ACK frame start */ + pxa2xx_dma_sof_set(s, ch); + + s->dma_ch[ch].redraw(s, fbptr, &miny, &maxy); + s->invalidated = 0; + + /* ACK frame completed */ + pxa2xx_dma_eof_set(s, ch); + } + } + + if (s->control[0] & LCCR0_DIS) { + /* ACK last frame completed */ + s->control[0] &= ~LCCR0_ENB; + s->status[0] |= LCSR0_LDD; + } + + if (miny >= 0) { + switch (s->orientation) { + case 0: + dpy_gfx_update(s->con, 0, miny, s->xres, maxy - miny + 1); + break; + case 90: + dpy_gfx_update(s->con, miny, 0, maxy - miny + 1, s->xres); + break; + case 180: + maxy = s->yres - maxy - 1; + miny = s->yres - miny - 1; + dpy_gfx_update(s->con, 0, maxy, s->xres, miny - maxy + 1); + break; + case 270: + maxy = s->yres - maxy - 1; + miny = s->yres - miny - 1; + dpy_gfx_update(s->con, maxy, 0, miny - maxy + 1, s->xres); + break; + } + } + pxa2xx_lcdc_int_update(s); + + qemu_irq_raise(s->vsync_cb); +} + +static void pxa2xx_invalidate_display(void *opaque) +{ + PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; + s->invalidated = 1; +} + +static void pxa2xx_lcdc_orientation(void *opaque, int angle) +{ + PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; + + switch (angle) { + case 0: + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot0; + break; + case 90: + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot90; + break; + case 180: + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot180; + break; + case 270: + s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot270; + break; + } + + s->orientation = angle; + s->xres = s->yres = -1; + pxa2xx_lcdc_resize(s); +} + +static const VMStateDescription vmstate_dma_channel = { + .name = "dma_channel", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(branch, struct DMAChannel), + VMSTATE_UINT8(up, struct DMAChannel), + VMSTATE_BUFFER(pbuffer, struct DMAChannel), + VMSTATE_UINT32(descriptor, struct DMAChannel), + VMSTATE_UINT32(source, struct DMAChannel), + VMSTATE_UINT32(id, struct DMAChannel), + VMSTATE_UINT32(command, struct DMAChannel), + VMSTATE_END_OF_LIST() + } +}; + +static int pxa2xx_lcdc_post_load(void *opaque, int version_id) +{ + PXA2xxLCDState *s = opaque; + + s->bpp = LCCR3_BPP(s->control[3]); + s->xres = s->yres = s->pal_for = -1; + + return 0; +} + +static const VMStateDescription vmstate_pxa2xx_lcdc = { + .name = "pxa2xx_lcdc", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = pxa2xx_lcdc_post_load, + .fields = (VMStateField[]) { + VMSTATE_INT32(irqlevel, PXA2xxLCDState), + VMSTATE_INT32(transp, PXA2xxLCDState), + VMSTATE_UINT32_ARRAY(control, PXA2xxLCDState, 6), + VMSTATE_UINT32_ARRAY(status, PXA2xxLCDState, 2), + VMSTATE_UINT32_ARRAY(ovl1c, PXA2xxLCDState, 2), + VMSTATE_UINT32_ARRAY(ovl2c, PXA2xxLCDState, 2), + VMSTATE_UINT32(ccr, PXA2xxLCDState), + VMSTATE_UINT32(cmdcr, PXA2xxLCDState), + VMSTATE_UINT32(trgbr, PXA2xxLCDState), + VMSTATE_UINT32(tcr, PXA2xxLCDState), + VMSTATE_UINT32(liidr, PXA2xxLCDState), + VMSTATE_UINT8(bscntr, PXA2xxLCDState), + VMSTATE_STRUCT_ARRAY(dma_ch, PXA2xxLCDState, 7, 0, + vmstate_dma_channel, struct DMAChannel), + VMSTATE_END_OF_LIST() + } +}; + +#define BITS 8 +#include "hw/pxa2xx_template.h" +#define BITS 15 +#include "hw/pxa2xx_template.h" +#define BITS 16 +#include "hw/pxa2xx_template.h" +#define BITS 24 +#include "hw/pxa2xx_template.h" +#define BITS 32 +#include "hw/pxa2xx_template.h" + +PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem, + hwaddr base, qemu_irq irq) +{ + PXA2xxLCDState *s; + DisplaySurface *surface; + + s = (PXA2xxLCDState *) g_malloc0(sizeof(PXA2xxLCDState)); + s->invalidated = 1; + s->irq = irq; + s->sysmem = sysmem; + + pxa2xx_lcdc_orientation(s, graphic_rotate); + + memory_region_init_io(&s->iomem, &pxa2xx_lcdc_ops, s, + "pxa2xx-lcd-controller", 0x00100000); + memory_region_add_subregion(sysmem, base, &s->iomem); + + s->con = graphic_console_init(pxa2xx_update_display, + pxa2xx_invalidate_display, + NULL, NULL, s); + surface = qemu_console_surface(s->con); + + switch (surface_bits_per_pixel(surface)) { + case 0: + s->dest_width = 0; + break; + case 8: + s->line_fn[0] = pxa2xx_draw_fn_8; + s->line_fn[1] = pxa2xx_draw_fn_8t; + s->dest_width = 1; + break; + case 15: + s->line_fn[0] = pxa2xx_draw_fn_15; + s->line_fn[1] = pxa2xx_draw_fn_15t; + s->dest_width = 2; + break; + case 16: + s->line_fn[0] = pxa2xx_draw_fn_16; + s->line_fn[1] = pxa2xx_draw_fn_16t; + s->dest_width = 2; + break; + case 24: + s->line_fn[0] = pxa2xx_draw_fn_24; + s->line_fn[1] = pxa2xx_draw_fn_24t; + s->dest_width = 3; + break; + case 32: + s->line_fn[0] = pxa2xx_draw_fn_32; + s->line_fn[1] = pxa2xx_draw_fn_32t; + s->dest_width = 4; + break; + default: + fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__); + exit(1); + } + + vmstate_register(NULL, 0, &vmstate_pxa2xx_lcdc, s); + + return s; +} + +void pxa2xx_lcd_vsync_notifier(PXA2xxLCDState *s, qemu_irq handler) +{ + s->vsync_cb = handler; +} diff --git a/hw/display/qxl-logger.c b/hw/display/qxl-logger.c new file mode 100644 index 0000000000..84f9aa1eda --- /dev/null +++ b/hw/display/qxl-logger.c @@ -0,0 +1,275 @@ +/* + * qxl command logging -- for debug purposes + * + * Copyright (C) 2010 Red Hat, Inc. + * + * maintained by Gerd Hoffmann + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include "qemu/timer.h" +#include "hw/qxl.h" + +static const char *qxl_type[] = { + [ QXL_CMD_NOP ] = "nop", + [ QXL_CMD_DRAW ] = "draw", + [ QXL_CMD_UPDATE ] = "update", + [ QXL_CMD_CURSOR ] = "cursor", + [ QXL_CMD_MESSAGE ] = "message", + [ QXL_CMD_SURFACE ] = "surface", +}; + +static const char *qxl_draw_type[] = { + [ QXL_DRAW_NOP ] = "nop", + [ QXL_DRAW_FILL ] = "fill", + [ QXL_DRAW_OPAQUE ] = "opaque", + [ QXL_DRAW_COPY ] = "copy", + [ QXL_COPY_BITS ] = "copy-bits", + [ QXL_DRAW_BLEND ] = "blend", + [ QXL_DRAW_BLACKNESS ] = "blackness", + [ QXL_DRAW_WHITENESS ] = "whitemess", + [ QXL_DRAW_INVERS ] = "invers", + [ QXL_DRAW_ROP3 ] = "rop3", + [ QXL_DRAW_STROKE ] = "stroke", + [ QXL_DRAW_TEXT ] = "text", + [ QXL_DRAW_TRANSPARENT ] = "transparent", + [ QXL_DRAW_ALPHA_BLEND ] = "alpha-blend", +}; + +static const char *qxl_draw_effect[] = { + [ QXL_EFFECT_BLEND ] = "blend", + [ QXL_EFFECT_OPAQUE ] = "opaque", + [ QXL_EFFECT_REVERT_ON_DUP ] = "revert-on-dup", + [ QXL_EFFECT_BLACKNESS_ON_DUP ] = "blackness-on-dup", + [ QXL_EFFECT_WHITENESS_ON_DUP ] = "whiteness-on-dup", + [ QXL_EFFECT_NOP_ON_DUP ] = "nop-on-dup", + [ QXL_EFFECT_NOP ] = "nop", + [ QXL_EFFECT_OPAQUE_BRUSH ] = "opaque-brush", +}; + +static const char *qxl_surface_cmd[] = { + [ QXL_SURFACE_CMD_CREATE ] = "create", + [ QXL_SURFACE_CMD_DESTROY ] = "destroy", +}; + +static const char *spice_surface_fmt[] = { + [ SPICE_SURFACE_FMT_INVALID ] = "invalid", + [ SPICE_SURFACE_FMT_1_A ] = "alpha/1", + [ SPICE_SURFACE_FMT_8_A ] = "alpha/8", + [ SPICE_SURFACE_FMT_16_555 ] = "555/16", + [ SPICE_SURFACE_FMT_16_565 ] = "565/16", + [ SPICE_SURFACE_FMT_32_xRGB ] = "xRGB/32", + [ SPICE_SURFACE_FMT_32_ARGB ] = "ARGB/32", +}; + +static const char *qxl_cursor_cmd[] = { + [ QXL_CURSOR_SET ] = "set", + [ QXL_CURSOR_MOVE ] = "move", + [ QXL_CURSOR_HIDE ] = "hide", + [ QXL_CURSOR_TRAIL ] = "trail", +}; + +static const char *spice_cursor_type[] = { + [ SPICE_CURSOR_TYPE_ALPHA ] = "alpha", + [ SPICE_CURSOR_TYPE_MONO ] = "mono", + [ SPICE_CURSOR_TYPE_COLOR4 ] = "color4", + [ SPICE_CURSOR_TYPE_COLOR8 ] = "color8", + [ SPICE_CURSOR_TYPE_COLOR16 ] = "color16", + [ SPICE_CURSOR_TYPE_COLOR24 ] = "color24", + [ SPICE_CURSOR_TYPE_COLOR32 ] = "color32", +}; + +static const char *qxl_v2n(const char *n[], size_t l, int v) +{ + if (v >= l || !n[v]) { + return "???"; + } + return n[v]; +} +#define qxl_name(_list, _value) qxl_v2n(_list, ARRAY_SIZE(_list), _value) + +static int qxl_log_image(PCIQXLDevice *qxl, QXLPHYSICAL addr, int group_id) +{ + QXLImage *image; + QXLImageDescriptor *desc; + + image = qxl_phys2virt(qxl, addr, group_id); + if (!image) { + return 1; + } + desc = &image->descriptor; + fprintf(stderr, " (id %" PRIx64 " type %d flags %d width %d height %d", + desc->id, desc->type, desc->flags, desc->width, desc->height); + switch (desc->type) { + case SPICE_IMAGE_TYPE_BITMAP: + fprintf(stderr, ", fmt %d flags %d x %d y %d stride %d" + " palette %" PRIx64 " data %" PRIx64, + image->bitmap.format, image->bitmap.flags, + image->bitmap.x, image->bitmap.y, + image->bitmap.stride, + image->bitmap.palette, image->bitmap.data); + break; + } + fprintf(stderr, ")"); + return 0; +} + +static void qxl_log_rect(QXLRect *rect) +{ + fprintf(stderr, " %dx%d+%d+%d", + rect->right - rect->left, + rect->bottom - rect->top, + rect->left, rect->top); +} + +static int qxl_log_cmd_draw_copy(PCIQXLDevice *qxl, QXLCopy *copy, + int group_id) +{ + int ret; + + fprintf(stderr, " src %" PRIx64, + copy->src_bitmap); + ret = qxl_log_image(qxl, copy->src_bitmap, group_id); + if (ret != 0) { + return ret; + } + fprintf(stderr, " area"); + qxl_log_rect(©->src_area); + fprintf(stderr, " rop %d", copy->rop_descriptor); + return 0; +} + +static int qxl_log_cmd_draw(PCIQXLDevice *qxl, QXLDrawable *draw, int group_id) +{ + fprintf(stderr, ": surface_id %d type %s effect %s", + draw->surface_id, + qxl_name(qxl_draw_type, draw->type), + qxl_name(qxl_draw_effect, draw->effect)); + switch (draw->type) { + case QXL_DRAW_COPY: + return qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id); + break; + } + return 0; +} + +static int qxl_log_cmd_draw_compat(PCIQXLDevice *qxl, QXLCompatDrawable *draw, + int group_id) +{ + fprintf(stderr, ": type %s effect %s", + qxl_name(qxl_draw_type, draw->type), + qxl_name(qxl_draw_effect, draw->effect)); + if (draw->bitmap_offset) { + fprintf(stderr, ": bitmap %d", + draw->bitmap_offset); + qxl_log_rect(&draw->bitmap_area); + } + switch (draw->type) { + case QXL_DRAW_COPY: + return qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id); + break; + } + return 0; +} + +static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd) +{ + fprintf(stderr, ": %s id %d", + qxl_name(qxl_surface_cmd, cmd->type), + cmd->surface_id); + if (cmd->type == QXL_SURFACE_CMD_CREATE) { + fprintf(stderr, " size %dx%d stride %d format %s (count %d, max %d)", + cmd->u.surface_create.width, + cmd->u.surface_create.height, + cmd->u.surface_create.stride, + qxl_name(spice_surface_fmt, cmd->u.surface_create.format), + qxl->guest_surfaces.count, qxl->guest_surfaces.max); + } + if (cmd->type == QXL_SURFACE_CMD_DESTROY) { + fprintf(stderr, " (count %d)", qxl->guest_surfaces.count); + } +} + +int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id) +{ + QXLCursor *cursor; + + fprintf(stderr, ": %s", + qxl_name(qxl_cursor_cmd, cmd->type)); + switch (cmd->type) { + case QXL_CURSOR_SET: + fprintf(stderr, " +%d+%d visible %s, shape @ 0x%" PRIx64, + cmd->u.set.position.x, + cmd->u.set.position.y, + cmd->u.set.visible ? "yes" : "no", + cmd->u.set.shape); + cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id); + if (!cursor) { + return 1; + } + fprintf(stderr, " type %s size %dx%d hot-spot +%d+%d" + " unique 0x%" PRIx64 " data-size %d", + qxl_name(spice_cursor_type, cursor->header.type), + cursor->header.width, cursor->header.height, + cursor->header.hot_spot_x, cursor->header.hot_spot_y, + cursor->header.unique, cursor->data_size); + break; + case QXL_CURSOR_MOVE: + fprintf(stderr, " +%d+%d", cmd->u.position.x, cmd->u.position.y); + break; + } + return 0; +} + +int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext) +{ + bool compat = ext->flags & QXL_COMMAND_FLAG_COMPAT; + void *data; + int ret; + + if (!qxl->cmdlog) { + return 0; + } + fprintf(stderr, "%" PRId64 " qxl-%d/%s:", qemu_get_clock_ns(vm_clock), + qxl->id, ring); + fprintf(stderr, " cmd @ 0x%" PRIx64 " %s%s", ext->cmd.data, + qxl_name(qxl_type, ext->cmd.type), + compat ? "(compat)" : ""); + + data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + if (!data) { + return 1; + } + switch (ext->cmd.type) { + case QXL_CMD_DRAW: + if (!compat) { + ret = qxl_log_cmd_draw(qxl, data, ext->group_id); + } else { + ret = qxl_log_cmd_draw_compat(qxl, data, ext->group_id); + } + if (ret) { + return ret; + } + break; + case QXL_CMD_SURFACE: + qxl_log_cmd_surface(qxl, data); + break; + case QXL_CMD_CURSOR: + qxl_log_cmd_cursor(qxl, data, ext->group_id); + break; + } + fprintf(stderr, "\n"); + return 0; +} diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c new file mode 100644 index 0000000000..8cd9be434d --- /dev/null +++ b/hw/display/qxl-render.c @@ -0,0 +1,280 @@ +/* + * qxl local rendering (aka display on sdl/vnc) + * + * Copyright (C) 2010 Red Hat, Inc. + * + * maintained by Gerd Hoffmann + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include "hw/qxl.h" + +static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) +{ + DisplaySurface *surface = qemu_console_surface(qxl->vga.con); + uint8_t *dst = surface_data(surface); + uint8_t *src; + int len, i; + + if (is_buffer_shared(surface)) { + return; + } + if (!qxl->guest_primary.data) { + trace_qxl_render_blit_guest_primary_initialized(); + qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); + } + trace_qxl_render_blit(qxl->guest_primary.qxl_stride, + rect->left, rect->right, rect->top, rect->bottom); + src = qxl->guest_primary.data; + if (qxl->guest_primary.qxl_stride < 0) { + /* qxl surface is upside down, walk src scanlines + * in reverse order to flip it */ + src += (qxl->guest_primary.surface.height - rect->top - 1) * + qxl->guest_primary.abs_stride; + } else { + src += rect->top * qxl->guest_primary.abs_stride; + } + dst += rect->top * qxl->guest_primary.abs_stride; + src += rect->left * qxl->guest_primary.bytes_pp; + dst += rect->left * qxl->guest_primary.bytes_pp; + len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp; + + for (i = rect->top; i < rect->bottom; i++) { + memcpy(dst, src, len); + dst += qxl->guest_primary.abs_stride; + src += qxl->guest_primary.qxl_stride; + } +} + +void qxl_render_resize(PCIQXLDevice *qxl) +{ + QXLSurfaceCreate *sc = &qxl->guest_primary.surface; + + qxl->guest_primary.qxl_stride = sc->stride; + qxl->guest_primary.abs_stride = abs(sc->stride); + qxl->guest_primary.resized++; + switch (sc->format) { + case SPICE_SURFACE_FMT_16_555: + qxl->guest_primary.bytes_pp = 2; + qxl->guest_primary.bits_pp = 15; + break; + case SPICE_SURFACE_FMT_16_565: + qxl->guest_primary.bytes_pp = 2; + qxl->guest_primary.bits_pp = 16; + break; + case SPICE_SURFACE_FMT_32_xRGB: + case SPICE_SURFACE_FMT_32_ARGB: + qxl->guest_primary.bytes_pp = 4; + qxl->guest_primary.bits_pp = 32; + break; + default: + fprintf(stderr, "%s: unhandled format: %x\n", __FUNCTION__, + qxl->guest_primary.surface.format); + qxl->guest_primary.bytes_pp = 4; + qxl->guest_primary.bits_pp = 32; + break; + } +} + +static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area) +{ + area->left = 0; + area->right = qxl->guest_primary.surface.width; + area->top = 0; + area->bottom = qxl->guest_primary.surface.height; +} + +static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) +{ + VGACommonState *vga = &qxl->vga; + DisplaySurface *surface; + int i; + + if (qxl->guest_primary.resized) { + qxl->guest_primary.resized = 0; + qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); + qxl_set_rect_to_surface(qxl, &qxl->dirty[0]); + qxl->num_dirty_rects = 1; + trace_qxl_render_guest_primary_resized( + qxl->guest_primary.surface.width, + qxl->guest_primary.surface.height, + qxl->guest_primary.qxl_stride, + qxl->guest_primary.bytes_pp, + qxl->guest_primary.bits_pp); + if (qxl->guest_primary.qxl_stride > 0) { + surface = qemu_create_displaysurface_from + (qxl->guest_primary.surface.width, + qxl->guest_primary.surface.height, + qxl->guest_primary.bits_pp, + qxl->guest_primary.abs_stride, + qxl->guest_primary.data, + false); + } else { + surface = qemu_create_displaysurface + (qxl->guest_primary.surface.width, + qxl->guest_primary.surface.height); + } + dpy_gfx_replace_surface(vga->con, surface); + } + for (i = 0; i < qxl->num_dirty_rects; i++) { + if (qemu_spice_rect_is_empty(qxl->dirty+i)) { + break; + } + qxl_blit(qxl, qxl->dirty+i); + dpy_gfx_update(vga->con, + qxl->dirty[i].left, qxl->dirty[i].top, + qxl->dirty[i].right - qxl->dirty[i].left, + qxl->dirty[i].bottom - qxl->dirty[i].top); + } + qxl->num_dirty_rects = 0; +} + +/* + * use ssd.lock to protect render_update_cookie_num. + * qxl_render_update is called by io thread or vcpu thread, and the completion + * callbacks are called by spice_server thread, defering to bh called from the + * io thread. + */ +void qxl_render_update(PCIQXLDevice *qxl) +{ + QXLCookie *cookie; + + qemu_mutex_lock(&qxl->ssd.lock); + + if (!runstate_is_running() || !qxl->guest_primary.commands) { + qxl_render_update_area_unlocked(qxl); + qemu_mutex_unlock(&qxl->ssd.lock); + return; + } + + qxl->guest_primary.commands = 0; + qxl->render_update_cookie_num++; + qemu_mutex_unlock(&qxl->ssd.lock); + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA, + 0); + qxl_set_rect_to_surface(qxl, &cookie->u.render.area); + qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL, + 0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie); +} + +void qxl_render_update_area_bh(void *opaque) +{ + PCIQXLDevice *qxl = opaque; + + qemu_mutex_lock(&qxl->ssd.lock); + qxl_render_update_area_unlocked(qxl); + qemu_mutex_unlock(&qxl->ssd.lock); +} + +void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie) +{ + qemu_mutex_lock(&qxl->ssd.lock); + trace_qxl_render_update_area_done(cookie); + qemu_bh_schedule(qxl->update_area_bh); + qxl->render_update_cookie_num--; + qemu_mutex_unlock(&qxl->ssd.lock); + g_free(cookie); +} + +static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor) +{ + QEMUCursor *c; + uint8_t *image, *mask; + size_t size; + + c = cursor_alloc(cursor->header.width, cursor->header.height); + c->hot_x = cursor->header.hot_spot_x; + c->hot_y = cursor->header.hot_spot_y; + switch (cursor->header.type) { + case SPICE_CURSOR_TYPE_ALPHA: + size = cursor->header.width * cursor->header.height * sizeof(uint32_t); + memcpy(c->data, cursor->chunk.data, size); + if (qxl->debug > 2) { + cursor_print_ascii_art(c, "qxl/alpha"); + } + break; + case SPICE_CURSOR_TYPE_MONO: + mask = cursor->chunk.data; + image = mask + cursor_get_mono_bpl(c) * c->width; + cursor_set_mono(c, 0xffffff, 0x000000, image, 1, mask); + if (qxl->debug > 2) { + cursor_print_ascii_art(c, "qxl/mono"); + } + break; + default: + fprintf(stderr, "%s: not implemented: type %d\n", + __FUNCTION__, cursor->header.type); + goto fail; + } + return c; + +fail: + cursor_put(c); + return NULL; +} + + +/* called from spice server thread context only */ +int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) +{ + QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + QXLCursor *cursor; + QEMUCursor *c; + + if (!cmd) { + return 1; + } + + if (!dpy_cursor_define_supported(qxl->vga.con)) { + return 0; + } + + if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) { + fprintf(stderr, "%s", __FUNCTION__); + qxl_log_cmd_cursor(qxl, cmd, ext->group_id); + fprintf(stderr, "\n"); + } + switch (cmd->type) { + case QXL_CURSOR_SET: + cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id); + if (!cursor) { + return 1; + } + if (cursor->chunk.data_size != cursor->data_size) { + fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__); + return 1; + } + c = qxl_cursor(qxl, cursor); + if (c == NULL) { + c = cursor_builtin_left_ptr(); + } + qemu_mutex_lock(&qxl->ssd.lock); + if (qxl->ssd.cursor) { + cursor_put(qxl->ssd.cursor); + } + qxl->ssd.cursor = c; + qxl->ssd.mouse_x = cmd->u.set.position.x; + qxl->ssd.mouse_y = cmd->u.set.position.y; + qemu_mutex_unlock(&qxl->ssd.lock); + break; + case QXL_CURSOR_MOVE: + qemu_mutex_lock(&qxl->ssd.lock); + qxl->ssd.mouse_x = cmd->u.position.x; + qxl->ssd.mouse_y = cmd->u.position.y; + qemu_mutex_unlock(&qxl->ssd.lock); + break; + } + return 0; +} diff --git a/hw/display/qxl.c b/hw/display/qxl.c new file mode 100644 index 0000000000..b66b41442a --- /dev/null +++ b/hw/display/qxl.c @@ -0,0 +1,2365 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * written by Yaniv Kamay, Izik Eidus, Gerd Hoffmann + * maintained by Gerd Hoffmann + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include + +#include "qemu-common.h" +#include "qemu/timer.h" +#include "qemu/queue.h" +#include "monitor/monitor.h" +#include "sysemu/sysemu.h" +#include "trace.h" + +#include "hw/qxl.h" + +/* + * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as + * such can be changed by the guest, so to avoid a guest trigerrable + * abort we just qxl_set_guest_bug and set the return to NULL. Still + * it may happen as a result of emulator bug as well. + */ +#undef SPICE_RING_PROD_ITEM +#define SPICE_RING_PROD_ITEM(qxl, r, ret) { \ + uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \ + if (prod >= ARRAY_SIZE((r)->items)) { \ + qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \ + "%u >= %zu", prod, ARRAY_SIZE((r)->items)); \ + ret = NULL; \ + } else { \ + ret = &(r)->items[prod].el; \ + } \ + } + +#undef SPICE_RING_CONS_ITEM +#define SPICE_RING_CONS_ITEM(qxl, r, ret) { \ + uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \ + if (cons >= ARRAY_SIZE((r)->items)) { \ + qxl_set_guest_bug(qxl, "SPICE_RING_CONS_ITEM indices mismatch " \ + "%u >= %zu", cons, ARRAY_SIZE((r)->items)); \ + ret = NULL; \ + } else { \ + ret = &(r)->items[cons].el; \ + } \ + } + +#undef ALIGN +#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1)) + +#define PIXEL_SIZE 0.2936875 //1280x1024 is 14.8" x 11.9" + +#define QXL_MODE(_x, _y, _b, _o) \ + { .x_res = _x, \ + .y_res = _y, \ + .bits = _b, \ + .stride = (_x) * (_b) / 8, \ + .x_mili = PIXEL_SIZE * (_x), \ + .y_mili = PIXEL_SIZE * (_y), \ + .orientation = _o, \ + } + +#define QXL_MODE_16_32(x_res, y_res, orientation) \ + QXL_MODE(x_res, y_res, 16, orientation), \ + QXL_MODE(x_res, y_res, 32, orientation) + +#define QXL_MODE_EX(x_res, y_res) \ + QXL_MODE_16_32(x_res, y_res, 0), \ + QXL_MODE_16_32(x_res, y_res, 1) + +static QXLMode qxl_modes[] = { + QXL_MODE_EX(640, 480), + QXL_MODE_EX(800, 480), + QXL_MODE_EX(800, 600), + QXL_MODE_EX(832, 624), + QXL_MODE_EX(960, 640), + QXL_MODE_EX(1024, 600), + QXL_MODE_EX(1024, 768), + QXL_MODE_EX(1152, 864), + QXL_MODE_EX(1152, 870), + QXL_MODE_EX(1280, 720), + QXL_MODE_EX(1280, 760), + QXL_MODE_EX(1280, 768), + QXL_MODE_EX(1280, 800), + QXL_MODE_EX(1280, 960), + QXL_MODE_EX(1280, 1024), + QXL_MODE_EX(1360, 768), + QXL_MODE_EX(1366, 768), + QXL_MODE_EX(1400, 1050), + QXL_MODE_EX(1440, 900), + QXL_MODE_EX(1600, 900), + QXL_MODE_EX(1600, 1200), + QXL_MODE_EX(1680, 1050), + QXL_MODE_EX(1920, 1080), + /* these modes need more than 8 MB video memory */ + QXL_MODE_EX(1920, 1200), + QXL_MODE_EX(1920, 1440), + QXL_MODE_EX(2048, 1536), + QXL_MODE_EX(2560, 1440), + QXL_MODE_EX(2560, 1600), + /* these modes need more than 16 MB video memory */ + QXL_MODE_EX(2560, 2048), + QXL_MODE_EX(2800, 2100), + QXL_MODE_EX(3200, 2400), +}; + +static void qxl_send_events(PCIQXLDevice *d, uint32_t events); +static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async); +static void qxl_reset_memslots(PCIQXLDevice *d); +static void qxl_reset_surfaces(PCIQXLDevice *d); +static void qxl_ring_set_dirty(PCIQXLDevice *qxl); + +void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) +{ + trace_qxl_set_guest_bug(qxl->id); + qxl_send_events(qxl, QXL_INTERRUPT_ERROR); + qxl->guest_bug = 1; + if (qxl->guestdebug) { + va_list ap; + va_start(ap, msg); + fprintf(stderr, "qxl-%d: guest bug: ", qxl->id); + vfprintf(stderr, msg, ap); + fprintf(stderr, "\n"); + va_end(ap); + } +} + +static void qxl_clear_guest_bug(PCIQXLDevice *qxl) +{ + qxl->guest_bug = 0; +} + +void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, + struct QXLRect *area, struct QXLRect *dirty_rects, + uint32_t num_dirty_rects, + uint32_t clear_dirty_region, + qxl_async_io async, struct QXLCookie *cookie) +{ + trace_qxl_spice_update_area(qxl->id, surface_id, area->left, area->right, + area->top, area->bottom); + trace_qxl_spice_update_area_rest(qxl->id, num_dirty_rects, + clear_dirty_region); + if (async == QXL_SYNC) { + qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area, + dirty_rects, num_dirty_rects, clear_dirty_region); + } else { + assert(cookie != NULL); + spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area, + clear_dirty_region, (uintptr_t)cookie); + } +} + +static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl, + uint32_t id) +{ + trace_qxl_spice_destroy_surface_wait_complete(qxl->id, id); + qemu_mutex_lock(&qxl->track_lock); + qxl->guest_surfaces.cmds[id] = 0; + qxl->guest_surfaces.count--; + qemu_mutex_unlock(&qxl->track_lock); +} + +static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id, + qxl_async_io async) +{ + QXLCookie *cookie; + + trace_qxl_spice_destroy_surface_wait(qxl->id, id, async); + if (async) { + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_DESTROY_SURFACE_ASYNC); + cookie->u.surface_id = id; + spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uintptr_t)cookie); + } else { + qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id); + qxl_spice_destroy_surface_wait_complete(qxl, id); + } +} + +static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl) +{ + trace_qxl_spice_flush_surfaces_async(qxl->id, qxl->guest_surfaces.count, + qxl->num_free_res); + spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, + (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_FLUSH_SURFACES_ASYNC)); +} + +void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, + uint32_t count) +{ + trace_qxl_spice_loadvm_commands(qxl->id, ext, count); + qxl->ssd.worker->loadvm_commands(qxl->ssd.worker, ext, count); +} + +void qxl_spice_oom(PCIQXLDevice *qxl) +{ + trace_qxl_spice_oom(qxl->id); + qxl->ssd.worker->oom(qxl->ssd.worker); +} + +void qxl_spice_reset_memslots(PCIQXLDevice *qxl) +{ + trace_qxl_spice_reset_memslots(qxl->id); + qxl->ssd.worker->reset_memslots(qxl->ssd.worker); +} + +static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl) +{ + trace_qxl_spice_destroy_surfaces_complete(qxl->id); + qemu_mutex_lock(&qxl->track_lock); + memset(qxl->guest_surfaces.cmds, 0, + sizeof(qxl->guest_surfaces.cmds) * qxl->ssd.num_surfaces); + qxl->guest_surfaces.count = 0; + qemu_mutex_unlock(&qxl->track_lock); +} + +static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async) +{ + trace_qxl_spice_destroy_surfaces(qxl->id, async); + if (async) { + spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, + (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_DESTROY_ALL_SURFACES_ASYNC)); + } else { + qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker); + qxl_spice_destroy_surfaces_complete(qxl); + } +} + +static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay) +{ + trace_qxl_spice_monitors_config(qxl->id); + if (replay) { + /* + * don't use QXL_COOKIE_TYPE_IO: + * - we are not running yet (post_load), we will assert + * in send_events + * - this is not a guest io, but a reply, so async_io isn't set. + */ + spice_qxl_monitors_config_async(&qxl->ssd.qxl, + qxl->guest_monitors_config, + MEMSLOT_GROUP_GUEST, + (uintptr_t)qxl_cookie_new( + QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG, + 0)); + } else { + qxl->guest_monitors_config = qxl->ram->monitors_config; + spice_qxl_monitors_config_async(&qxl->ssd.qxl, + qxl->ram->monitors_config, + MEMSLOT_GROUP_GUEST, + (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_MONITORS_CONFIG_ASYNC)); + } +} + +void qxl_spice_reset_image_cache(PCIQXLDevice *qxl) +{ + trace_qxl_spice_reset_image_cache(qxl->id); + qxl->ssd.worker->reset_image_cache(qxl->ssd.worker); +} + +void qxl_spice_reset_cursor(PCIQXLDevice *qxl) +{ + trace_qxl_spice_reset_cursor(qxl->id); + qxl->ssd.worker->reset_cursor(qxl->ssd.worker); + qemu_mutex_lock(&qxl->track_lock); + qxl->guest_cursor = 0; + qemu_mutex_unlock(&qxl->track_lock); + if (qxl->ssd.cursor) { + cursor_put(qxl->ssd.cursor); + } + qxl->ssd.cursor = cursor_builtin_hidden(); +} + + +static inline uint32_t msb_mask(uint32_t val) +{ + uint32_t mask; + + do { + mask = ~(val - 1) & val; + val &= ~mask; + } while (mask < val); + + return mask; +} + +static ram_addr_t qxl_rom_size(void) +{ + uint32_t required_rom_size = sizeof(QXLRom) + sizeof(QXLModes) + + sizeof(qxl_modes); + uint32_t rom_size = 8192; /* two pages */ + + required_rom_size = MAX(required_rom_size, TARGET_PAGE_SIZE); + required_rom_size = msb_mask(required_rom_size * 2 - 1); + assert(required_rom_size <= rom_size); + return rom_size; +} + +static void init_qxl_rom(PCIQXLDevice *d) +{ + QXLRom *rom = memory_region_get_ram_ptr(&d->rom_bar); + QXLModes *modes = (QXLModes *)(rom + 1); + uint32_t ram_header_size; + uint32_t surface0_area_size; + uint32_t num_pages; + uint32_t fb; + int i, n; + + memset(rom, 0, d->rom_size); + + rom->magic = cpu_to_le32(QXL_ROM_MAGIC); + rom->id = cpu_to_le32(d->id); + rom->log_level = cpu_to_le32(d->guestdebug); + rom->modes_offset = cpu_to_le32(sizeof(QXLRom)); + + rom->slot_gen_bits = MEMSLOT_GENERATION_BITS; + rom->slot_id_bits = MEMSLOT_SLOT_BITS; + rom->slots_start = 1; + rom->slots_end = NUM_MEMSLOTS - 1; + rom->n_surfaces = cpu_to_le32(d->ssd.num_surfaces); + + for (i = 0, n = 0; i < ARRAY_SIZE(qxl_modes); i++) { + fb = qxl_modes[i].y_res * qxl_modes[i].stride; + if (fb > d->vgamem_size) { + continue; + } + modes->modes[n].id = cpu_to_le32(i); + modes->modes[n].x_res = cpu_to_le32(qxl_modes[i].x_res); + modes->modes[n].y_res = cpu_to_le32(qxl_modes[i].y_res); + modes->modes[n].bits = cpu_to_le32(qxl_modes[i].bits); + modes->modes[n].stride = cpu_to_le32(qxl_modes[i].stride); + modes->modes[n].x_mili = cpu_to_le32(qxl_modes[i].x_mili); + modes->modes[n].y_mili = cpu_to_le32(qxl_modes[i].y_mili); + modes->modes[n].orientation = cpu_to_le32(qxl_modes[i].orientation); + n++; + } + modes->n_modes = cpu_to_le32(n); + + ram_header_size = ALIGN(sizeof(QXLRam), 4096); + surface0_area_size = ALIGN(d->vgamem_size, 4096); + num_pages = d->vga.vram_size; + num_pages -= ram_header_size; + num_pages -= surface0_area_size; + num_pages = num_pages / TARGET_PAGE_SIZE; + + rom->draw_area_offset = cpu_to_le32(0); + rom->surface0_area_size = cpu_to_le32(surface0_area_size); + rom->pages_offset = cpu_to_le32(surface0_area_size); + rom->num_pages = cpu_to_le32(num_pages); + rom->ram_header_offset = cpu_to_le32(d->vga.vram_size - ram_header_size); + + d->shadow_rom = *rom; + d->rom = rom; + d->modes = modes; +} + +static void init_qxl_ram(PCIQXLDevice *d) +{ + uint8_t *buf; + uint64_t *item; + + buf = d->vga.vram_ptr; + d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset)); + d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC); + d->ram->int_pending = cpu_to_le32(0); + d->ram->int_mask = cpu_to_le32(0); + d->ram->update_surface = 0; + SPICE_RING_INIT(&d->ram->cmd_ring); + SPICE_RING_INIT(&d->ram->cursor_ring); + SPICE_RING_INIT(&d->ram->release_ring); + SPICE_RING_PROD_ITEM(d, &d->ram->release_ring, item); + assert(item); + *item = 0; + qxl_ring_set_dirty(d); +} + +/* can be called from spice server thread context */ +static void qxl_set_dirty(MemoryRegion *mr, ram_addr_t addr, ram_addr_t end) +{ + memory_region_set_dirty(mr, addr, end - addr); +} + +static void qxl_rom_set_dirty(PCIQXLDevice *qxl) +{ + qxl_set_dirty(&qxl->rom_bar, 0, qxl->rom_size); +} + +/* called from spice server thread context only */ +static void qxl_ram_set_dirty(PCIQXLDevice *qxl, void *ptr) +{ + void *base = qxl->vga.vram_ptr; + intptr_t offset; + + offset = ptr - base; + offset &= ~(TARGET_PAGE_SIZE-1); + assert(offset < qxl->vga.vram_size); + qxl_set_dirty(&qxl->vga.vram, offset, offset + TARGET_PAGE_SIZE); +} + +/* can be called from spice server thread context */ +static void qxl_ring_set_dirty(PCIQXLDevice *qxl) +{ + ram_addr_t addr = qxl->shadow_rom.ram_header_offset; + ram_addr_t end = qxl->vga.vram_size; + qxl_set_dirty(&qxl->vga.vram, addr, end); +} + +/* + * keep track of some command state, for savevm/loadvm. + * called from spice server thread context only + */ +static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) +{ + switch (le32_to_cpu(ext->cmd.type)) { + case QXL_CMD_SURFACE: + { + QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + + if (!cmd) { + return 1; + } + uint32_t id = le32_to_cpu(cmd->surface_id); + + if (id >= qxl->ssd.num_surfaces) { + qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE id %d >= %d", id, + qxl->ssd.num_surfaces); + return 1; + } + if (cmd->type == QXL_SURFACE_CMD_CREATE && + (cmd->u.surface_create.stride & 0x03) != 0) { + qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE stride = %d %% 4 != 0\n", + cmd->u.surface_create.stride); + return 1; + } + qemu_mutex_lock(&qxl->track_lock); + if (cmd->type == QXL_SURFACE_CMD_CREATE) { + qxl->guest_surfaces.cmds[id] = ext->cmd.data; + qxl->guest_surfaces.count++; + if (qxl->guest_surfaces.max < qxl->guest_surfaces.count) + qxl->guest_surfaces.max = qxl->guest_surfaces.count; + } + if (cmd->type == QXL_SURFACE_CMD_DESTROY) { + qxl->guest_surfaces.cmds[id] = 0; + qxl->guest_surfaces.count--; + } + qemu_mutex_unlock(&qxl->track_lock); + break; + } + case QXL_CMD_CURSOR: + { + QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + + if (!cmd) { + return 1; + } + if (cmd->type == QXL_CURSOR_SET) { + qemu_mutex_lock(&qxl->track_lock); + qxl->guest_cursor = ext->cmd.data; + qemu_mutex_unlock(&qxl->track_lock); + } + break; + } + } + return 0; +} + +/* spice display interface callbacks */ + +static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + + trace_qxl_interface_attach_worker(qxl->id); + qxl->ssd.worker = qxl_worker; +} + +static void interface_set_compression_level(QXLInstance *sin, int level) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + + trace_qxl_interface_set_compression_level(qxl->id, level); + qxl->shadow_rom.compression_level = cpu_to_le32(level); + qxl->rom->compression_level = cpu_to_le32(level); + qxl_rom_set_dirty(qxl); +} + +static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + + trace_qxl_interface_set_mm_time(qxl->id, mm_time); + qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time); + qxl->rom->mm_clock = cpu_to_le32(mm_time); + qxl_rom_set_dirty(qxl); +} + +static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + + trace_qxl_interface_get_init_info(qxl->id); + info->memslot_gen_bits = MEMSLOT_GENERATION_BITS; + info->memslot_id_bits = MEMSLOT_SLOT_BITS; + info->num_memslots = NUM_MEMSLOTS; + info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; + info->internal_groupslot_id = 0; + info->qxl_ram_size = le32_to_cpu(qxl->shadow_rom.num_pages) << TARGET_PAGE_BITS; + info->n_surfaces = qxl->ssd.num_surfaces; +} + +static const char *qxl_mode_to_string(int mode) +{ + switch (mode) { + case QXL_MODE_COMPAT: + return "compat"; + case QXL_MODE_NATIVE: + return "native"; + case QXL_MODE_UNDEFINED: + return "undefined"; + case QXL_MODE_VGA: + return "vga"; + } + return "INVALID"; +} + +static const char *io_port_to_string(uint32_t io_port) +{ + if (io_port >= QXL_IO_RANGE_SIZE) { + return "out of range"; + } + static const char *io_port_to_string[QXL_IO_RANGE_SIZE + 1] = { + [QXL_IO_NOTIFY_CMD] = "QXL_IO_NOTIFY_CMD", + [QXL_IO_NOTIFY_CURSOR] = "QXL_IO_NOTIFY_CURSOR", + [QXL_IO_UPDATE_AREA] = "QXL_IO_UPDATE_AREA", + [QXL_IO_UPDATE_IRQ] = "QXL_IO_UPDATE_IRQ", + [QXL_IO_NOTIFY_OOM] = "QXL_IO_NOTIFY_OOM", + [QXL_IO_RESET] = "QXL_IO_RESET", + [QXL_IO_SET_MODE] = "QXL_IO_SET_MODE", + [QXL_IO_LOG] = "QXL_IO_LOG", + [QXL_IO_MEMSLOT_ADD] = "QXL_IO_MEMSLOT_ADD", + [QXL_IO_MEMSLOT_DEL] = "QXL_IO_MEMSLOT_DEL", + [QXL_IO_DETACH_PRIMARY] = "QXL_IO_DETACH_PRIMARY", + [QXL_IO_ATTACH_PRIMARY] = "QXL_IO_ATTACH_PRIMARY", + [QXL_IO_CREATE_PRIMARY] = "QXL_IO_CREATE_PRIMARY", + [QXL_IO_DESTROY_PRIMARY] = "QXL_IO_DESTROY_PRIMARY", + [QXL_IO_DESTROY_SURFACE_WAIT] = "QXL_IO_DESTROY_SURFACE_WAIT", + [QXL_IO_DESTROY_ALL_SURFACES] = "QXL_IO_DESTROY_ALL_SURFACES", + [QXL_IO_UPDATE_AREA_ASYNC] = "QXL_IO_UPDATE_AREA_ASYNC", + [QXL_IO_MEMSLOT_ADD_ASYNC] = "QXL_IO_MEMSLOT_ADD_ASYNC", + [QXL_IO_CREATE_PRIMARY_ASYNC] = "QXL_IO_CREATE_PRIMARY_ASYNC", + [QXL_IO_DESTROY_PRIMARY_ASYNC] = "QXL_IO_DESTROY_PRIMARY_ASYNC", + [QXL_IO_DESTROY_SURFACE_ASYNC] = "QXL_IO_DESTROY_SURFACE_ASYNC", + [QXL_IO_DESTROY_ALL_SURFACES_ASYNC] + = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC", + [QXL_IO_FLUSH_SURFACES_ASYNC] = "QXL_IO_FLUSH_SURFACES_ASYNC", + [QXL_IO_FLUSH_RELEASE] = "QXL_IO_FLUSH_RELEASE", + [QXL_IO_MONITORS_CONFIG_ASYNC] = "QXL_IO_MONITORS_CONFIG_ASYNC", + }; + return io_port_to_string[io_port]; +} + +/* called from spice server thread context only */ +static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + SimpleSpiceUpdate *update; + QXLCommandRing *ring; + QXLCommand *cmd; + int notify, ret; + + trace_qxl_ring_command_check(qxl->id, qxl_mode_to_string(qxl->mode)); + + switch (qxl->mode) { + case QXL_MODE_VGA: + ret = false; + qemu_mutex_lock(&qxl->ssd.lock); + update = QTAILQ_FIRST(&qxl->ssd.updates); + if (update != NULL) { + QTAILQ_REMOVE(&qxl->ssd.updates, update, next); + *ext = update->ext; + ret = true; + } + qemu_mutex_unlock(&qxl->ssd.lock); + if (ret) { + trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode)); + qxl_log_command(qxl, "vga", ext); + } + return ret; + case QXL_MODE_COMPAT: + case QXL_MODE_NATIVE: + case QXL_MODE_UNDEFINED: + ring = &qxl->ram->cmd_ring; + if (qxl->guest_bug || SPICE_RING_IS_EMPTY(ring)) { + return false; + } + SPICE_RING_CONS_ITEM(qxl, ring, cmd); + if (!cmd) { + return false; + } + ext->cmd = *cmd; + ext->group_id = MEMSLOT_GROUP_GUEST; + ext->flags = qxl->cmdflags; + SPICE_RING_POP(ring, notify); + qxl_ring_set_dirty(qxl); + if (notify) { + qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY); + } + qxl->guest_primary.commands++; + qxl_track_command(qxl, ext); + qxl_log_command(qxl, "cmd", ext); + trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode)); + return true; + default: + return false; + } +} + +/* called from spice server thread context only */ +static int interface_req_cmd_notification(QXLInstance *sin) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + int wait = 1; + + trace_qxl_ring_command_req_notification(qxl->id); + switch (qxl->mode) { + case QXL_MODE_COMPAT: + case QXL_MODE_NATIVE: + case QXL_MODE_UNDEFINED: + SPICE_RING_CONS_WAIT(&qxl->ram->cmd_ring, wait); + qxl_ring_set_dirty(qxl); + break; + default: + /* nothing */ + break; + } + return wait; +} + +/* called from spice server thread context only */ +static inline void qxl_push_free_res(PCIQXLDevice *d, int flush) +{ + QXLReleaseRing *ring = &d->ram->release_ring; + uint64_t *item; + int notify; + +#define QXL_FREE_BUNCH_SIZE 32 + + if (ring->prod - ring->cons + 1 == ring->num_items) { + /* ring full -- can't push */ + return; + } + if (!flush && d->oom_running) { + /* collect everything from oom handler before pushing */ + return; + } + if (!flush && d->num_free_res < QXL_FREE_BUNCH_SIZE) { + /* collect a bit more before pushing */ + return; + } + + SPICE_RING_PUSH(ring, notify); + trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->mode), + d->guest_surfaces.count, d->num_free_res, + d->last_release, notify ? "yes" : "no"); + trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons, + ring->num_items, ring->prod, ring->cons); + if (notify) { + qxl_send_events(d, QXL_INTERRUPT_DISPLAY); + } + SPICE_RING_PROD_ITEM(d, ring, item); + if (!item) { + return; + } + *item = 0; + d->num_free_res = 0; + d->last_release = NULL; + qxl_ring_set_dirty(d); +} + +/* called from spice server thread context only */ +static void interface_release_resource(QXLInstance *sin, + struct QXLReleaseInfoExt ext) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + QXLReleaseRing *ring; + uint64_t *item, id; + + if (ext.group_id == MEMSLOT_GROUP_HOST) { + /* host group -> vga mode update request */ + qemu_spice_destroy_update(&qxl->ssd, (void *)(intptr_t)ext.info->id); + return; + } + + /* + * ext->info points into guest-visible memory + * pci bar 0, $command.release_info + */ + ring = &qxl->ram->release_ring; + SPICE_RING_PROD_ITEM(qxl, ring, item); + if (!item) { + return; + } + if (*item == 0) { + /* stick head into the ring */ + id = ext.info->id; + ext.info->next = 0; + qxl_ram_set_dirty(qxl, &ext.info->next); + *item = id; + qxl_ring_set_dirty(qxl); + } else { + /* append item to the list */ + qxl->last_release->next = ext.info->id; + qxl_ram_set_dirty(qxl, &qxl->last_release->next); + ext.info->next = 0; + qxl_ram_set_dirty(qxl, &ext.info->next); + } + qxl->last_release = ext.info; + qxl->num_free_res++; + trace_qxl_ring_res_put(qxl->id, qxl->num_free_res); + qxl_push_free_res(qxl, 0); +} + +/* called from spice server thread context only */ +static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + QXLCursorRing *ring; + QXLCommand *cmd; + int notify; + + trace_qxl_ring_cursor_check(qxl->id, qxl_mode_to_string(qxl->mode)); + + switch (qxl->mode) { + case QXL_MODE_COMPAT: + case QXL_MODE_NATIVE: + case QXL_MODE_UNDEFINED: + ring = &qxl->ram->cursor_ring; + if (SPICE_RING_IS_EMPTY(ring)) { + return false; + } + SPICE_RING_CONS_ITEM(qxl, ring, cmd); + if (!cmd) { + return false; + } + ext->cmd = *cmd; + ext->group_id = MEMSLOT_GROUP_GUEST; + ext->flags = qxl->cmdflags; + SPICE_RING_POP(ring, notify); + qxl_ring_set_dirty(qxl); + if (notify) { + qxl_send_events(qxl, QXL_INTERRUPT_CURSOR); + } + qxl->guest_primary.commands++; + qxl_track_command(qxl, ext); + qxl_log_command(qxl, "csr", ext); + if (qxl->id == 0) { + qxl_render_cursor(qxl, ext); + } + trace_qxl_ring_cursor_get(qxl->id, qxl_mode_to_string(qxl->mode)); + return true; + default: + return false; + } +} + +/* called from spice server thread context only */ +static int interface_req_cursor_notification(QXLInstance *sin) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + int wait = 1; + + trace_qxl_ring_cursor_req_notification(qxl->id); + switch (qxl->mode) { + case QXL_MODE_COMPAT: + case QXL_MODE_NATIVE: + case QXL_MODE_UNDEFINED: + SPICE_RING_CONS_WAIT(&qxl->ram->cursor_ring, wait); + qxl_ring_set_dirty(qxl); + break; + default: + /* nothing */ + break; + } + return wait; +} + +/* called from spice server thread context */ +static void interface_notify_update(QXLInstance *sin, uint32_t update_id) +{ + /* + * Called by spice-server as a result of a QXL_CMD_UPDATE which is not in + * use by xf86-video-qxl and is defined out in the qxl windows driver. + * Probably was at some earlier version that is prior to git start (2009), + * and is still guest trigerrable. + */ + fprintf(stderr, "%s: deprecated\n", __func__); +} + +/* called from spice server thread context only */ +static int interface_flush_resources(QXLInstance *sin) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + int ret; + + ret = qxl->num_free_res; + if (ret) { + qxl_push_free_res(qxl, 1); + } + return ret; +} + +static void qxl_create_guest_primary_complete(PCIQXLDevice *d); + +/* called from spice server thread context only */ +static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie) +{ + uint32_t current_async; + + qemu_mutex_lock(&qxl->async_lock); + current_async = qxl->current_async; + qxl->current_async = QXL_UNDEFINED_IO; + qemu_mutex_unlock(&qxl->async_lock); + + trace_qxl_interface_async_complete_io(qxl->id, current_async, cookie); + if (!cookie) { + fprintf(stderr, "qxl: %s: error, cookie is NULL\n", __func__); + return; + } + if (cookie && current_async != cookie->io) { + fprintf(stderr, + "qxl: %s: error: current_async = %d != %" + PRId64 " = cookie->io\n", __func__, current_async, cookie->io); + } + switch (current_async) { + case QXL_IO_MEMSLOT_ADD_ASYNC: + case QXL_IO_DESTROY_PRIMARY_ASYNC: + case QXL_IO_UPDATE_AREA_ASYNC: + case QXL_IO_FLUSH_SURFACES_ASYNC: + case QXL_IO_MONITORS_CONFIG_ASYNC: + break; + case QXL_IO_CREATE_PRIMARY_ASYNC: + qxl_create_guest_primary_complete(qxl); + break; + case QXL_IO_DESTROY_ALL_SURFACES_ASYNC: + qxl_spice_destroy_surfaces_complete(qxl); + break; + case QXL_IO_DESTROY_SURFACE_ASYNC: + qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id); + break; + default: + fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__, + current_async); + } + qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD); +} + +/* called from spice server thread context only */ +static void interface_update_area_complete(QXLInstance *sin, + uint32_t surface_id, + QXLRect *dirty, uint32_t num_updated_rects) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + int i; + int qxl_i; + + qemu_mutex_lock(&qxl->ssd.lock); + if (surface_id != 0 || !qxl->render_update_cookie_num) { + qemu_mutex_unlock(&qxl->ssd.lock); + return; + } + trace_qxl_interface_update_area_complete(qxl->id, surface_id, dirty->left, + dirty->right, dirty->top, dirty->bottom); + trace_qxl_interface_update_area_complete_rest(qxl->id, num_updated_rects); + if (qxl->num_dirty_rects + num_updated_rects > QXL_NUM_DIRTY_RECTS) { + /* + * overflow - treat this as a full update. Not expected to be common. + */ + trace_qxl_interface_update_area_complete_overflow(qxl->id, + QXL_NUM_DIRTY_RECTS); + qxl->guest_primary.resized = 1; + } + if (qxl->guest_primary.resized) { + /* + * Don't bother copying or scheduling the bh since we will flip + * the whole area anyway on completion of the update_area async call + */ + qemu_mutex_unlock(&qxl->ssd.lock); + return; + } + qxl_i = qxl->num_dirty_rects; + for (i = 0; i < num_updated_rects; i++) { + qxl->dirty[qxl_i++] = dirty[i]; + } + qxl->num_dirty_rects += num_updated_rects; + trace_qxl_interface_update_area_complete_schedule_bh(qxl->id, + qxl->num_dirty_rects); + qemu_bh_schedule(qxl->update_area_bh); + qemu_mutex_unlock(&qxl->ssd.lock); +} + +/* called from spice server thread context only */ +static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token; + + switch (cookie->type) { + case QXL_COOKIE_TYPE_IO: + interface_async_complete_io(qxl, cookie); + g_free(cookie); + break; + case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA: + qxl_render_update_area_done(qxl, cookie); + break; + case QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG: + break; + default: + fprintf(stderr, "qxl: %s: unexpected cookie type %d\n", + __func__, cookie->type); + g_free(cookie); + } +} + +/* called from spice server thread context only */ +static void interface_set_client_capabilities(QXLInstance *sin, + uint8_t client_present, + uint8_t caps[58]) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + + if (qxl->revision < 4) { + trace_qxl_set_client_capabilities_unsupported_by_revision(qxl->id, + qxl->revision); + return; + } + + if (runstate_check(RUN_STATE_INMIGRATE) || + runstate_check(RUN_STATE_POSTMIGRATE)) { + return; + } + + qxl->shadow_rom.client_present = client_present; + memcpy(qxl->shadow_rom.client_capabilities, caps, + sizeof(qxl->shadow_rom.client_capabilities)); + qxl->rom->client_present = client_present; + memcpy(qxl->rom->client_capabilities, caps, + sizeof(qxl->rom->client_capabilities)); + qxl_rom_set_dirty(qxl); + + qxl_send_events(qxl, QXL_INTERRUPT_CLIENT); +} + +static uint32_t qxl_crc32(const uint8_t *p, unsigned len) +{ + /* + * zlib xors the seed with 0xffffffff, and xors the result + * again with 0xffffffff; Both are not done with linux's crc32, + * which we want to be compatible with, so undo that. + */ + return crc32(0xffffffff, p, len) ^ 0xffffffff; +} + +/* called from main context only */ +static int interface_client_monitors_config(QXLInstance *sin, + VDAgentMonitorsConfig *monitors_config) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + QXLRom *rom = memory_region_get_ram_ptr(&qxl->rom_bar); + int i; + + if (qxl->revision < 4) { + trace_qxl_client_monitors_config_unsupported_by_device(qxl->id, + qxl->revision); + return 0; + } + /* + * Older windows drivers set int_mask to 0 when their ISR is called, + * then later set it to ~0. So it doesn't relate to the actual interrupts + * handled. However, they are old, so clearly they don't support this + * interrupt + */ + if (qxl->ram->int_mask == 0 || qxl->ram->int_mask == ~0 || + !(qxl->ram->int_mask & QXL_INTERRUPT_CLIENT_MONITORS_CONFIG)) { + trace_qxl_client_monitors_config_unsupported_by_guest(qxl->id, + qxl->ram->int_mask, + monitors_config); + return 0; + } + if (!monitors_config) { + return 1; + } + memset(&rom->client_monitors_config, 0, + sizeof(rom->client_monitors_config)); + rom->client_monitors_config.count = monitors_config->num_of_monitors; + /* monitors_config->flags ignored */ + if (rom->client_monitors_config.count >= + ARRAY_SIZE(rom->client_monitors_config.heads)) { + trace_qxl_client_monitors_config_capped(qxl->id, + monitors_config->num_of_monitors, + ARRAY_SIZE(rom->client_monitors_config.heads)); + rom->client_monitors_config.count = + ARRAY_SIZE(rom->client_monitors_config.heads); + } + for (i = 0 ; i < rom->client_monitors_config.count ; ++i) { + VDAgentMonConfig *monitor = &monitors_config->monitors[i]; + QXLURect *rect = &rom->client_monitors_config.heads[i]; + /* monitor->depth ignored */ + rect->left = monitor->x; + rect->top = monitor->y; + rect->right = monitor->x + monitor->width; + rect->bottom = monitor->y + monitor->height; + } + rom->client_monitors_config_crc = qxl_crc32( + (const uint8_t *)&rom->client_monitors_config, + sizeof(rom->client_monitors_config)); + trace_qxl_client_monitors_config_crc(qxl->id, + sizeof(rom->client_monitors_config), + rom->client_monitors_config_crc); + + trace_qxl_interrupt_client_monitors_config(qxl->id, + rom->client_monitors_config.count, + rom->client_monitors_config.heads); + qxl_send_events(qxl, QXL_INTERRUPT_CLIENT_MONITORS_CONFIG); + return 1; +} + +static const QXLInterface qxl_interface = { + .base.type = SPICE_INTERFACE_QXL, + .base.description = "qxl gpu", + .base.major_version = SPICE_INTERFACE_QXL_MAJOR, + .base.minor_version = SPICE_INTERFACE_QXL_MINOR, + + .attache_worker = interface_attach_worker, + .set_compression_level = interface_set_compression_level, + .set_mm_time = interface_set_mm_time, + .get_init_info = interface_get_init_info, + + /* the callbacks below are called from spice server thread context */ + .get_command = interface_get_command, + .req_cmd_notification = interface_req_cmd_notification, + .release_resource = interface_release_resource, + .get_cursor_command = interface_get_cursor_command, + .req_cursor_notification = interface_req_cursor_notification, + .notify_update = interface_notify_update, + .flush_resources = interface_flush_resources, + .async_complete = interface_async_complete, + .update_area_complete = interface_update_area_complete, + .set_client_capabilities = interface_set_client_capabilities, + .client_monitors_config = interface_client_monitors_config, +}; + +static void qxl_enter_vga_mode(PCIQXLDevice *d) +{ + if (d->mode == QXL_MODE_VGA) { + return; + } + trace_qxl_enter_vga_mode(d->id); + qemu_spice_create_host_primary(&d->ssd); + d->mode = QXL_MODE_VGA; + vga_dirty_log_start(&d->vga); + vga_hw_update(); +} + +static void qxl_exit_vga_mode(PCIQXLDevice *d) +{ + if (d->mode != QXL_MODE_VGA) { + return; + } + trace_qxl_exit_vga_mode(d->id); + vga_dirty_log_stop(&d->vga); + qxl_destroy_primary(d, QXL_SYNC); +} + +static void qxl_update_irq(PCIQXLDevice *d) +{ + uint32_t pending = le32_to_cpu(d->ram->int_pending); + uint32_t mask = le32_to_cpu(d->ram->int_mask); + int level = !!(pending & mask); + qemu_set_irq(d->pci.irq[0], level); + qxl_ring_set_dirty(d); +} + +static void qxl_check_state(PCIQXLDevice *d) +{ + QXLRam *ram = d->ram; + int spice_display_running = qemu_spice_display_is_running(&d->ssd); + + assert(!spice_display_running || SPICE_RING_IS_EMPTY(&ram->cmd_ring)); + assert(!spice_display_running || SPICE_RING_IS_EMPTY(&ram->cursor_ring)); +} + +static void qxl_reset_state(PCIQXLDevice *d) +{ + QXLRom *rom = d->rom; + + qxl_check_state(d); + d->shadow_rom.update_id = cpu_to_le32(0); + *rom = d->shadow_rom; + qxl_rom_set_dirty(d); + init_qxl_ram(d); + d->num_free_res = 0; + d->last_release = NULL; + memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty)); +} + +static void qxl_soft_reset(PCIQXLDevice *d) +{ + trace_qxl_soft_reset(d->id); + qxl_check_state(d); + qxl_clear_guest_bug(d); + d->current_async = QXL_UNDEFINED_IO; + + if (d->id == 0) { + qxl_enter_vga_mode(d); + } else { + d->mode = QXL_MODE_UNDEFINED; + } +} + +static void qxl_hard_reset(PCIQXLDevice *d, int loadvm) +{ + trace_qxl_hard_reset(d->id, loadvm); + + qxl_spice_reset_cursor(d); + qxl_spice_reset_image_cache(d); + qxl_reset_surfaces(d); + qxl_reset_memslots(d); + + /* pre loadvm reset must not touch QXLRam. This lives in + * device memory, is migrated together with RAM and thus + * already loaded at this point */ + if (!loadvm) { + qxl_reset_state(d); + } + qemu_spice_create_host_memslot(&d->ssd); + qxl_soft_reset(d); +} + +static void qxl_reset_handler(DeviceState *dev) +{ + PCIQXLDevice *d = DO_UPCAST(PCIQXLDevice, pci.qdev, dev); + + qxl_hard_reset(d, 0); +} + +static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + VGACommonState *vga = opaque; + PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga); + + trace_qxl_io_write_vga(qxl->id, qxl_mode_to_string(qxl->mode), addr, val); + if (qxl->mode != QXL_MODE_VGA) { + qxl_destroy_primary(qxl, QXL_SYNC); + qxl_soft_reset(qxl); + } + vga_ioport_write(opaque, addr, val); +} + +static const MemoryRegionPortio qxl_vga_portio_list[] = { + { 0x04, 2, 1, .read = vga_ioport_read, + .write = qxl_vga_ioport_write }, /* 3b4 */ + { 0x0a, 1, 1, .read = vga_ioport_read, + .write = qxl_vga_ioport_write }, /* 3ba */ + { 0x10, 16, 1, .read = vga_ioport_read, + .write = qxl_vga_ioport_write }, /* 3c0 */ + { 0x24, 2, 1, .read = vga_ioport_read, + .write = qxl_vga_ioport_write }, /* 3d4 */ + { 0x2a, 1, 1, .read = vga_ioport_read, + .write = qxl_vga_ioport_write }, /* 3da */ + PORTIO_END_OF_LIST(), +}; + +static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, + qxl_async_io async) +{ + static const int regions[] = { + QXL_RAM_RANGE_INDEX, + QXL_VRAM_RANGE_INDEX, + QXL_VRAM64_RANGE_INDEX, + }; + uint64_t guest_start; + uint64_t guest_end; + int pci_region; + pcibus_t pci_start; + pcibus_t pci_end; + intptr_t virt_start; + QXLDevMemSlot memslot; + int i; + + guest_start = le64_to_cpu(d->guest_slots[slot_id].slot.mem_start); + guest_end = le64_to_cpu(d->guest_slots[slot_id].slot.mem_end); + + trace_qxl_memslot_add_guest(d->id, slot_id, guest_start, guest_end); + + if (slot_id >= NUM_MEMSLOTS) { + qxl_set_guest_bug(d, "%s: slot_id >= NUM_MEMSLOTS %d >= %d", __func__, + slot_id, NUM_MEMSLOTS); + return 1; + } + if (guest_start > guest_end) { + qxl_set_guest_bug(d, "%s: guest_start > guest_end 0x%" PRIx64 + " > 0x%" PRIx64, __func__, guest_start, guest_end); + return 1; + } + + for (i = 0; i < ARRAY_SIZE(regions); i++) { + pci_region = regions[i]; + pci_start = d->pci.io_regions[pci_region].addr; + pci_end = pci_start + d->pci.io_regions[pci_region].size; + /* mapped? */ + if (pci_start == -1) { + continue; + } + /* start address in range ? */ + if (guest_start < pci_start || guest_start > pci_end) { + continue; + } + /* end address in range ? */ + if (guest_end > pci_end) { + continue; + } + /* passed */ + break; + } + if (i == ARRAY_SIZE(regions)) { + qxl_set_guest_bug(d, "%s: finished loop without match", __func__); + return 1; + } + + switch (pci_region) { + case QXL_RAM_RANGE_INDEX: + virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram); + break; + case QXL_VRAM_RANGE_INDEX: + case 4 /* vram 64bit */: + virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar); + break; + default: + /* should not happen */ + qxl_set_guest_bug(d, "%s: pci_region = %d", __func__, pci_region); + return 1; + } + + memslot.slot_id = slot_id; + memslot.slot_group_id = MEMSLOT_GROUP_GUEST; /* guest group */ + memslot.virt_start = virt_start + (guest_start - pci_start); + memslot.virt_end = virt_start + (guest_end - pci_start); + memslot.addr_delta = memslot.virt_start - delta; + memslot.generation = d->rom->slot_generation = 0; + qxl_rom_set_dirty(d); + + qemu_spice_add_memslot(&d->ssd, &memslot, async); + d->guest_slots[slot_id].ptr = (void*)memslot.virt_start; + d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start; + d->guest_slots[slot_id].delta = delta; + d->guest_slots[slot_id].active = 1; + return 0; +} + +static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id) +{ + qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id); + d->guest_slots[slot_id].active = 0; +} + +static void qxl_reset_memslots(PCIQXLDevice *d) +{ + qxl_spice_reset_memslots(d); + memset(&d->guest_slots, 0, sizeof(d->guest_slots)); +} + +static void qxl_reset_surfaces(PCIQXLDevice *d) +{ + trace_qxl_reset_surfaces(d->id); + d->mode = QXL_MODE_UNDEFINED; + qxl_spice_destroy_surfaces(d, QXL_SYNC); +} + +/* can be also called from spice server thread context */ +void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) +{ + uint64_t phys = le64_to_cpu(pqxl); + uint32_t slot = (phys >> (64 - 8)) & 0xff; + uint64_t offset = phys & 0xffffffffffff; + + switch (group_id) { + case MEMSLOT_GROUP_HOST: + return (void *)(intptr_t)offset; + case MEMSLOT_GROUP_GUEST: + if (slot >= NUM_MEMSLOTS) { + qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot, + NUM_MEMSLOTS); + return NULL; + } + if (!qxl->guest_slots[slot].active) { + qxl_set_guest_bug(qxl, "inactive slot %d\n", slot); + return NULL; + } + if (offset < qxl->guest_slots[slot].delta) { + qxl_set_guest_bug(qxl, + "slot %d offset %"PRIu64" < delta %"PRIu64"\n", + slot, offset, qxl->guest_slots[slot].delta); + return NULL; + } + offset -= qxl->guest_slots[slot].delta; + if (offset > qxl->guest_slots[slot].size) { + qxl_set_guest_bug(qxl, + "slot %d offset %"PRIu64" > size %"PRIu64"\n", + slot, offset, qxl->guest_slots[slot].size); + return NULL; + } + return qxl->guest_slots[slot].ptr + offset; + } + return NULL; +} + +static void qxl_create_guest_primary_complete(PCIQXLDevice *qxl) +{ + /* for local rendering */ + qxl_render_resize(qxl); +} + +static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, + qxl_async_io async) +{ + QXLDevSurfaceCreate surface; + QXLSurfaceCreate *sc = &qxl->guest_primary.surface; + int size; + int requested_height = le32_to_cpu(sc->height); + int requested_stride = le32_to_cpu(sc->stride); + + size = abs(requested_stride) * requested_height; + if (size > qxl->vgamem_size) { + qxl_set_guest_bug(qxl, "%s: requested primary larger then framebuffer" + " size", __func__); + return; + } + + if (qxl->mode == QXL_MODE_NATIVE) { + qxl_set_guest_bug(qxl, "%s: nop since already in QXL_MODE_NATIVE", + __func__); + } + qxl_exit_vga_mode(qxl); + + surface.format = le32_to_cpu(sc->format); + surface.height = le32_to_cpu(sc->height); + surface.mem = le64_to_cpu(sc->mem); + surface.position = le32_to_cpu(sc->position); + surface.stride = le32_to_cpu(sc->stride); + surface.width = le32_to_cpu(sc->width); + surface.type = le32_to_cpu(sc->type); + surface.flags = le32_to_cpu(sc->flags); + trace_qxl_create_guest_primary(qxl->id, sc->width, sc->height, sc->mem, + sc->format, sc->position); + trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type, + sc->flags); + + if ((surface.stride & 0x3) != 0) { + qxl_set_guest_bug(qxl, "primary surface stride = %d %% 4 != 0", + surface.stride); + return; + } + + surface.mouse_mode = true; + surface.group_id = MEMSLOT_GROUP_GUEST; + if (loadvm) { + surface.flags |= QXL_SURF_FLAG_KEEP_DATA; + } + + qxl->mode = QXL_MODE_NATIVE; + qxl->cmdflags = 0; + qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async); + + if (async == QXL_SYNC) { + qxl_create_guest_primary_complete(qxl); + } +} + +/* return 1 if surface destoy was initiated (in QXL_ASYNC case) or + * done (in QXL_SYNC case), 0 otherwise. */ +static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async) +{ + if (d->mode == QXL_MODE_UNDEFINED) { + return 0; + } + trace_qxl_destroy_primary(d->id); + d->mode = QXL_MODE_UNDEFINED; + qemu_spice_destroy_primary_surface(&d->ssd, 0, async); + qxl_spice_reset_cursor(d); + return 1; +} + +static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) +{ + pcibus_t start = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr; + pcibus_t end = d->pci.io_regions[QXL_RAM_RANGE_INDEX].size + start; + QXLMode *mode = d->modes->modes + modenr; + uint64_t devmem = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr; + QXLMemSlot slot = { + .mem_start = start, + .mem_end = end + }; + QXLSurfaceCreate surface = { + .width = mode->x_res, + .height = mode->y_res, + .stride = -mode->x_res * 4, + .format = SPICE_SURFACE_FMT_32_xRGB, + .flags = loadvm ? QXL_SURF_FLAG_KEEP_DATA : 0, + .mouse_mode = true, + .mem = devmem + d->shadow_rom.draw_area_offset, + }; + + trace_qxl_set_mode(d->id, modenr, mode->x_res, mode->y_res, mode->bits, + devmem); + if (!loadvm) { + qxl_hard_reset(d, 0); + } + + d->guest_slots[0].slot = slot; + assert(qxl_add_memslot(d, 0, devmem, QXL_SYNC) == 0); + + d->guest_primary.surface = surface; + qxl_create_guest_primary(d, 0, QXL_SYNC); + + d->mode = QXL_MODE_COMPAT; + d->cmdflags = QXL_COMMAND_FLAG_COMPAT; + if (mode->bits == 16) { + d->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP; + } + d->shadow_rom.mode = cpu_to_le32(modenr); + d->rom->mode = cpu_to_le32(modenr); + qxl_rom_set_dirty(d); +} + +static void ioport_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PCIQXLDevice *d = opaque; + uint32_t io_port = addr; + qxl_async_io async = QXL_SYNC; + uint32_t orig_io_port = io_port; + + if (d->guest_bug && io_port != QXL_IO_RESET) { + return; + } + + if (d->revision <= QXL_REVISION_STABLE_V10 && + io_port > QXL_IO_FLUSH_RELEASE) { + qxl_set_guest_bug(d, "unsupported io %d for revision %d\n", + io_port, d->revision); + return; + } + + switch (io_port) { + case QXL_IO_RESET: + case QXL_IO_SET_MODE: + case QXL_IO_MEMSLOT_ADD: + case QXL_IO_MEMSLOT_DEL: + case QXL_IO_CREATE_PRIMARY: + case QXL_IO_UPDATE_IRQ: + case QXL_IO_LOG: + case QXL_IO_MEMSLOT_ADD_ASYNC: + case QXL_IO_CREATE_PRIMARY_ASYNC: + break; + default: + if (d->mode != QXL_MODE_VGA) { + break; + } + trace_qxl_io_unexpected_vga_mode(d->id, + addr, val, io_port_to_string(io_port)); + /* be nice to buggy guest drivers */ + if (io_port >= QXL_IO_UPDATE_AREA_ASYNC && + io_port < QXL_IO_RANGE_SIZE) { + qxl_send_events(d, QXL_INTERRUPT_IO_CMD); + } + return; + } + + /* we change the io_port to avoid ifdeffery in the main switch */ + orig_io_port = io_port; + switch (io_port) { + case QXL_IO_UPDATE_AREA_ASYNC: + io_port = QXL_IO_UPDATE_AREA; + goto async_common; + case QXL_IO_MEMSLOT_ADD_ASYNC: + io_port = QXL_IO_MEMSLOT_ADD; + goto async_common; + case QXL_IO_CREATE_PRIMARY_ASYNC: + io_port = QXL_IO_CREATE_PRIMARY; + goto async_common; + case QXL_IO_DESTROY_PRIMARY_ASYNC: + io_port = QXL_IO_DESTROY_PRIMARY; + goto async_common; + case QXL_IO_DESTROY_SURFACE_ASYNC: + io_port = QXL_IO_DESTROY_SURFACE_WAIT; + goto async_common; + case QXL_IO_DESTROY_ALL_SURFACES_ASYNC: + io_port = QXL_IO_DESTROY_ALL_SURFACES; + goto async_common; + case QXL_IO_FLUSH_SURFACES_ASYNC: + case QXL_IO_MONITORS_CONFIG_ASYNC: +async_common: + async = QXL_ASYNC; + qemu_mutex_lock(&d->async_lock); + if (d->current_async != QXL_UNDEFINED_IO) { + qxl_set_guest_bug(d, "%d async started before last (%d) complete", + io_port, d->current_async); + qemu_mutex_unlock(&d->async_lock); + return; + } + d->current_async = orig_io_port; + qemu_mutex_unlock(&d->async_lock); + break; + default: + break; + } + trace_qxl_io_write(d->id, qxl_mode_to_string(d->mode), addr, val, size, + async); + + switch (io_port) { + case QXL_IO_UPDATE_AREA: + { + QXLCookie *cookie = NULL; + QXLRect update = d->ram->update_area; + + if (d->ram->update_surface > d->ssd.num_surfaces) { + qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: invalid surface id %d\n", + d->ram->update_surface); + break; + } + if (update.left >= update.right || update.top >= update.bottom || + update.left < 0 || update.top < 0) { + qxl_set_guest_bug(d, + "QXL_IO_UPDATE_AREA: invalid area (%ux%u)x(%ux%u)\n", + update.left, update.top, update.right, update.bottom); + break; + } + if (async == QXL_ASYNC) { + cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, + QXL_IO_UPDATE_AREA_ASYNC); + cookie->u.area = update; + } + qxl_spice_update_area(d, d->ram->update_surface, + cookie ? &cookie->u.area : &update, + NULL, 0, 0, async, cookie); + break; + } + case QXL_IO_NOTIFY_CMD: + qemu_spice_wakeup(&d->ssd); + break; + case QXL_IO_NOTIFY_CURSOR: + qemu_spice_wakeup(&d->ssd); + break; + case QXL_IO_UPDATE_IRQ: + qxl_update_irq(d); + break; + case QXL_IO_NOTIFY_OOM: + if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) { + break; + } + d->oom_running = 1; + qxl_spice_oom(d); + d->oom_running = 0; + break; + case QXL_IO_SET_MODE: + qxl_set_mode(d, val, 0); + break; + case QXL_IO_LOG: + trace_qxl_io_log(d->id, d->ram->log_buf); + if (d->guestdebug) { + fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id, + qemu_get_clock_ns(vm_clock), d->ram->log_buf); + } + break; + case QXL_IO_RESET: + qxl_hard_reset(d, 0); + break; + case QXL_IO_MEMSLOT_ADD: + if (val >= NUM_MEMSLOTS) { + qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_ADD: val out of range"); + break; + } + if (d->guest_slots[val].active) { + qxl_set_guest_bug(d, + "QXL_IO_MEMSLOT_ADD: memory slot already active"); + break; + } + d->guest_slots[val].slot = d->ram->mem_slot; + qxl_add_memslot(d, val, 0, async); + break; + case QXL_IO_MEMSLOT_DEL: + if (val >= NUM_MEMSLOTS) { + qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_DEL: val out of range"); + break; + } + qxl_del_memslot(d, val); + break; + case QXL_IO_CREATE_PRIMARY: + if (val != 0) { + qxl_set_guest_bug(d, "QXL_IO_CREATE_PRIMARY (async=%d): val != 0", + async); + goto cancel_async; + } + d->guest_primary.surface = d->ram->create_surface; + qxl_create_guest_primary(d, 0, async); + break; + case QXL_IO_DESTROY_PRIMARY: + if (val != 0) { + qxl_set_guest_bug(d, "QXL_IO_DESTROY_PRIMARY (async=%d): val != 0", + async); + goto cancel_async; + } + if (!qxl_destroy_primary(d, async)) { + trace_qxl_io_destroy_primary_ignored(d->id, + qxl_mode_to_string(d->mode)); + goto cancel_async; + } + break; + case QXL_IO_DESTROY_SURFACE_WAIT: + if (val >= d->ssd.num_surfaces) { + qxl_set_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):" + "%" PRIu64 " >= NUM_SURFACES", async, val); + goto cancel_async; + } + qxl_spice_destroy_surface_wait(d, val, async); + break; + case QXL_IO_FLUSH_RELEASE: { + QXLReleaseRing *ring = &d->ram->release_ring; + if (ring->prod - ring->cons + 1 == ring->num_items) { + fprintf(stderr, + "ERROR: no flush, full release ring [p%d,%dc]\n", + ring->prod, ring->cons); + } + qxl_push_free_res(d, 1 /* flush */); + break; + } + case QXL_IO_FLUSH_SURFACES_ASYNC: + qxl_spice_flush_surfaces_async(d); + break; + case QXL_IO_DESTROY_ALL_SURFACES: + d->mode = QXL_MODE_UNDEFINED; + qxl_spice_destroy_surfaces(d, async); + break; + case QXL_IO_MONITORS_CONFIG_ASYNC: + qxl_spice_monitors_config_async(d, 0); + break; + default: + qxl_set_guest_bug(d, "%s: unexpected ioport=0x%x\n", __func__, io_port); + } + return; +cancel_async: + if (async) { + qxl_send_events(d, QXL_INTERRUPT_IO_CMD); + qemu_mutex_lock(&d->async_lock); + d->current_async = QXL_UNDEFINED_IO; + qemu_mutex_unlock(&d->async_lock); + } +} + +static uint64_t ioport_read(void *opaque, hwaddr addr, + unsigned size) +{ + PCIQXLDevice *qxl = opaque; + + trace_qxl_io_read_unexpected(qxl->id); + return 0xff; +} + +static const MemoryRegionOps qxl_io_ops = { + .read = ioport_read, + .write = ioport_write, + .valid = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static void pipe_read(void *opaque) +{ + PCIQXLDevice *d = opaque; + char dummy; + int len; + + do { + len = read(d->pipe[0], &dummy, sizeof(dummy)); + } while (len == sizeof(dummy)); + qxl_update_irq(d); +} + +static void qxl_send_events(PCIQXLDevice *d, uint32_t events) +{ + uint32_t old_pending; + uint32_t le_events = cpu_to_le32(events); + + trace_qxl_send_events(d->id, events); + if (!qemu_spice_display_is_running(&d->ssd)) { + /* spice-server tracks guest running state and should not do this */ + fprintf(stderr, "%s: spice-server bug: guest stopped, ignoring\n", + __func__); + trace_qxl_send_events_vm_stopped(d->id, events); + return; + } + old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events); + if ((old_pending & le_events) == le_events) { + return; + } + if (qemu_thread_is_self(&d->main)) { + qxl_update_irq(d); + } else { + if (write(d->pipe[1], d, 1) != 1) { + dprint(d, 1, "%s: write to pipe failed\n", __func__); + } + } +} + +static void init_pipe_signaling(PCIQXLDevice *d) +{ + if (pipe(d->pipe) < 0) { + fprintf(stderr, "%s:%s: qxl pipe creation failed\n", + __FILE__, __func__); + exit(1); + } + fcntl(d->pipe[0], F_SETFL, O_NONBLOCK); + fcntl(d->pipe[1], F_SETFL, O_NONBLOCK); + fcntl(d->pipe[0], F_SETOWN, getpid()); + + qemu_thread_get_self(&d->main); + qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d); +} + +/* graphics console */ + +static void qxl_hw_update(void *opaque) +{ + PCIQXLDevice *qxl = opaque; + VGACommonState *vga = &qxl->vga; + + switch (qxl->mode) { + case QXL_MODE_VGA: + vga->update(vga); + break; + case QXL_MODE_COMPAT: + case QXL_MODE_NATIVE: + qxl_render_update(qxl); + break; + default: + break; + } +} + +static void qxl_hw_invalidate(void *opaque) +{ + PCIQXLDevice *qxl = opaque; + VGACommonState *vga = &qxl->vga; + + vga->invalidate(vga); +} + +static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp) +{ + PCIQXLDevice *qxl = opaque; + VGACommonState *vga = &qxl->vga; + + switch (qxl->mode) { + case QXL_MODE_COMPAT: + case QXL_MODE_NATIVE: + qxl_render_update(qxl); + ppm_save(filename, qxl->ssd.ds, errp); + break; + case QXL_MODE_VGA: + vga->screen_dump(vga, filename, cswitch, errp); + break; + default: + break; + } +} + +static void qxl_hw_text_update(void *opaque, console_ch_t *chardata) +{ + PCIQXLDevice *qxl = opaque; + VGACommonState *vga = &qxl->vga; + + if (qxl->mode == QXL_MODE_VGA) { + vga->text_update(vga, chardata); + return; + } +} + +static void qxl_dirty_surfaces(PCIQXLDevice *qxl) +{ + uintptr_t vram_start; + int i; + + if (qxl->mode != QXL_MODE_NATIVE && qxl->mode != QXL_MODE_COMPAT) { + return; + } + + /* dirty the primary surface */ + qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset, + qxl->shadow_rom.surface0_area_size); + + vram_start = (uintptr_t)memory_region_get_ram_ptr(&qxl->vram_bar); + + /* dirty the off-screen surfaces */ + for (i = 0; i < qxl->ssd.num_surfaces; i++) { + QXLSurfaceCmd *cmd; + intptr_t surface_offset; + int surface_size; + + if (qxl->guest_surfaces.cmds[i] == 0) { + continue; + } + + cmd = qxl_phys2virt(qxl, qxl->guest_surfaces.cmds[i], + MEMSLOT_GROUP_GUEST); + assert(cmd); + assert(cmd->type == QXL_SURFACE_CMD_CREATE); + surface_offset = (intptr_t)qxl_phys2virt(qxl, + cmd->u.surface_create.data, + MEMSLOT_GROUP_GUEST); + assert(surface_offset); + surface_offset -= vram_start; + surface_size = cmd->u.surface_create.height * + abs(cmd->u.surface_create.stride); + trace_qxl_surfaces_dirty(qxl->id, i, (int)surface_offset, surface_size); + qxl_set_dirty(&qxl->vram_bar, surface_offset, surface_size); + } +} + +static void qxl_vm_change_state_handler(void *opaque, int running, + RunState state) +{ + PCIQXLDevice *qxl = opaque; + + if (running) { + /* + * if qxl_send_events was called from spice server context before + * migration ended, qxl_update_irq for these events might not have been + * called + */ + qxl_update_irq(qxl); + } else { + /* make sure surfaces are saved before migration */ + qxl_dirty_surfaces(qxl); + } +} + +/* display change listener */ + +static void display_update(DisplayChangeListener *dcl, + int x, int y, int w, int h) +{ + PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl); + + if (qxl->mode == QXL_MODE_VGA) { + qemu_spice_display_update(&qxl->ssd, x, y, w, h); + } +} + +static void display_switch(DisplayChangeListener *dcl, + struct DisplaySurface *surface) +{ + PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl); + + qxl->ssd.ds = surface; + if (qxl->mode == QXL_MODE_VGA) { + qemu_spice_display_switch(&qxl->ssd, surface); + } +} + +static void display_refresh(DisplayChangeListener *dcl) +{ + PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl); + + if (qxl->mode == QXL_MODE_VGA) { + qemu_spice_display_refresh(&qxl->ssd); + } else { + qemu_mutex_lock(&qxl->ssd.lock); + qemu_spice_cursor_refresh_unlocked(&qxl->ssd); + qemu_mutex_unlock(&qxl->ssd.lock); + } +} + +static DisplayChangeListenerOps display_listener_ops = { + .dpy_name = "spice/qxl", + .dpy_gfx_update = display_update, + .dpy_gfx_switch = display_switch, + .dpy_refresh = display_refresh, +}; + +static void qxl_init_ramsize(PCIQXLDevice *qxl) +{ + /* vga mode framebuffer / primary surface (bar 0, first part) */ + if (qxl->vgamem_size_mb < 8) { + qxl->vgamem_size_mb = 8; + } + qxl->vgamem_size = qxl->vgamem_size_mb * 1024 * 1024; + + /* vga ram (bar 0, total) */ + if (qxl->ram_size_mb != -1) { + qxl->vga.vram_size = qxl->ram_size_mb * 1024 * 1024; + } + if (qxl->vga.vram_size < qxl->vgamem_size * 2) { + qxl->vga.vram_size = qxl->vgamem_size * 2; + } + + /* vram32 (surfaces, 32bit, bar 1) */ + if (qxl->vram32_size_mb != -1) { + qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024; + } + if (qxl->vram32_size < 4096) { + qxl->vram32_size = 4096; + } + + /* vram (surfaces, 64bit, bar 4+5) */ + if (qxl->vram_size_mb != -1) { + qxl->vram_size = qxl->vram_size_mb * 1024 * 1024; + } + if (qxl->vram_size < qxl->vram32_size) { + qxl->vram_size = qxl->vram32_size; + } + + if (qxl->revision == 1) { + qxl->vram32_size = 4096; + qxl->vram_size = 4096; + } + qxl->vgamem_size = msb_mask(qxl->vgamem_size * 2 - 1); + qxl->vga.vram_size = msb_mask(qxl->vga.vram_size * 2 - 1); + qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1); + qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1); +} + +static int qxl_init_common(PCIQXLDevice *qxl) +{ + uint8_t* config = qxl->pci.config; + uint32_t pci_device_rev; + uint32_t io_size; + + qxl->mode = QXL_MODE_UNDEFINED; + qxl->generation = 1; + qxl->num_memslots = NUM_MEMSLOTS; + qemu_mutex_init(&qxl->track_lock); + qemu_mutex_init(&qxl->async_lock); + qxl->current_async = QXL_UNDEFINED_IO; + qxl->guest_bug = 0; + + switch (qxl->revision) { + case 1: /* spice 0.4 -- qxl-1 */ + pci_device_rev = QXL_REVISION_STABLE_V04; + io_size = 8; + break; + case 2: /* spice 0.6 -- qxl-2 */ + pci_device_rev = QXL_REVISION_STABLE_V06; + io_size = 16; + break; + case 3: /* qxl-3 */ + pci_device_rev = QXL_REVISION_STABLE_V10; + io_size = 32; /* PCI region size must be pow2 */ + break; + case 4: /* qxl-4 */ + pci_device_rev = QXL_REVISION_STABLE_V12; + io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1); + break; + default: + error_report("Invalid revision %d for qxl device (max %d)", + qxl->revision, QXL_DEFAULT_REVISION); + return -1; + } + + pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev); + pci_set_byte(&config[PCI_INTERRUPT_PIN], 1); + + qxl->rom_size = qxl_rom_size(); + memory_region_init_ram(&qxl->rom_bar, "qxl.vrom", qxl->rom_size); + vmstate_register_ram(&qxl->rom_bar, &qxl->pci.qdev); + init_qxl_rom(qxl); + init_qxl_ram(qxl); + + qxl->guest_surfaces.cmds = g_new0(QXLPHYSICAL, qxl->ssd.num_surfaces); + memory_region_init_ram(&qxl->vram_bar, "qxl.vram", qxl->vram_size); + vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev); + memory_region_init_alias(&qxl->vram32_bar, "qxl.vram32", &qxl->vram_bar, + 0, qxl->vram32_size); + + memory_region_init_io(&qxl->io_bar, &qxl_io_ops, qxl, + "qxl-ioports", io_size); + if (qxl->id == 0) { + vga_dirty_log_start(&qxl->vga); + } + memory_region_set_flush_coalesced(&qxl->io_bar); + + + pci_register_bar(&qxl->pci, QXL_IO_RANGE_INDEX, + PCI_BASE_ADDRESS_SPACE_IO, &qxl->io_bar); + + pci_register_bar(&qxl->pci, QXL_ROM_RANGE_INDEX, + PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->rom_bar); + + pci_register_bar(&qxl->pci, QXL_RAM_RANGE_INDEX, + PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vga.vram); + + pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX, + PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram32_bar); + + if (qxl->vram32_size < qxl->vram_size) { + /* + * Make the 64bit vram bar show up only in case it is + * configured to be larger than the 32bit vram bar. + */ + pci_register_bar(&qxl->pci, QXL_VRAM64_RANGE_INDEX, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_TYPE_64 | + PCI_BASE_ADDRESS_MEM_PREFETCH, + &qxl->vram_bar); + } + + /* print pci bar details */ + dprint(qxl, 1, "ram/%s: %d MB [region 0]\n", + qxl->id == 0 ? "pri" : "sec", + qxl->vga.vram_size / (1024*1024)); + dprint(qxl, 1, "vram/32: %d MB [region 1]\n", + qxl->vram32_size / (1024*1024)); + dprint(qxl, 1, "vram/64: %d MB %s\n", + qxl->vram_size / (1024*1024), + qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]"); + + qxl->ssd.qxl.base.sif = &qxl_interface.base; + qxl->ssd.qxl.id = qxl->id; + if (qemu_spice_add_interface(&qxl->ssd.qxl.base) != 0) { + error_report("qxl interface %d.%d not supported by spice-server", + SPICE_INTERFACE_QXL_MAJOR, SPICE_INTERFACE_QXL_MINOR); + return -1; + } + qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); + + init_pipe_signaling(qxl); + qxl_reset_state(qxl); + + qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl); + + return 0; +} + +static int qxl_init_primary(PCIDevice *dev) +{ + PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev); + VGACommonState *vga = &qxl->vga; + PortioList *qxl_vga_port_list = g_new(PortioList, 1); + DisplayState *ds; + int rc; + + qxl->id = 0; + qxl_init_ramsize(qxl); + vga->vram_size_mb = qxl->vga.vram_size >> 20; + vga_common_init(vga); + vga_init(vga, pci_address_space(dev), pci_address_space_io(dev), false); + portio_list_init(qxl_vga_port_list, qxl_vga_portio_list, vga, "vga"); + portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0); + + vga->con = graphic_console_init(qxl_hw_update, qxl_hw_invalidate, + qxl_hw_screen_dump, qxl_hw_text_update, + qxl); + qxl->ssd.con = vga->con, + qemu_spice_display_init_common(&qxl->ssd); + + rc = qxl_init_common(qxl); + if (rc != 0) { + return rc; + } + + qxl->ssd.dcl.ops = &display_listener_ops; + ds = qemu_console_displaystate(vga->con); + register_displaychangelistener(ds, &qxl->ssd.dcl); + return rc; +} + +static int qxl_init_secondary(PCIDevice *dev) +{ + static int device_id = 1; + PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev); + + qxl->id = device_id++; + qxl_init_ramsize(qxl); + memory_region_init_ram(&qxl->vga.vram, "qxl.vgavram", qxl->vga.vram_size); + vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev); + qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram); + + return qxl_init_common(qxl); +} + +static void qxl_pre_save(void *opaque) +{ + PCIQXLDevice* d = opaque; + uint8_t *ram_start = d->vga.vram_ptr; + + trace_qxl_pre_save(d->id); + if (d->last_release == NULL) { + d->last_release_offset = 0; + } else { + d->last_release_offset = (uint8_t *)d->last_release - ram_start; + } + assert(d->last_release_offset < d->vga.vram_size); +} + +static int qxl_pre_load(void *opaque) +{ + PCIQXLDevice* d = opaque; + + trace_qxl_pre_load(d->id); + qxl_hard_reset(d, 1); + qxl_exit_vga_mode(d); + return 0; +} + +static void qxl_create_memslots(PCIQXLDevice *d) +{ + int i; + + for (i = 0; i < NUM_MEMSLOTS; i++) { + if (!d->guest_slots[i].active) { + continue; + } + qxl_add_memslot(d, i, 0, QXL_SYNC); + } +} + +static int qxl_post_load(void *opaque, int version) +{ + PCIQXLDevice* d = opaque; + uint8_t *ram_start = d->vga.vram_ptr; + QXLCommandExt *cmds; + int in, out, newmode; + + assert(d->last_release_offset < d->vga.vram_size); + if (d->last_release_offset == 0) { + d->last_release = NULL; + } else { + d->last_release = (QXLReleaseInfo *)(ram_start + d->last_release_offset); + } + + d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset); + + trace_qxl_post_load(d->id, qxl_mode_to_string(d->mode)); + newmode = d->mode; + d->mode = QXL_MODE_UNDEFINED; + + switch (newmode) { + case QXL_MODE_UNDEFINED: + qxl_create_memslots(d); + break; + case QXL_MODE_VGA: + qxl_create_memslots(d); + qxl_enter_vga_mode(d); + break; + case QXL_MODE_NATIVE: + qxl_create_memslots(d); + qxl_create_guest_primary(d, 1, QXL_SYNC); + + /* replay surface-create and cursor-set commands */ + cmds = g_malloc0(sizeof(QXLCommandExt) * (d->ssd.num_surfaces + 1)); + for (in = 0, out = 0; in < d->ssd.num_surfaces; in++) { + if (d->guest_surfaces.cmds[in] == 0) { + continue; + } + cmds[out].cmd.data = d->guest_surfaces.cmds[in]; + cmds[out].cmd.type = QXL_CMD_SURFACE; + cmds[out].group_id = MEMSLOT_GROUP_GUEST; + out++; + } + if (d->guest_cursor) { + cmds[out].cmd.data = d->guest_cursor; + cmds[out].cmd.type = QXL_CMD_CURSOR; + cmds[out].group_id = MEMSLOT_GROUP_GUEST; + out++; + } + qxl_spice_loadvm_commands(d, cmds, out); + g_free(cmds); + if (d->guest_monitors_config) { + qxl_spice_monitors_config_async(d, 1); + } + break; + case QXL_MODE_COMPAT: + /* note: no need to call qxl_create_memslots, qxl_set_mode + * creates the mem slot. */ + qxl_set_mode(d, d->shadow_rom.mode, 1); + break; + } + return 0; +} + +#define QXL_SAVE_VERSION 21 + +static bool qxl_monitors_config_needed(void *opaque) +{ + PCIQXLDevice *qxl = opaque; + + return qxl->guest_monitors_config != 0; +} + + +static VMStateDescription qxl_memslot = { + .name = "qxl-memslot", + .version_id = QXL_SAVE_VERSION, + .minimum_version_id = QXL_SAVE_VERSION, + .fields = (VMStateField[]) { + VMSTATE_UINT64(slot.mem_start, struct guest_slots), + VMSTATE_UINT64(slot.mem_end, struct guest_slots), + VMSTATE_UINT32(active, struct guest_slots), + VMSTATE_END_OF_LIST() + } +}; + +static VMStateDescription qxl_surface = { + .name = "qxl-surface", + .version_id = QXL_SAVE_VERSION, + .minimum_version_id = QXL_SAVE_VERSION, + .fields = (VMStateField[]) { + VMSTATE_UINT32(width, QXLSurfaceCreate), + VMSTATE_UINT32(height, QXLSurfaceCreate), + VMSTATE_INT32(stride, QXLSurfaceCreate), + VMSTATE_UINT32(format, QXLSurfaceCreate), + VMSTATE_UINT32(position, QXLSurfaceCreate), + VMSTATE_UINT32(mouse_mode, QXLSurfaceCreate), + VMSTATE_UINT32(flags, QXLSurfaceCreate), + VMSTATE_UINT32(type, QXLSurfaceCreate), + VMSTATE_UINT64(mem, QXLSurfaceCreate), + VMSTATE_END_OF_LIST() + } +}; + +static VMStateDescription qxl_vmstate_monitors_config = { + .name = "qxl/monitors-config", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(guest_monitors_config, PCIQXLDevice), + VMSTATE_END_OF_LIST() + }, +}; + +static VMStateDescription qxl_vmstate = { + .name = "qxl", + .version_id = QXL_SAVE_VERSION, + .minimum_version_id = QXL_SAVE_VERSION, + .pre_save = qxl_pre_save, + .pre_load = qxl_pre_load, + .post_load = qxl_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(pci, PCIQXLDevice), + VMSTATE_STRUCT(vga, PCIQXLDevice, 0, vmstate_vga_common, VGACommonState), + VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice), + VMSTATE_UINT32(num_free_res, PCIQXLDevice), + VMSTATE_UINT32(last_release_offset, PCIQXLDevice), + VMSTATE_UINT32(mode, PCIQXLDevice), + VMSTATE_UINT32(ssd.unique, PCIQXLDevice), + VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice), + VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, 0, + qxl_memslot, struct guest_slots), + VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0, + qxl_surface, QXLSurfaceCreate), + VMSTATE_INT32_EQUAL(ssd.num_surfaces, PCIQXLDevice), + VMSTATE_VARRAY_INT32(guest_surfaces.cmds, PCIQXLDevice, + ssd.num_surfaces, 0, + vmstate_info_uint64, uint64_t), + VMSTATE_UINT64(guest_cursor, PCIQXLDevice), + VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &qxl_vmstate_monitors_config, + .needed = qxl_monitors_config_needed, + }, { + /* empty */ + } + } +}; + +static Property qxl_properties[] = { + DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, + 64 * 1024 * 1024), + DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size, + 64 * 1024 * 1024), + DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, + QXL_DEFAULT_REVISION), + DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), + DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), + DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), + DEFINE_PROP_UINT32("ram_size_mb", PCIQXLDevice, ram_size_mb, -1), + DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1), + DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1), + DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16), + DEFINE_PROP_INT32("surfaces", PCIQXLDevice, ssd.num_surfaces, 1024), + DEFINE_PROP_END_OF_LIST(), +}; + +static void qxl_primary_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->no_hotplug = 1; + k->init = qxl_init_primary; + k->romfile = "vgabios-qxl.bin"; + k->vendor_id = REDHAT_PCI_VENDOR_ID; + k->device_id = QXL_DEVICE_ID_STABLE; + k->class_id = PCI_CLASS_DISPLAY_VGA; + dc->desc = "Spice QXL GPU (primary, vga compatible)"; + dc->reset = qxl_reset_handler; + dc->vmsd = &qxl_vmstate; + dc->props = qxl_properties; +} + +static const TypeInfo qxl_primary_info = { + .name = "qxl-vga", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIQXLDevice), + .class_init = qxl_primary_class_init, +}; + +static void qxl_secondary_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = qxl_init_secondary; + k->vendor_id = REDHAT_PCI_VENDOR_ID; + k->device_id = QXL_DEVICE_ID_STABLE; + k->class_id = PCI_CLASS_DISPLAY_OTHER; + dc->desc = "Spice QXL GPU (secondary)"; + dc->reset = qxl_reset_handler; + dc->vmsd = &qxl_vmstate; + dc->props = qxl_properties; +} + +static const TypeInfo qxl_secondary_info = { + .name = "qxl", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIQXLDevice), + .class_init = qxl_secondary_class_init, +}; + +static void qxl_register_types(void) +{ + type_register_static(&qxl_primary_info); + type_register_static(&qxl_secondary_info); +} + +type_init(qxl_register_types) diff --git a/hw/display/sm501.c b/hw/display/sm501.c new file mode 100644 index 0000000000..d9fcead719 --- /dev/null +++ b/hw/display/sm501.c @@ -0,0 +1,1450 @@ +/* + * QEMU SM501 Device + * + * Copyright (c) 2008 Shin-ichiro KAWASAKI + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include "hw/hw.h" +#include "hw/char/serial.h" +#include "ui/console.h" +#include "hw/arm/devices.h" +#include "hw/sysbus.h" +#include "hw/qdev-addr.h" +#include "qemu/range.h" +#include "ui/pixel_ops.h" + +/* + * Status: 2010/05/07 + * - Minimum implementation for Linux console : mmio regs and CRT layer. + * - 2D grapihcs acceleration partially supported : only fill rectangle. + * + * TODO: + * - Panel support + * - Touch panel support + * - USB support + * - UART support + * - More 2D graphics engine support + * - Performance tuning + */ + +//#define DEBUG_SM501 +//#define DEBUG_BITBLT + +#ifdef DEBUG_SM501 +#define SM501_DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define SM501_DPRINTF(fmt, ...) do {} while(0) +#endif + + +#define MMIO_BASE_OFFSET 0x3e00000 + +/* SM501 register definitions taken from "linux/include/linux/sm501-regs.h" */ + +/* System Configuration area */ +/* System config base */ +#define SM501_SYS_CONFIG (0x000000) + +/* config 1 */ +#define SM501_SYSTEM_CONTROL (0x000000) + +#define SM501_SYSCTRL_PANEL_TRISTATE (1<<0) +#define SM501_SYSCTRL_MEM_TRISTATE (1<<1) +#define SM501_SYSCTRL_CRT_TRISTATE (1<<2) + +#define SM501_SYSCTRL_PCI_SLAVE_BURST_MASK (3<<4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_1 (0<<4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_2 (1<<4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_4 (2<<4) +#define SM501_SYSCTRL_PCI_SLAVE_BURST_8 (3<<4) + +#define SM501_SYSCTRL_PCI_CLOCK_RUN_EN (1<<6) +#define SM501_SYSCTRL_PCI_RETRY_DISABLE (1<<7) +#define SM501_SYSCTRL_PCI_SUBSYS_LOCK (1<<11) +#define SM501_SYSCTRL_PCI_BURST_READ_EN (1<<15) + +/* miscellaneous control */ + +#define SM501_MISC_CONTROL (0x000004) + +#define SM501_MISC_BUS_SH (0x0) +#define SM501_MISC_BUS_PCI (0x1) +#define SM501_MISC_BUS_XSCALE (0x2) +#define SM501_MISC_BUS_NEC (0x6) +#define SM501_MISC_BUS_MASK (0x7) + +#define SM501_MISC_VR_62MB (1<<3) +#define SM501_MISC_CDR_RESET (1<<7) +#define SM501_MISC_USB_LB (1<<8) +#define SM501_MISC_USB_SLAVE (1<<9) +#define SM501_MISC_BL_1 (1<<10) +#define SM501_MISC_MC (1<<11) +#define SM501_MISC_DAC_POWER (1<<12) +#define SM501_MISC_IRQ_INVERT (1<<16) +#define SM501_MISC_SH (1<<17) + +#define SM501_MISC_HOLD_EMPTY (0<<18) +#define SM501_MISC_HOLD_8 (1<<18) +#define SM501_MISC_HOLD_16 (2<<18) +#define SM501_MISC_HOLD_24 (3<<18) +#define SM501_MISC_HOLD_32 (4<<18) +#define SM501_MISC_HOLD_MASK (7<<18) + +#define SM501_MISC_FREQ_12 (1<<24) +#define SM501_MISC_PNL_24BIT (1<<25) +#define SM501_MISC_8051_LE (1<<26) + + + +#define SM501_GPIO31_0_CONTROL (0x000008) +#define SM501_GPIO63_32_CONTROL (0x00000C) +#define SM501_DRAM_CONTROL (0x000010) + +/* command list */ +#define SM501_ARBTRTN_CONTROL (0x000014) + +/* command list */ +#define SM501_COMMAND_LIST_STATUS (0x000024) + +/* interrupt debug */ +#define SM501_RAW_IRQ_STATUS (0x000028) +#define SM501_RAW_IRQ_CLEAR (0x000028) +#define SM501_IRQ_STATUS (0x00002C) +#define SM501_IRQ_MASK (0x000030) +#define SM501_DEBUG_CONTROL (0x000034) + +/* power management */ +#define SM501_POWERMODE_P2X_SRC (1<<29) +#define SM501_POWERMODE_V2X_SRC (1<<20) +#define SM501_POWERMODE_M_SRC (1<<12) +#define SM501_POWERMODE_M1_SRC (1<<4) + +#define SM501_CURRENT_GATE (0x000038) +#define SM501_CURRENT_CLOCK (0x00003C) +#define SM501_POWER_MODE_0_GATE (0x000040) +#define SM501_POWER_MODE_0_CLOCK (0x000044) +#define SM501_POWER_MODE_1_GATE (0x000048) +#define SM501_POWER_MODE_1_CLOCK (0x00004C) +#define SM501_SLEEP_MODE_GATE (0x000050) +#define SM501_POWER_MODE_CONTROL (0x000054) + +/* power gates for units within the 501 */ +#define SM501_GATE_HOST (0) +#define SM501_GATE_MEMORY (1) +#define SM501_GATE_DISPLAY (2) +#define SM501_GATE_2D_ENGINE (3) +#define SM501_GATE_CSC (4) +#define SM501_GATE_ZVPORT (5) +#define SM501_GATE_GPIO (6) +#define SM501_GATE_UART0 (7) +#define SM501_GATE_UART1 (8) +#define SM501_GATE_SSP (10) +#define SM501_GATE_USB_HOST (11) +#define SM501_GATE_USB_GADGET (12) +#define SM501_GATE_UCONTROLLER (17) +#define SM501_GATE_AC97 (18) + +/* panel clock */ +#define SM501_CLOCK_P2XCLK (24) +/* crt clock */ +#define SM501_CLOCK_V2XCLK (16) +/* main clock */ +#define SM501_CLOCK_MCLK (8) +/* SDRAM controller clock */ +#define SM501_CLOCK_M1XCLK (0) + +/* config 2 */ +#define SM501_PCI_MASTER_BASE (0x000058) +#define SM501_ENDIAN_CONTROL (0x00005C) +#define SM501_DEVICEID (0x000060) +/* 0x050100A0 */ + +#define SM501_DEVICEID_SM501 (0x05010000) +#define SM501_DEVICEID_IDMASK (0xffff0000) +#define SM501_DEVICEID_REVMASK (0x000000ff) + +#define SM501_PLLCLOCK_COUNT (0x000064) +#define SM501_MISC_TIMING (0x000068) +#define SM501_CURRENT_SDRAM_CLOCK (0x00006C) + +#define SM501_PROGRAMMABLE_PLL_CONTROL (0x000074) + +/* GPIO base */ +#define SM501_GPIO (0x010000) +#define SM501_GPIO_DATA_LOW (0x00) +#define SM501_GPIO_DATA_HIGH (0x04) +#define SM501_GPIO_DDR_LOW (0x08) +#define SM501_GPIO_DDR_HIGH (0x0C) +#define SM501_GPIO_IRQ_SETUP (0x10) +#define SM501_GPIO_IRQ_STATUS (0x14) +#define SM501_GPIO_IRQ_RESET (0x14) + +/* I2C controller base */ +#define SM501_I2C (0x010040) +#define SM501_I2C_BYTE_COUNT (0x00) +#define SM501_I2C_CONTROL (0x01) +#define SM501_I2C_STATUS (0x02) +#define SM501_I2C_RESET (0x02) +#define SM501_I2C_SLAVE_ADDRESS (0x03) +#define SM501_I2C_DATA (0x04) + +/* SSP base */ +#define SM501_SSP (0x020000) + +/* Uart 0 base */ +#define SM501_UART0 (0x030000) + +/* Uart 1 base */ +#define SM501_UART1 (0x030020) + +/* USB host port base */ +#define SM501_USB_HOST (0x040000) + +/* USB slave/gadget base */ +#define SM501_USB_GADGET (0x060000) + +/* USB slave/gadget data port base */ +#define SM501_USB_GADGET_DATA (0x070000) + +/* Display controller/video engine base */ +#define SM501_DC (0x080000) + +/* common defines for the SM501 address registers */ +#define SM501_ADDR_FLIP (1<<31) +#define SM501_ADDR_EXT (1<<27) +#define SM501_ADDR_CS1 (1<<26) +#define SM501_ADDR_MASK (0x3f << 26) + +#define SM501_FIFO_MASK (0x3 << 16) +#define SM501_FIFO_1 (0x0 << 16) +#define SM501_FIFO_3 (0x1 << 16) +#define SM501_FIFO_7 (0x2 << 16) +#define SM501_FIFO_11 (0x3 << 16) + +/* common registers for panel and the crt */ +#define SM501_OFF_DC_H_TOT (0x000) +#define SM501_OFF_DC_V_TOT (0x008) +#define SM501_OFF_DC_H_SYNC (0x004) +#define SM501_OFF_DC_V_SYNC (0x00C) + +#define SM501_DC_PANEL_CONTROL (0x000) + +#define SM501_DC_PANEL_CONTROL_FPEN (1<<27) +#define SM501_DC_PANEL_CONTROL_BIAS (1<<26) +#define SM501_DC_PANEL_CONTROL_DATA (1<<25) +#define SM501_DC_PANEL_CONTROL_VDD (1<<24) +#define SM501_DC_PANEL_CONTROL_DP (1<<23) + +#define SM501_DC_PANEL_CONTROL_TFT_888 (0<<21) +#define SM501_DC_PANEL_CONTROL_TFT_333 (1<<21) +#define SM501_DC_PANEL_CONTROL_TFT_444 (2<<21) + +#define SM501_DC_PANEL_CONTROL_DE (1<<20) + +#define SM501_DC_PANEL_CONTROL_LCD_TFT (0<<18) +#define SM501_DC_PANEL_CONTROL_LCD_STN8 (1<<18) +#define SM501_DC_PANEL_CONTROL_LCD_STN12 (2<<18) + +#define SM501_DC_PANEL_CONTROL_CP (1<<14) +#define SM501_DC_PANEL_CONTROL_VSP (1<<13) +#define SM501_DC_PANEL_CONTROL_HSP (1<<12) +#define SM501_DC_PANEL_CONTROL_CK (1<<9) +#define SM501_DC_PANEL_CONTROL_TE (1<<8) +#define SM501_DC_PANEL_CONTROL_VPD (1<<7) +#define SM501_DC_PANEL_CONTROL_VP (1<<6) +#define SM501_DC_PANEL_CONTROL_HPD (1<<5) +#define SM501_DC_PANEL_CONTROL_HP (1<<4) +#define SM501_DC_PANEL_CONTROL_GAMMA (1<<3) +#define SM501_DC_PANEL_CONTROL_EN (1<<2) + +#define SM501_DC_PANEL_CONTROL_8BPP (0<<0) +#define SM501_DC_PANEL_CONTROL_16BPP (1<<0) +#define SM501_DC_PANEL_CONTROL_32BPP (2<<0) + + +#define SM501_DC_PANEL_PANNING_CONTROL (0x004) +#define SM501_DC_PANEL_COLOR_KEY (0x008) +#define SM501_DC_PANEL_FB_ADDR (0x00C) +#define SM501_DC_PANEL_FB_OFFSET (0x010) +#define SM501_DC_PANEL_FB_WIDTH (0x014) +#define SM501_DC_PANEL_FB_HEIGHT (0x018) +#define SM501_DC_PANEL_TL_LOC (0x01C) +#define SM501_DC_PANEL_BR_LOC (0x020) +#define SM501_DC_PANEL_H_TOT (0x024) +#define SM501_DC_PANEL_H_SYNC (0x028) +#define SM501_DC_PANEL_V_TOT (0x02C) +#define SM501_DC_PANEL_V_SYNC (0x030) +#define SM501_DC_PANEL_CUR_LINE (0x034) + +#define SM501_DC_VIDEO_CONTROL (0x040) +#define SM501_DC_VIDEO_FB0_ADDR (0x044) +#define SM501_DC_VIDEO_FB_WIDTH (0x048) +#define SM501_DC_VIDEO_FB0_LAST_ADDR (0x04C) +#define SM501_DC_VIDEO_TL_LOC (0x050) +#define SM501_DC_VIDEO_BR_LOC (0x054) +#define SM501_DC_VIDEO_SCALE (0x058) +#define SM501_DC_VIDEO_INIT_SCALE (0x05C) +#define SM501_DC_VIDEO_YUV_CONSTANTS (0x060) +#define SM501_DC_VIDEO_FB1_ADDR (0x064) +#define SM501_DC_VIDEO_FB1_LAST_ADDR (0x068) + +#define SM501_DC_VIDEO_ALPHA_CONTROL (0x080) +#define SM501_DC_VIDEO_ALPHA_FB_ADDR (0x084) +#define SM501_DC_VIDEO_ALPHA_FB_OFFSET (0x088) +#define SM501_DC_VIDEO_ALPHA_FB_LAST_ADDR (0x08C) +#define SM501_DC_VIDEO_ALPHA_TL_LOC (0x090) +#define SM501_DC_VIDEO_ALPHA_BR_LOC (0x094) +#define SM501_DC_VIDEO_ALPHA_SCALE (0x098) +#define SM501_DC_VIDEO_ALPHA_INIT_SCALE (0x09C) +#define SM501_DC_VIDEO_ALPHA_CHROMA_KEY (0x0A0) +#define SM501_DC_VIDEO_ALPHA_COLOR_LOOKUP (0x0A4) + +#define SM501_DC_PANEL_HWC_BASE (0x0F0) +#define SM501_DC_PANEL_HWC_ADDR (0x0F0) +#define SM501_DC_PANEL_HWC_LOC (0x0F4) +#define SM501_DC_PANEL_HWC_COLOR_1_2 (0x0F8) +#define SM501_DC_PANEL_HWC_COLOR_3 (0x0FC) + +#define SM501_HWC_EN (1<<31) + +#define SM501_OFF_HWC_ADDR (0x00) +#define SM501_OFF_HWC_LOC (0x04) +#define SM501_OFF_HWC_COLOR_1_2 (0x08) +#define SM501_OFF_HWC_COLOR_3 (0x0C) + +#define SM501_DC_ALPHA_CONTROL (0x100) +#define SM501_DC_ALPHA_FB_ADDR (0x104) +#define SM501_DC_ALPHA_FB_OFFSET (0x108) +#define SM501_DC_ALPHA_TL_LOC (0x10C) +#define SM501_DC_ALPHA_BR_LOC (0x110) +#define SM501_DC_ALPHA_CHROMA_KEY (0x114) +#define SM501_DC_ALPHA_COLOR_LOOKUP (0x118) + +#define SM501_DC_CRT_CONTROL (0x200) + +#define SM501_DC_CRT_CONTROL_TVP (1<<15) +#define SM501_DC_CRT_CONTROL_CP (1<<14) +#define SM501_DC_CRT_CONTROL_VSP (1<<13) +#define SM501_DC_CRT_CONTROL_HSP (1<<12) +#define SM501_DC_CRT_CONTROL_VS (1<<11) +#define SM501_DC_CRT_CONTROL_BLANK (1<<10) +#define SM501_DC_CRT_CONTROL_SEL (1<<9) +#define SM501_DC_CRT_CONTROL_TE (1<<8) +#define SM501_DC_CRT_CONTROL_PIXEL_MASK (0xF << 4) +#define SM501_DC_CRT_CONTROL_GAMMA (1<<3) +#define SM501_DC_CRT_CONTROL_ENABLE (1<<2) + +#define SM501_DC_CRT_CONTROL_8BPP (0<<0) +#define SM501_DC_CRT_CONTROL_16BPP (1<<0) +#define SM501_DC_CRT_CONTROL_32BPP (2<<0) + +#define SM501_DC_CRT_FB_ADDR (0x204) +#define SM501_DC_CRT_FB_OFFSET (0x208) +#define SM501_DC_CRT_H_TOT (0x20C) +#define SM501_DC_CRT_H_SYNC (0x210) +#define SM501_DC_CRT_V_TOT (0x214) +#define SM501_DC_CRT_V_SYNC (0x218) +#define SM501_DC_CRT_SIGNATURE_ANALYZER (0x21C) +#define SM501_DC_CRT_CUR_LINE (0x220) +#define SM501_DC_CRT_MONITOR_DETECT (0x224) + +#define SM501_DC_CRT_HWC_BASE (0x230) +#define SM501_DC_CRT_HWC_ADDR (0x230) +#define SM501_DC_CRT_HWC_LOC (0x234) +#define SM501_DC_CRT_HWC_COLOR_1_2 (0x238) +#define SM501_DC_CRT_HWC_COLOR_3 (0x23C) + +#define SM501_DC_PANEL_PALETTE (0x400) + +#define SM501_DC_VIDEO_PALETTE (0x800) + +#define SM501_DC_CRT_PALETTE (0xC00) + +/* Zoom Video port base */ +#define SM501_ZVPORT (0x090000) + +/* AC97/I2S base */ +#define SM501_AC97 (0x0A0000) + +/* 8051 micro controller base */ +#define SM501_UCONTROLLER (0x0B0000) + +/* 8051 micro controller SRAM base */ +#define SM501_UCONTROLLER_SRAM (0x0C0000) + +/* DMA base */ +#define SM501_DMA (0x0D0000) + +/* 2d engine base */ +#define SM501_2D_ENGINE (0x100000) +#define SM501_2D_SOURCE (0x00) +#define SM501_2D_DESTINATION (0x04) +#define SM501_2D_DIMENSION (0x08) +#define SM501_2D_CONTROL (0x0C) +#define SM501_2D_PITCH (0x10) +#define SM501_2D_FOREGROUND (0x14) +#define SM501_2D_BACKGROUND (0x18) +#define SM501_2D_STRETCH (0x1C) +#define SM501_2D_COLOR_COMPARE (0x20) +#define SM501_2D_COLOR_COMPARE_MASK (0x24) +#define SM501_2D_MASK (0x28) +#define SM501_2D_CLIP_TL (0x2C) +#define SM501_2D_CLIP_BR (0x30) +#define SM501_2D_MONO_PATTERN_LOW (0x34) +#define SM501_2D_MONO_PATTERN_HIGH (0x38) +#define SM501_2D_WINDOW_WIDTH (0x3C) +#define SM501_2D_SOURCE_BASE (0x40) +#define SM501_2D_DESTINATION_BASE (0x44) +#define SM501_2D_ALPHA (0x48) +#define SM501_2D_WRAP (0x4C) +#define SM501_2D_STATUS (0x50) + +#define SM501_CSC_Y_SOURCE_BASE (0xC8) +#define SM501_CSC_CONSTANTS (0xCC) +#define SM501_CSC_Y_SOURCE_X (0xD0) +#define SM501_CSC_Y_SOURCE_Y (0xD4) +#define SM501_CSC_U_SOURCE_BASE (0xD8) +#define SM501_CSC_V_SOURCE_BASE (0xDC) +#define SM501_CSC_SOURCE_DIMENSION (0xE0) +#define SM501_CSC_SOURCE_PITCH (0xE4) +#define SM501_CSC_DESTINATION (0xE8) +#define SM501_CSC_DESTINATION_DIMENSION (0xEC) +#define SM501_CSC_DESTINATION_PITCH (0xF0) +#define SM501_CSC_SCALE_FACTOR (0xF4) +#define SM501_CSC_DESTINATION_BASE (0xF8) +#define SM501_CSC_CONTROL (0xFC) + +/* 2d engine data port base */ +#define SM501_2D_ENGINE_DATA (0x110000) + +/* end of register definitions */ + +#define SM501_HWC_WIDTH (64) +#define SM501_HWC_HEIGHT (64) + +/* SM501 local memory size taken from "linux/drivers/mfd/sm501.c" */ +static const uint32_t sm501_mem_local_size[] = { + [0] = 4*1024*1024, + [1] = 8*1024*1024, + [2] = 16*1024*1024, + [3] = 32*1024*1024, + [4] = 64*1024*1024, + [5] = 2*1024*1024, +}; +#define get_local_mem_size(s) sm501_mem_local_size[(s)->local_mem_size_index] + +typedef struct SM501State { + /* graphic console status */ + QemuConsole *con; + + /* status & internal resources */ + hwaddr base; + uint32_t local_mem_size_index; + uint8_t * local_mem; + MemoryRegion local_mem_region; + uint32_t last_width; + uint32_t last_height; + + /* mmio registers */ + uint32_t system_control; + uint32_t misc_control; + uint32_t gpio_31_0_control; + uint32_t gpio_63_32_control; + uint32_t dram_control; + uint32_t irq_mask; + uint32_t misc_timing; + uint32_t power_mode_control; + + uint32_t uart0_ier; + uint32_t uart0_lcr; + uint32_t uart0_mcr; + uint32_t uart0_scr; + + uint8_t dc_palette[0x400 * 3]; + + uint32_t dc_panel_control; + uint32_t dc_panel_panning_control; + uint32_t dc_panel_fb_addr; + uint32_t dc_panel_fb_offset; + uint32_t dc_panel_fb_width; + uint32_t dc_panel_fb_height; + uint32_t dc_panel_tl_location; + uint32_t dc_panel_br_location; + uint32_t dc_panel_h_total; + uint32_t dc_panel_h_sync; + uint32_t dc_panel_v_total; + uint32_t dc_panel_v_sync; + + uint32_t dc_panel_hwc_addr; + uint32_t dc_panel_hwc_location; + uint32_t dc_panel_hwc_color_1_2; + uint32_t dc_panel_hwc_color_3; + + uint32_t dc_crt_control; + uint32_t dc_crt_fb_addr; + uint32_t dc_crt_fb_offset; + uint32_t dc_crt_h_total; + uint32_t dc_crt_h_sync; + uint32_t dc_crt_v_total; + uint32_t dc_crt_v_sync; + + uint32_t dc_crt_hwc_addr; + uint32_t dc_crt_hwc_location; + uint32_t dc_crt_hwc_color_1_2; + uint32_t dc_crt_hwc_color_3; + + uint32_t twoD_source; + uint32_t twoD_destination; + uint32_t twoD_dimension; + uint32_t twoD_control; + uint32_t twoD_pitch; + uint32_t twoD_foreground; + uint32_t twoD_stretch; + uint32_t twoD_color_compare_mask; + uint32_t twoD_mask; + uint32_t twoD_window_width; + uint32_t twoD_source_base; + uint32_t twoD_destination_base; + +} SM501State; + +static uint32_t get_local_mem_size_index(uint32_t size) +{ + uint32_t norm_size = 0; + int i, index = 0; + + for (i = 0; i < ARRAY_SIZE(sm501_mem_local_size); i++) { + uint32_t new_size = sm501_mem_local_size[i]; + if (new_size >= size) { + if (norm_size == 0 || norm_size > new_size) { + norm_size = new_size; + index = i; + } + } + } + + return index; +} + +/** + * Check the availability of hardware cursor. + * @param crt 0 for PANEL, 1 for CRT. + */ +static inline int is_hwc_enabled(SM501State *state, int crt) +{ + uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr; + return addr & 0x80000000; +} + +/** + * Get the address which holds cursor pattern data. + * @param crt 0 for PANEL, 1 for CRT. + */ +static inline uint32_t get_hwc_address(SM501State *state, int crt) +{ + uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr; + return (addr & 0x03FFFFF0)/* >> 4*/; +} + +/** + * Get the cursor position in y coordinate. + * @param crt 0 for PANEL, 1 for CRT. + */ +static inline uint32_t get_hwc_y(SM501State *state, int crt) +{ + uint32_t location = crt ? state->dc_crt_hwc_location + : state->dc_panel_hwc_location; + return (location & 0x07FF0000) >> 16; +} + +/** + * Get the cursor position in x coordinate. + * @param crt 0 for PANEL, 1 for CRT. + */ +static inline uint32_t get_hwc_x(SM501State *state, int crt) +{ + uint32_t location = crt ? state->dc_crt_hwc_location + : state->dc_panel_hwc_location; + return location & 0x000007FF; +} + +/** + * Get the cursor position in x coordinate. + * @param crt 0 for PANEL, 1 for CRT. + * @param index 0, 1, 2 or 3 which specifies color of corsor dot. + */ +static inline uint16_t get_hwc_color(SM501State *state, int crt, int index) +{ + uint32_t color_reg = 0; + uint16_t color_565 = 0; + + if (index == 0) { + return 0; + } + + switch (index) { + case 1: + case 2: + color_reg = crt ? state->dc_crt_hwc_color_1_2 + : state->dc_panel_hwc_color_1_2; + break; + case 3: + color_reg = crt ? state->dc_crt_hwc_color_3 + : state->dc_panel_hwc_color_3; + break; + default: + printf("invalid hw cursor color.\n"); + abort(); + } + + switch (index) { + case 1: + case 3: + color_565 = (uint16_t)(color_reg & 0xFFFF); + break; + case 2: + color_565 = (uint16_t)((color_reg >> 16) & 0xFFFF); + break; + } + return color_565; +} + +static int within_hwc_y_range(SM501State *state, int y, int crt) +{ + int hwc_y = get_hwc_y(state, crt); + return (hwc_y <= y && y < hwc_y + SM501_HWC_HEIGHT); +} + +static void sm501_2d_operation(SM501State * s) +{ + /* obtain operation parameters */ + int operation = (s->twoD_control >> 16) & 0x1f; + int rtl = s->twoD_control & 0x8000000; + int src_x = (s->twoD_source >> 16) & 0x01FFF; + int src_y = s->twoD_source & 0xFFFF; + int dst_x = (s->twoD_destination >> 16) & 0x01FFF; + int dst_y = s->twoD_destination & 0xFFFF; + int operation_width = (s->twoD_dimension >> 16) & 0x1FFF; + int operation_height = s->twoD_dimension & 0xFFFF; + uint32_t color = s->twoD_foreground; + int format_flags = (s->twoD_stretch >> 20) & 0x3; + int addressing = (s->twoD_stretch >> 16) & 0xF; + + /* get frame buffer info */ + uint8_t * src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); + uint8_t * dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF); + int src_width = (s->dc_crt_h_total & 0x00000FFF) + 1; + int dst_width = (s->dc_crt_h_total & 0x00000FFF) + 1; + + if (addressing != 0x0) { + printf("%s: only XY addressing is supported.\n", __func__); + abort(); + } + + if ((s->twoD_source_base & 0x08000000) || + (s->twoD_destination_base & 0x08000000)) { + printf("%s: only local memory is supported.\n", __func__); + abort(); + } + + switch (operation) { + case 0x00: /* copy area */ +#define COPY_AREA(_bpp, _pixel_type, rtl) { \ + int y, x, index_d, index_s; \ + for (y = 0; y < operation_height; y++) { \ + for (x = 0; x < operation_width; x++) { \ + if (rtl) { \ + index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ + index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ + } else { \ + index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ + index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ + } \ + *(_pixel_type*)&dst[index_d] = *(_pixel_type*)&src[index_s];\ + } \ + } \ + } + switch (format_flags) { + case 0: + COPY_AREA(1, uint8_t, rtl); + break; + case 1: + COPY_AREA(2, uint16_t, rtl); + break; + case 2: + COPY_AREA(4, uint32_t, rtl); + break; + } + break; + + case 0x01: /* fill rectangle */ +#define FILL_RECT(_bpp, _pixel_type) { \ + int y, x; \ + for (y = 0; y < operation_height; y++) { \ + for (x = 0; x < operation_width; x++) { \ + int index = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ + *(_pixel_type*)&dst[index] = (_pixel_type)color; \ + } \ + } \ + } + + switch (format_flags) { + case 0: + FILL_RECT(1, uint8_t); + break; + case 1: + FILL_RECT(2, uint16_t); + break; + case 2: + FILL_RECT(4, uint32_t); + break; + } + break; + + default: + printf("non-implemented SM501 2D operation. %d\n", operation); + abort(); + break; + } +} + +static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, + unsigned size) +{ + SM501State * s = (SM501State *)opaque; + uint32_t ret = 0; + SM501_DPRINTF("sm501 system config regs : read addr=%x\n", (int)addr); + + switch(addr) { + case SM501_SYSTEM_CONTROL: + ret = s->system_control; + break; + case SM501_MISC_CONTROL: + ret = s->misc_control; + break; + case SM501_GPIO31_0_CONTROL: + ret = s->gpio_31_0_control; + break; + case SM501_GPIO63_32_CONTROL: + ret = s->gpio_63_32_control; + break; + case SM501_DEVICEID: + ret = 0x050100A0; + break; + case SM501_DRAM_CONTROL: + ret = (s->dram_control & 0x07F107C0) | s->local_mem_size_index << 13; + break; + case SM501_IRQ_MASK: + ret = s->irq_mask; + break; + case SM501_MISC_TIMING: + /* TODO : simulate gate control */ + ret = s->misc_timing; + break; + case SM501_CURRENT_GATE: + /* TODO : simulate gate control */ + ret = 0x00021807; + break; + case SM501_CURRENT_CLOCK: + ret = 0x2A1A0A09; + break; + case SM501_POWER_MODE_CONTROL: + ret = s->power_mode_control; + break; + + default: + printf("sm501 system config : not implemented register read." + " addr=%x\n", (int)addr); + abort(); + } + + return ret; +} + +static void sm501_system_config_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + SM501State * s = (SM501State *)opaque; + SM501_DPRINTF("sm501 system config regs : write addr=%x, val=%x\n", + (uint32_t)addr, (uint32_t)value); + + switch(addr) { + case SM501_SYSTEM_CONTROL: + s->system_control = value & 0xE300B8F7; + break; + case SM501_MISC_CONTROL: + s->misc_control = value & 0xFF7FFF20; + break; + case SM501_GPIO31_0_CONTROL: + s->gpio_31_0_control = value; + break; + case SM501_GPIO63_32_CONTROL: + s->gpio_63_32_control = value; + break; + case SM501_DRAM_CONTROL: + s->local_mem_size_index = (value >> 13) & 0x7; + /* rODO : check validity of size change */ + s->dram_control |= value & 0x7FFFFFC3; + break; + case SM501_IRQ_MASK: + s->irq_mask = value; + break; + case SM501_MISC_TIMING: + s->misc_timing = value & 0xF31F1FFF; + break; + case SM501_POWER_MODE_0_GATE: + case SM501_POWER_MODE_1_GATE: + case SM501_POWER_MODE_0_CLOCK: + case SM501_POWER_MODE_1_CLOCK: + /* TODO : simulate gate & clock control */ + break; + case SM501_POWER_MODE_CONTROL: + s->power_mode_control = value & 0x00000003; + break; + + default: + printf("sm501 system config : not implemented register write." + " addr=%x, val=%x\n", (int)addr, (uint32_t)value); + abort(); + } +} + +static const MemoryRegionOps sm501_system_config_ops = { + .read = sm501_system_config_read, + .write = sm501_system_config_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static uint32_t sm501_palette_read(void *opaque, hwaddr addr) +{ + SM501State * s = (SM501State *)opaque; + SM501_DPRINTF("sm501 palette read addr=%x\n", (int)addr); + + /* TODO : consider BYTE/WORD access */ + /* TODO : consider endian */ + + assert(range_covers_byte(0, 0x400 * 3, addr)); + return *(uint32_t*)&s->dc_palette[addr]; +} + +static void sm501_palette_write(void *opaque, + hwaddr addr, uint32_t value) +{ + SM501State * s = (SM501State *)opaque; + SM501_DPRINTF("sm501 palette write addr=%x, val=%x\n", + (int)addr, value); + + /* TODO : consider BYTE/WORD access */ + /* TODO : consider endian */ + + assert(range_covers_byte(0, 0x400 * 3, addr)); + *(uint32_t*)&s->dc_palette[addr] = value; +} + +static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, + unsigned size) +{ + SM501State * s = (SM501State *)opaque; + uint32_t ret = 0; + SM501_DPRINTF("sm501 disp ctrl regs : read addr=%x\n", (int)addr); + + switch(addr) { + + case SM501_DC_PANEL_CONTROL: + ret = s->dc_panel_control; + break; + case SM501_DC_PANEL_PANNING_CONTROL: + ret = s->dc_panel_panning_control; + break; + case SM501_DC_PANEL_FB_ADDR: + ret = s->dc_panel_fb_addr; + break; + case SM501_DC_PANEL_FB_OFFSET: + ret = s->dc_panel_fb_offset; + break; + case SM501_DC_PANEL_FB_WIDTH: + ret = s->dc_panel_fb_width; + break; + case SM501_DC_PANEL_FB_HEIGHT: + ret = s->dc_panel_fb_height; + break; + case SM501_DC_PANEL_TL_LOC: + ret = s->dc_panel_tl_location; + break; + case SM501_DC_PANEL_BR_LOC: + ret = s->dc_panel_br_location; + break; + + case SM501_DC_PANEL_H_TOT: + ret = s->dc_panel_h_total; + break; + case SM501_DC_PANEL_H_SYNC: + ret = s->dc_panel_h_sync; + break; + case SM501_DC_PANEL_V_TOT: + ret = s->dc_panel_v_total; + break; + case SM501_DC_PANEL_V_SYNC: + ret = s->dc_panel_v_sync; + break; + + case SM501_DC_CRT_CONTROL: + ret = s->dc_crt_control; + break; + case SM501_DC_CRT_FB_ADDR: + ret = s->dc_crt_fb_addr; + break; + case SM501_DC_CRT_FB_OFFSET: + ret = s->dc_crt_fb_offset; + break; + case SM501_DC_CRT_H_TOT: + ret = s->dc_crt_h_total; + break; + case SM501_DC_CRT_H_SYNC: + ret = s->dc_crt_h_sync; + break; + case SM501_DC_CRT_V_TOT: + ret = s->dc_crt_v_total; + break; + case SM501_DC_CRT_V_SYNC: + ret = s->dc_crt_v_sync; + break; + + case SM501_DC_CRT_HWC_ADDR: + ret = s->dc_crt_hwc_addr; + break; + case SM501_DC_CRT_HWC_LOC: + ret = s->dc_crt_hwc_location; + break; + case SM501_DC_CRT_HWC_COLOR_1_2: + ret = s->dc_crt_hwc_color_1_2; + break; + case SM501_DC_CRT_HWC_COLOR_3: + ret = s->dc_crt_hwc_color_3; + break; + + case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4: + ret = sm501_palette_read(opaque, addr - SM501_DC_PANEL_PALETTE); + break; + + default: + printf("sm501 disp ctrl : not implemented register read." + " addr=%x\n", (int)addr); + abort(); + } + + return ret; +} + +static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + SM501State * s = (SM501State *)opaque; + SM501_DPRINTF("sm501 disp ctrl regs : write addr=%x, val=%x\n", + (unsigned)addr, (unsigned)value); + + switch(addr) { + case SM501_DC_PANEL_CONTROL: + s->dc_panel_control = value & 0x0FFF73FF; + break; + case SM501_DC_PANEL_PANNING_CONTROL: + s->dc_panel_panning_control = value & 0xFF3FFF3F; + break; + case SM501_DC_PANEL_FB_ADDR: + s->dc_panel_fb_addr = value & 0x8FFFFFF0; + break; + case SM501_DC_PANEL_FB_OFFSET: + s->dc_panel_fb_offset = value & 0x3FF03FF0; + break; + case SM501_DC_PANEL_FB_WIDTH: + s->dc_panel_fb_width = value & 0x0FFF0FFF; + break; + case SM501_DC_PANEL_FB_HEIGHT: + s->dc_panel_fb_height = value & 0x0FFF0FFF; + break; + case SM501_DC_PANEL_TL_LOC: + s->dc_panel_tl_location = value & 0x07FF07FF; + break; + case SM501_DC_PANEL_BR_LOC: + s->dc_panel_br_location = value & 0x07FF07FF; + break; + + case SM501_DC_PANEL_H_TOT: + s->dc_panel_h_total = value & 0x0FFF0FFF; + break; + case SM501_DC_PANEL_H_SYNC: + s->dc_panel_h_sync = value & 0x00FF0FFF; + break; + case SM501_DC_PANEL_V_TOT: + s->dc_panel_v_total = value & 0x0FFF0FFF; + break; + case SM501_DC_PANEL_V_SYNC: + s->dc_panel_v_sync = value & 0x003F0FFF; + break; + + case SM501_DC_PANEL_HWC_ADDR: + s->dc_panel_hwc_addr = value & 0x8FFFFFF0; + break; + case SM501_DC_PANEL_HWC_LOC: + s->dc_panel_hwc_location = value & 0x0FFF0FFF; + break; + case SM501_DC_PANEL_HWC_COLOR_1_2: + s->dc_panel_hwc_color_1_2 = value; + break; + case SM501_DC_PANEL_HWC_COLOR_3: + s->dc_panel_hwc_color_3 = value & 0x0000FFFF; + break; + + case SM501_DC_CRT_CONTROL: + s->dc_crt_control = value & 0x0003FFFF; + break; + case SM501_DC_CRT_FB_ADDR: + s->dc_crt_fb_addr = value & 0x8FFFFFF0; + break; + case SM501_DC_CRT_FB_OFFSET: + s->dc_crt_fb_offset = value & 0x3FF03FF0; + break; + case SM501_DC_CRT_H_TOT: + s->dc_crt_h_total = value & 0x0FFF0FFF; + break; + case SM501_DC_CRT_H_SYNC: + s->dc_crt_h_sync = value & 0x00FF0FFF; + break; + case SM501_DC_CRT_V_TOT: + s->dc_crt_v_total = value & 0x0FFF0FFF; + break; + case SM501_DC_CRT_V_SYNC: + s->dc_crt_v_sync = value & 0x003F0FFF; + break; + + case SM501_DC_CRT_HWC_ADDR: + s->dc_crt_hwc_addr = value & 0x8FFFFFF0; + break; + case SM501_DC_CRT_HWC_LOC: + s->dc_crt_hwc_location = value & 0x0FFF0FFF; + break; + case SM501_DC_CRT_HWC_COLOR_1_2: + s->dc_crt_hwc_color_1_2 = value; + break; + case SM501_DC_CRT_HWC_COLOR_3: + s->dc_crt_hwc_color_3 = value & 0x0000FFFF; + break; + + case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4: + sm501_palette_write(opaque, addr - SM501_DC_PANEL_PALETTE, value); + break; + + default: + printf("sm501 disp ctrl : not implemented register write." + " addr=%x, val=%x\n", (int)addr, (unsigned)value); + abort(); + } +} + +static const MemoryRegionOps sm501_disp_ctrl_ops = { + .read = sm501_disp_ctrl_read, + .write = sm501_disp_ctrl_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, + unsigned size) +{ + SM501State * s = (SM501State *)opaque; + uint32_t ret = 0; + SM501_DPRINTF("sm501 2d engine regs : read addr=%x\n", (int)addr); + + switch(addr) { + case SM501_2D_SOURCE_BASE: + ret = s->twoD_source_base; + break; + default: + printf("sm501 disp ctrl : not implemented register read." + " addr=%x\n", (int)addr); + abort(); + } + + return ret; +} + +static void sm501_2d_engine_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + SM501State * s = (SM501State *)opaque; + SM501_DPRINTF("sm501 2d engine regs : write addr=%x, val=%x\n", + (unsigned)addr, (unsigned)value); + + switch(addr) { + case SM501_2D_SOURCE: + s->twoD_source = value; + break; + case SM501_2D_DESTINATION: + s->twoD_destination = value; + break; + case SM501_2D_DIMENSION: + s->twoD_dimension = value; + break; + case SM501_2D_CONTROL: + s->twoD_control = value; + + /* do 2d operation if start flag is set. */ + if (value & 0x80000000) { + sm501_2d_operation(s); + s->twoD_control &= ~0x80000000; /* start flag down */ + } + + break; + case SM501_2D_PITCH: + s->twoD_pitch = value; + break; + case SM501_2D_FOREGROUND: + s->twoD_foreground = value; + break; + case SM501_2D_STRETCH: + s->twoD_stretch = value; + break; + case SM501_2D_COLOR_COMPARE_MASK: + s->twoD_color_compare_mask = value; + break; + case SM501_2D_MASK: + s->twoD_mask = value; + break; + case SM501_2D_WINDOW_WIDTH: + s->twoD_window_width = value; + break; + case SM501_2D_SOURCE_BASE: + s->twoD_source_base = value; + break; + case SM501_2D_DESTINATION_BASE: + s->twoD_destination_base = value; + break; + default: + printf("sm501 2d engine : not implemented register write." + " addr=%x, val=%x\n", (int)addr, (unsigned)value); + abort(); + } +} + +static const MemoryRegionOps sm501_2d_engine_ops = { + .read = sm501_2d_engine_read, + .write = sm501_2d_engine_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/* draw line functions for all console modes */ + +typedef void draw_line_func(uint8_t *d, const uint8_t *s, + int width, const uint32_t *pal); + +typedef void draw_hwc_line_func(SM501State * s, int crt, uint8_t * palette, + int c_y, uint8_t *d, int width); + +#define DEPTH 8 +#include "hw/sm501_template.h" + +#define DEPTH 15 +#include "hw/sm501_template.h" + +#define BGR_FORMAT +#define DEPTH 15 +#include "hw/sm501_template.h" + +#define DEPTH 16 +#include "hw/sm501_template.h" + +#define BGR_FORMAT +#define DEPTH 16 +#include "hw/sm501_template.h" + +#define DEPTH 32 +#include "hw/sm501_template.h" + +#define BGR_FORMAT +#define DEPTH 32 +#include "hw/sm501_template.h" + +static draw_line_func * draw_line8_funcs[] = { + draw_line8_8, + draw_line8_15, + draw_line8_16, + draw_line8_32, + draw_line8_32bgr, + draw_line8_15bgr, + draw_line8_16bgr, +}; + +static draw_line_func * draw_line16_funcs[] = { + draw_line16_8, + draw_line16_15, + draw_line16_16, + draw_line16_32, + draw_line16_32bgr, + draw_line16_15bgr, + draw_line16_16bgr, +}; + +static draw_line_func * draw_line32_funcs[] = { + draw_line32_8, + draw_line32_15, + draw_line32_16, + draw_line32_32, + draw_line32_32bgr, + draw_line32_15bgr, + draw_line32_16bgr, +}; + +static draw_hwc_line_func * draw_hwc_line_funcs[] = { + draw_hwc_line_8, + draw_hwc_line_15, + draw_hwc_line_16, + draw_hwc_line_32, + draw_hwc_line_32bgr, + draw_hwc_line_15bgr, + draw_hwc_line_16bgr, +}; + +static inline int get_depth_index(DisplaySurface *surface) +{ + switch (surface_bits_per_pixel(surface)) { + default: + case 8: + return 0; + case 15: + return 1; + case 16: + return 2; + case 32: + if (is_surface_bgr(surface)) { + return 4; + } else { + return 3; + } + } +} + +static void sm501_draw_crt(SM501State * s) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int y; + int width = (s->dc_crt_h_total & 0x00000FFF) + 1; + int height = (s->dc_crt_v_total & 0x00000FFF) + 1; + + uint8_t * src = s->local_mem; + int src_bpp = 0; + int dst_bpp = surface_bytes_per_pixel(surface); + uint32_t * palette = (uint32_t *)&s->dc_palette[SM501_DC_CRT_PALETTE + - SM501_DC_PANEL_PALETTE]; + uint8_t hwc_palette[3 * 3]; + int ds_depth_index = get_depth_index(surface); + draw_line_func * draw_line = NULL; + draw_hwc_line_func * draw_hwc_line = NULL; + int full_update = 0; + int y_start = -1; + ram_addr_t page_min = ~0l; + ram_addr_t page_max = 0l; + ram_addr_t offset = 0; + + /* choose draw_line function */ + switch (s->dc_crt_control & 3) { + case SM501_DC_CRT_CONTROL_8BPP: + src_bpp = 1; + draw_line = draw_line8_funcs[ds_depth_index]; + break; + case SM501_DC_CRT_CONTROL_16BPP: + src_bpp = 2; + draw_line = draw_line16_funcs[ds_depth_index]; + break; + case SM501_DC_CRT_CONTROL_32BPP: + src_bpp = 4; + draw_line = draw_line32_funcs[ds_depth_index]; + break; + default: + printf("sm501 draw crt : invalid DC_CRT_CONTROL=%x.\n", + s->dc_crt_control); + abort(); + break; + } + + /* set up to draw hardware cursor */ + if (is_hwc_enabled(s, 1)) { + int i; + + /* get cursor palette */ + for (i = 0; i < 3; i++) { + uint16_t rgb565 = get_hwc_color(s, 1, i + 1); + hwc_palette[i * 3 + 0] = (rgb565 & 0xf800) >> 8; /* red */ + hwc_palette[i * 3 + 1] = (rgb565 & 0x07e0) >> 3; /* green */ + hwc_palette[i * 3 + 2] = (rgb565 & 0x001f) << 3; /* blue */ + } + + /* choose cursor draw line function */ + draw_hwc_line = draw_hwc_line_funcs[ds_depth_index]; + } + + /* adjust console size */ + if (s->last_width != width || s->last_height != height) { + qemu_console_resize(s->con, width, height); + surface = qemu_console_surface(s->con); + s->last_width = width; + s->last_height = height; + full_update = 1; + } + + /* draw each line according to conditions */ + for (y = 0; y < height; y++) { + int update_hwc = draw_hwc_line ? within_hwc_y_range(s, y, 1) : 0; + int update = full_update || update_hwc; + ram_addr_t page0 = offset; + ram_addr_t page1 = offset + width * src_bpp - 1; + + /* check dirty flags for each line */ + update = memory_region_get_dirty(&s->local_mem_region, page0, + page1 - page0, DIRTY_MEMORY_VGA); + + /* draw line and change status */ + if (update) { + uint8_t *d = surface_data(surface); + d += y * width * dst_bpp; + + /* draw graphics layer */ + draw_line(d, src, width, palette); + + /* draw haredware cursor */ + if (update_hwc) { + draw_hwc_line(s, 1, hwc_palette, y - get_hwc_y(s, 1), d, width); + } + + if (y_start < 0) + y_start = y; + if (page0 < page_min) + page_min = page0; + if (page1 > page_max) + page_max = page1; + } else { + if (y_start >= 0) { + /* flush to display */ + dpy_gfx_update(s->con, 0, y_start, width, y - y_start); + y_start = -1; + } + } + + src += width * src_bpp; + offset += width * src_bpp; + } + + /* complete flush to display */ + if (y_start >= 0) + dpy_gfx_update(s->con, 0, y_start, width, y - y_start); + + /* clear dirty flags */ + if (page_min != ~0l) { + memory_region_reset_dirty(&s->local_mem_region, + page_min, page_max + TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); + } +} + +static void sm501_update_display(void *opaque) +{ + SM501State * s = (SM501State *)opaque; + + if (s->dc_crt_control & SM501_DC_CRT_CONTROL_ENABLE) + sm501_draw_crt(s); +} + +void sm501_init(MemoryRegion *address_space_mem, uint32_t base, + uint32_t local_mem_bytes, qemu_irq irq, CharDriverState *chr) +{ + SM501State * s; + DeviceState *dev; + MemoryRegion *sm501_system_config = g_new(MemoryRegion, 1); + MemoryRegion *sm501_disp_ctrl = g_new(MemoryRegion, 1); + MemoryRegion *sm501_2d_engine = g_new(MemoryRegion, 1); + + /* allocate management data region */ + s = (SM501State *)g_malloc0(sizeof(SM501State)); + s->base = base; + s->local_mem_size_index + = get_local_mem_size_index(local_mem_bytes); + SM501_DPRINTF("local mem size=%x. index=%d\n", get_local_mem_size(s), + s->local_mem_size_index); + s->system_control = 0x00100000; + s->misc_control = 0x00001000; /* assumes SH, active=low */ + s->dc_panel_control = 0x00010000; + s->dc_crt_control = 0x00010000; + + /* allocate local memory */ + memory_region_init_ram(&s->local_mem_region, "sm501.local", + local_mem_bytes); + vmstate_register_ram_global(&s->local_mem_region); + s->local_mem = memory_region_get_ram_ptr(&s->local_mem_region); + memory_region_add_subregion(address_space_mem, base, &s->local_mem_region); + + /* map mmio */ + memory_region_init_io(sm501_system_config, &sm501_system_config_ops, s, + "sm501-system-config", 0x6c); + memory_region_add_subregion(address_space_mem, base + MMIO_BASE_OFFSET, + sm501_system_config); + memory_region_init_io(sm501_disp_ctrl, &sm501_disp_ctrl_ops, s, + "sm501-disp-ctrl", 0x1000); + memory_region_add_subregion(address_space_mem, + base + MMIO_BASE_OFFSET + SM501_DC, + sm501_disp_ctrl); + memory_region_init_io(sm501_2d_engine, &sm501_2d_engine_ops, s, + "sm501-2d-engine", 0x54); + memory_region_add_subregion(address_space_mem, + base + MMIO_BASE_OFFSET + SM501_2D_ENGINE, + sm501_2d_engine); + + /* bridge to usb host emulation module */ + dev = qdev_create(NULL, "sysbus-ohci"); + qdev_prop_set_uint32(dev, "num-ports", 2); + qdev_prop_set_taddr(dev, "dma-offset", base); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, + base + MMIO_BASE_OFFSET + SM501_USB_HOST); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + + /* bridge to serial emulation module */ + if (chr) { + serial_mm_init(address_space_mem, + base + MMIO_BASE_OFFSET + SM501_UART0, 2, + NULL, /* TODO : chain irq to IRL */ + 115200, chr, DEVICE_NATIVE_ENDIAN); + } + + /* create qemu graphic console */ + s->con = graphic_console_init(sm501_update_display, NULL, + NULL, NULL, s); +} diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c new file mode 100644 index 0000000000..2d5fa89e9b --- /dev/null +++ b/hw/display/tc6393xb.c @@ -0,0 +1,593 @@ +/* + * Toshiba TC6393XB I/O Controller. + * Found in Sharp Zaurus SL-6000 (tosa) or some + * Toshiba e-Series PDAs. + * + * Most features are currently unsupported!!! + * + * This code is licensed under the GNU GPL v2. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ +#include "hw/hw.h" +#include "hw/arm/devices.h" +#include "hw/block/flash.h" +#include "ui/console.h" +#include "ui/pixel_ops.h" +#include "sysemu/blockdev.h" + +#define IRQ_TC6393_NAND 0 +#define IRQ_TC6393_MMC 1 +#define IRQ_TC6393_OHCI 2 +#define IRQ_TC6393_SERIAL 3 +#define IRQ_TC6393_FB 4 + +#define TC6393XB_NR_IRQS 8 + +#define TC6393XB_GPIOS 16 + +#define SCR_REVID 0x08 /* b Revision ID */ +#define SCR_ISR 0x50 /* b Interrupt Status */ +#define SCR_IMR 0x52 /* b Interrupt Mask */ +#define SCR_IRR 0x54 /* b Interrupt Routing */ +#define SCR_GPER 0x60 /* w GP Enable */ +#define SCR_GPI_SR(i) (0x64 + (i)) /* b3 GPI Status */ +#define SCR_GPI_IMR(i) (0x68 + (i)) /* b3 GPI INT Mask */ +#define SCR_GPI_EDER(i) (0x6c + (i)) /* b3 GPI Edge Detect Enable */ +#define SCR_GPI_LIR(i) (0x70 + (i)) /* b3 GPI Level Invert */ +#define SCR_GPO_DSR(i) (0x78 + (i)) /* b3 GPO Data Set */ +#define SCR_GPO_DOECR(i) (0x7c + (i)) /* b3 GPO Data OE Control */ +#define SCR_GP_IARCR(i) (0x80 + (i)) /* b3 GP Internal Active Register Control */ +#define SCR_GP_IARLCR(i) (0x84 + (i)) /* b3 GP INTERNAL Active Register Level Control */ +#define SCR_GPI_BCR(i) (0x88 + (i)) /* b3 GPI Buffer Control */ +#define SCR_GPA_IARCR 0x8c /* w GPa Internal Active Register Control */ +#define SCR_GPA_IARLCR 0x90 /* w GPa Internal Active Register Level Control */ +#define SCR_GPA_BCR 0x94 /* w GPa Buffer Control */ +#define SCR_CCR 0x98 /* w Clock Control */ +#define SCR_PLL2CR 0x9a /* w PLL2 Control */ +#define SCR_PLL1CR 0x9c /* l PLL1 Control */ +#define SCR_DIARCR 0xa0 /* b Device Internal Active Register Control */ +#define SCR_DBOCR 0xa1 /* b Device Buffer Off Control */ +#define SCR_FER 0xe0 /* b Function Enable */ +#define SCR_MCR 0xe4 /* w Mode Control */ +#define SCR_CONFIG 0xfc /* b Configuration Control */ +#define SCR_DEBUG 0xff /* b Debug */ + +#define NAND_CFG_COMMAND 0x04 /* w Command */ +#define NAND_CFG_BASE 0x10 /* l Control Base Address */ +#define NAND_CFG_INTP 0x3d /* b Interrupt Pin */ +#define NAND_CFG_INTE 0x48 /* b Int Enable */ +#define NAND_CFG_EC 0x4a /* b Event Control */ +#define NAND_CFG_ICC 0x4c /* b Internal Clock Control */ +#define NAND_CFG_ECCC 0x5b /* b ECC Control */ +#define NAND_CFG_NFTC 0x60 /* b NAND Flash Transaction Control */ +#define NAND_CFG_NFM 0x61 /* b NAND Flash Monitor */ +#define NAND_CFG_NFPSC 0x62 /* b NAND Flash Power Supply Control */ +#define NAND_CFG_NFDC 0x63 /* b NAND Flash Detect Control */ + +#define NAND_DATA 0x00 /* l Data */ +#define NAND_MODE 0x04 /* b Mode */ +#define NAND_STATUS 0x05 /* b Status */ +#define NAND_ISR 0x06 /* b Interrupt Status */ +#define NAND_IMR 0x07 /* b Interrupt Mask */ + +#define NAND_MODE_WP 0x80 +#define NAND_MODE_CE 0x10 +#define NAND_MODE_ALE 0x02 +#define NAND_MODE_CLE 0x01 +#define NAND_MODE_ECC_MASK 0x60 +#define NAND_MODE_ECC_EN 0x20 +#define NAND_MODE_ECC_READ 0x40 +#define NAND_MODE_ECC_RST 0x60 + +struct TC6393xbState { + MemoryRegion iomem; + qemu_irq irq; + qemu_irq *sub_irqs; + struct { + uint8_t ISR; + uint8_t IMR; + uint8_t IRR; + uint16_t GPER; + uint8_t GPI_SR[3]; + uint8_t GPI_IMR[3]; + uint8_t GPI_EDER[3]; + uint8_t GPI_LIR[3]; + uint8_t GP_IARCR[3]; + uint8_t GP_IARLCR[3]; + uint8_t GPI_BCR[3]; + uint16_t GPA_IARCR; + uint16_t GPA_IARLCR; + uint16_t CCR; + uint16_t PLL2CR; + uint32_t PLL1CR; + uint8_t DIARCR; + uint8_t DBOCR; + uint8_t FER; + uint16_t MCR; + uint8_t CONFIG; + uint8_t DEBUG; + } scr; + uint32_t gpio_dir; + uint32_t gpio_level; + uint32_t prev_level; + qemu_irq handler[TC6393XB_GPIOS]; + qemu_irq *gpio_in; + + struct { + uint8_t mode; + uint8_t isr; + uint8_t imr; + } nand; + int nand_enable; + uint32_t nand_phys; + DeviceState *flash; + ECCState ecc; + + QemuConsole *con; + MemoryRegion vram; + uint16_t *vram_ptr; + uint32_t scr_width, scr_height; /* in pixels */ + qemu_irq l3v; + unsigned blank : 1, + blanked : 1; +}; + +qemu_irq *tc6393xb_gpio_in_get(TC6393xbState *s) +{ + return s->gpio_in; +} + +static void tc6393xb_gpio_set(void *opaque, int line, int level) +{ +// TC6393xbState *s = opaque; + + if (line > TC6393XB_GPIOS) { + printf("%s: No GPIO pin %i\n", __FUNCTION__, line); + return; + } + + // FIXME: how does the chip reflect the GPIO input level change? +} + +void tc6393xb_gpio_out_set(TC6393xbState *s, int line, + qemu_irq handler) +{ + if (line >= TC6393XB_GPIOS) { + fprintf(stderr, "TC6393xb: no GPIO pin %d\n", line); + return; + } + + s->handler[line] = handler; +} + +static void tc6393xb_gpio_handler_update(TC6393xbState *s) +{ + uint32_t level, diff; + int bit; + + level = s->gpio_level & s->gpio_dir; + + for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { + bit = ffs(diff) - 1; + qemu_set_irq(s->handler[bit], (level >> bit) & 1); + } + + s->prev_level = level; +} + +qemu_irq tc6393xb_l3v_get(TC6393xbState *s) +{ + return s->l3v; +} + +static void tc6393xb_l3v(void *opaque, int line, int level) +{ + TC6393xbState *s = opaque; + s->blank = !level; + fprintf(stderr, "L3V: %d\n", level); +} + +static void tc6393xb_sub_irq(void *opaque, int line, int level) { + TC6393xbState *s = opaque; + uint8_t isr = s->scr.ISR; + if (level) + isr |= 1 << line; + else + isr &= ~(1 << line); + s->scr.ISR = isr; + qemu_set_irq(s->irq, isr & s->scr.IMR); +} + +#define SCR_REG_B(N) \ + case SCR_ ##N: return s->scr.N +#define SCR_REG_W(N) \ + case SCR_ ##N: return s->scr.N; \ + case SCR_ ##N + 1: return s->scr.N >> 8; +#define SCR_REG_L(N) \ + case SCR_ ##N: return s->scr.N; \ + case SCR_ ##N + 1: return s->scr.N >> 8; \ + case SCR_ ##N + 2: return s->scr.N >> 16; \ + case SCR_ ##N + 3: return s->scr.N >> 24; +#define SCR_REG_A(N) \ + case SCR_ ##N(0): return s->scr.N[0]; \ + case SCR_ ##N(1): return s->scr.N[1]; \ + case SCR_ ##N(2): return s->scr.N[2] + +static uint32_t tc6393xb_scr_readb(TC6393xbState *s, hwaddr addr) +{ + switch (addr) { + case SCR_REVID: + return 3; + case SCR_REVID+1: + return 0; + SCR_REG_B(ISR); + SCR_REG_B(IMR); + SCR_REG_B(IRR); + SCR_REG_W(GPER); + SCR_REG_A(GPI_SR); + SCR_REG_A(GPI_IMR); + SCR_REG_A(GPI_EDER); + SCR_REG_A(GPI_LIR); + case SCR_GPO_DSR(0): + case SCR_GPO_DSR(1): + case SCR_GPO_DSR(2): + return (s->gpio_level >> ((addr - SCR_GPO_DSR(0)) * 8)) & 0xff; + case SCR_GPO_DOECR(0): + case SCR_GPO_DOECR(1): + case SCR_GPO_DOECR(2): + return (s->gpio_dir >> ((addr - SCR_GPO_DOECR(0)) * 8)) & 0xff; + SCR_REG_A(GP_IARCR); + SCR_REG_A(GP_IARLCR); + SCR_REG_A(GPI_BCR); + SCR_REG_W(GPA_IARCR); + SCR_REG_W(GPA_IARLCR); + SCR_REG_W(CCR); + SCR_REG_W(PLL2CR); + SCR_REG_L(PLL1CR); + SCR_REG_B(DIARCR); + SCR_REG_B(DBOCR); + SCR_REG_B(FER); + SCR_REG_W(MCR); + SCR_REG_B(CONFIG); + SCR_REG_B(DEBUG); + } + fprintf(stderr, "tc6393xb_scr: unhandled read at %08x\n", (uint32_t) addr); + return 0; +} +#undef SCR_REG_B +#undef SCR_REG_W +#undef SCR_REG_L +#undef SCR_REG_A + +#define SCR_REG_B(N) \ + case SCR_ ##N: s->scr.N = value; return; +#define SCR_REG_W(N) \ + case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \ + case SCR_ ##N + 1: s->scr.N = (s->scr.N & 0xff) | (value << 8); return +#define SCR_REG_L(N) \ + case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \ + case SCR_ ##N + 1: s->scr.N = (s->scr.N & ~(0xff << 8)) | (value & (0xff << 8)); return; \ + case SCR_ ##N + 2: s->scr.N = (s->scr.N & ~(0xff << 16)) | (value & (0xff << 16)); return; \ + case SCR_ ##N + 3: s->scr.N = (s->scr.N & ~(0xff << 24)) | (value & (0xff << 24)); return; +#define SCR_REG_A(N) \ + case SCR_ ##N(0): s->scr.N[0] = value; return; \ + case SCR_ ##N(1): s->scr.N[1] = value; return; \ + case SCR_ ##N(2): s->scr.N[2] = value; return + +static void tc6393xb_scr_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) +{ + switch (addr) { + SCR_REG_B(ISR); + SCR_REG_B(IMR); + SCR_REG_B(IRR); + SCR_REG_W(GPER); + SCR_REG_A(GPI_SR); + SCR_REG_A(GPI_IMR); + SCR_REG_A(GPI_EDER); + SCR_REG_A(GPI_LIR); + case SCR_GPO_DSR(0): + case SCR_GPO_DSR(1): + case SCR_GPO_DSR(2): + s->gpio_level = (s->gpio_level & ~(0xff << ((addr - SCR_GPO_DSR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DSR(0))*8)); + tc6393xb_gpio_handler_update(s); + return; + case SCR_GPO_DOECR(0): + case SCR_GPO_DOECR(1): + case SCR_GPO_DOECR(2): + s->gpio_dir = (s->gpio_dir & ~(0xff << ((addr - SCR_GPO_DOECR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DOECR(0))*8)); + tc6393xb_gpio_handler_update(s); + return; + SCR_REG_A(GP_IARCR); + SCR_REG_A(GP_IARLCR); + SCR_REG_A(GPI_BCR); + SCR_REG_W(GPA_IARCR); + SCR_REG_W(GPA_IARLCR); + SCR_REG_W(CCR); + SCR_REG_W(PLL2CR); + SCR_REG_L(PLL1CR); + SCR_REG_B(DIARCR); + SCR_REG_B(DBOCR); + SCR_REG_B(FER); + SCR_REG_W(MCR); + SCR_REG_B(CONFIG); + SCR_REG_B(DEBUG); + } + fprintf(stderr, "tc6393xb_scr: unhandled write at %08x: %02x\n", + (uint32_t) addr, value & 0xff); +} +#undef SCR_REG_B +#undef SCR_REG_W +#undef SCR_REG_L +#undef SCR_REG_A + +static void tc6393xb_nand_irq(TC6393xbState *s) { + qemu_set_irq(s->sub_irqs[IRQ_TC6393_NAND], + (s->nand.imr & 0x80) && (s->nand.imr & s->nand.isr)); +} + +static uint32_t tc6393xb_nand_cfg_readb(TC6393xbState *s, hwaddr addr) { + switch (addr) { + case NAND_CFG_COMMAND: + return s->nand_enable ? 2 : 0; + case NAND_CFG_BASE: + case NAND_CFG_BASE + 1: + case NAND_CFG_BASE + 2: + case NAND_CFG_BASE + 3: + return s->nand_phys >> (addr - NAND_CFG_BASE); + } + fprintf(stderr, "tc6393xb_nand_cfg: unhandled read at %08x\n", (uint32_t) addr); + return 0; +} +static void tc6393xb_nand_cfg_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) { + switch (addr) { + case NAND_CFG_COMMAND: + s->nand_enable = (value & 0x2); + return; + case NAND_CFG_BASE: + case NAND_CFG_BASE + 1: + case NAND_CFG_BASE + 2: + case NAND_CFG_BASE + 3: + s->nand_phys &= ~(0xff << ((addr - NAND_CFG_BASE) * 8)); + s->nand_phys |= (value & 0xff) << ((addr - NAND_CFG_BASE) * 8); + return; + } + fprintf(stderr, "tc6393xb_nand_cfg: unhandled write at %08x: %02x\n", + (uint32_t) addr, value & 0xff); +} + +static uint32_t tc6393xb_nand_readb(TC6393xbState *s, hwaddr addr) { + switch (addr) { + case NAND_DATA + 0: + case NAND_DATA + 1: + case NAND_DATA + 2: + case NAND_DATA + 3: + return nand_getio(s->flash); + case NAND_MODE: + return s->nand.mode; + case NAND_STATUS: + return 0x14; + case NAND_ISR: + return s->nand.isr; + case NAND_IMR: + return s->nand.imr; + } + fprintf(stderr, "tc6393xb_nand: unhandled read at %08x\n", (uint32_t) addr); + return 0; +} +static void tc6393xb_nand_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) { +// fprintf(stderr, "tc6393xb_nand: write at %08x: %02x\n", +// (uint32_t) addr, value & 0xff); + switch (addr) { + case NAND_DATA + 0: + case NAND_DATA + 1: + case NAND_DATA + 2: + case NAND_DATA + 3: + nand_setio(s->flash, value); + s->nand.isr |= 1; + tc6393xb_nand_irq(s); + return; + case NAND_MODE: + s->nand.mode = value; + nand_setpins(s->flash, + value & NAND_MODE_CLE, + value & NAND_MODE_ALE, + !(value & NAND_MODE_CE), + value & NAND_MODE_WP, + 0); // FIXME: gnd + switch (value & NAND_MODE_ECC_MASK) { + case NAND_MODE_ECC_RST: + ecc_reset(&s->ecc); + break; + case NAND_MODE_ECC_READ: + // FIXME + break; + case NAND_MODE_ECC_EN: + ecc_reset(&s->ecc); + } + return; + case NAND_ISR: + s->nand.isr = value; + tc6393xb_nand_irq(s); + return; + case NAND_IMR: + s->nand.imr = value; + tc6393xb_nand_irq(s); + return; + } + fprintf(stderr, "tc6393xb_nand: unhandled write at %08x: %02x\n", + (uint32_t) addr, value & 0xff); +} + +#define BITS 8 +#include "hw/tc6393xb_template.h" +#define BITS 15 +#include "hw/tc6393xb_template.h" +#define BITS 16 +#include "hw/tc6393xb_template.h" +#define BITS 24 +#include "hw/tc6393xb_template.h" +#define BITS 32 +#include "hw/tc6393xb_template.h" + +static void tc6393xb_draw_graphic(TC6393xbState *s, int full_update) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + + switch (surface_bits_per_pixel(surface)) { + case 8: + tc6393xb_draw_graphic8(s); + break; + case 15: + tc6393xb_draw_graphic15(s); + break; + case 16: + tc6393xb_draw_graphic16(s); + break; + case 24: + tc6393xb_draw_graphic24(s); + break; + case 32: + tc6393xb_draw_graphic32(s); + break; + default: + printf("tc6393xb: unknown depth %d\n", + surface_bits_per_pixel(surface)); + return; + } + + dpy_gfx_update(s->con, 0, 0, s->scr_width, s->scr_height); +} + +static void tc6393xb_draw_blank(TC6393xbState *s, int full_update) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int i, w; + uint8_t *d; + + if (!full_update) + return; + + w = s->scr_width * surface_bytes_per_pixel(surface); + d = surface_data(surface); + for(i = 0; i < s->scr_height; i++) { + memset(d, 0, w); + d += surface_stride(surface); + } + + dpy_gfx_update(s->con, 0, 0, s->scr_width, s->scr_height); +} + +static void tc6393xb_update_display(void *opaque) +{ + TC6393xbState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + int full_update; + + if (s->scr_width == 0 || s->scr_height == 0) + return; + + full_update = 0; + if (s->blanked != s->blank) { + s->blanked = s->blank; + full_update = 1; + } + if (s->scr_width != surface_width(surface) || + s->scr_height != surface_height(surface)) { + qemu_console_resize(s->con, s->scr_width, s->scr_height); + full_update = 1; + } + if (s->blanked) + tc6393xb_draw_blank(s, full_update); + else + tc6393xb_draw_graphic(s, full_update); +} + + +static uint64_t tc6393xb_readb(void *opaque, hwaddr addr, + unsigned size) +{ + TC6393xbState *s = opaque; + + switch (addr >> 8) { + case 0: + return tc6393xb_scr_readb(s, addr & 0xff); + case 1: + return tc6393xb_nand_cfg_readb(s, addr & 0xff); + }; + + if ((addr &~0xff) == s->nand_phys && s->nand_enable) { +// return tc6393xb_nand_readb(s, addr & 0xff); + uint8_t d = tc6393xb_nand_readb(s, addr & 0xff); +// fprintf(stderr, "tc6393xb_nand: read at %08x: %02hhx\n", (uint32_t) addr, d); + return d; + } + +// fprintf(stderr, "tc6393xb: unhandled read at %08x\n", (uint32_t) addr); + return 0; +} + +static void tc6393xb_writeb(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { + TC6393xbState *s = opaque; + + switch (addr >> 8) { + case 0: + tc6393xb_scr_writeb(s, addr & 0xff, value); + return; + case 1: + tc6393xb_nand_cfg_writeb(s, addr & 0xff, value); + return; + }; + + if ((addr &~0xff) == s->nand_phys && s->nand_enable) + tc6393xb_nand_writeb(s, addr & 0xff, value); + else + fprintf(stderr, "tc6393xb: unhandled write at %08x: %02x\n", + (uint32_t) addr, (int)value & 0xff); +} + +TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq) +{ + TC6393xbState *s; + DriveInfo *nand; + static const MemoryRegionOps tc6393xb_ops = { + .read = tc6393xb_readb, + .write = tc6393xb_writeb, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + }; + + s = (TC6393xbState *) g_malloc0(sizeof(TC6393xbState)); + s->irq = irq; + s->gpio_in = qemu_allocate_irqs(tc6393xb_gpio_set, s, TC6393XB_GPIOS); + + s->l3v = *qemu_allocate_irqs(tc6393xb_l3v, s, 1); + s->blanked = 1; + + s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS); + + nand = drive_get(IF_MTD, 0, 0); + s->flash = nand_init(nand ? nand->bdrv : NULL, NAND_MFR_TOSHIBA, 0x76); + + memory_region_init_io(&s->iomem, &tc6393xb_ops, s, "tc6393xb", 0x10000); + memory_region_add_subregion(sysmem, base, &s->iomem); + + memory_region_init_ram(&s->vram, "tc6393xb.vram", 0x100000); + vmstate_register_ram_global(&s->vram); + s->vram_ptr = memory_region_get_ram_ptr(&s->vram); + memory_region_add_subregion(sysmem, base + 0x100000, &s->vram); + s->scr_width = 480; + s->scr_height = 640; + s->con = graphic_console_init(tc6393xb_update_display, + NULL, /* invalidate */ + NULL, /* screen_dump */ + NULL, /* text_update */ + s); + + return s; +} diff --git a/hw/display/tcx.c b/hw/display/tcx.c new file mode 100644 index 0000000000..c44068e7c9 --- /dev/null +++ b/hw/display/tcx.c @@ -0,0 +1,739 @@ +/* + * QEMU TCX Frame buffer + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "ui/console.h" +#include "ui/pixel_ops.h" +#include "hw/sysbus.h" +#include "hw/qdev-addr.h" + +#define MAXX 1024 +#define MAXY 768 +#define TCX_DAC_NREGS 16 +#define TCX_THC_NREGS_8 0x081c +#define TCX_THC_NREGS_24 0x1000 +#define TCX_TEC_NREGS 0x1000 + +typedef struct TCXState { + SysBusDevice busdev; + QemuConsole *con; + uint8_t *vram; + uint32_t *vram24, *cplane; + MemoryRegion vram_mem; + MemoryRegion vram_8bit; + MemoryRegion vram_24bit; + MemoryRegion vram_cplane; + MemoryRegion dac; + MemoryRegion tec; + MemoryRegion thc24; + MemoryRegion thc8; + ram_addr_t vram24_offset, cplane_offset; + uint32_t vram_size; + uint32_t palette[256]; + uint8_t r[256], g[256], b[256]; + uint16_t width, height, depth; + uint8_t dac_index, dac_state; +} TCXState; + +static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp); +static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp); + +static void tcx_set_dirty(TCXState *s) +{ + memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY); +} + +static void tcx24_set_dirty(TCXState *s) +{ + memory_region_set_dirty(&s->vram_mem, s->vram24_offset, MAXX * MAXY * 4); + memory_region_set_dirty(&s->vram_mem, s->cplane_offset, MAXX * MAXY * 4); +} + +static void update_palette_entries(TCXState *s, int start, int end) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int i; + + for (i = start; i < end; i++) { + switch (surface_bits_per_pixel(surface)) { + default: + case 8: + s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]); + break; + case 15: + s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]); + break; + case 16: + s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]); + break; + case 32: + if (is_surface_bgr(surface)) { + s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); + } else { + s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); + } + break; + } + } + if (s->depth == 24) { + tcx24_set_dirty(s); + } else { + tcx_set_dirty(s); + } +} + +static void tcx_draw_line32(TCXState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + int x; + uint8_t val; + uint32_t *p = (uint32_t *)d; + + for(x = 0; x < width; x++) { + val = *s++; + *p++ = s1->palette[val]; + } +} + +static void tcx_draw_line16(TCXState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + int x; + uint8_t val; + uint16_t *p = (uint16_t *)d; + + for(x = 0; x < width; x++) { + val = *s++; + *p++ = s1->palette[val]; + } +} + +static void tcx_draw_line8(TCXState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + int x; + uint8_t val; + + for(x = 0; x < width; x++) { + val = *s++; + *d++ = s1->palette[val]; + } +} + +/* + XXX Could be much more optimal: + * detect if line/page/whole screen is in 24 bit mode + * if destination is also BGR, use memcpy + */ +static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, + const uint8_t *s, int width, + const uint32_t *cplane, + const uint32_t *s24) +{ + DisplaySurface *surface = qemu_console_surface(s1->con); + int x, bgr, r, g, b; + uint8_t val, *p8; + uint32_t *p = (uint32_t *)d; + uint32_t dval; + + bgr = is_surface_bgr(surface); + for(x = 0; x < width; x++, s++, s24++) { + if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) { + // 24-bit direct, BGR order + p8 = (uint8_t *)s24; + p8++; + b = *p8++; + g = *p8++; + r = *p8; + if (bgr) + dval = rgb_to_pixel32bgr(r, g, b); + else + dval = rgb_to_pixel32(r, g, b); + } else { + val = *s; + dval = s1->palette[val]; + } + *p++ = dval; + } +} + +static inline int check_dirty(TCXState *s, ram_addr_t page, ram_addr_t page24, + ram_addr_t cpage) +{ + int ret; + + ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); + ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4, + DIRTY_MEMORY_VGA); + ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4, + DIRTY_MEMORY_VGA); + return ret; +} + +static inline void reset_dirty(TCXState *ts, ram_addr_t page_min, + ram_addr_t page_max, ram_addr_t page24, + ram_addr_t cpage) +{ + memory_region_reset_dirty(&ts->vram_mem, + page_min, page_max + TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); + memory_region_reset_dirty(&ts->vram_mem, + page24 + page_min * 4, + page24 + page_max * 4 + TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); + memory_region_reset_dirty(&ts->vram_mem, + cpage + page_min * 4, + cpage + page_max * 4 + TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); +} + +/* Fixed line length 1024 allows us to do nice tricks not possible on + VGA... */ +static void tcx_update_display(void *opaque) +{ + TCXState *ts = opaque; + DisplaySurface *surface = qemu_console_surface(ts->con); + ram_addr_t page, page_min, page_max; + int y, y_start, dd, ds; + uint8_t *d, *s; + void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width); + + if (surface_bits_per_pixel(surface) == 0) { + return; + } + + page = 0; + y_start = -1; + page_min = -1; + page_max = 0; + d = surface_data(surface); + s = ts->vram; + dd = surface_stride(surface); + ds = 1024; + + switch (surface_bits_per_pixel(surface)) { + case 32: + f = tcx_draw_line32; + break; + case 15: + case 16: + f = tcx_draw_line16; + break; + default: + case 8: + f = tcx_draw_line8; + break; + case 0: + return; + } + + for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) { + if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA)) { + if (y_start < 0) + y_start = y; + if (page < page_min) + page_min = page; + if (page > page_max) + page_max = page; + f(ts, d, s, ts->width); + d += dd; + s += ds; + f(ts, d, s, ts->width); + d += dd; + s += ds; + f(ts, d, s, ts->width); + d += dd; + s += ds; + f(ts, d, s, ts->width); + d += dd; + s += ds; + } else { + if (y_start >= 0) { + /* flush to display */ + dpy_gfx_update(ts->con, 0, y_start, + ts->width, y - y_start); + y_start = -1; + } + d += dd * 4; + s += ds * 4; + } + } + if (y_start >= 0) { + /* flush to display */ + dpy_gfx_update(ts->con, 0, y_start, + ts->width, y - y_start); + } + /* reset modified pages */ + if (page_max >= page_min) { + memory_region_reset_dirty(&ts->vram_mem, + page_min, page_max + TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); + } +} + +static void tcx24_update_display(void *opaque) +{ + TCXState *ts = opaque; + DisplaySurface *surface = qemu_console_surface(ts->con); + ram_addr_t page, page_min, page_max, cpage, page24; + int y, y_start, dd, ds; + uint8_t *d, *s; + uint32_t *cptr, *s24; + + if (surface_bits_per_pixel(surface) != 32) { + return; + } + + page = 0; + page24 = ts->vram24_offset; + cpage = ts->cplane_offset; + y_start = -1; + page_min = -1; + page_max = 0; + d = surface_data(surface); + s = ts->vram; + s24 = ts->vram24; + cptr = ts->cplane; + dd = surface_stride(surface); + ds = 1024; + + for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE, + page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) { + if (check_dirty(ts, page, page24, cpage)) { + if (y_start < 0) + y_start = y; + if (page < page_min) + page_min = page; + if (page > page_max) + page_max = page; + tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); + d += dd; + s += ds; + cptr += ds; + s24 += ds; + tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); + d += dd; + s += ds; + cptr += ds; + s24 += ds; + tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); + d += dd; + s += ds; + cptr += ds; + s24 += ds; + tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); + d += dd; + s += ds; + cptr += ds; + s24 += ds; + } else { + if (y_start >= 0) { + /* flush to display */ + dpy_gfx_update(ts->con, 0, y_start, + ts->width, y - y_start); + y_start = -1; + } + d += dd * 4; + s += ds * 4; + cptr += ds * 4; + s24 += ds * 4; + } + } + if (y_start >= 0) { + /* flush to display */ + dpy_gfx_update(ts->con, 0, y_start, + ts->width, y - y_start); + } + /* reset modified pages */ + if (page_max >= page_min) { + reset_dirty(ts, page_min, page_max, page24, cpage); + } +} + +static void tcx_invalidate_display(void *opaque) +{ + TCXState *s = opaque; + + tcx_set_dirty(s); + qemu_console_resize(s->con, s->width, s->height); +} + +static void tcx24_invalidate_display(void *opaque) +{ + TCXState *s = opaque; + + tcx_set_dirty(s); + tcx24_set_dirty(s); + qemu_console_resize(s->con, s->width, s->height); +} + +static int vmstate_tcx_post_load(void *opaque, int version_id) +{ + TCXState *s = opaque; + + update_palette_entries(s, 0, 256); + if (s->depth == 24) { + tcx24_set_dirty(s); + } else { + tcx_set_dirty(s); + } + + return 0; +} + +static const VMStateDescription vmstate_tcx = { + .name ="tcx", + .version_id = 4, + .minimum_version_id = 4, + .minimum_version_id_old = 4, + .post_load = vmstate_tcx_post_load, + .fields = (VMStateField []) { + VMSTATE_UINT16(height, TCXState), + VMSTATE_UINT16(width, TCXState), + VMSTATE_UINT16(depth, TCXState), + VMSTATE_BUFFER(r, TCXState), + VMSTATE_BUFFER(g, TCXState), + VMSTATE_BUFFER(b, TCXState), + VMSTATE_UINT8(dac_index, TCXState), + VMSTATE_UINT8(dac_state, TCXState), + VMSTATE_END_OF_LIST() + } +}; + +static void tcx_reset(DeviceState *d) +{ + TCXState *s = container_of(d, TCXState, busdev.qdev); + + /* Initialize palette */ + memset(s->r, 0, 256); + memset(s->g, 0, 256); + memset(s->b, 0, 256); + s->r[255] = s->g[255] = s->b[255] = 255; + update_palette_entries(s, 0, 256); + memset(s->vram, 0, MAXX*MAXY); + memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4), + DIRTY_MEMORY_VGA); + s->dac_index = 0; + s->dac_state = 0; +} + +static uint64_t tcx_dac_readl(void *opaque, hwaddr addr, + unsigned size) +{ + return 0; +} + +static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + TCXState *s = opaque; + + switch (addr) { + case 0: + s->dac_index = val >> 24; + s->dac_state = 0; + break; + case 4: + switch (s->dac_state) { + case 0: + s->r[s->dac_index] = val >> 24; + update_palette_entries(s, s->dac_index, s->dac_index + 1); + s->dac_state++; + break; + case 1: + s->g[s->dac_index] = val >> 24; + update_palette_entries(s, s->dac_index, s->dac_index + 1); + s->dac_state++; + break; + case 2: + s->b[s->dac_index] = val >> 24; + update_palette_entries(s, s->dac_index, s->dac_index + 1); + s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement + default: + s->dac_state = 0; + break; + } + break; + default: + break; + } +} + +static const MemoryRegionOps tcx_dac_ops = { + .read = tcx_dac_readl, + .write = tcx_dac_writel, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t dummy_readl(void *opaque, hwaddr addr, + unsigned size) +{ + return 0; +} + +static void dummy_writel(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ +} + +static const MemoryRegionOps dummy_ops = { + .read = dummy_readl, + .write = dummy_writel, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static int tcx_init1(SysBusDevice *dev) +{ + TCXState *s = FROM_SYSBUS(TCXState, dev); + ram_addr_t vram_offset = 0; + int size; + uint8_t *vram_base; + + memory_region_init_ram(&s->vram_mem, "tcx.vram", + s->vram_size * (1 + 4 + 4)); + vmstate_register_ram_global(&s->vram_mem); + vram_base = memory_region_get_ram_ptr(&s->vram_mem); + + /* 8-bit plane */ + s->vram = vram_base; + size = s->vram_size; + memory_region_init_alias(&s->vram_8bit, "tcx.vram.8bit", + &s->vram_mem, vram_offset, size); + sysbus_init_mmio(dev, &s->vram_8bit); + vram_offset += size; + vram_base += size; + + /* DAC */ + memory_region_init_io(&s->dac, &tcx_dac_ops, s, "tcx.dac", TCX_DAC_NREGS); + sysbus_init_mmio(dev, &s->dac); + + /* TEC (dummy) */ + memory_region_init_io(&s->tec, &dummy_ops, s, "tcx.tec", TCX_TEC_NREGS); + sysbus_init_mmio(dev, &s->tec); + /* THC: NetBSD writes here even with 8-bit display: dummy */ + memory_region_init_io(&s->thc24, &dummy_ops, s, "tcx.thc24", + TCX_THC_NREGS_24); + sysbus_init_mmio(dev, &s->thc24); + + if (s->depth == 24) { + /* 24-bit plane */ + size = s->vram_size * 4; + s->vram24 = (uint32_t *)vram_base; + s->vram24_offset = vram_offset; + memory_region_init_alias(&s->vram_24bit, "tcx.vram.24bit", + &s->vram_mem, vram_offset, size); + sysbus_init_mmio(dev, &s->vram_24bit); + vram_offset += size; + vram_base += size; + + /* Control plane */ + size = s->vram_size * 4; + s->cplane = (uint32_t *)vram_base; + s->cplane_offset = vram_offset; + memory_region_init_alias(&s->vram_cplane, "tcx.vram.cplane", + &s->vram_mem, vram_offset, size); + sysbus_init_mmio(dev, &s->vram_cplane); + + s->con = graphic_console_init(tcx24_update_display, + tcx24_invalidate_display, + tcx24_screen_dump, NULL, s); + } else { + /* THC 8 bit (dummy) */ + memory_region_init_io(&s->thc8, &dummy_ops, s, "tcx.thc8", + TCX_THC_NREGS_8); + sysbus_init_mmio(dev, &s->thc8); + + s->con = graphic_console_init(tcx_update_display, + tcx_invalidate_display, + tcx_screen_dump, NULL, s); + } + + qemu_console_resize(s->con, s->width, s->height); + return 0; +} + +static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp) +{ + TCXState *s = opaque; + FILE *f; + uint8_t *d, *d1, v; + int ret, y, x; + + f = fopen(filename, "wb"); + if (!f) { + error_setg(errp, "failed to open file '%s': %s", filename, + strerror(errno)); + return; + } + ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); + if (ret < 0) { + goto write_err; + } + d1 = s->vram; + for(y = 0; y < s->height; y++) { + d = d1; + for(x = 0; x < s->width; x++) { + v = *d; + ret = fputc(s->r[v], f); + if (ret == EOF) { + goto write_err; + } + ret = fputc(s->g[v], f); + if (ret == EOF) { + goto write_err; + } + ret = fputc(s->b[v], f); + if (ret == EOF) { + goto write_err; + } + d++; + } + d1 += MAXX; + } + +out: + fclose(f); + return; + +write_err: + error_setg(errp, "failed to write to file '%s': %s", filename, + strerror(errno)); + unlink(filename); + goto out; +} + +static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp) +{ + TCXState *s = opaque; + FILE *f; + uint8_t *d, *d1, v; + uint32_t *s24, *cptr, dval; + int ret, y, x; + + f = fopen(filename, "wb"); + if (!f) { + error_setg(errp, "failed to open file '%s': %s", filename, + strerror(errno)); + return; + } + ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); + if (ret < 0) { + goto write_err; + } + d1 = s->vram; + s24 = s->vram24; + cptr = s->cplane; + for(y = 0; y < s->height; y++) { + d = d1; + for(x = 0; x < s->width; x++, d++, s24++) { + if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct + dval = *s24 & 0x00ffffff; + ret = fputc((dval >> 16) & 0xff, f); + if (ret == EOF) { + goto write_err; + } + ret = fputc((dval >> 8) & 0xff, f); + if (ret == EOF) { + goto write_err; + } + ret = fputc(dval & 0xff, f); + if (ret == EOF) { + goto write_err; + } + } else { + v = *d; + ret = fputc(s->r[v], f); + if (ret == EOF) { + goto write_err; + } + ret = fputc(s->g[v], f); + if (ret == EOF) { + goto write_err; + } + ret = fputc(s->b[v], f); + if (ret == EOF) { + goto write_err; + } + } + } + d1 += MAXX; + } + +out: + fclose(f); + return; + +write_err: + error_setg(errp, "failed to write to file '%s': %s", filename, + strerror(errno)); + unlink(filename); + goto out; +} + +static Property tcx_properties[] = { + DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1), + DEFINE_PROP_UINT16("width", TCXState, width, -1), + DEFINE_PROP_UINT16("height", TCXState, height, -1), + DEFINE_PROP_UINT16("depth", TCXState, depth, -1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tcx_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = tcx_init1; + dc->reset = tcx_reset; + dc->vmsd = &vmstate_tcx; + dc->props = tcx_properties; +} + +static const TypeInfo tcx_info = { + .name = "SUNW,tcx", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TCXState), + .class_init = tcx_class_init, +}; + +static void tcx_register_types(void) +{ + type_register_static(&tcx_info); +} + +type_init(tcx_register_types) diff --git a/hw/display/vga.c b/hw/display/vga.c new file mode 100644 index 0000000000..dc31fd574c --- /dev/null +++ b/hw/display/vga.c @@ -0,0 +1,2457 @@ +/* + * QEMU VGA Emulator. + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw/hw.h" +#include "hw/vga.h" +#include "ui/console.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "hw/vga_int.h" +#include "ui/pixel_ops.h" +#include "qemu/timer.h" +#include "hw/xen/xen.h" +#include "trace.h" + +//#define DEBUG_VGA +//#define DEBUG_VGA_MEM +//#define DEBUG_VGA_REG + +//#define DEBUG_BOCHS_VBE + +/* 16 state changes per vertical frame @60 Hz */ +#define VGA_TEXT_CURSOR_PERIOD_MS (1000 * 2 * 16 / 60) + +/* + * Video Graphics Array (VGA) + * + * Chipset docs for original IBM VGA: + * http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf + * + * FreeVGA site: + * http://www.osdever.net/FreeVGA/home.htm + * + * Standard VGA features and Bochs VBE extensions are implemented. + */ + +/* force some bits to zero */ +const uint8_t sr_mask[8] = { + 0x03, + 0x3d, + 0x0f, + 0x3f, + 0x0e, + 0x00, + 0x00, + 0xff, +}; + +const uint8_t gr_mask[16] = { + 0x0f, /* 0x00 */ + 0x0f, /* 0x01 */ + 0x0f, /* 0x02 */ + 0x1f, /* 0x03 */ + 0x03, /* 0x04 */ + 0x7b, /* 0x05 */ + 0x0f, /* 0x06 */ + 0x0f, /* 0x07 */ + 0xff, /* 0x08 */ + 0x00, /* 0x09 */ + 0x00, /* 0x0a */ + 0x00, /* 0x0b */ + 0x00, /* 0x0c */ + 0x00, /* 0x0d */ + 0x00, /* 0x0e */ + 0x00, /* 0x0f */ +}; + +#define cbswap_32(__x) \ +((uint32_t)( \ + (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) + +#ifdef HOST_WORDS_BIGENDIAN +#define PAT(x) cbswap_32(x) +#else +#define PAT(x) (x) +#endif + +#ifdef HOST_WORDS_BIGENDIAN +#define BIG 1 +#else +#define BIG 0 +#endif + +#ifdef HOST_WORDS_BIGENDIAN +#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff) +#else +#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff) +#endif + +static const uint32_t mask16[16] = { + PAT(0x00000000), + PAT(0x000000ff), + PAT(0x0000ff00), + PAT(0x0000ffff), + PAT(0x00ff0000), + PAT(0x00ff00ff), + PAT(0x00ffff00), + PAT(0x00ffffff), + PAT(0xff000000), + PAT(0xff0000ff), + PAT(0xff00ff00), + PAT(0xff00ffff), + PAT(0xffff0000), + PAT(0xffff00ff), + PAT(0xffffff00), + PAT(0xffffffff), +}; + +#undef PAT + +#ifdef HOST_WORDS_BIGENDIAN +#define PAT(x) (x) +#else +#define PAT(x) cbswap_32(x) +#endif + +static const uint32_t dmask16[16] = { + PAT(0x00000000), + PAT(0x000000ff), + PAT(0x0000ff00), + PAT(0x0000ffff), + PAT(0x00ff0000), + PAT(0x00ff00ff), + PAT(0x00ffff00), + PAT(0x00ffffff), + PAT(0xff000000), + PAT(0xff0000ff), + PAT(0xff00ff00), + PAT(0xff00ffff), + PAT(0xffff0000), + PAT(0xffff00ff), + PAT(0xffffff00), + PAT(0xffffffff), +}; + +static const uint32_t dmask4[4] = { + PAT(0x00000000), + PAT(0x0000ffff), + PAT(0xffff0000), + PAT(0xffffffff), +}; + +static uint32_t expand4[256]; +static uint16_t expand2[256]; +static uint8_t expand4to8[16]; + +static void vga_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp); + +static void vga_update_memory_access(VGACommonState *s) +{ + MemoryRegion *region, *old_region = s->chain4_alias; + hwaddr base, offset, size; + + s->chain4_alias = NULL; + + if ((s->sr[VGA_SEQ_PLANE_WRITE] & VGA_SR02_ALL_PLANES) == + VGA_SR02_ALL_PLANES && s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { + offset = 0; + switch ((s->gr[VGA_GFX_MISC] >> 2) & 3) { + case 0: + base = 0xa0000; + size = 0x20000; + break; + case 1: + base = 0xa0000; + size = 0x10000; + offset = s->bank_offset; + break; + case 2: + base = 0xb0000; + size = 0x8000; + break; + case 3: + default: + base = 0xb8000; + size = 0x8000; + break; + } + base += isa_mem_base; + region = g_malloc(sizeof(*region)); + memory_region_init_alias(region, "vga.chain4", &s->vram, offset, size); + memory_region_add_subregion_overlap(s->legacy_address_space, base, + region, 2); + s->chain4_alias = region; + } + if (old_region) { + memory_region_del_subregion(s->legacy_address_space, old_region); + memory_region_destroy(old_region); + g_free(old_region); + s->plane_updated = 0xf; + } +} + +static void vga_dumb_update_retrace_info(VGACommonState *s) +{ + (void) s; +} + +static void vga_precise_update_retrace_info(VGACommonState *s) +{ + int htotal_chars; + int hretr_start_char; + int hretr_skew_chars; + int hretr_end_char; + + int vtotal_lines; + int vretr_start_line; + int vretr_end_line; + + int dots; +#if 0 + int div2, sldiv2; +#endif + int clocking_mode; + int clock_sel; + const int clk_hz[] = {25175000, 28322000, 25175000, 25175000}; + int64_t chars_per_sec; + struct vga_precise_retrace *r = &s->retrace_info.precise; + + htotal_chars = s->cr[VGA_CRTC_H_TOTAL] + 5; + hretr_start_char = s->cr[VGA_CRTC_H_SYNC_START]; + hretr_skew_chars = (s->cr[VGA_CRTC_H_SYNC_END] >> 5) & 3; + hretr_end_char = s->cr[VGA_CRTC_H_SYNC_END] & 0x1f; + + vtotal_lines = (s->cr[VGA_CRTC_V_TOTAL] | + (((s->cr[VGA_CRTC_OVERFLOW] & 1) | + ((s->cr[VGA_CRTC_OVERFLOW] >> 4) & 2)) << 8)) + 2; + vretr_start_line = s->cr[VGA_CRTC_V_SYNC_START] | + ((((s->cr[VGA_CRTC_OVERFLOW] >> 2) & 1) | + ((s->cr[VGA_CRTC_OVERFLOW] >> 6) & 2)) << 8); + vretr_end_line = s->cr[VGA_CRTC_V_SYNC_END] & 0xf; + + clocking_mode = (s->sr[VGA_SEQ_CLOCK_MODE] >> 3) & 1; + clock_sel = (s->msr >> 2) & 3; + dots = (s->msr & 1) ? 8 : 9; + + chars_per_sec = clk_hz[clock_sel] / dots; + + htotal_chars <<= clocking_mode; + + r->total_chars = vtotal_lines * htotal_chars; + if (r->freq) { + r->ticks_per_char = get_ticks_per_sec() / (r->total_chars * r->freq); + } else { + r->ticks_per_char = get_ticks_per_sec() / chars_per_sec; + } + + r->vstart = vretr_start_line; + r->vend = r->vstart + vretr_end_line + 1; + + r->hstart = hretr_start_char + hretr_skew_chars; + r->hend = r->hstart + hretr_end_char + 1; + r->htotal = htotal_chars; + +#if 0 + div2 = (s->cr[VGA_CRTC_MODE] >> 2) & 1; + sldiv2 = (s->cr[VGA_CRTC_MODE] >> 3) & 1; + printf ( + "hz=%f\n" + "htotal = %d\n" + "hretr_start = %d\n" + "hretr_skew = %d\n" + "hretr_end = %d\n" + "vtotal = %d\n" + "vretr_start = %d\n" + "vretr_end = %d\n" + "div2 = %d sldiv2 = %d\n" + "clocking_mode = %d\n" + "clock_sel = %d %d\n" + "dots = %d\n" + "ticks/char = %" PRId64 "\n" + "\n", + (double) get_ticks_per_sec() / (r->ticks_per_char * r->total_chars), + htotal_chars, + hretr_start_char, + hretr_skew_chars, + hretr_end_char, + vtotal_lines, + vretr_start_line, + vretr_end_line, + div2, sldiv2, + clocking_mode, + clock_sel, + clk_hz[clock_sel], + dots, + r->ticks_per_char + ); +#endif +} + +static uint8_t vga_precise_retrace(VGACommonState *s) +{ + struct vga_precise_retrace *r = &s->retrace_info.precise; + uint8_t val = s->st01 & ~(ST01_V_RETRACE | ST01_DISP_ENABLE); + + if (r->total_chars) { + int cur_line, cur_line_char, cur_char; + int64_t cur_tick; + + cur_tick = qemu_get_clock_ns(vm_clock); + + cur_char = (cur_tick / r->ticks_per_char) % r->total_chars; + cur_line = cur_char / r->htotal; + + if (cur_line >= r->vstart && cur_line <= r->vend) { + val |= ST01_V_RETRACE | ST01_DISP_ENABLE; + } else { + cur_line_char = cur_char % r->htotal; + if (cur_line_char >= r->hstart && cur_line_char <= r->hend) { + val |= ST01_DISP_ENABLE; + } + } + + return val; + } else { + return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE); + } +} + +static uint8_t vga_dumb_retrace(VGACommonState *s) +{ + return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE); +} + +int vga_ioport_invalid(VGACommonState *s, uint32_t addr) +{ + if (s->msr & VGA_MIS_COLOR) { + /* Color */ + return (addr >= 0x3b0 && addr <= 0x3bf); + } else { + /* Monochrome */ + return (addr >= 0x3d0 && addr <= 0x3df); + } +} + +uint32_t vga_ioport_read(void *opaque, uint32_t addr) +{ + VGACommonState *s = opaque; + int val, index; + + qemu_flush_coalesced_mmio_buffer(); + + if (vga_ioport_invalid(s, addr)) { + val = 0xff; + } else { + switch(addr) { + case VGA_ATT_W: + if (s->ar_flip_flop == 0) { + val = s->ar_index; + } else { + val = 0; + } + break; + case VGA_ATT_R: + index = s->ar_index & 0x1f; + if (index < VGA_ATT_C) { + val = s->ar[index]; + } else { + val = 0; + } + break; + case VGA_MIS_W: + val = s->st00; + break; + case VGA_SEQ_I: + val = s->sr_index; + break; + case VGA_SEQ_D: + val = s->sr[s->sr_index]; +#ifdef DEBUG_VGA_REG + printf("vga: read SR%x = 0x%02x\n", s->sr_index, val); +#endif + break; + case VGA_PEL_IR: + val = s->dac_state; + break; + case VGA_PEL_IW: + val = s->dac_write_index; + break; + case VGA_PEL_D: + val = s->palette[s->dac_read_index * 3 + s->dac_sub_index]; + if (++s->dac_sub_index == 3) { + s->dac_sub_index = 0; + s->dac_read_index++; + } + break; + case VGA_FTC_R: + val = s->fcr; + break; + case VGA_MIS_R: + val = s->msr; + break; + case VGA_GFX_I: + val = s->gr_index; + break; + case VGA_GFX_D: + val = s->gr[s->gr_index]; +#ifdef DEBUG_VGA_REG + printf("vga: read GR%x = 0x%02x\n", s->gr_index, val); +#endif + break; + case VGA_CRT_IM: + case VGA_CRT_IC: + val = s->cr_index; + break; + case VGA_CRT_DM: + case VGA_CRT_DC: + val = s->cr[s->cr_index]; +#ifdef DEBUG_VGA_REG + printf("vga: read CR%x = 0x%02x\n", s->cr_index, val); +#endif + break; + case VGA_IS1_RM: + case VGA_IS1_RC: + /* just toggle to fool polling */ + val = s->st01 = s->retrace(s); + s->ar_flip_flop = 0; + break; + default: + val = 0x00; + break; + } + } +#if defined(DEBUG_VGA) + printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val); +#endif + return val; +} + +void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + VGACommonState *s = opaque; + int index; + + qemu_flush_coalesced_mmio_buffer(); + + /* check port range access depending on color/monochrome mode */ + if (vga_ioport_invalid(s, addr)) { + return; + } +#ifdef DEBUG_VGA + printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val); +#endif + + switch(addr) { + case VGA_ATT_W: + if (s->ar_flip_flop == 0) { + val &= 0x3f; + s->ar_index = val; + } else { + index = s->ar_index & 0x1f; + switch(index) { + case VGA_ATC_PALETTE0 ... VGA_ATC_PALETTEF: + s->ar[index] = val & 0x3f; + break; + case VGA_ATC_MODE: + s->ar[index] = val & ~0x10; + break; + case VGA_ATC_OVERSCAN: + s->ar[index] = val; + break; + case VGA_ATC_PLANE_ENABLE: + s->ar[index] = val & ~0xc0; + break; + case VGA_ATC_PEL: + s->ar[index] = val & ~0xf0; + break; + case VGA_ATC_COLOR_PAGE: + s->ar[index] = val & ~0xf0; + break; + default: + break; + } + } + s->ar_flip_flop ^= 1; + break; + case VGA_MIS_W: + s->msr = val & ~0x10; + s->update_retrace_info(s); + break; + case VGA_SEQ_I: + s->sr_index = val & 7; + break; + case VGA_SEQ_D: +#ifdef DEBUG_VGA_REG + printf("vga: write SR%x = 0x%02x\n", s->sr_index, val); +#endif + s->sr[s->sr_index] = val & sr_mask[s->sr_index]; + if (s->sr_index == VGA_SEQ_CLOCK_MODE) { + s->update_retrace_info(s); + } + vga_update_memory_access(s); + break; + case VGA_PEL_IR: + s->dac_read_index = val; + s->dac_sub_index = 0; + s->dac_state = 3; + break; + case VGA_PEL_IW: + s->dac_write_index = val; + s->dac_sub_index = 0; + s->dac_state = 0; + break; + case VGA_PEL_D: + s->dac_cache[s->dac_sub_index] = val; + if (++s->dac_sub_index == 3) { + memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3); + s->dac_sub_index = 0; + s->dac_write_index++; + } + break; + case VGA_GFX_I: + s->gr_index = val & 0x0f; + break; + case VGA_GFX_D: +#ifdef DEBUG_VGA_REG + printf("vga: write GR%x = 0x%02x\n", s->gr_index, val); +#endif + s->gr[s->gr_index] = val & gr_mask[s->gr_index]; + vga_update_memory_access(s); + break; + case VGA_CRT_IM: + case VGA_CRT_IC: + s->cr_index = val; + break; + case VGA_CRT_DM: + case VGA_CRT_DC: +#ifdef DEBUG_VGA_REG + printf("vga: write CR%x = 0x%02x\n", s->cr_index, val); +#endif + /* handle CR0-7 protection */ + if ((s->cr[VGA_CRTC_V_SYNC_END] & VGA_CR11_LOCK_CR0_CR7) && + s->cr_index <= VGA_CRTC_OVERFLOW) { + /* can always write bit 4 of CR7 */ + if (s->cr_index == VGA_CRTC_OVERFLOW) { + s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x10) | + (val & 0x10); + } + return; + } + s->cr[s->cr_index] = val; + + switch(s->cr_index) { + case VGA_CRTC_H_TOTAL: + case VGA_CRTC_H_SYNC_START: + case VGA_CRTC_H_SYNC_END: + case VGA_CRTC_V_TOTAL: + case VGA_CRTC_OVERFLOW: + case VGA_CRTC_V_SYNC_END: + case VGA_CRTC_MODE: + s->update_retrace_info(s); + break; + } + break; + case VGA_IS1_RM: + case VGA_IS1_RC: + s->fcr = val & 0x10; + break; + } +} + +static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr) +{ + VGACommonState *s = opaque; + uint32_t val; + val = s->vbe_index; + return val; +} + +uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr) +{ + VGACommonState *s = opaque; + uint32_t val; + + if (s->vbe_index < VBE_DISPI_INDEX_NB) { + if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) { + switch(s->vbe_index) { + /* XXX: do not hardcode ? */ + case VBE_DISPI_INDEX_XRES: + val = VBE_DISPI_MAX_XRES; + break; + case VBE_DISPI_INDEX_YRES: + val = VBE_DISPI_MAX_YRES; + break; + case VBE_DISPI_INDEX_BPP: + val = VBE_DISPI_MAX_BPP; + break; + default: + val = s->vbe_regs[s->vbe_index]; + break; + } + } else { + val = s->vbe_regs[s->vbe_index]; + } + } else if (s->vbe_index == VBE_DISPI_INDEX_VIDEO_MEMORY_64K) { + val = s->vram_size / (64 * 1024); + } else { + val = 0; + } +#ifdef DEBUG_BOCHS_VBE + printf("VBE: read index=0x%x val=0x%x\n", s->vbe_index, val); +#endif + return val; +} + +void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val) +{ + VGACommonState *s = opaque; + s->vbe_index = val; +} + +void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val) +{ + VGACommonState *s = opaque; + + if (s->vbe_index <= VBE_DISPI_INDEX_NB) { +#ifdef DEBUG_BOCHS_VBE + printf("VBE: write index=0x%x val=0x%x\n", s->vbe_index, val); +#endif + switch(s->vbe_index) { + case VBE_DISPI_INDEX_ID: + if (val == VBE_DISPI_ID0 || + val == VBE_DISPI_ID1 || + val == VBE_DISPI_ID2 || + val == VBE_DISPI_ID3 || + val == VBE_DISPI_ID4) { + s->vbe_regs[s->vbe_index] = val; + } + break; + case VBE_DISPI_INDEX_XRES: + if ((val <= VBE_DISPI_MAX_XRES) && ((val & 7) == 0)) { + s->vbe_regs[s->vbe_index] = val; + } + break; + case VBE_DISPI_INDEX_YRES: + if (val <= VBE_DISPI_MAX_YRES) { + s->vbe_regs[s->vbe_index] = val; + } + break; + case VBE_DISPI_INDEX_BPP: + if (val == 0) + val = 8; + if (val == 4 || val == 8 || val == 15 || + val == 16 || val == 24 || val == 32) { + s->vbe_regs[s->vbe_index] = val; + } + break; + case VBE_DISPI_INDEX_BANK: + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) { + val &= (s->vbe_bank_mask >> 2); + } else { + val &= s->vbe_bank_mask; + } + s->vbe_regs[s->vbe_index] = val; + s->bank_offset = (val << 16); + vga_update_memory_access(s); + break; + case VBE_DISPI_INDEX_ENABLE: + if ((val & VBE_DISPI_ENABLED) && + !(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { + int h, shift_control; + + s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = + s->vbe_regs[VBE_DISPI_INDEX_XRES]; + s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = + s->vbe_regs[VBE_DISPI_INDEX_YRES]; + s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0; + s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0; + + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) + s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1; + else + s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] * + ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); + s->vbe_start_addr = 0; + + /* clear the screen (should be done in BIOS) */ + if (!(val & VBE_DISPI_NOCLEARMEM)) { + memset(s->vram_ptr, 0, + s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset); + } + + /* we initialize the VGA graphic mode (should be done + in BIOS) */ + /* graphic mode + memory map 1 */ + s->gr[VGA_GFX_MISC] = (s->gr[VGA_GFX_MISC] & ~0x0c) | 0x04 | + VGA_GR06_GRAPHICS_MODE; + s->cr[VGA_CRTC_MODE] |= 3; /* no CGA modes */ + s->cr[VGA_CRTC_OFFSET] = s->vbe_line_offset >> 3; + /* width */ + s->cr[VGA_CRTC_H_DISP] = + (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1; + /* height (only meaningful if < 1024) */ + h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1; + s->cr[VGA_CRTC_V_DISP_END] = h; + s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x42) | + ((h >> 7) & 0x02) | ((h >> 3) & 0x40); + /* line compare to 1023 */ + s->cr[VGA_CRTC_LINE_COMPARE] = 0xff; + s->cr[VGA_CRTC_OVERFLOW] |= 0x10; + s->cr[VGA_CRTC_MAX_SCAN] |= 0x40; + + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) { + shift_control = 0; + s->sr[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */ + } else { + shift_control = 2; + /* set chain 4 mode */ + s->sr[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M; + /* activate all planes */ + s->sr[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES; + } + s->gr[VGA_GFX_MODE] = (s->gr[VGA_GFX_MODE] & ~0x60) | + (shift_control << 5); + s->cr[VGA_CRTC_MAX_SCAN] &= ~0x9f; /* no double scan */ + } else { + /* XXX: the bios should do that */ + s->bank_offset = 0; + } + s->dac_8bit = (val & VBE_DISPI_8BIT_DAC) > 0; + s->vbe_regs[s->vbe_index] = val; + vga_update_memory_access(s); + break; + case VBE_DISPI_INDEX_VIRT_WIDTH: + { + int w, h, line_offset; + + if (val < s->vbe_regs[VBE_DISPI_INDEX_XRES]) + return; + w = val; + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) + line_offset = w >> 1; + else + line_offset = w * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); + h = s->vram_size / line_offset; + /* XXX: support weird bochs semantics ? */ + if (h < s->vbe_regs[VBE_DISPI_INDEX_YRES]) + return; + s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = w; + s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h; + s->vbe_line_offset = line_offset; + } + break; + case VBE_DISPI_INDEX_X_OFFSET: + case VBE_DISPI_INDEX_Y_OFFSET: + { + int x; + s->vbe_regs[s->vbe_index] = val; + s->vbe_start_addr = s->vbe_line_offset * s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]; + x = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET]; + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) + s->vbe_start_addr += x >> 1; + else + s->vbe_start_addr += x * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); + s->vbe_start_addr >>= 2; + } + break; + default: + break; + } + } +} + +/* called for accesses between 0xa0000 and 0xc0000 */ +uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr) +{ + int memory_map_mode, plane; + uint32_t ret; + + /* convert to VGA memory offset */ + memory_map_mode = (s->gr[VGA_GFX_MISC] >> 2) & 3; + addr &= 0x1ffff; + switch(memory_map_mode) { + case 0: + break; + case 1: + if (addr >= 0x10000) + return 0xff; + addr += s->bank_offset; + break; + case 2: + addr -= 0x10000; + if (addr >= 0x8000) + return 0xff; + break; + default: + case 3: + addr -= 0x18000; + if (addr >= 0x8000) + return 0xff; + break; + } + + if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { + /* chain 4 mode : simplest access */ + ret = s->vram_ptr[addr]; + } else if (s->gr[VGA_GFX_MODE] & 0x10) { + /* odd/even mode (aka text mode mapping) */ + plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1); + ret = s->vram_ptr[((addr & ~1) << 1) | plane]; + } else { + /* standard VGA latched access */ + s->latch = ((uint32_t *)s->vram_ptr)[addr]; + + if (!(s->gr[VGA_GFX_MODE] & 0x08)) { + /* read mode 0 */ + plane = s->gr[VGA_GFX_PLANE_READ]; + ret = GET_PLANE(s->latch, plane); + } else { + /* read mode 1 */ + ret = (s->latch ^ mask16[s->gr[VGA_GFX_COMPARE_VALUE]]) & + mask16[s->gr[VGA_GFX_COMPARE_MASK]]; + ret |= ret >> 16; + ret |= ret >> 8; + ret = (~ret) & 0xff; + } + } + return ret; +} + +/* called for accesses between 0xa0000 and 0xc0000 */ +void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) +{ + int memory_map_mode, plane, write_mode, b, func_select, mask; + uint32_t write_mask, bit_mask, set_mask; + +#ifdef DEBUG_VGA_MEM + printf("vga: [0x" TARGET_FMT_plx "] = 0x%02x\n", addr, val); +#endif + /* convert to VGA memory offset */ + memory_map_mode = (s->gr[VGA_GFX_MISC] >> 2) & 3; + addr &= 0x1ffff; + switch(memory_map_mode) { + case 0: + break; + case 1: + if (addr >= 0x10000) + return; + addr += s->bank_offset; + break; + case 2: + addr -= 0x10000; + if (addr >= 0x8000) + return; + break; + default: + case 3: + addr -= 0x18000; + if (addr >= 0x8000) + return; + break; + } + + if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { + /* chain 4 mode : simplest access */ + plane = addr & 3; + mask = (1 << plane); + if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) { + s->vram_ptr[addr] = val; +#ifdef DEBUG_VGA_MEM + printf("vga: chain4: [0x" TARGET_FMT_plx "]\n", addr); +#endif + s->plane_updated |= mask; /* only used to detect font change */ + memory_region_set_dirty(&s->vram, addr, 1); + } + } else if (s->gr[VGA_GFX_MODE] & 0x10) { + /* odd/even mode (aka text mode mapping) */ + plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1); + mask = (1 << plane); + if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) { + addr = ((addr & ~1) << 1) | plane; + s->vram_ptr[addr] = val; +#ifdef DEBUG_VGA_MEM + printf("vga: odd/even: [0x" TARGET_FMT_plx "]\n", addr); +#endif + s->plane_updated |= mask; /* only used to detect font change */ + memory_region_set_dirty(&s->vram, addr, 1); + } + } else { + /* standard VGA latched access */ + write_mode = s->gr[VGA_GFX_MODE] & 3; + switch(write_mode) { + default: + case 0: + /* rotate */ + b = s->gr[VGA_GFX_DATA_ROTATE] & 7; + val = ((val >> b) | (val << (8 - b))) & 0xff; + val |= val << 8; + val |= val << 16; + + /* apply set/reset mask */ + set_mask = mask16[s->gr[VGA_GFX_SR_ENABLE]]; + val = (val & ~set_mask) | + (mask16[s->gr[VGA_GFX_SR_VALUE]] & set_mask); + bit_mask = s->gr[VGA_GFX_BIT_MASK]; + break; + case 1: + val = s->latch; + goto do_write; + case 2: + val = mask16[val & 0x0f]; + bit_mask = s->gr[VGA_GFX_BIT_MASK]; + break; + case 3: + /* rotate */ + b = s->gr[VGA_GFX_DATA_ROTATE] & 7; + val = (val >> b) | (val << (8 - b)); + + bit_mask = s->gr[VGA_GFX_BIT_MASK] & val; + val = mask16[s->gr[VGA_GFX_SR_VALUE]]; + break; + } + + /* apply logical operation */ + func_select = s->gr[VGA_GFX_DATA_ROTATE] >> 3; + switch(func_select) { + case 0: + default: + /* nothing to do */ + break; + case 1: + /* and */ + val &= s->latch; + break; + case 2: + /* or */ + val |= s->latch; + break; + case 3: + /* xor */ + val ^= s->latch; + break; + } + + /* apply bit mask */ + bit_mask |= bit_mask << 8; + bit_mask |= bit_mask << 16; + val = (val & bit_mask) | (s->latch & ~bit_mask); + + do_write: + /* mask data according to sr[2] */ + mask = s->sr[VGA_SEQ_PLANE_WRITE]; + s->plane_updated |= mask; /* only used to detect font change */ + write_mask = mask16[mask]; + ((uint32_t *)s->vram_ptr)[addr] = + (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) | + (val & write_mask); +#ifdef DEBUG_VGA_MEM + printf("vga: latch: [0x" TARGET_FMT_plx "] mask=0x%08x val=0x%08x\n", + addr * 4, write_mask, val); +#endif + memory_region_set_dirty(&s->vram, addr << 2, sizeof(uint32_t)); + } +} + +typedef void vga_draw_glyph8_func(uint8_t *d, int linesize, + const uint8_t *font_ptr, int h, + uint32_t fgcol, uint32_t bgcol); +typedef void vga_draw_glyph9_func(uint8_t *d, int linesize, + const uint8_t *font_ptr, int h, + uint32_t fgcol, uint32_t bgcol, int dup9); +typedef void vga_draw_line_func(VGACommonState *s1, uint8_t *d, + const uint8_t *s, int width); + +#define DEPTH 8 +#include "hw/vga_template.h" + +#define DEPTH 15 +#include "hw/vga_template.h" + +#define BGR_FORMAT +#define DEPTH 15 +#include "hw/vga_template.h" + +#define DEPTH 16 +#include "hw/vga_template.h" + +#define BGR_FORMAT +#define DEPTH 16 +#include "hw/vga_template.h" + +#define DEPTH 32 +#include "hw/vga_template.h" + +#define BGR_FORMAT +#define DEPTH 32 +#include "hw/vga_template.h" + +static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel8(r, g, b); + col |= col << 8; + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel15(r, g, b); + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel15bgr_dup(unsigned int r, unsigned int g, + unsigned int b) +{ + unsigned int col; + col = rgb_to_pixel15bgr(r, g, b); + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel16(r, g, b); + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel16bgr_dup(unsigned int r, unsigned int g, + unsigned int b) +{ + unsigned int col; + col = rgb_to_pixel16bgr(r, g, b); + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel32(r, g, b); + return col; +} + +static unsigned int rgb_to_pixel32bgr_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel32bgr(r, g, b); + return col; +} + +/* return true if the palette was modified */ +static int update_palette16(VGACommonState *s) +{ + int full_update, i; + uint32_t v, col, *palette; + + full_update = 0; + palette = s->last_palette; + for(i = 0; i < 16; i++) { + v = s->ar[i]; + if (s->ar[VGA_ATC_MODE] & 0x80) { + v = ((s->ar[VGA_ATC_COLOR_PAGE] & 0xf) << 4) | (v & 0xf); + } else { + v = ((s->ar[VGA_ATC_COLOR_PAGE] & 0xc) << 4) | (v & 0x3f); + } + v = v * 3; + col = s->rgb_to_pixel(c6_to_8(s->palette[v]), + c6_to_8(s->palette[v + 1]), + c6_to_8(s->palette[v + 2])); + if (col != palette[i]) { + full_update = 1; + palette[i] = col; + } + } + return full_update; +} + +/* return true if the palette was modified */ +static int update_palette256(VGACommonState *s) +{ + int full_update, i; + uint32_t v, col, *palette; + + full_update = 0; + palette = s->last_palette; + v = 0; + for(i = 0; i < 256; i++) { + if (s->dac_8bit) { + col = s->rgb_to_pixel(s->palette[v], + s->palette[v + 1], + s->palette[v + 2]); + } else { + col = s->rgb_to_pixel(c6_to_8(s->palette[v]), + c6_to_8(s->palette[v + 1]), + c6_to_8(s->palette[v + 2])); + } + if (col != palette[i]) { + full_update = 1; + palette[i] = col; + } + v += 3; + } + return full_update; +} + +static void vga_get_offsets(VGACommonState *s, + uint32_t *pline_offset, + uint32_t *pstart_addr, + uint32_t *pline_compare) +{ + uint32_t start_addr, line_offset, line_compare; + + if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { + line_offset = s->vbe_line_offset; + start_addr = s->vbe_start_addr; + line_compare = 65535; + } else { + /* compute line_offset in bytes */ + line_offset = s->cr[VGA_CRTC_OFFSET]; + line_offset <<= 3; + + /* starting address */ + start_addr = s->cr[VGA_CRTC_START_LO] | + (s->cr[VGA_CRTC_START_HI] << 8); + + /* line compare */ + line_compare = s->cr[VGA_CRTC_LINE_COMPARE] | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x10) << 4) | + ((s->cr[VGA_CRTC_MAX_SCAN] & 0x40) << 3); + } + *pline_offset = line_offset; + *pstart_addr = start_addr; + *pline_compare = line_compare; +} + +/* update start_addr and line_offset. Return TRUE if modified */ +static int update_basic_params(VGACommonState *s) +{ + int full_update; + uint32_t start_addr, line_offset, line_compare; + + full_update = 0; + + s->get_offsets(s, &line_offset, &start_addr, &line_compare); + + if (line_offset != s->line_offset || + start_addr != s->start_addr || + line_compare != s->line_compare) { + s->line_offset = line_offset; + s->start_addr = start_addr; + s->line_compare = line_compare; + full_update = 1; + } + return full_update; +} + +#define NB_DEPTHS 7 + +static inline int get_depth_index(DisplaySurface *s) +{ + switch (surface_bits_per_pixel(s)) { + default: + case 8: + return 0; + case 15: + return 1; + case 16: + return 2; + case 32: + if (is_surface_bgr(s)) { + return 4; + } else { + return 3; + } + } +} + +static vga_draw_glyph8_func * const vga_draw_glyph8_table[NB_DEPTHS] = { + vga_draw_glyph8_8, + vga_draw_glyph8_16, + vga_draw_glyph8_16, + vga_draw_glyph8_32, + vga_draw_glyph8_32, + vga_draw_glyph8_16, + vga_draw_glyph8_16, +}; + +static vga_draw_glyph8_func * const vga_draw_glyph16_table[NB_DEPTHS] = { + vga_draw_glyph16_8, + vga_draw_glyph16_16, + vga_draw_glyph16_16, + vga_draw_glyph16_32, + vga_draw_glyph16_32, + vga_draw_glyph16_16, + vga_draw_glyph16_16, +}; + +static vga_draw_glyph9_func * const vga_draw_glyph9_table[NB_DEPTHS] = { + vga_draw_glyph9_8, + vga_draw_glyph9_16, + vga_draw_glyph9_16, + vga_draw_glyph9_32, + vga_draw_glyph9_32, + vga_draw_glyph9_16, + vga_draw_glyph9_16, +}; + +static const uint8_t cursor_glyph[32 * 4] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static void vga_get_text_resolution(VGACommonState *s, int *pwidth, int *pheight, + int *pcwidth, int *pcheight) +{ + int width, cwidth, height, cheight; + + /* total width & height */ + cheight = (s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1; + cwidth = 8; + if (!(s->sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_CHAR_CLK_8DOTS)) { + cwidth = 9; + } + if (s->sr[VGA_SEQ_CLOCK_MODE] & 0x08) { + cwidth = 16; /* NOTE: no 18 pixel wide */ + } + width = (s->cr[VGA_CRTC_H_DISP] + 1); + if (s->cr[VGA_CRTC_V_TOTAL] == 100) { + /* ugly hack for CGA 160x100x16 - explain me the logic */ + height = 100; + } else { + height = s->cr[VGA_CRTC_V_DISP_END] | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x02) << 7) | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x40) << 3); + height = (height + 1) / cheight; + } + + *pwidth = width; + *pheight = height; + *pcwidth = cwidth; + *pcheight = cheight; +} + +typedef unsigned int rgb_to_pixel_dup_func(unsigned int r, unsigned int g, unsigned b); + +static rgb_to_pixel_dup_func * const rgb_to_pixel_dup_table[NB_DEPTHS] = { + rgb_to_pixel8_dup, + rgb_to_pixel15_dup, + rgb_to_pixel16_dup, + rgb_to_pixel32_dup, + rgb_to_pixel32bgr_dup, + rgb_to_pixel15bgr_dup, + rgb_to_pixel16bgr_dup, +}; + +/* + * Text mode update + * Missing: + * - double scan + * - double width + * - underline + * - flashing + */ +static void vga_draw_text(VGACommonState *s, int full_update) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr; + int cx_min, cx_max, linesize, x_incr, line, line1; + uint32_t offset, fgcol, bgcol, v, cursor_offset; + uint8_t *d1, *d, *src, *dest, *cursor_ptr; + const uint8_t *font_ptr, *font_base[2]; + int dup9, line_offset, depth_index; + uint32_t *palette; + uint32_t *ch_attr_ptr; + vga_draw_glyph8_func *vga_draw_glyph8; + vga_draw_glyph9_func *vga_draw_glyph9; + int64_t now = qemu_get_clock_ms(vm_clock); + + /* compute font data address (in plane 2) */ + v = s->sr[VGA_SEQ_CHARACTER_MAP]; + offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2; + if (offset != s->font_offsets[0]) { + s->font_offsets[0] = offset; + full_update = 1; + } + font_base[0] = s->vram_ptr + offset; + + offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2; + font_base[1] = s->vram_ptr + offset; + if (offset != s->font_offsets[1]) { + s->font_offsets[1] = offset; + full_update = 1; + } + if (s->plane_updated & (1 << 2) || s->chain4_alias) { + /* if the plane 2 was modified since the last display, it + indicates the font may have been modified */ + s->plane_updated = 0; + full_update = 1; + } + full_update |= update_basic_params(s); + + line_offset = s->line_offset; + + vga_get_text_resolution(s, &width, &height, &cw, &cheight); + if ((height * width) <= 1) { + /* better than nothing: exit if transient size is too small */ + return; + } + if ((height * width) > CH_ATTR_SIZE) { + /* better than nothing: exit if transient size is too big */ + return; + } + + if (width != s->last_width || height != s->last_height || + cw != s->last_cw || cheight != s->last_ch || s->last_depth) { + s->last_scr_width = width * cw; + s->last_scr_height = height * cheight; + qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height); + surface = qemu_console_surface(s->con); + dpy_text_resize(s->con, width, height); + s->last_depth = 0; + s->last_width = width; + s->last_height = height; + s->last_ch = cheight; + s->last_cw = cw; + full_update = 1; + } + s->rgb_to_pixel = + rgb_to_pixel_dup_table[get_depth_index(surface)]; + full_update |= update_palette16(s); + palette = s->last_palette; + x_incr = cw * surface_bytes_per_pixel(surface); + + if (full_update) { + s->full_update_text = 1; + } + if (s->full_update_gfx) { + s->full_update_gfx = 0; + full_update |= 1; + } + + cursor_offset = ((s->cr[VGA_CRTC_CURSOR_HI] << 8) | + s->cr[VGA_CRTC_CURSOR_LO]) - s->start_addr; + if (cursor_offset != s->cursor_offset || + s->cr[VGA_CRTC_CURSOR_START] != s->cursor_start || + s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end) { + /* if the cursor position changed, we update the old and new + chars */ + if (s->cursor_offset < CH_ATTR_SIZE) + s->last_ch_attr[s->cursor_offset] = -1; + if (cursor_offset < CH_ATTR_SIZE) + s->last_ch_attr[cursor_offset] = -1; + s->cursor_offset = cursor_offset; + s->cursor_start = s->cr[VGA_CRTC_CURSOR_START]; + s->cursor_end = s->cr[VGA_CRTC_CURSOR_END]; + } + cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4; + if (now >= s->cursor_blink_time) { + s->cursor_blink_time = now + VGA_TEXT_CURSOR_PERIOD_MS / 2; + s->cursor_visible_phase = !s->cursor_visible_phase; + } + + depth_index = get_depth_index(surface); + if (cw == 16) + vga_draw_glyph8 = vga_draw_glyph16_table[depth_index]; + else + vga_draw_glyph8 = vga_draw_glyph8_table[depth_index]; + vga_draw_glyph9 = vga_draw_glyph9_table[depth_index]; + + dest = surface_data(surface); + linesize = surface_stride(surface); + ch_attr_ptr = s->last_ch_attr; + line = 0; + offset = s->start_addr * 4; + for(cy = 0; cy < height; cy++) { + d1 = dest; + src = s->vram_ptr + offset; + cx_min = width; + cx_max = -1; + for(cx = 0; cx < width; cx++) { + ch_attr = *(uint16_t *)src; + if (full_update || ch_attr != *ch_attr_ptr || src == cursor_ptr) { + if (cx < cx_min) + cx_min = cx; + if (cx > cx_max) + cx_max = cx; + *ch_attr_ptr = ch_attr; +#ifdef HOST_WORDS_BIGENDIAN + ch = ch_attr >> 8; + cattr = ch_attr & 0xff; +#else + ch = ch_attr & 0xff; + cattr = ch_attr >> 8; +#endif + font_ptr = font_base[(cattr >> 3) & 1]; + font_ptr += 32 * 4 * ch; + bgcol = palette[cattr >> 4]; + fgcol = palette[cattr & 0x0f]; + if (cw != 9) { + vga_draw_glyph8(d1, linesize, + font_ptr, cheight, fgcol, bgcol); + } else { + dup9 = 0; + if (ch >= 0xb0 && ch <= 0xdf && + (s->ar[VGA_ATC_MODE] & 0x04)) { + dup9 = 1; + } + vga_draw_glyph9(d1, linesize, + font_ptr, cheight, fgcol, bgcol, dup9); + } + if (src == cursor_ptr && + !(s->cr[VGA_CRTC_CURSOR_START] & 0x20) && + s->cursor_visible_phase) { + int line_start, line_last, h; + /* draw the cursor */ + line_start = s->cr[VGA_CRTC_CURSOR_START] & 0x1f; + line_last = s->cr[VGA_CRTC_CURSOR_END] & 0x1f; + /* XXX: check that */ + if (line_last > cheight - 1) + line_last = cheight - 1; + if (line_last >= line_start && line_start < cheight) { + h = line_last - line_start + 1; + d = d1 + linesize * line_start; + if (cw != 9) { + vga_draw_glyph8(d, linesize, + cursor_glyph, h, fgcol, bgcol); + } else { + vga_draw_glyph9(d, linesize, + cursor_glyph, h, fgcol, bgcol, 1); + } + } + } + } + d1 += x_incr; + src += 4; + ch_attr_ptr++; + } + if (cx_max != -1) { + dpy_gfx_update(s->con, cx_min * cw, cy * cheight, + (cx_max - cx_min + 1) * cw, cheight); + } + dest += linesize * cheight; + line1 = line + cheight; + offset += line_offset; + if (line < s->line_compare && line1 >= s->line_compare) { + offset = 0; + } + line = line1; + } +} + +enum { + VGA_DRAW_LINE2, + VGA_DRAW_LINE2D2, + VGA_DRAW_LINE4, + VGA_DRAW_LINE4D2, + VGA_DRAW_LINE8D2, + VGA_DRAW_LINE8, + VGA_DRAW_LINE15, + VGA_DRAW_LINE16, + VGA_DRAW_LINE24, + VGA_DRAW_LINE32, + VGA_DRAW_LINE_NB, +}; + +static vga_draw_line_func * const vga_draw_line_table[NB_DEPTHS * VGA_DRAW_LINE_NB] = { + vga_draw_line2_8, + vga_draw_line2_16, + vga_draw_line2_16, + vga_draw_line2_32, + vga_draw_line2_32, + vga_draw_line2_16, + vga_draw_line2_16, + + vga_draw_line2d2_8, + vga_draw_line2d2_16, + vga_draw_line2d2_16, + vga_draw_line2d2_32, + vga_draw_line2d2_32, + vga_draw_line2d2_16, + vga_draw_line2d2_16, + + vga_draw_line4_8, + vga_draw_line4_16, + vga_draw_line4_16, + vga_draw_line4_32, + vga_draw_line4_32, + vga_draw_line4_16, + vga_draw_line4_16, + + vga_draw_line4d2_8, + vga_draw_line4d2_16, + vga_draw_line4d2_16, + vga_draw_line4d2_32, + vga_draw_line4d2_32, + vga_draw_line4d2_16, + vga_draw_line4d2_16, + + vga_draw_line8d2_8, + vga_draw_line8d2_16, + vga_draw_line8d2_16, + vga_draw_line8d2_32, + vga_draw_line8d2_32, + vga_draw_line8d2_16, + vga_draw_line8d2_16, + + vga_draw_line8_8, + vga_draw_line8_16, + vga_draw_line8_16, + vga_draw_line8_32, + vga_draw_line8_32, + vga_draw_line8_16, + vga_draw_line8_16, + + vga_draw_line15_8, + vga_draw_line15_15, + vga_draw_line15_16, + vga_draw_line15_32, + vga_draw_line15_32bgr, + vga_draw_line15_15bgr, + vga_draw_line15_16bgr, + + vga_draw_line16_8, + vga_draw_line16_15, + vga_draw_line16_16, + vga_draw_line16_32, + vga_draw_line16_32bgr, + vga_draw_line16_15bgr, + vga_draw_line16_16bgr, + + vga_draw_line24_8, + vga_draw_line24_15, + vga_draw_line24_16, + vga_draw_line24_32, + vga_draw_line24_32bgr, + vga_draw_line24_15bgr, + vga_draw_line24_16bgr, + + vga_draw_line32_8, + vga_draw_line32_15, + vga_draw_line32_16, + vga_draw_line32_32, + vga_draw_line32_32bgr, + vga_draw_line32_15bgr, + vga_draw_line32_16bgr, +}; + +static int vga_get_bpp(VGACommonState *s) +{ + int ret; + + if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { + ret = s->vbe_regs[VBE_DISPI_INDEX_BPP]; + } else { + ret = 0; + } + return ret; +} + +static void vga_get_resolution(VGACommonState *s, int *pwidth, int *pheight) +{ + int width, height; + + if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { + width = s->vbe_regs[VBE_DISPI_INDEX_XRES]; + height = s->vbe_regs[VBE_DISPI_INDEX_YRES]; + } else { + width = (s->cr[VGA_CRTC_H_DISP] + 1) * 8; + height = s->cr[VGA_CRTC_V_DISP_END] | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x02) << 7) | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x40) << 3); + height = (height + 1); + } + *pwidth = width; + *pheight = height; +} + +void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2) +{ + int y; + if (y1 >= VGA_MAX_HEIGHT) + return; + if (y2 >= VGA_MAX_HEIGHT) + y2 = VGA_MAX_HEIGHT; + for(y = y1; y < y2; y++) { + s->invalidated_y_table[y >> 5] |= 1 << (y & 0x1f); + } +} + +void vga_sync_dirty_bitmap(VGACommonState *s) +{ + memory_region_sync_dirty_bitmap(&s->vram); +} + +void vga_dirty_log_start(VGACommonState *s) +{ + memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA); +} + +void vga_dirty_log_stop(VGACommonState *s) +{ + memory_region_set_log(&s->vram, false, DIRTY_MEMORY_VGA); +} + +/* + * graphic modes + */ +static void vga_draw_graphic(VGACommonState *s, int full_update) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int y1, y, update, linesize, y_start, double_scan, mask, depth; + int width, height, shift_control, line_offset, bwidth, bits; + ram_addr_t page0, page1, page_min, page_max; + int disp_width, multi_scan, multi_run; + uint8_t *d; + uint32_t v, addr1, addr; + vga_draw_line_func *vga_draw_line; +#if defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) + static const bool byteswap = false; +#else + static const bool byteswap = true; +#endif + + full_update |= update_basic_params(s); + + if (!full_update) + vga_sync_dirty_bitmap(s); + + s->get_resolution(s, &width, &height); + disp_width = width; + + shift_control = (s->gr[VGA_GFX_MODE] >> 5) & 3; + double_scan = (s->cr[VGA_CRTC_MAX_SCAN] >> 7); + if (shift_control != 1) { + multi_scan = (((s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1) << double_scan) + - 1; + } else { + /* in CGA modes, multi_scan is ignored */ + /* XXX: is it correct ? */ + multi_scan = double_scan; + } + multi_run = multi_scan; + if (shift_control != s->shift_control || + double_scan != s->double_scan) { + full_update = 1; + s->shift_control = shift_control; + s->double_scan = double_scan; + } + + if (shift_control == 0) { + if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { + disp_width <<= 1; + } + } else if (shift_control == 1) { + if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { + disp_width <<= 1; + } + } + + depth = s->get_bpp(s); + if (s->line_offset != s->last_line_offset || + disp_width != s->last_width || + height != s->last_height || + s->last_depth != depth) { + if (depth == 32 || (depth == 16 && !byteswap)) { + surface = qemu_create_displaysurface_from(disp_width, + height, depth, s->line_offset, + s->vram_ptr + (s->start_addr * 4), byteswap); + dpy_gfx_replace_surface(s->con, surface); + } else { + qemu_console_resize(s->con, disp_width, height); + surface = qemu_console_surface(s->con); + } + s->last_scr_width = disp_width; + s->last_scr_height = height; + s->last_width = disp_width; + s->last_height = height; + s->last_line_offset = s->line_offset; + s->last_depth = depth; + full_update = 1; + } else if (is_buffer_shared(surface) && + (full_update || surface_data(surface) != s->vram_ptr + + (s->start_addr * 4))) { + DisplaySurface *surface; + surface = qemu_create_displaysurface_from(disp_width, + height, depth, s->line_offset, + s->vram_ptr + (s->start_addr * 4), byteswap); + dpy_gfx_replace_surface(s->con, surface); + } + + s->rgb_to_pixel = + rgb_to_pixel_dup_table[get_depth_index(surface)]; + + if (shift_control == 0) { + full_update |= update_palette16(s); + if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { + v = VGA_DRAW_LINE4D2; + } else { + v = VGA_DRAW_LINE4; + } + bits = 4; + } else if (shift_control == 1) { + full_update |= update_palette16(s); + if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { + v = VGA_DRAW_LINE2D2; + } else { + v = VGA_DRAW_LINE2; + } + bits = 4; + } else { + switch(s->get_bpp(s)) { + default: + case 0: + full_update |= update_palette256(s); + v = VGA_DRAW_LINE8D2; + bits = 4; + break; + case 8: + full_update |= update_palette256(s); + v = VGA_DRAW_LINE8; + bits = 8; + break; + case 15: + v = VGA_DRAW_LINE15; + bits = 16; + break; + case 16: + v = VGA_DRAW_LINE16; + bits = 16; + break; + case 24: + v = VGA_DRAW_LINE24; + bits = 24; + break; + case 32: + v = VGA_DRAW_LINE32; + bits = 32; + break; + } + } + vga_draw_line = vga_draw_line_table[v * NB_DEPTHS + + get_depth_index(surface)]; + + if (!is_buffer_shared(surface) && s->cursor_invalidate) { + s->cursor_invalidate(s); + } + + line_offset = s->line_offset; +#if 0 + printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n", + width, height, v, line_offset, s->cr[9], s->cr[VGA_CRTC_MODE], + s->line_compare, s->sr[VGA_SEQ_CLOCK_MODE]); +#endif + addr1 = (s->start_addr * 4); + bwidth = (width * bits + 7) / 8; + y_start = -1; + page_min = -1; + page_max = 0; + d = surface_data(surface); + linesize = surface_stride(surface); + y1 = 0; + for(y = 0; y < height; y++) { + addr = addr1; + if (!(s->cr[VGA_CRTC_MODE] & 1)) { + int shift; + /* CGA compatibility handling */ + shift = 14 + ((s->cr[VGA_CRTC_MODE] >> 6) & 1); + addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift); + } + if (!(s->cr[VGA_CRTC_MODE] & 2)) { + addr = (addr & ~0x8000) | ((y1 & 2) << 14); + } + update = full_update; + page0 = addr; + page1 = addr + bwidth - 1; + update |= memory_region_get_dirty(&s->vram, page0, page1 - page0, + DIRTY_MEMORY_VGA); + /* explicit invalidation for the hardware cursor */ + update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1; + if (update) { + if (y_start < 0) + y_start = y; + if (page0 < page_min) + page_min = page0; + if (page1 > page_max) + page_max = page1; + if (!(is_buffer_shared(surface))) { + vga_draw_line(s, d, s->vram_ptr + addr, width); + if (s->cursor_draw_line) + s->cursor_draw_line(s, d, y); + } + } else { + if (y_start >= 0) { + /* flush to display */ + dpy_gfx_update(s->con, 0, y_start, + disp_width, y - y_start); + y_start = -1; + } + } + if (!multi_run) { + mask = (s->cr[VGA_CRTC_MODE] & 3) ^ 3; + if ((y1 & mask) == mask) + addr1 += line_offset; + y1++; + multi_run = multi_scan; + } else { + multi_run--; + } + /* line compare acts on the displayed lines */ + if (y == s->line_compare) + addr1 = 0; + d += linesize; + } + if (y_start >= 0) { + /* flush to display */ + dpy_gfx_update(s->con, 0, y_start, + disp_width, y - y_start); + } + /* reset modified pages */ + if (page_max >= page_min) { + memory_region_reset_dirty(&s->vram, + page_min, + page_max - page_min, + DIRTY_MEMORY_VGA); + } + memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4); +} + +static void vga_draw_blank(VGACommonState *s, int full_update) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int i, w, val; + uint8_t *d; + + if (!full_update) + return; + if (s->last_scr_width <= 0 || s->last_scr_height <= 0) + return; + + s->rgb_to_pixel = + rgb_to_pixel_dup_table[get_depth_index(surface)]; + if (surface_bits_per_pixel(surface) == 8) { + val = s->rgb_to_pixel(0, 0, 0); + } else { + val = 0; + } + w = s->last_scr_width * surface_bytes_per_pixel(surface); + d = surface_data(surface); + for(i = 0; i < s->last_scr_height; i++) { + memset(d, val, w); + d += surface_stride(surface); + } + dpy_gfx_update(s->con, 0, 0, + s->last_scr_width, s->last_scr_height); +} + +#define GMODE_TEXT 0 +#define GMODE_GRAPH 1 +#define GMODE_BLANK 2 + +static void vga_update_display(void *opaque) +{ + VGACommonState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + int full_update, graphic_mode; + + qemu_flush_coalesced_mmio_buffer(); + + if (surface_bits_per_pixel(surface) == 0) { + /* nothing to do */ + } else { + full_update = 0; + if (!(s->ar_index & 0x20)) { + graphic_mode = GMODE_BLANK; + } else { + graphic_mode = s->gr[VGA_GFX_MISC] & VGA_GR06_GRAPHICS_MODE; + } + if (graphic_mode != s->graphic_mode) { + s->graphic_mode = graphic_mode; + s->cursor_blink_time = qemu_get_clock_ms(vm_clock); + full_update = 1; + } + switch(graphic_mode) { + case GMODE_TEXT: + vga_draw_text(s, full_update); + break; + case GMODE_GRAPH: + vga_draw_graphic(s, full_update); + break; + case GMODE_BLANK: + default: + vga_draw_blank(s, full_update); + break; + } + } +} + +/* force a full display refresh */ +static void vga_invalidate_display(void *opaque) +{ + VGACommonState *s = opaque; + + s->last_width = -1; + s->last_height = -1; +} + +void vga_common_reset(VGACommonState *s) +{ + s->sr_index = 0; + memset(s->sr, '\0', sizeof(s->sr)); + s->gr_index = 0; + memset(s->gr, '\0', sizeof(s->gr)); + s->ar_index = 0; + memset(s->ar, '\0', sizeof(s->ar)); + s->ar_flip_flop = 0; + s->cr_index = 0; + memset(s->cr, '\0', sizeof(s->cr)); + s->msr = 0; + s->fcr = 0; + s->st00 = 0; + s->st01 = 0; + s->dac_state = 0; + s->dac_sub_index = 0; + s->dac_read_index = 0; + s->dac_write_index = 0; + memset(s->dac_cache, '\0', sizeof(s->dac_cache)); + s->dac_8bit = 0; + memset(s->palette, '\0', sizeof(s->palette)); + s->bank_offset = 0; + s->vbe_index = 0; + memset(s->vbe_regs, '\0', sizeof(s->vbe_regs)); + s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID5; + s->vbe_start_addr = 0; + s->vbe_line_offset = 0; + s->vbe_bank_mask = (s->vram_size >> 16) - 1; + memset(s->font_offsets, '\0', sizeof(s->font_offsets)); + s->graphic_mode = -1; /* force full update */ + s->shift_control = 0; + s->double_scan = 0; + s->line_offset = 0; + s->line_compare = 0; + s->start_addr = 0; + s->plane_updated = 0; + s->last_cw = 0; + s->last_ch = 0; + s->last_width = 0; + s->last_height = 0; + s->last_scr_width = 0; + s->last_scr_height = 0; + s->cursor_start = 0; + s->cursor_end = 0; + s->cursor_offset = 0; + memset(s->invalidated_y_table, '\0', sizeof(s->invalidated_y_table)); + memset(s->last_palette, '\0', sizeof(s->last_palette)); + memset(s->last_ch_attr, '\0', sizeof(s->last_ch_attr)); + switch (vga_retrace_method) { + case VGA_RETRACE_DUMB: + break; + case VGA_RETRACE_PRECISE: + memset(&s->retrace_info, 0, sizeof (s->retrace_info)); + break; + } + vga_update_memory_access(s); +} + +static void vga_reset(void *opaque) +{ + VGACommonState *s = opaque; + vga_common_reset(s); +} + +#define TEXTMODE_X(x) ((x) % width) +#define TEXTMODE_Y(x) ((x) / width) +#define VMEM2CHTYPE(v) ((v & 0xff0007ff) | \ + ((v & 0x00000800) << 10) | ((v & 0x00007000) >> 1)) +/* relay text rendering to the display driver + * instead of doing a full vga_update_display() */ +static void vga_update_text(void *opaque, console_ch_t *chardata) +{ + VGACommonState *s = opaque; + int graphic_mode, i, cursor_offset, cursor_visible; + int cw, cheight, width, height, size, c_min, c_max; + uint32_t *src; + console_ch_t *dst, val; + char msg_buffer[80]; + int full_update = 0; + + qemu_flush_coalesced_mmio_buffer(); + + if (!(s->ar_index & 0x20)) { + graphic_mode = GMODE_BLANK; + } else { + graphic_mode = s->gr[VGA_GFX_MISC] & VGA_GR06_GRAPHICS_MODE; + } + if (graphic_mode != s->graphic_mode) { + s->graphic_mode = graphic_mode; + full_update = 1; + } + if (s->last_width == -1) { + s->last_width = 0; + full_update = 1; + } + + switch (graphic_mode) { + case GMODE_TEXT: + /* TODO: update palette */ + full_update |= update_basic_params(s); + + /* total width & height */ + cheight = (s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1; + cw = 8; + if (!(s->sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_CHAR_CLK_8DOTS)) { + cw = 9; + } + if (s->sr[VGA_SEQ_CLOCK_MODE] & 0x08) { + cw = 16; /* NOTE: no 18 pixel wide */ + } + width = (s->cr[VGA_CRTC_H_DISP] + 1); + if (s->cr[VGA_CRTC_V_TOTAL] == 100) { + /* ugly hack for CGA 160x100x16 - explain me the logic */ + height = 100; + } else { + height = s->cr[VGA_CRTC_V_DISP_END] | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x02) << 7) | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x40) << 3); + height = (height + 1) / cheight; + } + + size = (height * width); + if (size > CH_ATTR_SIZE) { + if (!full_update) + return; + + snprintf(msg_buffer, sizeof(msg_buffer), "%i x %i Text mode", + width, height); + break; + } + + if (width != s->last_width || height != s->last_height || + cw != s->last_cw || cheight != s->last_ch) { + s->last_scr_width = width * cw; + s->last_scr_height = height * cheight; + qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height); + dpy_text_resize(s->con, width, height); + s->last_depth = 0; + s->last_width = width; + s->last_height = height; + s->last_ch = cheight; + s->last_cw = cw; + full_update = 1; + } + + if (full_update) { + s->full_update_gfx = 1; + } + if (s->full_update_text) { + s->full_update_text = 0; + full_update |= 1; + } + + /* Update "hardware" cursor */ + cursor_offset = ((s->cr[VGA_CRTC_CURSOR_HI] << 8) | + s->cr[VGA_CRTC_CURSOR_LO]) - s->start_addr; + if (cursor_offset != s->cursor_offset || + s->cr[VGA_CRTC_CURSOR_START] != s->cursor_start || + s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end || full_update) { + cursor_visible = !(s->cr[VGA_CRTC_CURSOR_START] & 0x20); + if (cursor_visible && cursor_offset < size && cursor_offset >= 0) + dpy_text_cursor(s->con, + TEXTMODE_X(cursor_offset), + TEXTMODE_Y(cursor_offset)); + else + dpy_text_cursor(s->con, -1, -1); + s->cursor_offset = cursor_offset; + s->cursor_start = s->cr[VGA_CRTC_CURSOR_START]; + s->cursor_end = s->cr[VGA_CRTC_CURSOR_END]; + } + + src = (uint32_t *) s->vram_ptr + s->start_addr; + dst = chardata; + + if (full_update) { + for (i = 0; i < size; src ++, dst ++, i ++) + console_write_ch(dst, VMEM2CHTYPE(le32_to_cpu(*src))); + + dpy_text_update(s->con, 0, 0, width, height); + } else { + c_max = 0; + + for (i = 0; i < size; src ++, dst ++, i ++) { + console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src))); + if (*dst != val) { + *dst = val; + c_max = i; + break; + } + } + c_min = i; + for (; i < size; src ++, dst ++, i ++) { + console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src))); + if (*dst != val) { + *dst = val; + c_max = i; + } + } + + if (c_min <= c_max) { + i = TEXTMODE_Y(c_min); + dpy_text_update(s->con, 0, i, width, TEXTMODE_Y(c_max) - i + 1); + } + } + + return; + case GMODE_GRAPH: + if (!full_update) + return; + + s->get_resolution(s, &width, &height); + snprintf(msg_buffer, sizeof(msg_buffer), "%i x %i Graphic mode", + width, height); + break; + case GMODE_BLANK: + default: + if (!full_update) + return; + + snprintf(msg_buffer, sizeof(msg_buffer), "VGA Blank mode"); + break; + } + + /* Display a message */ + s->last_width = 60; + s->last_height = height = 3; + dpy_text_cursor(s->con, -1, -1); + dpy_text_resize(s->con, s->last_width, height); + + for (dst = chardata, i = 0; i < s->last_width * height; i ++) + console_write_ch(dst ++, ' '); + + size = strlen(msg_buffer); + width = (s->last_width - size) / 2; + dst = chardata + s->last_width + width; + for (i = 0; i < size; i ++) + console_write_ch(dst ++, 0x00200100 | msg_buffer[i]); + + dpy_text_update(s->con, 0, 0, s->last_width, height); +} + +static uint64_t vga_mem_read(void *opaque, hwaddr addr, + unsigned size) +{ + VGACommonState *s = opaque; + + return vga_mem_readb(s, addr); +} + +static void vga_mem_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VGACommonState *s = opaque; + + return vga_mem_writeb(s, addr, data); +} + +const MemoryRegionOps vga_mem_ops = { + .read = vga_mem_read, + .write = vga_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static int vga_common_post_load(void *opaque, int version_id) +{ + VGACommonState *s = opaque; + + /* force refresh */ + s->graphic_mode = -1; + return 0; +} + +const VMStateDescription vmstate_vga_common = { + .name = "vga", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .post_load = vga_common_post_load, + .fields = (VMStateField []) { + VMSTATE_UINT32(latch, VGACommonState), + VMSTATE_UINT8(sr_index, VGACommonState), + VMSTATE_PARTIAL_BUFFER(sr, VGACommonState, 8), + VMSTATE_UINT8(gr_index, VGACommonState), + VMSTATE_PARTIAL_BUFFER(gr, VGACommonState, 16), + VMSTATE_UINT8(ar_index, VGACommonState), + VMSTATE_BUFFER(ar, VGACommonState), + VMSTATE_INT32(ar_flip_flop, VGACommonState), + VMSTATE_UINT8(cr_index, VGACommonState), + VMSTATE_BUFFER(cr, VGACommonState), + VMSTATE_UINT8(msr, VGACommonState), + VMSTATE_UINT8(fcr, VGACommonState), + VMSTATE_UINT8(st00, VGACommonState), + VMSTATE_UINT8(st01, VGACommonState), + + VMSTATE_UINT8(dac_state, VGACommonState), + VMSTATE_UINT8(dac_sub_index, VGACommonState), + VMSTATE_UINT8(dac_read_index, VGACommonState), + VMSTATE_UINT8(dac_write_index, VGACommonState), + VMSTATE_BUFFER(dac_cache, VGACommonState), + VMSTATE_BUFFER(palette, VGACommonState), + + VMSTATE_INT32(bank_offset, VGACommonState), + VMSTATE_UINT8_EQUAL(is_vbe_vmstate, VGACommonState), + VMSTATE_UINT16(vbe_index, VGACommonState), + VMSTATE_UINT16_ARRAY(vbe_regs, VGACommonState, VBE_DISPI_INDEX_NB), + VMSTATE_UINT32(vbe_start_addr, VGACommonState), + VMSTATE_UINT32(vbe_line_offset, VGACommonState), + VMSTATE_UINT32(vbe_bank_mask, VGACommonState), + VMSTATE_END_OF_LIST() + } +}; + +void vga_common_init(VGACommonState *s) +{ + int i, j, v, b; + + for(i = 0;i < 256; i++) { + v = 0; + for(j = 0; j < 8; j++) { + v |= ((i >> j) & 1) << (j * 4); + } + expand4[i] = v; + + v = 0; + for(j = 0; j < 4; j++) { + v |= ((i >> (2 * j)) & 3) << (j * 4); + } + expand2[i] = v; + } + for(i = 0; i < 16; i++) { + v = 0; + for(j = 0; j < 4; j++) { + b = ((i >> j) & 1); + v |= b << (2 * j); + v |= b << (2 * j + 1); + } + expand4to8[i] = v; + } + + /* valid range: 1 MB -> 256 MB */ + s->vram_size = 1024 * 1024; + while (s->vram_size < (s->vram_size_mb << 20) && + s->vram_size < (256 << 20)) { + s->vram_size <<= 1; + } + s->vram_size_mb = s->vram_size >> 20; + + s->is_vbe_vmstate = 1; + memory_region_init_ram(&s->vram, "vga.vram", s->vram_size); + vmstate_register_ram_global(&s->vram); + xen_register_framebuffer(&s->vram); + s->vram_ptr = memory_region_get_ram_ptr(&s->vram); + s->get_bpp = vga_get_bpp; + s->get_offsets = vga_get_offsets; + s->get_resolution = vga_get_resolution; + s->update = vga_update_display; + s->invalidate = vga_invalidate_display; + s->screen_dump = vga_screen_dump; + s->text_update = vga_update_text; + switch (vga_retrace_method) { + case VGA_RETRACE_DUMB: + s->retrace = vga_dumb_retrace; + s->update_retrace_info = vga_dumb_update_retrace_info; + break; + + case VGA_RETRACE_PRECISE: + s->retrace = vga_precise_retrace; + s->update_retrace_info = vga_precise_update_retrace_info; + break; + } + vga_dirty_log_start(s); +} + +static const MemoryRegionPortio vga_portio_list[] = { + { 0x04, 2, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3b4 */ + { 0x0a, 1, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3ba */ + { 0x10, 16, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3c0 */ + { 0x24, 2, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3d4 */ + { 0x2a, 1, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3da */ + PORTIO_END_OF_LIST(), +}; + +static const MemoryRegionPortio vbe_portio_list[] = { + { 0, 1, 2, .read = vbe_ioport_read_index, .write = vbe_ioport_write_index }, +# ifdef TARGET_I386 + { 1, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data }, +# endif + { 2, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data }, + PORTIO_END_OF_LIST(), +}; + +/* Used by both ISA and PCI */ +MemoryRegion *vga_init_io(VGACommonState *s, + const MemoryRegionPortio **vga_ports, + const MemoryRegionPortio **vbe_ports) +{ + MemoryRegion *vga_mem; + + *vga_ports = vga_portio_list; + *vbe_ports = vbe_portio_list; + + vga_mem = g_malloc(sizeof(*vga_mem)); + memory_region_init_io(vga_mem, &vga_mem_ops, s, + "vga-lowmem", 0x20000); + memory_region_set_flush_coalesced(vga_mem); + + return vga_mem; +} + +void vga_init(VGACommonState *s, MemoryRegion *address_space, + MemoryRegion *address_space_io, bool init_vga_ports) +{ + MemoryRegion *vga_io_memory; + const MemoryRegionPortio *vga_ports, *vbe_ports; + PortioList *vga_port_list = g_new(PortioList, 1); + PortioList *vbe_port_list = g_new(PortioList, 1); + + qemu_register_reset(vga_reset, s); + + s->bank_offset = 0; + + s->legacy_address_space = address_space; + + vga_io_memory = vga_init_io(s, &vga_ports, &vbe_ports); + memory_region_add_subregion_overlap(address_space, + isa_mem_base + 0x000a0000, + vga_io_memory, + 1); + memory_region_set_coalescing(vga_io_memory); + if (init_vga_ports) { + portio_list_init(vga_port_list, vga_ports, s, "vga"); + portio_list_add(vga_port_list, address_space_io, 0x3b0); + } + if (vbe_ports) { + portio_list_init(vbe_port_list, vbe_ports, s, "vbe"); + portio_list_add(vbe_port_list, address_space_io, 0x1ce); + } +} + +void vga_init_vbe(VGACommonState *s, MemoryRegion *system_memory) +{ + /* With pc-0.12 and below we map both the PCI BAR and the fixed VBE region, + * so use an alias to avoid double-mapping the same region. + */ + memory_region_init_alias(&s->vram_vbe, "vram.vbe", + &s->vram, 0, memory_region_size(&s->vram)); + /* XXX: use optimized standard vga accesses */ + memory_region_add_subregion(system_memory, + VBE_DISPI_LFB_PHYSICAL_ADDRESS, + &s->vram_vbe); + s->vbe_mapped = 1; +} +/********************************************************/ +/* vga screen dump */ + +void ppm_save(const char *filename, struct DisplaySurface *ds, Error **errp) +{ + int width = pixman_image_get_width(ds->image); + int height = pixman_image_get_height(ds->image); + FILE *f; + int y; + int ret; + pixman_image_t *linebuf; + + trace_ppm_save(filename, ds); + f = fopen(filename, "wb"); + if (!f) { + error_setg(errp, "failed to open file '%s': %s", filename, + strerror(errno)); + return; + } + ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255); + if (ret < 0) { + linebuf = NULL; + goto write_err; + } + linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); + for (y = 0; y < height; y++) { + qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y); + clearerr(f); + ret = fwrite(pixman_image_get_data(linebuf), 1, + pixman_image_get_stride(linebuf), f); + (void)ret; + if (ferror(f)) { + goto write_err; + } + } + +out: + qemu_pixman_image_unref(linebuf); + fclose(f); + return; + +write_err: + error_setg(errp, "failed to write to file '%s': %s", filename, + strerror(errno)); + unlink(filename); + goto out; +} + +/* save the vga display in a PPM image even if no display is + available */ +static void vga_screen_dump(void *opaque, const char *filename, bool cswitch, + Error **errp) +{ + VGACommonState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + + if (cswitch) { + vga_invalidate_display(s); + } + vga_hw_update(); + ppm_save(filename, surface, errp); +} diff --git a/hw/exynos4210_fimd.c b/hw/exynos4210_fimd.c deleted file mode 100644 index bf316c62ab..0000000000 --- a/hw/exynos4210_fimd.c +++ /dev/null @@ -1,1931 +0,0 @@ -/* - * Samsung exynos4210 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, see . - */ - -#include "qemu-common.h" -#include "exec/cpu-all.h" -#include "hw/sysbus.h" -#include "ui/console.h" -#include "ui/pixel_ops.h" -#include "qemu/bswap.h" - -/* Debug messages configuration */ -#define EXYNOS4210_FIMD_DEBUG 0 -#define EXYNOS4210_FIMD_MODE_TRACE 0 - -#if EXYNOS4210_FIMD_DEBUG == 0 - #define DPRINT_L1(fmt, args...) do { } while (0) - #define DPRINT_L2(fmt, args...) do { } while (0) - #define DPRINT_ERROR(fmt, args...) do { } while (0) -#elif EXYNOS4210_FIMD_DEBUG == 1 - #define DPRINT_L1(fmt, args...) \ - do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) - #define DPRINT_L2(fmt, args...) do { } while (0) - #define DPRINT_ERROR(fmt, args...) \ - do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0) -#else - #define DPRINT_L1(fmt, args...) \ - do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) - #define DPRINT_L2(fmt, args...) \ - do {fprintf(stderr, "QEMU FIMD: "fmt, ## args); } while (0) - #define DPRINT_ERROR(fmt, args...) \ - do {fprintf(stderr, "QEMU FIMD ERROR: "fmt, ## args); } while (0) -#endif - -#if EXYNOS4210_FIMD_MODE_TRACE == 0 - #define DPRINT_TRACE(fmt, args...) do { } while (0) -#else - #define DPRINT_TRACE(fmt, args...) \ - do {fprintf(stderr, "QEMU FIMD: "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_VIDTCON_START 0x10 -#define FIMD_VIDTCON_END 0x1C -#define FIMD_VIDTCON2_SIZE_MASK 0x07FF -#define FIMD_VIDTCON2_HOR_SHIFT 0 -#define FIMD_VIDTCON2_VER_SHIFT 11 - -/* Window control registers */ -#define FIMD_WINCON_START 0x0020 -#define FIMD_WINCON_END 0x0030 -#define FIMD_WINCON_ROMASK 0x82200000 -#define FIMD_WINCON_ENWIN (1 << 0) -#define FIMD_WINCON_BLD_PIX (1 << 6) -#define FIMD_WINCON_ALPHA_MUL (1 << 7) -#define FIMD_WINCON_ALPHA_SEL (1 << 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_BUFSTAT_L (1 << 21) -#define FIMD_WINCON_BUFSTAT_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) -#define IS_PALETTIZED_MODE(w) (w->wincon & 0xC) -#define PAL_MODE_WITH_ALPHA(x) ((x) == 7) -#define WIN_BPP_MODE(w) ((w->wincon >> 2) & 0xF) -#define WIN_BPP_MODE_WITH_ALPHA(w) \ - (WIN_BPP_MODE(w) == 0xD || WIN_BPP_MODE(w) == 0xE) - -/* Shadow control register */ -#define FIMD_SHADOWCON 0x0034 -#define FIMD_WINDOW_PROTECTED(s, w) ((s) & (1 << (10 + (w)))) -/* Channel mapping control register */ -#define FIMD_WINCHMAP 0x003C - -/* Window position control registers */ -#define FIMD_VIDOSD_START 0x0040 -#define FIMD_VIDOSD_END 0x0088 -#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_VIDWADD0_START 0x00A0 -#define FIMD_VIDWADD0_END 0x00C4 -#define FIMD_VIDWADD0_END 0x00C4 -#define FIMD_VIDWADD1_START 0x00D0 -#define FIMD_VIDWADD1_END 0x00F4 -#define FIMD_VIDWADD2_START 0x0100 -#define FIMD_VIDWADD2_END 0x0110 -#define FIMD_VIDWADD2_PAGEWIDTH 0x1FFF -#define FIMD_VIDWADD2_OFFSIZE 0x1FFF -#define FIMD_VIDWADD2_OFFSIZE_SHIFT 13 -#define FIMD_VIDW0ADD0_B2 0x20A0 -#define FIMD_VIDW4ADD0_B2 0x20C0 - -/* Video interrupt control registers */ -#define FIMD_VIDINTCON0 0x130 -#define FIMD_VIDINTCON1 0x134 - -/* Window color key registers */ -#define FIMD_WKEYCON_START 0x140 -#define FIMD_WKEYCON_END 0x15C -#define FIMD_WKEYCON0_COMPKEY 0x00FFFFFF -#define FIMD_WKEYCON0_CTL_SHIFT 24 -#define FIMD_WKEYCON0_DIRCON (1 << 24) -#define FIMD_WKEYCON0_KEYEN (1 << 25) -#define FIMD_WKEYCON0_KEYBLEN (1 << 26) -/* Window color key alpha control register */ -#define FIMD_WKEYALPHA_START 0x160 -#define FIMD_WKEYALPHA_END 0x16C - -/* Dithering control register */ -#define FIMD_DITHMODE 0x170 - -/* Window alpha control registers */ -#define FIMD_VIDALPHA_ALPHA_LOWER 0x000F0F0F -#define FIMD_VIDALPHA_ALPHA_UPPER 0x00F0F0F0 -#define FIMD_VIDWALPHA_START 0x21C -#define FIMD_VIDWALPHA_END 0x240 - -/* Window color map registers */ -#define FIMD_WINMAP_START 0x180 -#define FIMD_WINMAP_END 0x190 -#define FIMD_WINMAP_EN (1 << 24) -#define FIMD_WINMAP_COLOR_MASK 0x00FFFFFF - -/* Window palette control registers */ -#define FIMD_WPALCON_HIGH 0x019C -#define FIMD_WPALCON_LOW 0x01A0 -#define FIMD_WPALCON_UPDATEEN (1 << 9) -#define FIMD_WPAL_W0PAL_L 0x07 -#define FIMD_WPAL_W0PAL_L_SHT 0 -#define FIMD_WPAL_W1PAL_L 0x07 -#define FIMD_WPAL_W1PAL_L_SHT 3 -#define FIMD_WPAL_W2PAL_L 0x01 -#define FIMD_WPAL_W2PAL_L_SHT 6 -#define FIMD_WPAL_W2PAL_H 0x06 -#define FIMD_WPAL_W2PAL_H_SHT 8 -#define FIMD_WPAL_W3PAL_L 0x01 -#define FIMD_WPAL_W3PAL_L_SHT 7 -#define FIMD_WPAL_W3PAL_H 0x06 -#define FIMD_WPAL_W3PAL_H_SHT 12 -#define FIMD_WPAL_W4PAL_L 0x01 -#define FIMD_WPAL_W4PAL_L_SHT 8 -#define FIMD_WPAL_W4PAL_H 0x06 -#define FIMD_WPAL_W4PAL_H_SHT 16 - -/* Trigger control registers */ -#define FIMD_TRIGCON 0x01A4 -#define FIMD_TRIGCON_ROMASK 0x00000004 - -/* LCD I80 Interface Control */ -#define FIMD_I80IFCON_START 0x01B0 -#define FIMD_I80IFCON_END 0x01BC -/* Color gain control register */ -#define FIMD_COLORGAINCON 0x01C0 -/* LCD i80 Interface Command Control */ -#define FIMD_LDI_CMDCON0 0x01D0 -#define FIMD_LDI_CMDCON1 0x01D4 -/* I80 System Interface Manual Command Control */ -#define FIMD_SIFCCON0 0x01E0 -#define FIMD_SIFCCON2 0x01E8 - -/* Hue Control Registers */ -#define FIMD_HUECOEFCR_START 0x01EC -#define FIMD_HUECOEFCR_END 0x01F4 -#define FIMD_HUECOEFCB_START 0x01FC -#define FIMD_HUECOEFCB_END 0x0208 -#define FIMD_HUEOFFSET 0x020C - -/* 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_START 0x0244 -#define FIMD_BLENDEQ_END 0x0250 -#define FIMD_BLENDCON 0x0260 -#define FIMD_ALPHA_8BIT (1 << 0) -#define FIMD_BLENDEQ_COEF_MASK 0xF - -/* Window RTQOS Control Registers */ -#define FIMD_WRTQOSCON_START 0x0264 -#define FIMD_WRTQOSCON_END 0x0274 - -/* LCD I80 Interface Command */ -#define FIMD_I80IFCMD_START 0x0280 -#define FIMD_I80IFCMD_END 0x02AC - -/* Shadow windows control registers */ -#define FIMD_SHD_ADD0_START 0x40A0 -#define FIMD_SHD_ADD0_END 0x40C0 -#define FIMD_SHD_ADD1_START 0x40D0 -#define FIMD_SHD_ADD1_END 0x40F0 -#define FIMD_SHD_ADD2_START 0x4100 -#define FIMD_SHD_ADD2_END 0x4110 - -/* Palette memory */ -#define FIMD_PAL_MEM_START 0x2400 -#define FIMD_PAL_MEM_END 0x37FC -/* Palette memory aliases for windows 0 and 1 */ -#define FIMD_PALMEM_AL_START 0x0400 -#define FIMD_PALMEM_AL_END 0x0BFC - -typedef struct { - uint8_t r, g, b; - /* D[31..24]dummy, D[23..16]rAlpha, D[15..8]gAlpha, D[7..0]bAlpha */ - uint32_t a; -} rgba; -#define RGBA_SIZE 7 - -typedef void pixel_to_rgb_func(uint32_t pixel, rgba *p); -typedef struct Exynos4210fimdWindow Exynos4210fimdWindow; - -struct Exynos4210fimdWindow { - uint32_t wincon; /* Window control register */ - uint32_t buf_start[3]; /* Start address for video frame buffer */ - uint32_t buf_end[3]; /* End address for video frame buffer */ - 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 blendeq; /* Window blending equation control register */ - uint32_t rtqoscon; /* Window RTQOS Control Registers */ - uint32_t palette[256]; /* Palette 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 */ - - pixel_to_rgb_func *pixel_to_rgb; - void (*draw_line)(Exynos4210fimdWindow *w, uint8_t *src, uint8_t *dst, - bool blend); - uint32_t (*get_alpha)(Exynos4210fimdWindow *w, uint32_t pix_a); - uint16_t lefttop_x, lefttop_y; /* VIDOSD0 register */ - uint16_t rightbot_x, rightbot_y; /* VIDOSD1 register */ - uint32_t osdsize; /* VIDOSD2&3 register */ - uint32_t alpha_val[2]; /* VIDOSD2&3, VIDWALPHA registers */ - uint16_t virtpage_width; /* VIDWADD2 register */ - uint16_t virtpage_offsize; /* VIDWADD2 register */ - MemoryRegionSection mem_section; /* RAM fragment containing framebuffer */ - uint8_t *host_fb_addr; /* Host pointer to window's framebuffer */ - hwaddr fb_len; /* Framebuffer length */ -}; - -typedef struct { - SysBusDevice busdev; - MemoryRegion iomem; - QemuConsole *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 mapping control register */ - uint32_t vidintcon[2]; /* Video interrupt control registers */ - uint32_t dithmode; /* Dithering control register */ - uint32_t wpalcon[2]; /* Window palette 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 control register */ - uint32_t i80ifcmd[12]; /* LCD I80 Interface Command */ - - Exynos4210fimdWindow window[5]; /* Window-specific registers */ - uint8_t *ifb; /* Internal frame buffer */ - bool invalidate; /* Image needs to be redrawn */ - bool enabled; /* Display controller is enabled */ -} Exynos4210fimdState; - -/* Perform byte/halfword/word swap of data according to WINCON */ -static inline void fimd_swap_data(unsigned int swap_ctl, uint64_t *data) -{ - int i; - uint64_t res; - uint64_t x = *data; - - if (swap_ctl & FIMD_WINCON_SWAP_BITS) { - res = 0; - for (i = 0; i < 64; i++) { - if (x & (1ULL << (64 - i))) { - res |= (1ULL << i); - } - } - x = res; - } - - if (swap_ctl & FIMD_WINCON_SWAP_BYTE) { - x = bswap64(x); - } - - if (swap_ctl & FIMD_WINCON_SWAP_HWORD) { - x = ((x & 0x000000000000FFFFULL) << 48) | - ((x & 0x00000000FFFF0000ULL) << 16) | - ((x & 0x0000FFFF00000000ULL) >> 16) | - ((x & 0xFFFF000000000000ULL) >> 48); - } - - if (swap_ctl & FIMD_WINCON_SWAP_WORD) { - x = ((x & 0x00000000FFFFFFFFULL) << 32) | - ((x & 0xFFFFFFFF00000000ULL) >> 32); - } - - *data = x; -} - -/* Conversion routines of Pixel data from frame buffer area to internal RGBA - * pixel representation. - * Every color component internally represented as 8-bit value. If original - * data has less than 8 bit for component, data is extended to 8 bit. For - * example, if blue component has only two possible values 0 and 1 it will be - * extended to 0 and 0xFF */ - -/* One bit for alpha representation */ -#define DEF_PIXEL_TO_RGB_A1(N, R, G, B) \ -static void N(uint32_t pixel, rgba *p) \ -{ \ - p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \ - ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \ - pixel >>= (B); \ - p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \ - ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \ - pixel >>= (G); \ - p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \ - ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \ - pixel >>= (R); \ - p->a = (pixel & 0x1); \ -} - -DEF_PIXEL_TO_RGB_A1(pixel_a444_to_rgb, 4, 4, 4) -DEF_PIXEL_TO_RGB_A1(pixel_a555_to_rgb, 5, 5, 5) -DEF_PIXEL_TO_RGB_A1(pixel_a666_to_rgb, 6, 6, 6) -DEF_PIXEL_TO_RGB_A1(pixel_a665_to_rgb, 6, 6, 5) -DEF_PIXEL_TO_RGB_A1(pixel_a888_to_rgb, 8, 8, 8) -DEF_PIXEL_TO_RGB_A1(pixel_a887_to_rgb, 8, 8, 7) - -/* Alpha component is always zero */ -#define DEF_PIXEL_TO_RGB_A0(N, R, G, B) \ -static void N(uint32_t pixel, rgba *p) \ -{ \ - p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \ - ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \ - pixel >>= (B); \ - p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \ - ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \ - pixel >>= (G); \ - p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \ - ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \ - p->a = 0x0; \ -} - -DEF_PIXEL_TO_RGB_A0(pixel_565_to_rgb, 5, 6, 5) -DEF_PIXEL_TO_RGB_A0(pixel_555_to_rgb, 5, 5, 5) -DEF_PIXEL_TO_RGB_A0(pixel_666_to_rgb, 6, 6, 6) -DEF_PIXEL_TO_RGB_A0(pixel_888_to_rgb, 8, 8, 8) - -/* Alpha component has some meaningful value */ -#define DEF_PIXEL_TO_RGB_A(N, R, G, B, A) \ -static void N(uint32_t pixel, rgba *p) \ -{ \ - p->b = ((pixel & ((1 << (B)) - 1)) << (8 - (B))) | \ - ((pixel >> (2 * (B) - 8)) & ((1 << (8 - (B))) - 1)); \ - pixel >>= (B); \ - p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)) | \ - ((pixel >> (2 * (G) - 8)) & ((1 << (8 - (G))) - 1)); \ - pixel >>= (G); \ - p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)) | \ - ((pixel >> (2 * (R) - 8)) & ((1 << (8 - (R))) - 1)); \ - pixel >>= (R); \ - p->a = (pixel & ((1 << (A)) - 1)) << (8 - (A)) | \ - ((pixel >> (2 * (A) - 8)) & ((1 << (8 - (A))) - 1)); \ - p->a = p->a | (p->a << 8) | (p->a << 16); \ -} - -DEF_PIXEL_TO_RGB_A(pixel_4444_to_rgb, 4, 4, 4, 4) -DEF_PIXEL_TO_RGB_A(pixel_8888_to_rgb, 8, 8, 8, 8) - -/* Lookup table to extent 2-bit color component to 8 bit */ -static const uint8_t pixel_lutable_2b[4] = { - 0x0, 0x55, 0xAA, 0xFF -}; -/* Lookup table to extent 3-bit color component to 8 bit */ -static const uint8_t pixel_lutable_3b[8] = { - 0x0, 0x24, 0x49, 0x6D, 0x92, 0xB6, 0xDB, 0xFF -}; -/* Special case for a232 bpp mode */ -static void pixel_a232_to_rgb(uint32_t pixel, rgba *p) -{ - p->b = pixel_lutable_2b[(pixel & 0x3)]; - pixel >>= 2; - p->g = pixel_lutable_3b[(pixel & 0x7)]; - pixel >>= 3; - p->r = pixel_lutable_2b[(pixel & 0x3)]; - pixel >>= 2; - p->a = (pixel & 0x1); -} - -/* Special case for (5+1, 5+1, 5+1) mode. Data bit 15 is common LSB - * for all three color components */ -static void pixel_1555_to_rgb(uint32_t pixel, rgba *p) -{ - uint8_t comm = (pixel >> 15) & 1; - p->b = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3); - pixel >>= 5; - p->g = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3); - pixel >>= 5; - p->r = ((((pixel & 0x1F) << 1) | comm) << 2) | ((pixel >> 3) & 0x3); - p->a = 0x0; -} - -/* Put/get pixel to/from internal LCD Controller framebuffer */ - -static int put_pixel_ifb(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_pixel_ifb(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) & 0x00FFFFFF; - return RGBA_SIZE; -} - -static pixel_to_rgb_func *palette_data_format[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 -}; - -/* Returns Index in palette data formats table for given window number WINDOW */ -static uint32_t -exynos4210_fimd_palette_format(Exynos4210fimdState *s, int window) -{ - uint32_t ret; - - switch (window) { - case 0: - ret = (s->wpalcon[1] >> FIMD_WPAL_W0PAL_L_SHT) & FIMD_WPAL_W0PAL_L; - if (ret != 7) { - ret = 6 - ret; - } - break; - case 1: - ret = (s->wpalcon[1] >> FIMD_WPAL_W1PAL_L_SHT) & FIMD_WPAL_W1PAL_L; - if (ret != 7) { - ret = 6 - ret; - } - break; - case 2: - ret = ((s->wpalcon[0] >> FIMD_WPAL_W2PAL_H_SHT) & FIMD_WPAL_W2PAL_H) | - ((s->wpalcon[1] >> FIMD_WPAL_W2PAL_L_SHT) & FIMD_WPAL_W2PAL_L); - break; - case 3: - ret = ((s->wpalcon[0] >> FIMD_WPAL_W3PAL_H_SHT) & FIMD_WPAL_W3PAL_H) | - ((s->wpalcon[1] >> FIMD_WPAL_W3PAL_L_SHT) & FIMD_WPAL_W3PAL_L); - break; - case 4: - ret = ((s->wpalcon[0] >> FIMD_WPAL_W4PAL_H_SHT) & FIMD_WPAL_W4PAL_H) | - ((s->wpalcon[1] >> FIMD_WPAL_W4PAL_L_SHT) & FIMD_WPAL_W4PAL_L); - break; - default: - hw_error("exynos4210.fimd: incorrect window number %d\n", window); - ret = 0; - break; - } - return ret; -} - -#define FIMD_1_MINUS_COLOR(x) \ - ((0xFF - ((x) & 0xFF)) | (0xFF00 - ((x) & 0xFF00)) | \ - (0xFF0000 - ((x) & 0xFF0000))) -#define EXTEND_LOWER_HALFBYTE(x) (((x) & 0xF0F0F) | (((x) << 4) & 0xF0F0F0)) -#define EXTEND_UPPER_HALFBYTE(x) (((x) & 0xF0F0F0) | (((x) >> 4) & 0xF0F0F)) - -/* Multiply three lower bytes of two 32-bit words with each other. - * Each byte with values 0-255 is considered as a number with possible values - * in a range [0 - 1] */ -static inline uint32_t fimd_mult_each_byte(uint32_t a, uint32_t b) -{ - uint32_t tmp; - uint32_t ret; - - ret = ((tmp = (((a & 0xFF) * (b & 0xFF)) / 0xFF)) > 0xFF) ? 0xFF : tmp; - ret |= ((tmp = ((((a >> 8) & 0xFF) * ((b >> 8) & 0xFF)) / 0xFF)) > 0xFF) ? - 0xFF00 : tmp << 8; - ret |= ((tmp = ((((a >> 16) & 0xFF) * ((b >> 16) & 0xFF)) / 0xFF)) > 0xFF) ? - 0xFF0000 : tmp << 16; - return ret; -} - -/* For each corresponding bytes of two 32-bit words: (a*b + c*d) - * Byte values 0-255 are mapped to a range [0 .. 1] */ -static inline uint32_t -fimd_mult_and_sum_each_byte(uint32_t a, uint32_t b, uint32_t c, uint32_t d) -{ - uint32_t tmp; - uint32_t ret; - - ret = ((tmp = (((a & 0xFF) * (b & 0xFF) + (c & 0xFF) * (d & 0xFF)) / 0xFF)) - > 0xFF) ? 0xFF : tmp; - ret |= ((tmp = ((((a >> 8) & 0xFF) * ((b >> 8) & 0xFF) + ((c >> 8) & 0xFF) * - ((d >> 8) & 0xFF)) / 0xFF)) > 0xFF) ? 0xFF00 : tmp << 8; - ret |= ((tmp = ((((a >> 16) & 0xFF) * ((b >> 16) & 0xFF) + - ((c >> 16) & 0xFF) * ((d >> 16) & 0xFF)) / 0xFF)) > 0xFF) ? - 0xFF0000 : tmp << 16; - return ret; -} - -/* These routines cover all possible sources of window's transparent factor - * used in blending equation. Choice of routine is affected by WPALCON - * registers, BLENDCON register and window's WINCON register */ - -static uint32_t fimd_get_alpha_pix(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return pix_a; -} - -static uint32_t -fimd_get_alpha_pix_extlow(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return EXTEND_LOWER_HALFBYTE(pix_a); -} - -static uint32_t -fimd_get_alpha_pix_exthigh(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return EXTEND_UPPER_HALFBYTE(pix_a); -} - -static uint32_t fimd_get_alpha_mult(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return fimd_mult_each_byte(pix_a, w->alpha_val[0]); -} - -static uint32_t fimd_get_alpha_mult_ext(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return fimd_mult_each_byte(EXTEND_LOWER_HALFBYTE(pix_a), - EXTEND_UPPER_HALFBYTE(w->alpha_val[0])); -} - -static uint32_t fimd_get_alpha_aen(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return w->alpha_val[pix_a]; -} - -static uint32_t fimd_get_alpha_aen_ext(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return EXTEND_UPPER_HALFBYTE(w->alpha_val[pix_a]); -} - -static uint32_t fimd_get_alpha_sel(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return w->alpha_val[(w->wincon & FIMD_WINCON_ALPHA_SEL) ? 1 : 0]; -} - -static uint32_t fimd_get_alpha_sel_ext(Exynos4210fimdWindow *w, uint32_t pix_a) -{ - return EXTEND_UPPER_HALFBYTE(w->alpha_val[(w->wincon & - FIMD_WINCON_ALPHA_SEL) ? 1 : 0]); -} - -/* Updates currently active alpha value get function for specified window */ -static void fimd_update_get_alpha(Exynos4210fimdState *s, int win) -{ - Exynos4210fimdWindow *w = &s->window[win]; - const bool alpha_is_8bit = s->blendcon & FIMD_ALPHA_8BIT; - - if (w->wincon & FIMD_WINCON_BLD_PIX) { - if ((w->wincon & FIMD_WINCON_ALPHA_SEL) && WIN_BPP_MODE_WITH_ALPHA(w)) { - /* In this case, alpha component contains meaningful value */ - if (w->wincon & FIMD_WINCON_ALPHA_MUL) { - w->get_alpha = alpha_is_8bit ? - fimd_get_alpha_mult : fimd_get_alpha_mult_ext; - } else { - w->get_alpha = alpha_is_8bit ? - fimd_get_alpha_pix : fimd_get_alpha_pix_extlow; - } - } else { - if (IS_PALETTIZED_MODE(w) && - PAL_MODE_WITH_ALPHA(exynos4210_fimd_palette_format(s, win))) { - /* Alpha component has 8-bit numeric value */ - w->get_alpha = alpha_is_8bit ? - fimd_get_alpha_pix : fimd_get_alpha_pix_exthigh; - } else { - /* Alpha has only two possible values (AEN) */ - w->get_alpha = alpha_is_8bit ? - fimd_get_alpha_aen : fimd_get_alpha_aen_ext; - } - } - } else { - w->get_alpha = alpha_is_8bit ? fimd_get_alpha_sel : - fimd_get_alpha_sel_ext; - } -} - -/* Blends current window's (w) pixel (foreground pixel *ret) with background - * window (w_blend) pixel p_bg according to formula: - * NEW_COLOR = a_coef x FG_PIXEL_COLOR + b_coef x BG_PIXEL_COLOR - * NEW_ALPHA = p_coef x FG_ALPHA + q_coef x BG_ALPHA - */ -static void -exynos4210_fimd_blend_pixel(Exynos4210fimdWindow *w, rgba p_bg, rgba *ret) -{ - rgba p_fg = *ret; - uint32_t bg_color = ((p_bg.r & 0xFF) << 16) | ((p_bg.g & 0xFF) << 8) | - (p_bg.b & 0xFF); - uint32_t fg_color = ((p_fg.r & 0xFF) << 16) | ((p_fg.g & 0xFF) << 8) | - (p_fg.b & 0xFF); - uint32_t alpha_fg = p_fg.a; - int i; - /* It is possible that blending equation parameters a and b do not - * depend on window BLENEQ register. Account for this with first_coef */ - enum { A_COEF = 0, B_COEF = 1, P_COEF = 2, Q_COEF = 3, COEF_NUM = 4}; - uint32_t first_coef = A_COEF; - uint32_t blend_param[COEF_NUM]; - - if (w->keycon[0] & FIMD_WKEYCON0_KEYEN) { - uint32_t colorkey = (w->keycon[1] & - ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) & FIMD_WKEYCON0_COMPKEY; - - if ((w->keycon[0] & FIMD_WKEYCON0_DIRCON) && - (bg_color & ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) == colorkey) { - /* Foreground pixel is displayed */ - if (w->keycon[0] & FIMD_WKEYCON0_KEYBLEN) { - alpha_fg = w->keyalpha; - blend_param[A_COEF] = alpha_fg; - blend_param[B_COEF] = FIMD_1_MINUS_COLOR(alpha_fg); - } else { - alpha_fg = 0; - blend_param[A_COEF] = 0xFFFFFF; - blend_param[B_COEF] = 0x0; - } - first_coef = P_COEF; - } else if ((w->keycon[0] & FIMD_WKEYCON0_DIRCON) == 0 && - (fg_color & ~(w->keycon[0] & FIMD_WKEYCON0_COMPKEY)) == colorkey) { - /* Background pixel is displayed */ - if (w->keycon[0] & FIMD_WKEYCON0_KEYBLEN) { - alpha_fg = w->keyalpha; - blend_param[A_COEF] = alpha_fg; - blend_param[B_COEF] = FIMD_1_MINUS_COLOR(alpha_fg); - } else { - alpha_fg = 0; - blend_param[A_COEF] = 0x0; - blend_param[B_COEF] = 0xFFFFFF; - } - first_coef = P_COEF; - } - } - - for (i = first_coef; i < COEF_NUM; i++) { - switch ((w->blendeq >> i * 6) & FIMD_BLENDEQ_COEF_MASK) { - case 0: - blend_param[i] = 0; - break; - case 1: - blend_param[i] = 0xFFFFFF; - break; - case 2: - blend_param[i] = alpha_fg; - break; - case 3: - blend_param[i] = FIMD_1_MINUS_COLOR(alpha_fg); - break; - case 4: - blend_param[i] = p_bg.a; - break; - case 5: - blend_param[i] = FIMD_1_MINUS_COLOR(p_bg.a); - break; - case 6: - blend_param[i] = w->alpha_val[0]; - break; - case 10: - blend_param[i] = fg_color; - break; - case 11: - blend_param[i] = FIMD_1_MINUS_COLOR(fg_color); - break; - case 12: - blend_param[i] = bg_color; - break; - case 13: - blend_param[i] = FIMD_1_MINUS_COLOR(bg_color); - break; - default: - hw_error("exynos4210.fimd: blend equation coef illegal value\n"); - break; - } - } - - fg_color = fimd_mult_and_sum_each_byte(bg_color, blend_param[B_COEF], - fg_color, blend_param[A_COEF]); - ret->b = fg_color & 0xFF; - fg_color >>= 8; - ret->g = fg_color & 0xFF; - fg_color >>= 8; - ret->r = fg_color & 0xFF; - ret->a = fimd_mult_and_sum_each_byte(alpha_fg, blend_param[P_COEF], - p_bg.a, blend_param[Q_COEF]); -} - -/* These routines read data from video frame buffer in system RAM, convert - * this data to display controller internal representation, if necessary, - * perform pixel blending with data, currently presented in internal buffer. - * Result is stored in display controller internal frame buffer. */ - -/* Draw line with index in palette table in RAM frame buffer data */ -#define DEF_DRAW_LINE_PALETTE(N) \ -static void glue(draw_line_palette_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ - uint8_t *dst, bool blend) \ -{ \ - int width = w->rightbot_x - w->lefttop_x + 1; \ - uint8_t *ifb = dst; \ - uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ - uint64_t data; \ - rgba p, p_old; \ - int i; \ - do { \ - data = ldq_raw((void *)src); \ - src += 8; \ - fimd_swap_data(swap, &data); \ - for (i = (64 / (N) - 1); i >= 0; i--) { \ - w->pixel_to_rgb(w->palette[(data >> ((N) * i)) & \ - ((1ULL << (N)) - 1)], &p); \ - p.a = w->get_alpha(w, p.a); \ - if (blend) { \ - ifb += get_pixel_ifb(ifb, &p_old); \ - exynos4210_fimd_blend_pixel(w, p_old, &p); \ - } \ - dst += put_pixel_ifb(p, dst); \ - } \ - width -= (64 / (N)); \ - } while (width > 0); \ -} - -/* Draw line with direct color value in RAM frame buffer data */ -#define DEF_DRAW_LINE_NOPALETTE(N) \ -static void glue(draw_line_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ - uint8_t *dst, bool blend) \ -{ \ - int width = w->rightbot_x - w->lefttop_x + 1; \ - uint8_t *ifb = dst; \ - uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ - uint64_t data; \ - rgba p, p_old; \ - int i; \ - do { \ - data = ldq_raw((void *)src); \ - src += 8; \ - fimd_swap_data(swap, &data); \ - for (i = (64 / (N) - 1); i >= 0; i--) { \ - w->pixel_to_rgb((data >> ((N) * i)) & ((1ULL << (N)) - 1), &p); \ - p.a = w->get_alpha(w, p.a); \ - if (blend) { \ - ifb += get_pixel_ifb(ifb, &p_old); \ - exynos4210_fimd_blend_pixel(w, p_old, &p); \ - } \ - dst += put_pixel_ifb(p, dst); \ - } \ - width -= (64 / (N)); \ - } while (width > 0); \ -} - -DEF_DRAW_LINE_PALETTE(1) -DEF_DRAW_LINE_PALETTE(2) -DEF_DRAW_LINE_PALETTE(4) -DEF_DRAW_LINE_PALETTE(8) -DEF_DRAW_LINE_NOPALETTE(8) /* 8bpp mode has palette and non-palette versions */ -DEF_DRAW_LINE_NOPALETTE(16) -DEF_DRAW_LINE_NOPALETTE(32) - -/* Special draw line routine for window color map case */ -static void draw_line_mapcolor(Exynos4210fimdWindow *w, uint8_t *src, - uint8_t *dst, bool blend) -{ - rgba p, p_old; - uint8_t *ifb = dst; - int width = w->rightbot_x - w->lefttop_x + 1; - uint32_t map_color = w->winmap & FIMD_WINMAP_COLOR_MASK; - - do { - pixel_888_to_rgb(map_color, &p); - p.a = w->get_alpha(w, p.a); - if (blend) { - ifb += get_pixel_ifb(ifb, &p_old); - exynos4210_fimd_blend_pixel(w, p_old, &p); - } - dst += put_pixel_ifb(p, dst); - } while (--width); -} - -/* Write RGB to QEMU's GraphicConsole framebuffer */ - -static int put_to_qemufb_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_to_qemufb_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_to_qemufb_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_to_qemufb_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_to_qemufb_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; -} - -/* Routine to copy pixel from internal buffer to QEMU buffer */ -static int (*put_pixel_toqemu)(const rgba p, uint8_t *pixel); -static inline void fimd_update_putpix_qemu(int bpp) -{ - switch (bpp) { - case 8: - put_pixel_toqemu = put_to_qemufb_pixel8; - break; - case 15: - put_pixel_toqemu = put_to_qemufb_pixel15; - break; - case 16: - put_pixel_toqemu = put_to_qemufb_pixel16; - break; - case 24: - put_pixel_toqemu = put_to_qemufb_pixel24; - break; - case 32: - put_pixel_toqemu = put_to_qemufb_pixel32; - break; - default: - hw_error("exynos4210.fimd: unsupported BPP (%d)", bpp); - break; - } -} - -/* Routine to copy a line from internal frame buffer to QEMU display */ -static void fimd_copy_line_toqemu(int width, uint8_t *src, uint8_t *dst) -{ - rgba p; - - do { - src += get_pixel_ifb(src, &p); - dst += put_pixel_toqemu(p, dst); - } while (--width); -} - -/* Parse BPPMODE_F = WINCON1[5:2] bits */ -static void exynos4210_fimd_update_win_bppmode(Exynos4210fimdState *s, int win) -{ - Exynos4210fimdWindow *w = &s->window[win]; - - if (w->winmap & FIMD_WINMAP_EN) { - w->draw_line = draw_line_mapcolor; - return; - } - - switch (WIN_BPP_MODE(w)) { - case 0: - w->draw_line = draw_line_palette_1; - w->pixel_to_rgb = - palette_data_format[exynos4210_fimd_palette_format(s, win)]; - break; - case 1: - w->draw_line = draw_line_palette_2; - w->pixel_to_rgb = - palette_data_format[exynos4210_fimd_palette_format(s, win)]; - break; - case 2: - w->draw_line = draw_line_palette_4; - w->pixel_to_rgb = - palette_data_format[exynos4210_fimd_palette_format(s, win)]; - break; - case 3: - w->draw_line = draw_line_palette_8; - w->pixel_to_rgb = - palette_data_format[exynos4210_fimd_palette_format(s, win)]; - break; - case 4: - w->draw_line = draw_line_8; - w->pixel_to_rgb = pixel_a232_to_rgb; - break; - case 5: - w->draw_line = draw_line_16; - w->pixel_to_rgb = pixel_565_to_rgb; - break; - case 6: - w->draw_line = draw_line_16; - w->pixel_to_rgb = pixel_a555_to_rgb; - break; - case 7: - w->draw_line = draw_line_16; - w->pixel_to_rgb = pixel_1555_to_rgb; - break; - case 8: - w->draw_line = draw_line_32; - w->pixel_to_rgb = pixel_666_to_rgb; - break; - case 9: - w->draw_line = draw_line_32; - w->pixel_to_rgb = pixel_a665_to_rgb; - break; - case 10: - w->draw_line = draw_line_32; - w->pixel_to_rgb = pixel_a666_to_rgb; - break; - case 11: - w->draw_line = draw_line_32; - w->pixel_to_rgb = pixel_888_to_rgb; - break; - case 12: - w->draw_line = draw_line_32; - w->pixel_to_rgb = pixel_a887_to_rgb; - break; - case 13: - w->draw_line = draw_line_32; - if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon & - FIMD_WINCON_ALPHA_SEL)) { - w->pixel_to_rgb = pixel_8888_to_rgb; - } else { - w->pixel_to_rgb = pixel_a888_to_rgb; - } - break; - case 14: - w->draw_line = draw_line_16; - if ((w->wincon & FIMD_WINCON_BLD_PIX) && (w->wincon & - FIMD_WINCON_ALPHA_SEL)) { - w->pixel_to_rgb = pixel_4444_to_rgb; - } else { - w->pixel_to_rgb = pixel_a444_to_rgb; - } - break; - case 15: - w->draw_line = draw_line_16; - w->pixel_to_rgb = pixel_555_to_rgb; - break; - } -} - -#if EXYNOS4210_FIMD_MODE_TRACE > 0 -static const char *exynos4210_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 (palettized)"; - case 4: - return "8 bpp (non-palettized, A: 1-R:2-G:3-B:2)"; - case 5: - return "16 bpp (non-palettized, R:5-G:6-B:5)"; - case 6: - return "16 bpp (non-palettized, A:1-R:5-G:5-B:5)"; - case 7: - return "16 bpp (non-palettized, I :1-R:5-G:5-B:5)"; - case 8: - return "Unpacked 18 bpp (non-palettized, R:6-G:6-B:6)"; - case 9: - return "Unpacked 18bpp (non-palettized,A:1-R:6-G:6-B:5)"; - case 10: - return "Unpacked 19bpp (non-palettized,A:1-R:6-G:6-B:6)"; - case 11: - return "Unpacked 24 bpp (non-palettized R:8-G:8-B:8)"; - case 12: - return "Unpacked 24 bpp (non-palettized A:1-R:8-G:8-B:7)"; - case 13: - return "Unpacked 25 bpp (non-palettized A:1-R:8-G:8-B:8)"; - case 14: - return "Unpacked 13 bpp (non-palettized A:1-R:4-G:4-B:4)"; - case 15: - return "Unpacked 15 bpp (non-palettized R:5-G:5-B:5)"; - default: - return "Non-existing bpp mode"; - } -} - -static inline void exynos4210_fimd_trace_bppmode(Exynos4210fimdState *s, - int win_num, uint32_t val) -{ - Exynos4210fimdWindow *w = &s->window[win_num]; - - if (w->winmap & FIMD_WINMAP_EN) { - printf("QEMU FIMD: Window %d is mapped with MAPCOLOR=0x%x\n", - win_num, w->winmap & 0xFFFFFF); - return; - } - - if ((val != 0xFFFFFFFF) && ((w->wincon >> 2) & 0xF) == ((val >> 2) & 0xF)) { - return; - } - printf("QEMU FIMD: Window %d BPP mode set to %s\n", win_num, - exynos4210_fimd_get_bppmode((val >> 2) & 0xF)); -} -#else -static inline void exynos4210_fimd_trace_bppmode(Exynos4210fimdState *s, - int win_num, uint32_t val) -{ - -} -#endif - -static inline int fimd_get_buffer_id(Exynos4210fimdWindow *w) -{ - switch (w->wincon & FIMD_WINCON_BUFSTATUS) { - case FIMD_WINCON_BUF0_STAT: - return 0; - case FIMD_WINCON_BUF1_STAT: - return 1; - case FIMD_WINCON_BUF2_STAT: - return 2; - default: - DPRINT_ERROR("Non-existent buffer index\n"); - return 0; - } -} - -/* Updates specified window's MemorySection based on values of WINCON, - * VIDOSDA, VIDOSDB, VIDWADDx and SHADOWCON registers */ -static void fimd_update_memory_section(Exynos4210fimdState *s, unsigned win) -{ - Exynos4210fimdWindow *w = &s->window[win]; - hwaddr fb_start_addr, fb_mapped_len; - - if (!s->enabled || !(w->wincon & FIMD_WINCON_ENWIN) || - FIMD_WINDOW_PROTECTED(s->shadowcon, win)) { - return; - } - - if (w->host_fb_addr) { - cpu_physical_memory_unmap(w->host_fb_addr, w->fb_len, 0, 0); - w->host_fb_addr = NULL; - w->fb_len = 0; - } - - fb_start_addr = w->buf_start[fimd_get_buffer_id(w)]; - /* Total number of bytes of virtual screen used by current window */ - w->fb_len = fb_mapped_len = (w->virtpage_width + w->virtpage_offsize) * - (w->rightbot_y - w->lefttop_y + 1); - w->mem_section = memory_region_find(sysbus_address_space(&s->busdev), - fb_start_addr, w->fb_len); - assert(w->mem_section.mr); - assert(w->mem_section.offset_within_address_space == fb_start_addr); - DPRINT_TRACE("Window %u framebuffer changed: address=0x%08x, len=0x%x\n", - win, fb_start_addr, w->fb_len); - - if (w->mem_section.size != w->fb_len || - !memory_region_is_ram(w->mem_section.mr)) { - DPRINT_ERROR("Failed to find window %u framebuffer region\n", win); - goto error_return; - } - - w->host_fb_addr = cpu_physical_memory_map(fb_start_addr, &fb_mapped_len, 0); - if (!w->host_fb_addr) { - DPRINT_ERROR("Failed to map window %u framebuffer\n", win); - goto error_return; - } - - if (fb_mapped_len != w->fb_len) { - DPRINT_ERROR("Window %u mapped framebuffer length is less then " - "expected\n", win); - cpu_physical_memory_unmap(w->host_fb_addr, fb_mapped_len, 0, 0); - goto error_return; - } - return; - -error_return: - w->mem_section.mr = NULL; - w->mem_section.size = 0; - w->host_fb_addr = NULL; - w->fb_len = 0; -} - -static void exynos4210_fimd_enable(Exynos4210fimdState *s, bool enabled) -{ - if (enabled && !s->enabled) { - unsigned w; - s->enabled = true; - for (w = 0; w < NUM_OF_WINDOWS; w++) { - fimd_update_memory_section(s, w); - } - } - s->enabled = enabled; - DPRINT_TRACE("display controller %s\n", enabled ? "enabled" : "disabled"); -} - -static inline uint32_t unpack_upper_4(uint32_t x) -{ - return ((x & 0xF00) << 12) | ((x & 0xF0) << 8) | ((x & 0xF) << 4); -} - -static inline uint32_t pack_upper_4(uint32_t x) -{ - return (((x & 0xF00000) >> 12) | ((x & 0xF000) >> 8) | - ((x & 0xF0) >> 4)) & 0xFFF; -} - -static void exynos4210_fimd_update_irq(Exynos4210fimdState *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 exynos4210_fimd_invalidate(void *opaque) -{ - Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; - s->invalidate = true; -} - -static void exynos4210_update_resolution(Exynos4210fimdState *s) -{ - DisplaySurface *surface = qemu_console_surface(s->console); - - /* 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 || surface_width(surface) != width || - surface_height(surface) != height) { - DPRINT_L1("Resolution changed from %ux%u to %ux%u\n", - surface_width(surface), surface_height(surface), width, height); - qemu_console_resize(s->console, width, height); - s->ifb = g_realloc(s->ifb, width * height * RGBA_SIZE + 1); - memset(s->ifb, 0, width * height * RGBA_SIZE + 1); - exynos4210_fimd_invalidate(s); - } -} - -static void exynos4210_fimd_update(void *opaque) -{ - Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; - DisplaySurface *surface = qemu_console_surface(s->console); - Exynos4210fimdWindow *w; - int i, line; - hwaddr fb_line_addr, inc_size; - int scrn_height; - int first_line = -1, last_line = -1, scrn_width; - bool blend = false; - uint8_t *host_fb_addr; - bool is_dirty = false; - 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 || !surface_bits_per_pixel(surface) || - !s->enabled) { - return; - } - exynos4210_update_resolution(s); - - for (i = 0; i < NUM_OF_WINDOWS; i++) { - w = &s->window[i]; - if ((w->wincon & FIMD_WINCON_ENWIN) && w->host_fb_addr) { - scrn_height = w->rightbot_y - w->lefttop_y + 1; - scrn_width = w->virtpage_width; - /* Total width of virtual screen page in bytes */ - inc_size = scrn_width + w->virtpage_offsize; - memory_region_sync_dirty_bitmap(w->mem_section.mr); - host_fb_addr = w->host_fb_addr; - fb_line_addr = w->mem_section.offset_within_region; - - for (line = 0; line < scrn_height; line++) { - is_dirty = memory_region_get_dirty(w->mem_section.mr, - fb_line_addr, scrn_width, DIRTY_MEMORY_VGA); - - if (s->invalidate || is_dirty) { - if (first_line == -1) { - first_line = line; - } - last_line = line; - w->draw_line(w, host_fb_addr, s->ifb + - w->lefttop_x * RGBA_SIZE + (w->lefttop_y + line) * - global_width * RGBA_SIZE, blend); - } - host_fb_addr += inc_size; - fb_line_addr += inc_size; - is_dirty = false; - } - memory_region_reset_dirty(w->mem_section.mr, - w->mem_section.offset_within_region, - w->fb_len, DIRTY_MEMORY_VGA); - blend = true; - } - } - - /* Copy resulting image to QEMU_CONSOLE. */ - if (first_line >= 0) { - uint8_t *d; - int bpp; - - bpp = surface_bits_per_pixel(surface); - fimd_update_putpix_qemu(bpp); - bpp = (bpp + 1) >> 3; - d = surface_data(surface); - for (line = first_line; line <= last_line; line++) { - fimd_copy_line_toqemu(global_width, s->ifb + global_width * line * - RGBA_SIZE, d + global_width * line * bpp); - } - dpy_gfx_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) { - exynos4210_fimd_enable(s, false); - } - exynos4210_fimd_update_irq(s); -} - -static void exynos4210_fimd_reset(DeviceState *d) -{ - Exynos4210fimdState *s = DO_UPCAST(Exynos4210fimdState, busdev.qdev, d); - unsigned w; - - DPRINT_TRACE("Display controller reset\n"); - /* Set all display controller registers to 0 */ - memset(&s->vidcon, 0, (uint8_t *)&s->window - (uint8_t *)&s->vidcon); - for (w = 0; w < NUM_OF_WINDOWS; w++) { - memset(&s->window[w], 0, sizeof(Exynos4210fimdWindow)); - s->window[w].blendeq = 0xC2; - exynos4210_fimd_update_win_bppmode(s, w); - exynos4210_fimd_trace_bppmode(s, w, 0xFFFFFFFF); - fimd_update_get_alpha(s, w); - } - - if (s->ifb != NULL) { - g_free(s->ifb); - } - s->ifb = NULL; - - exynos4210_fimd_invalidate(s); - exynos4210_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 exynos4210_fimd_write(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; - unsigned w, i; - uint32_t old_value; - - DPRINT_L2("write offset 0x%08x, value=%llu(0x%08llx)\n", offset, - (long long unsigned int)val, (long long unsigned int)val); - - switch (offset) { - case FIMD_VIDCON0: - if ((val & FIMD_VIDCON0_ENVID_MASK) == FIMD_VIDCON0_ENVID_MASK) { - exynos4210_fimd_enable(s, true); - } else { - if ((val & FIMD_VIDCON0_ENVID) == 0) { - exynos4210_fimd_enable(s, false); - } - } - s->vidcon[0] = val; - break; - case FIMD_VIDCON1: - /* Leave read-only bits as is */ - val = (val & (~FIMD_VIDCON1_ROMASK)) | - (s->vidcon[1] & FIMD_VIDCON1_ROMASK); - s->vidcon[1] = val; - break; - case FIMD_VIDCON2 ... FIMD_VIDCON3: - s->vidcon[(offset) >> 2] = val; - break; - case FIMD_VIDTCON_START ... FIMD_VIDTCON_END: - s->vidtcon[(offset - FIMD_VIDTCON_START) >> 2] = val; - break; - case FIMD_WINCON_START ... FIMD_WINCON_END: - w = (offset - FIMD_WINCON_START) >> 2; - /* Window's current buffer ID */ - i = fimd_get_buffer_id(&s->window[w]); - old_value = s->window[w].wincon; - val = (val & ~FIMD_WINCON_ROMASK) | - (s->window[w].wincon & FIMD_WINCON_ROMASK); - if (w == 0) { - /* Window 0 wincon ALPHA_MUL bit must always be 0 */ - val &= ~FIMD_WINCON_ALPHA_MUL; - } - exynos4210_fimd_trace_bppmode(s, 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_BUFSTAT_H) | FIMD_WINCON_BUFSTAT_L; - break; - case FIMD_WINCON_BUF2_SEL: - if (val & FIMD_WINCON_BUFMODE) { - val = (val & ~FIMD_WINCON_BUFSTAT_L) | FIMD_WINCON_BUFSTAT_H; - } - break; - default: - break; - } - s->window[w].wincon = val; - exynos4210_fimd_update_win_bppmode(s, w); - fimd_update_get_alpha(s, w); - if ((i != fimd_get_buffer_id(&s->window[w])) || - (!(old_value & FIMD_WINCON_ENWIN) && (s->window[w].wincon & - FIMD_WINCON_ENWIN))) { - fimd_update_memory_section(s, w); - } - break; - case FIMD_SHADOWCON: - old_value = s->shadowcon; - s->shadowcon = val; - for (w = 0; w < NUM_OF_WINDOWS; w++) { - if (FIMD_WINDOW_PROTECTED(old_value, w) && - !FIMD_WINDOW_PROTECTED(s->shadowcon, w)) { - fimd_update_memory_section(s, w); - } - } - break; - case FIMD_WINCHMAP: - s->winchmap = val; - break; - case FIMD_VIDOSD_START ... FIMD_VIDOSD_END: - w = (offset - FIMD_VIDOSD_START) >> 4; - i = ((offset - FIMD_VIDOSD_START) & 0xF) >> 2; - switch (i) { - case 0: - old_value = s->window[w].lefttop_y; - s->window[w].lefttop_x = (val >> FIMD_VIDOSD_HOR_SHIFT) & - FIMD_VIDOSD_COORD_MASK; - s->window[w].lefttop_y = (val >> FIMD_VIDOSD_VER_SHIFT) & - FIMD_VIDOSD_COORD_MASK; - if (s->window[w].lefttop_y != old_value) { - fimd_update_memory_section(s, w); - } - break; - case 1: - old_value = s->window[w].rightbot_y; - s->window[w].rightbot_x = (val >> FIMD_VIDOSD_HOR_SHIFT) & - FIMD_VIDOSD_COORD_MASK; - s->window[w].rightbot_y = (val >> FIMD_VIDOSD_VER_SHIFT) & - FIMD_VIDOSD_COORD_MASK; - if (s->window[w].rightbot_y != old_value) { - fimd_update_memory_section(s, w); - } - break; - case 2: - if (w == 0) { - s->window[w].osdsize = val; - } else { - s->window[w].alpha_val[0] = - unpack_upper_4((val & FIMD_VIDOSD_ALPHA_AEN0) >> - FIMD_VIDOSD_AEN0_SHIFT) | - (s->window[w].alpha_val[0] & FIMD_VIDALPHA_ALPHA_LOWER); - s->window[w].alpha_val[1] = - unpack_upper_4(val & FIMD_VIDOSD_ALPHA_AEN1) | - (s->window[w].alpha_val[1] & FIMD_VIDALPHA_ALPHA_LOWER); - } - break; - case 3: - if (w != 1 && w != 2) { - DPRINT_ERROR("Bad write offset 0x%08x\n", offset); - return; - } - s->window[w].osdsize = val; - break; - } - break; - case FIMD_VIDWADD0_START ... FIMD_VIDWADD0_END: - w = (offset - FIMD_VIDWADD0_START) >> 3; - i = ((offset - FIMD_VIDWADD0_START) >> 2) & 1; - if (i == fimd_get_buffer_id(&s->window[w]) && - s->window[w].buf_start[i] != val) { - s->window[w].buf_start[i] = val; - fimd_update_memory_section(s, w); - break; - } - s->window[w].buf_start[i] = val; - break; - case FIMD_VIDWADD1_START ... FIMD_VIDWADD1_END: - w = (offset - FIMD_VIDWADD1_START) >> 3; - i = ((offset - FIMD_VIDWADD1_START) >> 2) & 1; - s->window[w].buf_end[i] = val; - break; - case FIMD_VIDWADD2_START ... FIMD_VIDWADD2_END: - w = (offset - FIMD_VIDWADD2_START) >> 2; - if (((val & FIMD_VIDWADD2_PAGEWIDTH) != s->window[w].virtpage_width) || - (((val >> FIMD_VIDWADD2_OFFSIZE_SHIFT) & FIMD_VIDWADD2_OFFSIZE) != - s->window[w].virtpage_offsize)) { - s->window[w].virtpage_width = val & FIMD_VIDWADD2_PAGEWIDTH; - s->window[w].virtpage_offsize = - (val >> FIMD_VIDWADD2_OFFSIZE_SHIFT) & FIMD_VIDWADD2_OFFSIZE; - fimd_update_memory_section(s, w); - } - break; - case FIMD_VIDINTCON0: - s->vidintcon[0] = val; - break; - case FIMD_VIDINTCON1: - s->vidintcon[1] &= ~(val & 7); - exynos4210_fimd_update_irq(s); - break; - case FIMD_WKEYCON_START ... FIMD_WKEYCON_END: - w = ((offset - FIMD_WKEYCON_START) >> 3) + 1; - i = ((offset - FIMD_WKEYCON_START) >> 2) & 1; - s->window[w].keycon[i] = val; - break; - case FIMD_WKEYALPHA_START ... FIMD_WKEYALPHA_END: - w = ((offset - FIMD_WKEYALPHA_START) >> 2) + 1; - s->window[w].keyalpha = val; - break; - case FIMD_DITHMODE: - s->dithmode = val; - break; - case FIMD_WINMAP_START ... FIMD_WINMAP_END: - w = (offset - FIMD_WINMAP_START) >> 2; - old_value = s->window[w].winmap; - s->window[w].winmap = val; - if ((val & FIMD_WINMAP_EN) ^ (old_value & FIMD_WINMAP_EN)) { - exynos4210_fimd_invalidate(s); - exynos4210_fimd_update_win_bppmode(s, w); - exynos4210_fimd_trace_bppmode(s, w, 0xFFFFFFFF); - exynos4210_fimd_update(s); - } - break; - case FIMD_WPALCON_HIGH ... FIMD_WPALCON_LOW: - i = (offset - FIMD_WPALCON_HIGH) >> 2; - s->wpalcon[i] = val; - if (s->wpalcon[1] & FIMD_WPALCON_UPDATEEN) { - for (w = 0; w < NUM_OF_WINDOWS; w++) { - exynos4210_fimd_update_win_bppmode(s, w); - fimd_update_get_alpha(s, w); - } - } - break; - case FIMD_TRIGCON: - val = (val & ~FIMD_TRIGCON_ROMASK) | (s->trigcon & FIMD_TRIGCON_ROMASK); - s->trigcon = val; - break; - case FIMD_I80IFCON_START ... FIMD_I80IFCON_END: - s->i80ifcon[(offset - FIMD_I80IFCON_START) >> 2] = val; - break; - case FIMD_COLORGAINCON: - s->colorgaincon = val; - break; - case FIMD_LDI_CMDCON0 ... FIMD_LDI_CMDCON1: - s->ldi_cmdcon[(offset - FIMD_LDI_CMDCON0) >> 2] = val; - break; - case FIMD_SIFCCON0 ... FIMD_SIFCCON2: - i = (offset - FIMD_SIFCCON0) >> 2; - if (i != 2) { - s->sifccon[i] = val; - } - break; - case FIMD_HUECOEFCR_START ... FIMD_HUECOEFCR_END: - i = (offset - FIMD_HUECOEFCR_START) >> 2; - s->huecoef_cr[i] = val; - break; - case FIMD_HUECOEFCB_START ... FIMD_HUECOEFCB_END: - i = (offset - FIMD_HUECOEFCB_START) >> 2; - s->huecoef_cb[i] = val; - break; - case FIMD_HUEOFFSET: - s->hueoffset = val; - break; - case FIMD_VIDWALPHA_START ... FIMD_VIDWALPHA_END: - w = ((offset - FIMD_VIDWALPHA_START) >> 3); - i = ((offset - FIMD_VIDWALPHA_START) >> 2) & 1; - if (w == 0) { - s->window[w].alpha_val[i] = val; - } else { - s->window[w].alpha_val[i] = (val & FIMD_VIDALPHA_ALPHA_LOWER) | - (s->window[w].alpha_val[i] & FIMD_VIDALPHA_ALPHA_UPPER); - } - break; - case FIMD_BLENDEQ_START ... FIMD_BLENDEQ_END: - s->window[(offset - FIMD_BLENDEQ_START) >> 2].blendeq = val; - break; - case FIMD_BLENDCON: - old_value = s->blendcon; - s->blendcon = val; - if ((s->blendcon & FIMD_ALPHA_8BIT) != (old_value & FIMD_ALPHA_8BIT)) { - for (w = 0; w < NUM_OF_WINDOWS; w++) { - fimd_update_get_alpha(s, w); - } - } - break; - case FIMD_WRTQOSCON_START ... FIMD_WRTQOSCON_END: - s->window[(offset - FIMD_WRTQOSCON_START) >> 2].rtqoscon = val; - break; - case FIMD_I80IFCMD_START ... FIMD_I80IFCMD_END: - s->i80ifcmd[(offset - FIMD_I80IFCMD_START) >> 2] = val; - break; - case FIMD_VIDW0ADD0_B2 ... FIMD_VIDW4ADD0_B2: - if (offset & 0x0004) { - DPRINT_ERROR("bad write offset 0x%08x\n", offset); - break; - } - w = (offset - FIMD_VIDW0ADD0_B2) >> 3; - if (fimd_get_buffer_id(&s->window[w]) == 2 && - s->window[w].buf_start[2] != val) { - s->window[w].buf_start[2] = val; - fimd_update_memory_section(s, w); - break; - } - s->window[w].buf_start[2] = val; - break; - case FIMD_SHD_ADD0_START ... FIMD_SHD_ADD0_END: - if (offset & 0x0004) { - DPRINT_ERROR("bad write offset 0x%08x\n", offset); - break; - } - s->window[(offset - FIMD_SHD_ADD0_START) >> 3].shadow_buf_start = val; - break; - case FIMD_SHD_ADD1_START ... FIMD_SHD_ADD1_END: - if (offset & 0x0004) { - DPRINT_ERROR("bad write offset 0x%08x\n", offset); - break; - } - s->window[(offset - FIMD_SHD_ADD1_START) >> 3].shadow_buf_end = val; - break; - case FIMD_SHD_ADD2_START ... FIMD_SHD_ADD2_END: - s->window[(offset - FIMD_SHD_ADD2_START) >> 2].shadow_buf_size = val; - break; - case FIMD_PAL_MEM_START ... FIMD_PAL_MEM_END: - w = (offset - FIMD_PAL_MEM_START) >> 10; - i = ((offset - FIMD_PAL_MEM_START) >> 2) & 0xFF; - s->window[w].palette[i] = val; - break; - case FIMD_PALMEM_AL_START ... FIMD_PALMEM_AL_END: - /* Palette memory aliases for windows 0 and 1 */ - w = (offset - FIMD_PALMEM_AL_START) >> 10; - i = ((offset - FIMD_PALMEM_AL_START) >> 2) & 0xFF; - s->window[w].palette[i] = val; - break; - default: - DPRINT_ERROR("bad write offset 0x%08x\n", offset); - break; - } -} - -static uint64_t exynos4210_fimd_read(void *opaque, hwaddr offset, - unsigned size) -{ - Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; - int w, i; - uint32_t ret = 0; - - DPRINT_L2("read offset 0x%08x\n", offset); - - switch (offset) { - case FIMD_VIDCON0 ... FIMD_VIDCON3: - return s->vidcon[(offset - FIMD_VIDCON0) >> 2]; - case FIMD_VIDTCON_START ... FIMD_VIDTCON_END: - return s->vidtcon[(offset - FIMD_VIDTCON_START) >> 2]; - case FIMD_WINCON_START ... FIMD_WINCON_END: - return s->window[(offset - FIMD_WINCON_START) >> 2].wincon; - case FIMD_SHADOWCON: - return s->shadowcon; - case FIMD_WINCHMAP: - return s->winchmap; - case FIMD_VIDOSD_START ... FIMD_VIDOSD_END: - w = (offset - FIMD_VIDOSD_START) >> 4; - i = ((offset - FIMD_VIDOSD_START) & 0xF) >> 2; - switch (i) { - case 0: - ret = ((s->window[w].lefttop_x & FIMD_VIDOSD_COORD_MASK) << - FIMD_VIDOSD_HOR_SHIFT) | - (s->window[w].lefttop_y & FIMD_VIDOSD_COORD_MASK); - break; - case 1: - ret = ((s->window[w].rightbot_x & FIMD_VIDOSD_COORD_MASK) << - FIMD_VIDOSD_HOR_SHIFT) | - (s->window[w].rightbot_y & FIMD_VIDOSD_COORD_MASK); - break; - case 2: - if (w == 0) { - ret = s->window[w].osdsize; - } else { - ret = (pack_upper_4(s->window[w].alpha_val[0]) << - FIMD_VIDOSD_AEN0_SHIFT) | - pack_upper_4(s->window[w].alpha_val[1]); - } - break; - case 3: - if (w != 1 && w != 2) { - DPRINT_ERROR("bad read offset 0x%08x\n", offset); - return 0xBAADBAAD; - } - ret = s->window[w].osdsize; - break; - } - return ret; - case FIMD_VIDWADD0_START ... FIMD_VIDWADD0_END: - w = (offset - FIMD_VIDWADD0_START) >> 3; - i = ((offset - FIMD_VIDWADD0_START) >> 2) & 1; - return s->window[w].buf_start[i]; - case FIMD_VIDWADD1_START ... FIMD_VIDWADD1_END: - w = (offset - FIMD_VIDWADD1_START) >> 3; - i = ((offset - FIMD_VIDWADD1_START) >> 2) & 1; - return s->window[w].buf_end[i]; - case FIMD_VIDWADD2_START ... FIMD_VIDWADD2_END: - w = (offset - FIMD_VIDWADD2_START) >> 2; - return s->window[w].virtpage_width | (s->window[w].virtpage_offsize << - FIMD_VIDWADD2_OFFSIZE_SHIFT); - case FIMD_VIDINTCON0 ... FIMD_VIDINTCON1: - return s->vidintcon[(offset - FIMD_VIDINTCON0) >> 2]; - case FIMD_WKEYCON_START ... FIMD_WKEYCON_END: - w = ((offset - FIMD_WKEYCON_START) >> 3) + 1; - i = ((offset - FIMD_WKEYCON_START) >> 2) & 1; - return s->window[w].keycon[i]; - case FIMD_WKEYALPHA_START ... FIMD_WKEYALPHA_END: - w = ((offset - FIMD_WKEYALPHA_START) >> 2) + 1; - return s->window[w].keyalpha; - case FIMD_DITHMODE: - return s->dithmode; - case FIMD_WINMAP_START ... FIMD_WINMAP_END: - return s->window[(offset - FIMD_WINMAP_START) >> 2].winmap; - case FIMD_WPALCON_HIGH ... FIMD_WPALCON_LOW: - return s->wpalcon[(offset - FIMD_WPALCON_HIGH) >> 2]; - case FIMD_TRIGCON: - return s->trigcon; - case FIMD_I80IFCON_START ... FIMD_I80IFCON_END: - return s->i80ifcon[(offset - FIMD_I80IFCON_START) >> 2]; - case FIMD_COLORGAINCON: - return s->colorgaincon; - case FIMD_LDI_CMDCON0 ... FIMD_LDI_CMDCON1: - return s->ldi_cmdcon[(offset - FIMD_LDI_CMDCON0) >> 2]; - case FIMD_SIFCCON0 ... FIMD_SIFCCON2: - i = (offset - FIMD_SIFCCON0) >> 2; - return s->sifccon[i]; - case FIMD_HUECOEFCR_START ... FIMD_HUECOEFCR_END: - i = (offset - FIMD_HUECOEFCR_START) >> 2; - return s->huecoef_cr[i]; - case FIMD_HUECOEFCB_START ... FIMD_HUECOEFCB_END: - i = (offset - FIMD_HUECOEFCB_START) >> 2; - return s->huecoef_cb[i]; - case FIMD_HUEOFFSET: - return s->hueoffset; - case FIMD_VIDWALPHA_START ... FIMD_VIDWALPHA_END: - w = ((offset - FIMD_VIDWALPHA_START) >> 3); - i = ((offset - FIMD_VIDWALPHA_START) >> 2) & 1; - return s->window[w].alpha_val[i] & - (w == 0 ? 0xFFFFFF : FIMD_VIDALPHA_ALPHA_LOWER); - case FIMD_BLENDEQ_START ... FIMD_BLENDEQ_END: - return s->window[(offset - FIMD_BLENDEQ_START) >> 2].blendeq; - case FIMD_BLENDCON: - return s->blendcon; - case FIMD_WRTQOSCON_START ... FIMD_WRTQOSCON_END: - return s->window[(offset - FIMD_WRTQOSCON_START) >> 2].rtqoscon; - case FIMD_I80IFCMD_START ... FIMD_I80IFCMD_END: - return s->i80ifcmd[(offset - FIMD_I80IFCMD_START) >> 2]; - case FIMD_VIDW0ADD0_B2 ... FIMD_VIDW4ADD0_B2: - if (offset & 0x0004) { - break; - } - return s->window[(offset - FIMD_VIDW0ADD0_B2) >> 3].buf_start[2]; - case FIMD_SHD_ADD0_START ... FIMD_SHD_ADD0_END: - if (offset & 0x0004) { - break; - } - return s->window[(offset - FIMD_SHD_ADD0_START) >> 3].shadow_buf_start; - case FIMD_SHD_ADD1_START ... FIMD_SHD_ADD1_END: - if (offset & 0x0004) { - break; - } - return s->window[(offset - FIMD_SHD_ADD1_START) >> 3].shadow_buf_end; - case FIMD_SHD_ADD2_START ... FIMD_SHD_ADD2_END: - return s->window[(offset - FIMD_SHD_ADD2_START) >> 2].shadow_buf_size; - case FIMD_PAL_MEM_START ... FIMD_PAL_MEM_END: - w = (offset - FIMD_PAL_MEM_START) >> 10; - i = ((offset - FIMD_PAL_MEM_START) >> 2) & 0xFF; - return s->window[w].palette[i]; - case FIMD_PALMEM_AL_START ... FIMD_PALMEM_AL_END: - /* Palette aliases for win 0,1 */ - w = (offset - FIMD_PALMEM_AL_START) >> 10; - i = ((offset - FIMD_PALMEM_AL_START) >> 2) & 0xFF; - return s->window[w].palette[i]; - } - - DPRINT_ERROR("bad read offset 0x%08x\n", offset); - return 0xBAADBAAD; -} - -static const MemoryRegionOps exynos4210_fimd_mmio_ops = { - .read = exynos4210_fimd_read, - .write = exynos4210_fimd_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - .unaligned = false - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int exynos4210_fimd_load(void *opaque, int version_id) -{ - Exynos4210fimdState *s = (Exynos4210fimdState *)opaque; - int w; - - if (version_id != 1) { - return -EINVAL; - } - - for (w = 0; w < NUM_OF_WINDOWS; w++) { - exynos4210_fimd_update_win_bppmode(s, w); - fimd_update_get_alpha(s, w); - fimd_update_memory_section(s, w); - } - - /* Redraw the whole screen */ - exynos4210_update_resolution(s); - exynos4210_fimd_invalidate(s); - exynos4210_fimd_enable(s, (s->vidcon[0] & FIMD_VIDCON0_ENVID_MASK) == - FIMD_VIDCON0_ENVID_MASK); - return 0; -} - -static const VMStateDescription exynos4210_fimd_window_vmstate = { - .name = "exynos4210.fimd_window", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(wincon, Exynos4210fimdWindow), - VMSTATE_UINT32_ARRAY(buf_start, Exynos4210fimdWindow, 3), - VMSTATE_UINT32_ARRAY(buf_end, Exynos4210fimdWindow, 3), - VMSTATE_UINT32_ARRAY(keycon, Exynos4210fimdWindow, 2), - VMSTATE_UINT32(keyalpha, Exynos4210fimdWindow), - VMSTATE_UINT32(winmap, Exynos4210fimdWindow), - VMSTATE_UINT32(blendeq, Exynos4210fimdWindow), - VMSTATE_UINT32(rtqoscon, Exynos4210fimdWindow), - VMSTATE_UINT32_ARRAY(palette, Exynos4210fimdWindow, 256), - VMSTATE_UINT32(shadow_buf_start, Exynos4210fimdWindow), - VMSTATE_UINT32(shadow_buf_end, Exynos4210fimdWindow), - VMSTATE_UINT32(shadow_buf_size, Exynos4210fimdWindow), - VMSTATE_UINT16(lefttop_x, Exynos4210fimdWindow), - VMSTATE_UINT16(lefttop_y, Exynos4210fimdWindow), - VMSTATE_UINT16(rightbot_x, Exynos4210fimdWindow), - VMSTATE_UINT16(rightbot_y, Exynos4210fimdWindow), - VMSTATE_UINT32(osdsize, Exynos4210fimdWindow), - VMSTATE_UINT32_ARRAY(alpha_val, Exynos4210fimdWindow, 2), - VMSTATE_UINT16(virtpage_width, Exynos4210fimdWindow), - VMSTATE_UINT16(virtpage_offsize, Exynos4210fimdWindow), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription exynos4210_fimd_vmstate = { - .name = "exynos4210.fimd", - .version_id = 1, - .minimum_version_id = 1, - .post_load = exynos4210_fimd_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(vidcon, Exynos4210fimdState, 4), - VMSTATE_UINT32_ARRAY(vidtcon, Exynos4210fimdState, 4), - VMSTATE_UINT32(shadowcon, Exynos4210fimdState), - VMSTATE_UINT32(winchmap, Exynos4210fimdState), - VMSTATE_UINT32_ARRAY(vidintcon, Exynos4210fimdState, 2), - VMSTATE_UINT32(dithmode, Exynos4210fimdState), - VMSTATE_UINT32_ARRAY(wpalcon, Exynos4210fimdState, 2), - VMSTATE_UINT32(trigcon, Exynos4210fimdState), - VMSTATE_UINT32_ARRAY(i80ifcon, Exynos4210fimdState, 4), - VMSTATE_UINT32(colorgaincon, Exynos4210fimdState), - VMSTATE_UINT32_ARRAY(ldi_cmdcon, Exynos4210fimdState, 2), - VMSTATE_UINT32_ARRAY(sifccon, Exynos4210fimdState, 3), - VMSTATE_UINT32_ARRAY(huecoef_cr, Exynos4210fimdState, 4), - VMSTATE_UINT32_ARRAY(huecoef_cb, Exynos4210fimdState, 4), - VMSTATE_UINT32(hueoffset, Exynos4210fimdState), - VMSTATE_UINT32_ARRAY(i80ifcmd, Exynos4210fimdState, 12), - VMSTATE_UINT32(blendcon, Exynos4210fimdState), - VMSTATE_STRUCT_ARRAY(window, Exynos4210fimdState, 5, 1, - exynos4210_fimd_window_vmstate, Exynos4210fimdWindow), - VMSTATE_END_OF_LIST() - } -}; - -static int exynos4210_fimd_init(SysBusDevice *dev) -{ - Exynos4210fimdState *s = FROM_SYSBUS(Exynos4210fimdState, 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, &exynos4210_fimd_mmio_ops, s, - "exynos4210.fimd", FIMD_REGS_SIZE); - sysbus_init_mmio(dev, &s->iomem); - s->console = graphic_console_init(exynos4210_fimd_update, - exynos4210_fimd_invalidate, NULL, NULL, s); - - return 0; -} - -static void exynos4210_fimd_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - dc->vmsd = &exynos4210_fimd_vmstate; - dc->reset = exynos4210_fimd_reset; - k->init = exynos4210_fimd_init; -} - -static const TypeInfo exynos4210_fimd_info = { - .name = "exynos4210.fimd", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210fimdState), - .class_init = exynos4210_fimd_class_init, -}; - -static void exynos4210_fimd_register_types(void) -{ - type_register_static(&exynos4210_fimd_info); -} - -type_init(exynos4210_fimd_register_types) diff --git a/hw/framebuffer.c b/hw/framebuffer.c deleted file mode 100644 index 7326a98a41..0000000000 --- a/hw/framebuffer.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Framebuffer device helper routines - * - * Copyright (c) 2009 CodeSourcery - * Written by Paul Brook - * - * This code is licensed under the GNU GPLv2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -/* TODO: - - Do something similar for framebuffers with local ram - - Handle rotation here instead of hacking dest_pitch - - Use common pixel conversion routines instead of per-device drawfn - - Remove all DisplayState knowledge from devices. - */ - -#include "hw/hw.h" -#include "ui/console.h" -#include "hw/framebuffer.h" - -/* Render an image from a shared memory framebuffer. */ - -void framebuffer_update_display( - DisplaySurface *ds, - MemoryRegion *address_space, - hwaddr base, - int cols, /* Width in pixels. */ - int rows, /* Height in pixels. */ - int src_width, /* Length of source line, in bytes. */ - int dest_row_pitch, /* Bytes between adjacent horizontal output pixels. */ - int dest_col_pitch, /* Bytes between adjacent vertical output pixels. */ - int invalidate, /* nonzero to redraw the whole image. */ - drawfn fn, - void *opaque, - int *first_row, /* Input and output. */ - int *last_row /* Output only */) -{ - hwaddr src_len; - uint8_t *dest; - uint8_t *src; - uint8_t *src_base; - int first, last = 0; - int dirty; - int i; - ram_addr_t addr; - MemoryRegionSection mem_section; - MemoryRegion *mem; - - i = *first_row; - *first_row = -1; - src_len = src_width * rows; - - mem_section = memory_region_find(address_space, base, src_len); - if (mem_section.size != src_len || !memory_region_is_ram(mem_section.mr)) { - return; - } - mem = mem_section.mr; - assert(mem); - assert(mem_section.offset_within_address_space == base); - - memory_region_sync_dirty_bitmap(mem); - src_base = cpu_physical_memory_map(base, &src_len, 0); - /* If we can't map the framebuffer then bail. We could try harder, - but it's not really worth it as dirty flag tracking will probably - already have failed above. */ - if (!src_base) - return; - if (src_len != src_width * rows) { - cpu_physical_memory_unmap(src_base, src_len, 0, 0); - return; - } - src = src_base; - dest = surface_data(ds); - if (dest_col_pitch < 0) - dest -= dest_col_pitch * (cols - 1); - if (dest_row_pitch < 0) { - dest -= dest_row_pitch * (rows - 1); - } - first = -1; - addr = mem_section.offset_within_region; - - addr += i * src_width; - src += i * src_width; - dest += i * dest_row_pitch; - - for (; i < rows; i++) { - dirty = memory_region_get_dirty(mem, addr, src_width, - DIRTY_MEMORY_VGA); - if (dirty || invalidate) { - fn(opaque, dest, src, cols, dest_col_pitch); - if (first == -1) - first = i; - last = i; - } - addr += src_width; - src += src_width; - dest += dest_row_pitch; - } - cpu_physical_memory_unmap(src_base, src_len, 0, 0); - if (first < 0) { - return; - } - memory_region_reset_dirty(mem, mem_section.offset_within_region, src_len, - DIRTY_MEMORY_VGA); - *first_row = first; - *last_row = last; -} diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index 6df5fd9196..fe0123489c 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -7,7 +7,6 @@ obj-$(CONFIG_XEN) += xen_platform.o xen_apic.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o obj-y += kvm/ -obj-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o obj-y += pc-testdev.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/lm32/Makefile.objs b/hw/lm32/Makefile.objs index e328ec8400..a894c466ca 100644 --- a/hw/lm32/Makefile.objs +++ b/hw/lm32/Makefile.objs @@ -9,10 +9,7 @@ obj-y += milkymist-memcard.o obj-y += milkymist-pfpu.o obj-y += milkymist-softusb.o obj-y += milkymist-sysctl.o -obj-$(CONFIG_GLX) += milkymist-tmu2.o obj-y += milkymist-uart.o -obj-y += milkymist-vgafb.o -obj-y += framebuffer.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/milkymist-tmu2.c b/hw/milkymist-tmu2.c deleted file mode 100644 index b723a04cc9..0000000000 --- a/hw/milkymist-tmu2.c +++ /dev/null @@ -1,490 +0,0 @@ -/* - * QEMU model of the Milkymist texture mapping unit. - * - * Copyright (c) 2010 Michael Walle - * Copyright (c) 2010 Sebastien Bourdeauducq - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - * - * Specification available at: - * http://www.milkymist.org/socdoc/tmu2.pdf - * - */ - -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "qemu/error-report.h" - -#include -#include -#include - -enum { - R_CTL = 0, - R_HMESHLAST, - R_VMESHLAST, - R_BRIGHTNESS, - R_CHROMAKEY, - R_VERTICESADDR, - R_TEXFBUF, - R_TEXHRES, - R_TEXVRES, - R_TEXHMASK, - R_TEXVMASK, - R_DSTFBUF, - R_DSTHRES, - R_DSTVRES, - R_DSTHOFFSET, - R_DSTVOFFSET, - R_DSTSQUAREW, - R_DSTSQUAREH, - R_ALPHA, - R_MAX -}; - -enum { - CTL_START_BUSY = (1<<0), - CTL_CHROMAKEY = (1<<1), -}; - -enum { - MAX_BRIGHTNESS = 63, - MAX_ALPHA = 63, -}; - -enum { - MESH_MAXSIZE = 128, -}; - -struct vertex { - int x; - int y; -} QEMU_PACKED; - -struct MilkymistTMU2State { - SysBusDevice busdev; - MemoryRegion regs_region; - CharDriverState *chr; - qemu_irq irq; - - uint32_t regs[R_MAX]; - - Display *dpy; - GLXFBConfig glx_fb_config; - GLXContext glx_context; -}; -typedef struct MilkymistTMU2State MilkymistTMU2State; - -static const int glx_fbconfig_attr[] = { - GLX_GREEN_SIZE, 5, - GLX_GREEN_SIZE, 6, - GLX_BLUE_SIZE, 5, - None -}; - -static int tmu2_glx_init(MilkymistTMU2State *s) -{ - GLXFBConfig *configs; - int nelements; - - s->dpy = XOpenDisplay(NULL); /* FIXME: call XCloseDisplay() */ - if (s->dpy == NULL) { - return 1; - } - - configs = glXChooseFBConfig(s->dpy, 0, glx_fbconfig_attr, &nelements); - if (configs == NULL) { - return 1; - } - - s->glx_fb_config = *configs; - XFree(configs); - - /* FIXME: call glXDestroyContext() */ - s->glx_context = glXCreateNewContext(s->dpy, s->glx_fb_config, - GLX_RGBA_TYPE, NULL, 1); - if (s->glx_context == NULL) { - return 1; - } - - return 0; -} - -static void tmu2_gl_map(struct vertex *mesh, int texhres, int texvres, - int hmeshlast, int vmeshlast, int ho, int vo, int sw, int sh) -{ - int x, y; - int x0, y0, x1, y1; - int u0, v0, u1, v1, u2, v2, u3, v3; - double xscale = 1.0 / ((double)(64 * texhres)); - double yscale = 1.0 / ((double)(64 * texvres)); - - glLoadIdentity(); - glTranslatef(ho, vo, 0); - glEnable(GL_TEXTURE_2D); - glBegin(GL_QUADS); - - for (y = 0; y < vmeshlast; y++) { - y0 = y * sh; - y1 = y0 + sh; - for (x = 0; x < hmeshlast; x++) { - x0 = x * sw; - x1 = x0 + sw; - - u0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].x); - v0 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x].y); - u1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].x); - v1 = be32_to_cpu(mesh[MESH_MAXSIZE * y + x + 1].y); - u2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].x); - v2 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x + 1].y); - u3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].x); - v3 = be32_to_cpu(mesh[MESH_MAXSIZE * (y + 1) + x].y); - - glTexCoord2d(((double)u0) * xscale, ((double)v0) * yscale); - glVertex3i(x0, y0, 0); - glTexCoord2d(((double)u1) * xscale, ((double)v1) * yscale); - glVertex3i(x1, y0, 0); - glTexCoord2d(((double)u2) * xscale, ((double)v2) * yscale); - glVertex3i(x1, y1, 0); - glTexCoord2d(((double)u3) * xscale, ((double)v3) * yscale); - glVertex3i(x0, y1, 0); - } - } - - glEnd(); -} - -static void tmu2_start(MilkymistTMU2State *s) -{ - int pbuffer_attrib[6] = { - GLX_PBUFFER_WIDTH, - 0, - GLX_PBUFFER_HEIGHT, - 0, - GLX_PRESERVED_CONTENTS, - True - }; - - GLXPbuffer pbuffer; - GLuint texture; - void *fb; - hwaddr fb_len; - void *mesh; - hwaddr mesh_len; - float m; - - trace_milkymist_tmu2_start(); - - /* Create and set up a suitable OpenGL context */ - pbuffer_attrib[1] = s->regs[R_DSTHRES]; - pbuffer_attrib[3] = s->regs[R_DSTVRES]; - pbuffer = glXCreatePbuffer(s->dpy, s->glx_fb_config, pbuffer_attrib); - glXMakeContextCurrent(s->dpy, pbuffer, pbuffer, s->glx_context); - - /* Fixup endianness. TODO: would it work on BE hosts? */ - glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); - glPixelStorei(GL_PACK_SWAP_BYTES, 1); - - /* Row alignment */ - glPixelStorei(GL_UNPACK_ALIGNMENT, 2); - glPixelStorei(GL_PACK_ALIGNMENT, 2); - - /* Read the QEMU source framebuffer into an OpenGL texture */ - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - fb_len = 2*s->regs[R_TEXHRES]*s->regs[R_TEXVRES]; - fb = cpu_physical_memory_map(s->regs[R_TEXFBUF], &fb_len, 0); - if (fb == NULL) { - glDeleteTextures(1, &texture); - glXMakeContextCurrent(s->dpy, None, None, NULL); - glXDestroyPbuffer(s->dpy, pbuffer); - return; - } - glTexImage2D(GL_TEXTURE_2D, 0, 3, s->regs[R_TEXHRES], s->regs[R_TEXVRES], - 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, fb); - cpu_physical_memory_unmap(fb, fb_len, 0, fb_len); - - /* Set up texturing options */ - /* WARNING: - * Many cases of TMU2 masking are not supported by OpenGL. - * We only implement the most common ones: - * - full bilinear filtering vs. nearest texel - * - texture clamping vs. texture wrapping - */ - if ((s->regs[R_TEXHMASK] & 0x3f) > 0x20) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } - if ((s->regs[R_TEXHMASK] >> 6) & s->regs[R_TEXHRES]) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - } - if ((s->regs[R_TEXVMASK] >> 6) & s->regs[R_TEXVRES]) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - - /* Translucency and decay */ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - m = (float)(s->regs[R_BRIGHTNESS] + 1) / 64.0f; - glColor4f(m, m, m, (float)(s->regs[R_ALPHA] + 1) / 64.0f); - - /* Read the QEMU dest. framebuffer into the OpenGL framebuffer */ - fb_len = 2 * s->regs[R_DSTHRES] * s->regs[R_DSTVRES]; - fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 0); - if (fb == NULL) { - glDeleteTextures(1, &texture); - glXMakeContextCurrent(s->dpy, None, None, NULL); - glXDestroyPbuffer(s->dpy, pbuffer); - return; - } - - glDrawPixels(s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB, - GL_UNSIGNED_SHORT_5_6_5, fb); - cpu_physical_memory_unmap(fb, fb_len, 0, fb_len); - glViewport(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES]); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0.0, s->regs[R_DSTHRES], 0.0, s->regs[R_DSTVRES], -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - - /* Map the texture */ - mesh_len = MESH_MAXSIZE*MESH_MAXSIZE*sizeof(struct vertex); - mesh = cpu_physical_memory_map(s->regs[R_VERTICESADDR], &mesh_len, 0); - if (mesh == NULL) { - glDeleteTextures(1, &texture); - glXMakeContextCurrent(s->dpy, None, None, NULL); - glXDestroyPbuffer(s->dpy, pbuffer); - return; - } - - tmu2_gl_map((struct vertex *)mesh, - s->regs[R_TEXHRES], s->regs[R_TEXVRES], - s->regs[R_HMESHLAST], s->regs[R_VMESHLAST], - s->regs[R_DSTHOFFSET], s->regs[R_DSTVOFFSET], - s->regs[R_DSTSQUAREW], s->regs[R_DSTSQUAREH]); - cpu_physical_memory_unmap(mesh, mesh_len, 0, mesh_len); - - /* Write back the OpenGL framebuffer to the QEMU framebuffer */ - fb_len = 2 * s->regs[R_DSTHRES] * s->regs[R_DSTVRES]; - fb = cpu_physical_memory_map(s->regs[R_DSTFBUF], &fb_len, 1); - if (fb == NULL) { - glDeleteTextures(1, &texture); - glXMakeContextCurrent(s->dpy, None, None, NULL); - glXDestroyPbuffer(s->dpy, pbuffer); - return; - } - - glReadPixels(0, 0, s->regs[R_DSTHRES], s->regs[R_DSTVRES], GL_RGB, - GL_UNSIGNED_SHORT_5_6_5, fb); - cpu_physical_memory_unmap(fb, fb_len, 1, fb_len); - - /* Free OpenGL allocs */ - glDeleteTextures(1, &texture); - glXMakeContextCurrent(s->dpy, None, None, NULL); - glXDestroyPbuffer(s->dpy, pbuffer); - - s->regs[R_CTL] &= ~CTL_START_BUSY; - - trace_milkymist_tmu2_pulse_irq(); - qemu_irq_pulse(s->irq); -} - -static uint64_t tmu2_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistTMU2State *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_CTL: - case R_HMESHLAST: - case R_VMESHLAST: - case R_BRIGHTNESS: - case R_CHROMAKEY: - case R_VERTICESADDR: - case R_TEXFBUF: - case R_TEXHRES: - case R_TEXVRES: - case R_TEXHMASK: - case R_TEXVMASK: - case R_DSTFBUF: - case R_DSTHRES: - case R_DSTVRES: - case R_DSTHOFFSET: - case R_DSTVOFFSET: - case R_DSTSQUAREW: - case R_DSTSQUAREH: - case R_ALPHA: - r = s->regs[addr]; - break; - - default: - error_report("milkymist_tmu2: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_tmu2_memory_read(addr << 2, r); - - return r; -} - -static void tmu2_check_registers(MilkymistTMU2State *s) -{ - if (s->regs[R_BRIGHTNESS] > MAX_BRIGHTNESS) { - error_report("milkymist_tmu2: max brightness is %d", MAX_BRIGHTNESS); - } - - if (s->regs[R_ALPHA] > MAX_ALPHA) { - error_report("milkymist_tmu2: max alpha is %d", MAX_ALPHA); - } - - if (s->regs[R_VERTICESADDR] & 0x07) { - error_report("milkymist_tmu2: vertex mesh address has to be 64-bit " - "aligned"); - } - - if (s->regs[R_TEXFBUF] & 0x01) { - error_report("milkymist_tmu2: texture buffer address has to be " - "16-bit aligned"); - } -} - -static void tmu2_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistTMU2State *s = opaque; - - trace_milkymist_tmu2_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_CTL: - s->regs[addr] = value; - if (value & CTL_START_BUSY) { - tmu2_start(s); - } - break; - case R_BRIGHTNESS: - case R_HMESHLAST: - case R_VMESHLAST: - case R_CHROMAKEY: - case R_VERTICESADDR: - case R_TEXFBUF: - case R_TEXHRES: - case R_TEXVRES: - case R_TEXHMASK: - case R_TEXVMASK: - case R_DSTFBUF: - case R_DSTHRES: - case R_DSTVRES: - case R_DSTHOFFSET: - case R_DSTVOFFSET: - case R_DSTSQUAREW: - case R_DSTSQUAREH: - case R_ALPHA: - s->regs[addr] = value; - break; - - default: - error_report("milkymist_tmu2: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - tmu2_check_registers(s); -} - -static const MemoryRegionOps tmu2_mmio_ops = { - .read = tmu2_read, - .write = tmu2_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void milkymist_tmu2_reset(DeviceState *d) -{ - MilkymistTMU2State *s = container_of(d, MilkymistTMU2State, busdev.qdev); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } -} - -static int milkymist_tmu2_init(SysBusDevice *dev) -{ - MilkymistTMU2State *s = FROM_SYSBUS(typeof(*s), dev); - - if (tmu2_glx_init(s)) { - return 1; - } - - sysbus_init_irq(dev, &s->irq); - - memory_region_init_io(&s->regs_region, &tmu2_mmio_ops, s, - "milkymist-tmu2", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - return 0; -} - -static const VMStateDescription vmstate_milkymist_tmu2 = { - .name = "milkymist-tmu2", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistTMU2State, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static void milkymist_tmu2_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_tmu2_init; - dc->reset = milkymist_tmu2_reset; - dc->vmsd = &vmstate_milkymist_tmu2; -} - -static const TypeInfo milkymist_tmu2_info = { - .name = "milkymist-tmu2", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistTMU2State), - .class_init = milkymist_tmu2_class_init, -}; - -static void milkymist_tmu2_register_types(void) -{ - type_register_static(&milkymist_tmu2_info); -} - -type_init(milkymist_tmu2_register_types) diff --git a/hw/milkymist-vgafb.c b/hw/milkymist-vgafb.c deleted file mode 100644 index 98762ecd21..0000000000 --- a/hw/milkymist-vgafb.c +++ /dev/null @@ -1,335 +0,0 @@ - -/* - * QEMU model of the Milkymist VGA framebuffer. - * - * Copyright (c) 2010-2012 Michael Walle - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - * - * Specification available at: - * http://www.milkymist.org/socdoc/vgafb.pdf - */ - -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "ui/console.h" -#include "hw/framebuffer.h" -#include "ui/pixel_ops.h" -#include "qemu/error-report.h" - -#define BITS 8 -#include "hw/milkymist-vgafb_template.h" -#define BITS 15 -#include "hw/milkymist-vgafb_template.h" -#define BITS 16 -#include "hw/milkymist-vgafb_template.h" -#define BITS 24 -#include "hw/milkymist-vgafb_template.h" -#define BITS 32 -#include "hw/milkymist-vgafb_template.h" - -enum { - R_CTRL = 0, - R_HRES, - R_HSYNC_START, - R_HSYNC_END, - R_HSCAN, - R_VRES, - R_VSYNC_START, - R_VSYNC_END, - R_VSCAN, - R_BASEADDRESS, - R_BASEADDRESS_ACT, - R_BURST_COUNT, - R_DDC, - R_SOURCE_CLOCK, - R_MAX -}; - -enum { - CTRL_RESET = (1<<0), -}; - -struct MilkymistVgafbState { - SysBusDevice busdev; - MemoryRegion regs_region; - QemuConsole *con; - - int invalidate; - uint32_t fb_offset; - uint32_t fb_mask; - - uint32_t regs[R_MAX]; -}; -typedef struct MilkymistVgafbState MilkymistVgafbState; - -static int vgafb_enabled(MilkymistVgafbState *s) -{ - return !(s->regs[R_CTRL] & CTRL_RESET); -} - -static void vgafb_update_display(void *opaque) -{ - MilkymistVgafbState *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - int first = 0; - int last = 0; - drawfn fn; - - if (!vgafb_enabled(s)) { - return; - } - - int dest_width = s->regs[R_HRES]; - - switch (surface_bits_per_pixel(surface)) { - case 0: - return; - case 8: - fn = draw_line_8; - break; - case 15: - fn = draw_line_15; - dest_width *= 2; - break; - case 16: - fn = draw_line_16; - dest_width *= 2; - break; - case 24: - fn = draw_line_24; - dest_width *= 3; - break; - case 32: - fn = draw_line_32; - dest_width *= 4; - break; - default: - hw_error("milkymist_vgafb: bad color depth\n"); - break; - } - - framebuffer_update_display(surface, sysbus_address_space(&s->busdev), - s->regs[R_BASEADDRESS] + s->fb_offset, - s->regs[R_HRES], - s->regs[R_VRES], - s->regs[R_HRES] * 2, - dest_width, - 0, - s->invalidate, - fn, - NULL, - &first, &last); - - if (first >= 0) { - dpy_gfx_update(s->con, 0, first, s->regs[R_HRES], last - first + 1); - } - s->invalidate = 0; -} - -static void vgafb_invalidate_display(void *opaque) -{ - MilkymistVgafbState *s = opaque; - s->invalidate = 1; -} - -static void vgafb_resize(MilkymistVgafbState *s) -{ - if (!vgafb_enabled(s)) { - return; - } - - qemu_console_resize(s->con, s->regs[R_HRES], s->regs[R_VRES]); - s->invalidate = 1; -} - -static uint64_t vgafb_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistVgafbState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_CTRL: - case R_HRES: - case R_HSYNC_START: - case R_HSYNC_END: - case R_HSCAN: - case R_VRES: - case R_VSYNC_START: - case R_VSYNC_END: - case R_VSCAN: - case R_BASEADDRESS: - case R_BURST_COUNT: - case R_DDC: - case R_SOURCE_CLOCK: - r = s->regs[addr]; - break; - case R_BASEADDRESS_ACT: - r = s->regs[R_BASEADDRESS]; - break; - - default: - error_report("milkymist_vgafb: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_vgafb_memory_read(addr << 2, r); - - return r; -} - -static void vgafb_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistVgafbState *s = opaque; - - trace_milkymist_vgafb_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_CTRL: - s->regs[addr] = value; - vgafb_resize(s); - break; - case R_HSYNC_START: - case R_HSYNC_END: - case R_HSCAN: - case R_VSYNC_START: - case R_VSYNC_END: - case R_VSCAN: - case R_BURST_COUNT: - case R_DDC: - case R_SOURCE_CLOCK: - s->regs[addr] = value; - break; - case R_BASEADDRESS: - if (value & 0x1f) { - error_report("milkymist_vgafb: framebuffer base address have to " - "be 32 byte aligned"); - break; - } - s->regs[addr] = value & s->fb_mask; - s->invalidate = 1; - break; - case R_HRES: - case R_VRES: - s->regs[addr] = value; - vgafb_resize(s); - break; - case R_BASEADDRESS_ACT: - error_report("milkymist_vgafb: write to read-only register 0x" - TARGET_FMT_plx, addr << 2); - break; - - default: - error_report("milkymist_vgafb: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } -} - -static const MemoryRegionOps vgafb_mmio_ops = { - .read = vgafb_read, - .write = vgafb_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void milkymist_vgafb_reset(DeviceState *d) -{ - MilkymistVgafbState *s = container_of(d, MilkymistVgafbState, busdev.qdev); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - - /* defaults */ - s->regs[R_CTRL] = CTRL_RESET; - s->regs[R_HRES] = 640; - s->regs[R_VRES] = 480; - s->regs[R_BASEADDRESS] = 0; -} - -static int milkymist_vgafb_init(SysBusDevice *dev) -{ - MilkymistVgafbState *s = FROM_SYSBUS(typeof(*s), dev); - - memory_region_init_io(&s->regs_region, &vgafb_mmio_ops, s, - "milkymist-vgafb", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - s->con = graphic_console_init(vgafb_update_display, - vgafb_invalidate_display, - NULL, NULL, s); - - return 0; -} - -static int vgafb_post_load(void *opaque, int version_id) -{ - vgafb_invalidate_display(opaque); - return 0; -} - -static const VMStateDescription vmstate_milkymist_vgafb = { - .name = "milkymist-vgafb", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .post_load = vgafb_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistVgafbState, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static Property milkymist_vgafb_properties[] = { - DEFINE_PROP_UINT32("fb_offset", MilkymistVgafbState, fb_offset, 0x0), - DEFINE_PROP_UINT32("fb_mask", MilkymistVgafbState, fb_mask, 0xffffffff), - DEFINE_PROP_END_OF_LIST(), -}; - -static void milkymist_vgafb_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_vgafb_init; - dc->reset = milkymist_vgafb_reset; - dc->vmsd = &vmstate_milkymist_vgafb; - dc->props = milkymist_vgafb_properties; -} - -static const TypeInfo milkymist_vgafb_info = { - .name = "milkymist-vgafb", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistVgafbState), - .class_init = milkymist_vgafb_class_init, -}; - -static void milkymist_vgafb_register_types(void) -{ - type_register_static(&milkymist_vgafb_info); -} - -type_init(milkymist_vgafb_register_types) diff --git a/hw/milkymist-vgafb_template.h b/hw/milkymist-vgafb_template.h index 1d33ee8b50..e0036e16cf 100644 --- a/hw/milkymist-vgafb_template.h +++ b/hw/milkymist-vgafb_template.h @@ -61,7 +61,7 @@ static void glue(draw_line_, BITS)(void *opaque, uint8_t *d, const uint8_t *s, uint8_t r, g, b; while (width--) { - rgb565 = lduw_raw(s); + memcpy(&rgb565, s, sizeof(rgb565)); r = ((rgb565 >> 11) & 0x1f) << 3; g = ((rgb565 >> 5) & 0x3f) << 2; b = ((rgb565 >> 0) & 0x1f) << 3; diff --git a/hw/omap_dss.c b/hw/omap_dss.c deleted file mode 100644 index ea3afcef5e..0000000000 --- a/hw/omap_dss.c +++ /dev/null @@ -1,1086 +0,0 @@ -/* - * OMAP2 Display Subsystem. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski - * - * 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 or - * (at your option) version 3 of the License. - * - * 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, see . - */ -#include "hw/hw.h" -#include "ui/console.h" -#include "hw/arm/omap.h" - -struct omap_dss_s { - qemu_irq irq; - qemu_irq drq; - DisplayState *state; - MemoryRegion iomem_diss1, iomem_disc1, iomem_rfbi1, iomem_venc1, iomem_im3; - - int autoidle; - int control; - int enable; - - struct omap_dss_panel_s { - int enable; - int nx; - int ny; - - int x; - int y; - } dig, lcd; - - struct { - uint32_t idlemode; - uint32_t irqst; - uint32_t irqen; - uint32_t control; - uint32_t config; - uint32_t capable; - uint32_t timing[4]; - int line; - uint32_t bg[2]; - uint32_t trans[2]; - - struct omap_dss_plane_s { - int enable; - int bpp; - int posx; - int posy; - int nx; - int ny; - - hwaddr addr[3]; - - uint32_t attr; - uint32_t tresh; - int rowinc; - int colinc; - int wininc; - } l[3]; - - int invalidate; - uint16_t palette[256]; - } dispc; - - struct { - int idlemode; - uint32_t control; - int enable; - int pixels; - int busy; - int skiplines; - uint16_t rxbuf; - uint32_t config[2]; - uint32_t time[4]; - uint32_t data[6]; - uint16_t vsync; - uint16_t hsync; - struct rfbi_chip_s *chip[2]; - } rfbi; -}; - -static void omap_dispc_interrupt_update(struct omap_dss_s *s) -{ - qemu_set_irq(s->irq, s->dispc.irqst & s->dispc.irqen); -} - -static void omap_rfbi_reset(struct omap_dss_s *s) -{ - s->rfbi.idlemode = 0; - s->rfbi.control = 2; - s->rfbi.enable = 0; - s->rfbi.pixels = 0; - s->rfbi.skiplines = 0; - s->rfbi.busy = 0; - s->rfbi.config[0] = 0x00310000; - s->rfbi.config[1] = 0x00310000; - s->rfbi.time[0] = 0; - s->rfbi.time[1] = 0; - s->rfbi.time[2] = 0; - s->rfbi.time[3] = 0; - s->rfbi.data[0] = 0; - s->rfbi.data[1] = 0; - s->rfbi.data[2] = 0; - s->rfbi.data[3] = 0; - s->rfbi.data[4] = 0; - s->rfbi.data[5] = 0; - s->rfbi.vsync = 0; - s->rfbi.hsync = 0; -} - -void omap_dss_reset(struct omap_dss_s *s) -{ - s->autoidle = 0; - s->control = 0; - s->enable = 0; - - s->dig.enable = 0; - s->dig.nx = 1; - s->dig.ny = 1; - - s->lcd.enable = 0; - s->lcd.nx = 1; - s->lcd.ny = 1; - - s->dispc.idlemode = 0; - s->dispc.irqst = 0; - s->dispc.irqen = 0; - s->dispc.control = 0; - s->dispc.config = 0; - s->dispc.capable = 0x161; - s->dispc.timing[0] = 0; - s->dispc.timing[1] = 0; - s->dispc.timing[2] = 0; - s->dispc.timing[3] = 0; - s->dispc.line = 0; - s->dispc.bg[0] = 0; - s->dispc.bg[1] = 0; - s->dispc.trans[0] = 0; - s->dispc.trans[1] = 0; - - s->dispc.l[0].enable = 0; - s->dispc.l[0].bpp = 0; - s->dispc.l[0].addr[0] = 0; - s->dispc.l[0].addr[1] = 0; - s->dispc.l[0].addr[2] = 0; - s->dispc.l[0].posx = 0; - s->dispc.l[0].posy = 0; - s->dispc.l[0].nx = 1; - s->dispc.l[0].ny = 1; - s->dispc.l[0].attr = 0; - s->dispc.l[0].tresh = 0; - s->dispc.l[0].rowinc = 1; - s->dispc.l[0].colinc = 1; - s->dispc.l[0].wininc = 0; - - omap_rfbi_reset(s); - omap_dispc_interrupt_update(s); -} - -static uint64_t omap_diss_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* DSS_REVISIONNUMBER */ - return 0x20; - - case 0x10: /* DSS_SYSCONFIG */ - return s->autoidle; - - case 0x14: /* DSS_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x40: /* DSS_CONTROL */ - return s->control; - - case 0x50: /* DSS_PSA_LCD_REG_1 */ - case 0x54: /* DSS_PSA_LCD_REG_2 */ - case 0x58: /* DSS_PSA_VIDEO_REG */ - /* TODO: fake some values when appropriate s->control bits are set */ - return 0; - - case 0x5c: /* DSS_STATUS */ - return 1 + (s->control & 1); - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_diss_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); - } - - switch (addr) { - case 0x00: /* DSS_REVISIONNUMBER */ - case 0x14: /* DSS_SYSSTATUS */ - case 0x50: /* DSS_PSA_LCD_REG_1 */ - case 0x54: /* DSS_PSA_LCD_REG_2 */ - case 0x58: /* DSS_PSA_VIDEO_REG */ - case 0x5c: /* DSS_STATUS */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* DSS_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ - omap_dss_reset(s); - s->autoidle = value & 1; - break; - - case 0x40: /* DSS_CONTROL */ - s->control = value & 0x3dd; - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_diss_ops = { - .read = omap_diss_read, - .write = omap_diss_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t omap_disc_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x000: /* DISPC_REVISION */ - return 0x20; - - case 0x010: /* DISPC_SYSCONFIG */ - return s->dispc.idlemode; - - case 0x014: /* DISPC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x018: /* DISPC_IRQSTATUS */ - return s->dispc.irqst; - - case 0x01c: /* DISPC_IRQENABLE */ - return s->dispc.irqen; - - case 0x040: /* DISPC_CONTROL */ - return s->dispc.control; - - case 0x044: /* DISPC_CONFIG */ - return s->dispc.config; - - case 0x048: /* DISPC_CAPABLE */ - return s->dispc.capable; - - case 0x04c: /* DISPC_DEFAULT_COLOR0 */ - return s->dispc.bg[0]; - case 0x050: /* DISPC_DEFAULT_COLOR1 */ - return s->dispc.bg[1]; - case 0x054: /* DISPC_TRANS_COLOR0 */ - return s->dispc.trans[0]; - case 0x058: /* DISPC_TRANS_COLOR1 */ - return s->dispc.trans[1]; - - case 0x05c: /* DISPC_LINE_STATUS */ - return 0x7ff; - case 0x060: /* DISPC_LINE_NUMBER */ - return s->dispc.line; - - case 0x064: /* DISPC_TIMING_H */ - return s->dispc.timing[0]; - case 0x068: /* DISPC_TIMING_V */ - return s->dispc.timing[1]; - case 0x06c: /* DISPC_POL_FREQ */ - return s->dispc.timing[2]; - case 0x070: /* DISPC_DIVISOR */ - return s->dispc.timing[3]; - - case 0x078: /* DISPC_SIZE_DIG */ - return ((s->dig.ny - 1) << 16) | (s->dig.nx - 1); - case 0x07c: /* DISPC_SIZE_LCD */ - return ((s->lcd.ny - 1) << 16) | (s->lcd.nx - 1); - - case 0x080: /* DISPC_GFX_BA0 */ - return s->dispc.l[0].addr[0]; - case 0x084: /* DISPC_GFX_BA1 */ - return s->dispc.l[0].addr[1]; - case 0x088: /* DISPC_GFX_POSITION */ - return (s->dispc.l[0].posy << 16) | s->dispc.l[0].posx; - case 0x08c: /* DISPC_GFX_SIZE */ - return ((s->dispc.l[0].ny - 1) << 16) | (s->dispc.l[0].nx - 1); - case 0x0a0: /* DISPC_GFX_ATTRIBUTES */ - return s->dispc.l[0].attr; - case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */ - return s->dispc.l[0].tresh; - case 0x0a8: /* DISPC_GFX_FIFO_SIZE_STATUS */ - return 256; - case 0x0ac: /* DISPC_GFX_ROW_INC */ - return s->dispc.l[0].rowinc; - case 0x0b0: /* DISPC_GFX_PIXEL_INC */ - return s->dispc.l[0].colinc; - case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */ - return s->dispc.l[0].wininc; - case 0x0b8: /* DISPC_GFX_TABLE_BA */ - return s->dispc.l[0].addr[2]; - - case 0x0bc: /* DISPC_VID1_BA0 */ - case 0x0c0: /* DISPC_VID1_BA1 */ - case 0x0c4: /* DISPC_VID1_POSITION */ - case 0x0c8: /* DISPC_VID1_SIZE */ - case 0x0cc: /* DISPC_VID1_ATTRIBUTES */ - case 0x0d0: /* DISPC_VID1_FIFO_TRESHOLD */ - case 0x0d4: /* DISPC_VID1_FIFO_SIZE_STATUS */ - case 0x0d8: /* DISPC_VID1_ROW_INC */ - case 0x0dc: /* DISPC_VID1_PIXEL_INC */ - case 0x0e0: /* DISPC_VID1_FIR */ - case 0x0e4: /* DISPC_VID1_PICTURE_SIZE */ - case 0x0e8: /* DISPC_VID1_ACCU0 */ - case 0x0ec: /* DISPC_VID1_ACCU1 */ - case 0x0f0 ... 0x140: /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */ - case 0x14c: /* DISPC_VID2_BA0 */ - case 0x150: /* DISPC_VID2_BA1 */ - case 0x154: /* DISPC_VID2_POSITION */ - case 0x158: /* DISPC_VID2_SIZE */ - case 0x15c: /* DISPC_VID2_ATTRIBUTES */ - case 0x160: /* DISPC_VID2_FIFO_TRESHOLD */ - case 0x164: /* DISPC_VID2_FIFO_SIZE_STATUS */ - case 0x168: /* DISPC_VID2_ROW_INC */ - case 0x16c: /* DISPC_VID2_PIXEL_INC */ - case 0x170: /* DISPC_VID2_FIR */ - case 0x174: /* DISPC_VID2_PICTURE_SIZE */ - case 0x178: /* DISPC_VID2_ACCU0 */ - case 0x17c: /* DISPC_VID2_ACCU1 */ - case 0x180 ... 0x1d0: /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */ - case 0x1d4: /* DISPC_DATA_CYCLE1 */ - case 0x1d8: /* DISPC_DATA_CYCLE2 */ - case 0x1dc: /* DISPC_DATA_CYCLE3 */ - return 0; - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_disc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); - } - - switch (addr) { - case 0x010: /* DISPC_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ - omap_dss_reset(s); - s->dispc.idlemode = value & 0x301b; - break; - - case 0x018: /* DISPC_IRQSTATUS */ - s->dispc.irqst &= ~value; - omap_dispc_interrupt_update(s); - break; - - case 0x01c: /* DISPC_IRQENABLE */ - s->dispc.irqen = value & 0xffff; - omap_dispc_interrupt_update(s); - break; - - case 0x040: /* DISPC_CONTROL */ - s->dispc.control = value & 0x07ff9fff; - s->dig.enable = (value >> 1) & 1; - s->lcd.enable = (value >> 0) & 1; - if (value & (1 << 12)) /* OVERLAY_OPTIMIZATION */ - if (!((s->dispc.l[1].attr | s->dispc.l[2].attr) & 1)) { - fprintf(stderr, "%s: Overlay Optimization when no overlay " - "region effectively exists leads to " - "unpredictable behaviour!\n", __func__); - } - if (value & (1 << 6)) { /* GODIGITAL */ - /* XXX: Shadowed fields are: - * s->dispc.config - * s->dispc.capable - * s->dispc.bg[0] - * s->dispc.bg[1] - * s->dispc.trans[0] - * s->dispc.trans[1] - * s->dispc.line - * s->dispc.timing[0] - * s->dispc.timing[1] - * s->dispc.timing[2] - * s->dispc.timing[3] - * s->lcd.nx - * s->lcd.ny - * s->dig.nx - * s->dig.ny - * s->dispc.l[0].addr[0] - * s->dispc.l[0].addr[1] - * s->dispc.l[0].addr[2] - * s->dispc.l[0].posx - * s->dispc.l[0].posy - * s->dispc.l[0].nx - * s->dispc.l[0].ny - * s->dispc.l[0].tresh - * s->dispc.l[0].rowinc - * s->dispc.l[0].colinc - * s->dispc.l[0].wininc - * All they need to be loaded here from their shadow registers. - */ - } - if (value & (1 << 5)) { /* GOLCD */ - /* XXX: Likewise for LCD here. */ - } - s->dispc.invalidate = 1; - break; - - case 0x044: /* DISPC_CONFIG */ - s->dispc.config = value & 0x3fff; - /* XXX: - * bits 2:1 (LOADMODE) reset to 0 after set to 1 and palette loaded - * bits 2:1 (LOADMODE) reset to 2 after set to 3 and palette loaded - */ - s->dispc.invalidate = 1; - break; - - case 0x048: /* DISPC_CAPABLE */ - s->dispc.capable = value & 0x3ff; - break; - - case 0x04c: /* DISPC_DEFAULT_COLOR0 */ - s->dispc.bg[0] = value & 0xffffff; - s->dispc.invalidate = 1; - break; - case 0x050: /* DISPC_DEFAULT_COLOR1 */ - s->dispc.bg[1] = value & 0xffffff; - s->dispc.invalidate = 1; - break; - case 0x054: /* DISPC_TRANS_COLOR0 */ - s->dispc.trans[0] = value & 0xffffff; - s->dispc.invalidate = 1; - break; - case 0x058: /* DISPC_TRANS_COLOR1 */ - s->dispc.trans[1] = value & 0xffffff; - s->dispc.invalidate = 1; - break; - - case 0x060: /* DISPC_LINE_NUMBER */ - s->dispc.line = value & 0x7ff; - break; - - case 0x064: /* DISPC_TIMING_H */ - s->dispc.timing[0] = value & 0x0ff0ff3f; - break; - case 0x068: /* DISPC_TIMING_V */ - s->dispc.timing[1] = value & 0x0ff0ff3f; - break; - case 0x06c: /* DISPC_POL_FREQ */ - s->dispc.timing[2] = value & 0x0003ffff; - break; - case 0x070: /* DISPC_DIVISOR */ - s->dispc.timing[3] = value & 0x00ff00ff; - break; - - case 0x078: /* DISPC_SIZE_DIG */ - s->dig.nx = ((value >> 0) & 0x7ff) + 1; /* PPL */ - s->dig.ny = ((value >> 16) & 0x7ff) + 1; /* LPP */ - s->dispc.invalidate = 1; - break; - case 0x07c: /* DISPC_SIZE_LCD */ - s->lcd.nx = ((value >> 0) & 0x7ff) + 1; /* PPL */ - s->lcd.ny = ((value >> 16) & 0x7ff) + 1; /* LPP */ - s->dispc.invalidate = 1; - break; - case 0x080: /* DISPC_GFX_BA0 */ - s->dispc.l[0].addr[0] = (hwaddr) value; - s->dispc.invalidate = 1; - break; - case 0x084: /* DISPC_GFX_BA1 */ - s->dispc.l[0].addr[1] = (hwaddr) value; - s->dispc.invalidate = 1; - break; - case 0x088: /* DISPC_GFX_POSITION */ - s->dispc.l[0].posx = ((value >> 0) & 0x7ff); /* GFXPOSX */ - s->dispc.l[0].posy = ((value >> 16) & 0x7ff); /* GFXPOSY */ - s->dispc.invalidate = 1; - break; - case 0x08c: /* DISPC_GFX_SIZE */ - s->dispc.l[0].nx = ((value >> 0) & 0x7ff) + 1; /* GFXSIZEX */ - s->dispc.l[0].ny = ((value >> 16) & 0x7ff) + 1; /* GFXSIZEY */ - s->dispc.invalidate = 1; - break; - case 0x0a0: /* DISPC_GFX_ATTRIBUTES */ - s->dispc.l[0].attr = value & 0x7ff; - if (value & (3 << 9)) - fprintf(stderr, "%s: Big-endian pixel format not supported\n", - __FUNCTION__); - s->dispc.l[0].enable = value & 1; - s->dispc.l[0].bpp = (value >> 1) & 0xf; - s->dispc.invalidate = 1; - break; - case 0x0a4: /* DISPC_GFX_FIFO_TRESHOLD */ - s->dispc.l[0].tresh = value & 0x01ff01ff; - break; - case 0x0ac: /* DISPC_GFX_ROW_INC */ - s->dispc.l[0].rowinc = value; - s->dispc.invalidate = 1; - break; - case 0x0b0: /* DISPC_GFX_PIXEL_INC */ - s->dispc.l[0].colinc = value; - s->dispc.invalidate = 1; - break; - case 0x0b4: /* DISPC_GFX_WINDOW_SKIP */ - s->dispc.l[0].wininc = value; - break; - case 0x0b8: /* DISPC_GFX_TABLE_BA */ - s->dispc.l[0].addr[2] = (hwaddr) value; - s->dispc.invalidate = 1; - break; - - case 0x0bc: /* DISPC_VID1_BA0 */ - case 0x0c0: /* DISPC_VID1_BA1 */ - case 0x0c4: /* DISPC_VID1_POSITION */ - case 0x0c8: /* DISPC_VID1_SIZE */ - case 0x0cc: /* DISPC_VID1_ATTRIBUTES */ - case 0x0d0: /* DISPC_VID1_FIFO_TRESHOLD */ - case 0x0d8: /* DISPC_VID1_ROW_INC */ - case 0x0dc: /* DISPC_VID1_PIXEL_INC */ - case 0x0e0: /* DISPC_VID1_FIR */ - case 0x0e4: /* DISPC_VID1_PICTURE_SIZE */ - case 0x0e8: /* DISPC_VID1_ACCU0 */ - case 0x0ec: /* DISPC_VID1_ACCU1 */ - case 0x0f0 ... 0x140: /* DISPC_VID1_FIR_COEF, DISPC_VID1_CONV_COEF */ - case 0x14c: /* DISPC_VID2_BA0 */ - case 0x150: /* DISPC_VID2_BA1 */ - case 0x154: /* DISPC_VID2_POSITION */ - case 0x158: /* DISPC_VID2_SIZE */ - case 0x15c: /* DISPC_VID2_ATTRIBUTES */ - case 0x160: /* DISPC_VID2_FIFO_TRESHOLD */ - case 0x168: /* DISPC_VID2_ROW_INC */ - case 0x16c: /* DISPC_VID2_PIXEL_INC */ - case 0x170: /* DISPC_VID2_FIR */ - case 0x174: /* DISPC_VID2_PICTURE_SIZE */ - case 0x178: /* DISPC_VID2_ACCU0 */ - case 0x17c: /* DISPC_VID2_ACCU1 */ - case 0x180 ... 0x1d0: /* DISPC_VID2_FIR_COEF, DISPC_VID2_CONV_COEF */ - case 0x1d4: /* DISPC_DATA_CYCLE1 */ - case 0x1d8: /* DISPC_DATA_CYCLE2 */ - case 0x1dc: /* DISPC_DATA_CYCLE3 */ - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_disc_ops = { - .read = omap_disc_read, - .write = omap_disc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void omap_rfbi_transfer_stop(struct omap_dss_s *s) -{ - if (!s->rfbi.busy) - return; - - /* TODO: in non-Bypass mode we probably need to just deassert the DRQ. */ - - s->rfbi.busy = 0; -} - -static void omap_rfbi_transfer_start(struct omap_dss_s *s) -{ - void *data; - hwaddr len; - hwaddr data_addr; - int pitch; - static void *bounce_buffer; - static hwaddr bounce_len; - - if (!s->rfbi.enable || s->rfbi.busy) - return; - - if (s->rfbi.control & (1 << 1)) { /* BYPASS */ - /* TODO: in non-Bypass mode we probably need to just assert the - * DRQ and wait for DMA to write the pixels. */ - fprintf(stderr, "%s: Bypass mode unimplemented\n", __FUNCTION__); - return; - } - - if (!(s->dispc.control & (1 << 11))) /* RFBIMODE */ - return; - /* TODO: check that LCD output is enabled in DISPC. */ - - s->rfbi.busy = 1; - - len = s->rfbi.pixels * 2; - - data_addr = s->dispc.l[0].addr[0]; - data = cpu_physical_memory_map(data_addr, &len, 0); - if (data && len != s->rfbi.pixels * 2) { - cpu_physical_memory_unmap(data, len, 0, 0); - data = NULL; - len = s->rfbi.pixels * 2; - } - if (!data) { - if (len > bounce_len) { - bounce_buffer = g_realloc(bounce_buffer, len); - } - data = bounce_buffer; - cpu_physical_memory_read(data_addr, data, len); - } - - /* TODO bpp */ - s->rfbi.pixels = 0; - - /* TODO: negative values */ - pitch = s->dispc.l[0].nx + (s->dispc.l[0].rowinc - 1) / 2; - - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) - s->rfbi.chip[0]->block(s->rfbi.chip[0]->opaque, 1, data, len, pitch); - if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) - s->rfbi.chip[1]->block(s->rfbi.chip[1]->opaque, 1, data, len, pitch); - - if (data != bounce_buffer) { - cpu_physical_memory_unmap(data, len, 0, len); - } - - omap_rfbi_transfer_stop(s); - - /* TODO */ - s->dispc.irqst |= 1; /* FRAMEDONE */ - omap_dispc_interrupt_update(s); -} - -static uint64_t omap_rfbi_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* RFBI_REVISION */ - return 0x10; - - case 0x10: /* RFBI_SYSCONFIG */ - return s->rfbi.idlemode; - - case 0x14: /* RFBI_SYSSTATUS */ - return 1 | (s->rfbi.busy << 8); /* RESETDONE */ - - case 0x40: /* RFBI_CONTROL */ - return s->rfbi.control; - - case 0x44: /* RFBI_PIXELCNT */ - return s->rfbi.pixels; - - case 0x48: /* RFBI_LINE_NUMBER */ - return s->rfbi.skiplines; - - case 0x58: /* RFBI_READ */ - case 0x5c: /* RFBI_STATUS */ - return s->rfbi.rxbuf; - - case 0x60: /* RFBI_CONFIG0 */ - return s->rfbi.config[0]; - case 0x64: /* RFBI_ONOFF_TIME0 */ - return s->rfbi.time[0]; - case 0x68: /* RFBI_CYCLE_TIME0 */ - return s->rfbi.time[1]; - case 0x6c: /* RFBI_DATA_CYCLE1_0 */ - return s->rfbi.data[0]; - case 0x70: /* RFBI_DATA_CYCLE2_0 */ - return s->rfbi.data[1]; - case 0x74: /* RFBI_DATA_CYCLE3_0 */ - return s->rfbi.data[2]; - - case 0x78: /* RFBI_CONFIG1 */ - return s->rfbi.config[1]; - case 0x7c: /* RFBI_ONOFF_TIME1 */ - return s->rfbi.time[2]; - case 0x80: /* RFBI_CYCLE_TIME1 */ - return s->rfbi.time[3]; - case 0x84: /* RFBI_DATA_CYCLE1_1 */ - return s->rfbi.data[3]; - case 0x88: /* RFBI_DATA_CYCLE2_1 */ - return s->rfbi.data[4]; - case 0x8c: /* RFBI_DATA_CYCLE3_1 */ - return s->rfbi.data[5]; - - case 0x90: /* RFBI_VSYNC_WIDTH */ - return s->rfbi.vsync; - case 0x94: /* RFBI_HSYNC_WIDTH */ - return s->rfbi.hsync; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_rfbi_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_dss_s *s = (struct omap_dss_s *) opaque; - - if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); - } - - switch (addr) { - case 0x10: /* RFBI_SYSCONFIG */ - if (value & 2) /* SOFTRESET */ - omap_rfbi_reset(s); - s->rfbi.idlemode = value & 0x19; - break; - - case 0x40: /* RFBI_CONTROL */ - s->rfbi.control = value & 0xf; - s->rfbi.enable = value & 1; - if (value & (1 << 4) && /* ITE */ - !(s->rfbi.config[0] & s->rfbi.config[1] & 0xc)) - omap_rfbi_transfer_start(s); - break; - - case 0x44: /* RFBI_PIXELCNT */ - s->rfbi.pixels = value; - break; - - case 0x48: /* RFBI_LINE_NUMBER */ - s->rfbi.skiplines = value & 0x7ff; - break; - - case 0x4c: /* RFBI_CMD */ - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) - s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 0, value & 0xffff); - if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) - s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 0, value & 0xffff); - break; - case 0x50: /* RFBI_PARAM */ - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) - s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value & 0xffff); - if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) - s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value & 0xffff); - break; - case 0x54: /* RFBI_DATA */ - /* TODO: take into account the format set up in s->rfbi.config[?] and - * s->rfbi.data[?], but special-case the most usual scenario so that - * speed doesn't suffer. */ - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) { - s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value & 0xffff); - s->rfbi.chip[0]->write(s->rfbi.chip[0]->opaque, 1, value >> 16); - } - if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) { - s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value & 0xffff); - s->rfbi.chip[1]->write(s->rfbi.chip[1]->opaque, 1, value >> 16); - } - if (!-- s->rfbi.pixels) - omap_rfbi_transfer_stop(s); - break; - case 0x58: /* RFBI_READ */ - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) - s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 1); - else if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) - s->rfbi.rxbuf = s->rfbi.chip[1]->read(s->rfbi.chip[1]->opaque, 1); - if (!-- s->rfbi.pixels) - omap_rfbi_transfer_stop(s); - break; - - case 0x5c: /* RFBI_STATUS */ - if ((s->rfbi.control & (1 << 2)) && s->rfbi.chip[0]) - s->rfbi.rxbuf = s->rfbi.chip[0]->read(s->rfbi.chip[0]->opaque, 0); - else if ((s->rfbi.control & (1 << 3)) && s->rfbi.chip[1]) - s->rfbi.rxbuf = s->rfbi.chip[1]->read(s->rfbi.chip[1]->opaque, 0); - if (!-- s->rfbi.pixels) - omap_rfbi_transfer_stop(s); - break; - - case 0x60: /* RFBI_CONFIG0 */ - s->rfbi.config[0] = value & 0x003f1fff; - break; - - case 0x64: /* RFBI_ONOFF_TIME0 */ - s->rfbi.time[0] = value & 0x3fffffff; - break; - case 0x68: /* RFBI_CYCLE_TIME0 */ - s->rfbi.time[1] = value & 0x0fffffff; - break; - case 0x6c: /* RFBI_DATA_CYCLE1_0 */ - s->rfbi.data[0] = value & 0x0f1f0f1f; - break; - case 0x70: /* RFBI_DATA_CYCLE2_0 */ - s->rfbi.data[1] = value & 0x0f1f0f1f; - break; - case 0x74: /* RFBI_DATA_CYCLE3_0 */ - s->rfbi.data[2] = value & 0x0f1f0f1f; - break; - case 0x78: /* RFBI_CONFIG1 */ - s->rfbi.config[1] = value & 0x003f1fff; - break; - - case 0x7c: /* RFBI_ONOFF_TIME1 */ - s->rfbi.time[2] = value & 0x3fffffff; - break; - case 0x80: /* RFBI_CYCLE_TIME1 */ - s->rfbi.time[3] = value & 0x0fffffff; - break; - case 0x84: /* RFBI_DATA_CYCLE1_1 */ - s->rfbi.data[3] = value & 0x0f1f0f1f; - break; - case 0x88: /* RFBI_DATA_CYCLE2_1 */ - s->rfbi.data[4] = value & 0x0f1f0f1f; - break; - case 0x8c: /* RFBI_DATA_CYCLE3_1 */ - s->rfbi.data[5] = value & 0x0f1f0f1f; - break; - - case 0x90: /* RFBI_VSYNC_WIDTH */ - s->rfbi.vsync = value & 0xffff; - break; - case 0x94: /* RFBI_HSYNC_WIDTH */ - s->rfbi.hsync = value & 0xffff; - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_rfbi_ops = { - .read = omap_rfbi_read, - .write = omap_rfbi_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t omap_venc_read(void *opaque, hwaddr addr, - unsigned size) -{ - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x00: /* REV_ID */ - case 0x04: /* STATUS */ - case 0x08: /* F_CONTROL */ - case 0x10: /* VIDOUT_CTRL */ - case 0x14: /* SYNC_CTRL */ - case 0x1c: /* LLEN */ - case 0x20: /* FLENS */ - case 0x24: /* HFLTR_CTRL */ - case 0x28: /* CC_CARR_WSS_CARR */ - case 0x2c: /* C_PHASE */ - case 0x30: /* GAIN_U */ - case 0x34: /* GAIN_V */ - case 0x38: /* GAIN_Y */ - case 0x3c: /* BLACK_LEVEL */ - case 0x40: /* BLANK_LEVEL */ - case 0x44: /* X_COLOR */ - case 0x48: /* M_CONTROL */ - case 0x4c: /* BSTAMP_WSS_DATA */ - case 0x50: /* S_CARR */ - case 0x54: /* LINE21 */ - case 0x58: /* LN_SEL */ - case 0x5c: /* L21__WC_CTL */ - case 0x60: /* HTRIGGER_VTRIGGER */ - case 0x64: /* SAVID__EAVID */ - case 0x68: /* FLEN__FAL */ - case 0x6c: /* LAL__PHASE_RESET */ - case 0x70: /* HS_INT_START_STOP_X */ - case 0x74: /* HS_EXT_START_STOP_X */ - case 0x78: /* VS_INT_START_X */ - case 0x7c: /* VS_INT_STOP_X__VS_INT_START_Y */ - case 0x80: /* VS_INT_STOP_Y__VS_INT_START_X */ - case 0x84: /* VS_EXT_STOP_X__VS_EXT_START_Y */ - case 0x88: /* VS_EXT_STOP_Y */ - case 0x90: /* AVID_START_STOP_X */ - case 0x94: /* AVID_START_STOP_Y */ - case 0xa0: /* FID_INT_START_X__FID_INT_START_Y */ - case 0xa4: /* FID_INT_OFFSET_Y__FID_EXT_START_X */ - case 0xa8: /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */ - case 0xb0: /* TVDETGP_INT_START_STOP_X */ - case 0xb4: /* TVDETGP_INT_START_STOP_Y */ - case 0xb8: /* GEN_CTRL */ - case 0xc4: /* DAC_TST__DAC_A */ - case 0xc8: /* DAC_B__DAC_C */ - return 0; - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_venc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - if (size != 4) { - return omap_badwidth_write32(opaque, addr, size); - } - - switch (addr) { - case 0x08: /* F_CONTROL */ - case 0x10: /* VIDOUT_CTRL */ - case 0x14: /* SYNC_CTRL */ - case 0x1c: /* LLEN */ - case 0x20: /* FLENS */ - case 0x24: /* HFLTR_CTRL */ - case 0x28: /* CC_CARR_WSS_CARR */ - case 0x2c: /* C_PHASE */ - case 0x30: /* GAIN_U */ - case 0x34: /* GAIN_V */ - case 0x38: /* GAIN_Y */ - case 0x3c: /* BLACK_LEVEL */ - case 0x40: /* BLANK_LEVEL */ - case 0x44: /* X_COLOR */ - case 0x48: /* M_CONTROL */ - case 0x4c: /* BSTAMP_WSS_DATA */ - case 0x50: /* S_CARR */ - case 0x54: /* LINE21 */ - case 0x58: /* LN_SEL */ - case 0x5c: /* L21__WC_CTL */ - case 0x60: /* HTRIGGER_VTRIGGER */ - case 0x64: /* SAVID__EAVID */ - case 0x68: /* FLEN__FAL */ - case 0x6c: /* LAL__PHASE_RESET */ - case 0x70: /* HS_INT_START_STOP_X */ - case 0x74: /* HS_EXT_START_STOP_X */ - case 0x78: /* VS_INT_START_X */ - case 0x7c: /* VS_INT_STOP_X__VS_INT_START_Y */ - case 0x80: /* VS_INT_STOP_Y__VS_INT_START_X */ - case 0x84: /* VS_EXT_STOP_X__VS_EXT_START_Y */ - case 0x88: /* VS_EXT_STOP_Y */ - case 0x90: /* AVID_START_STOP_X */ - case 0x94: /* AVID_START_STOP_Y */ - case 0xa0: /* FID_INT_START_X__FID_INT_START_Y */ - case 0xa4: /* FID_INT_OFFSET_Y__FID_EXT_START_X */ - case 0xa8: /* FID_EXT_START_Y__FID_EXT_OFFSET_Y */ - case 0xb0: /* TVDETGP_INT_START_STOP_X */ - case 0xb4: /* TVDETGP_INT_START_STOP_Y */ - case 0xb8: /* GEN_CTRL */ - case 0xc4: /* DAC_TST__DAC_A */ - case 0xc8: /* DAC_B__DAC_C */ - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_venc_ops = { - .read = omap_venc_read, - .write = omap_venc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t omap_im3_read(void *opaque, hwaddr addr, - unsigned size) -{ - if (size != 4) { - return omap_badwidth_read32(opaque, addr); - } - - switch (addr) { - case 0x0a8: /* SBIMERRLOGA */ - case 0x0b0: /* SBIMERRLOG */ - case 0x190: /* SBIMSTATE */ - case 0x198: /* SBTMSTATE_L */ - case 0x19c: /* SBTMSTATE_H */ - case 0x1a8: /* SBIMCONFIG_L */ - case 0x1ac: /* SBIMCONFIG_H */ - case 0x1f8: /* SBID_L */ - case 0x1fc: /* SBID_H */ - return 0; - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_im3_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - if (size != 4) { - return omap_badwidth_write32(opaque, addr, value); - } - - switch (addr) { - case 0x0b0: /* SBIMERRLOG */ - case 0x190: /* SBIMSTATE */ - case 0x198: /* SBTMSTATE_L */ - case 0x19c: /* SBTMSTATE_H */ - case 0x1a8: /* SBIMCONFIG_L */ - case 0x1ac: /* SBIMCONFIG_H */ - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_im3_ops = { - .read = omap_im3_read, - .write = omap_im3_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_dss_s *omap_dss_init(struct omap_target_agent_s *ta, - MemoryRegion *sysmem, - hwaddr l3_base, - qemu_irq irq, qemu_irq drq, - omap_clk fck1, omap_clk fck2, omap_clk ck54m, - omap_clk ick1, omap_clk ick2) -{ - struct omap_dss_s *s = (struct omap_dss_s *) - g_malloc0(sizeof(struct omap_dss_s)); - - s->irq = irq; - s->drq = drq; - omap_dss_reset(s); - - memory_region_init_io(&s->iomem_diss1, &omap_diss_ops, s, "omap.diss1", - omap_l4_region_size(ta, 0)); - memory_region_init_io(&s->iomem_disc1, &omap_disc_ops, s, "omap.disc1", - omap_l4_region_size(ta, 1)); - memory_region_init_io(&s->iomem_rfbi1, &omap_rfbi_ops, s, "omap.rfbi1", - omap_l4_region_size(ta, 2)); - memory_region_init_io(&s->iomem_venc1, &omap_venc_ops, s, "omap.venc1", - omap_l4_region_size(ta, 3)); - memory_region_init_io(&s->iomem_im3, &omap_im3_ops, s, - "omap.im3", 0x1000); - - omap_l4_attach(ta, 0, &s->iomem_diss1); - omap_l4_attach(ta, 1, &s->iomem_disc1); - omap_l4_attach(ta, 2, &s->iomem_rfbi1); - omap_l4_attach(ta, 3, &s->iomem_venc1); - memory_region_add_subregion(sysmem, l3_base, &s->iomem_im3); - -#if 0 - s->state = graphic_console_init(omap_update_display, - omap_invalidate_display, omap_screen_dump, s); -#endif - - return s; -} - -void omap_rfbi_attach(struct omap_dss_s *s, int cs, struct rfbi_chip_s *chip) -{ - if (cs < 0 || cs > 1) - hw_error("%s: wrong CS %i\n", __FUNCTION__, cs); - s->rfbi.chip[cs] = chip; -} diff --git a/hw/omap_lcdc.c b/hw/omap_lcdc.c deleted file mode 100644 index 4048cc102d..0000000000 --- a/hw/omap_lcdc.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * OMAP LCD controller. - * - * Copyright (C) 2006-2007 Andrzej Zaborowski - * - * 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, see . - */ -#include "hw/hw.h" -#include "ui/console.h" -#include "hw/arm/omap.h" -#include "hw/framebuffer.h" -#include "ui/pixel_ops.h" - -struct omap_lcd_panel_s { - MemoryRegion *sysmem; - MemoryRegion iomem; - qemu_irq irq; - QemuConsole *con; - - int plm; - int tft; - int mono; - int enable; - int width; - int height; - int interrupts; - uint32_t timing[3]; - uint32_t subpanel; - uint32_t ctrl; - - struct omap_dma_lcd_channel_s *dma; - uint16_t palette[256]; - int palette_done; - int frame_done; - int invalidate; - int sync_error; -}; - -static void omap_lcd_interrupts(struct omap_lcd_panel_s *s) -{ - if (s->frame_done && (s->interrupts & 1)) { - qemu_irq_raise(s->irq); - return; - } - - if (s->palette_done && (s->interrupts & 2)) { - qemu_irq_raise(s->irq); - return; - } - - if (s->sync_error) { - qemu_irq_raise(s->irq); - return; - } - - qemu_irq_lower(s->irq); -} - -#define draw_line_func drawfn - -#define DEPTH 8 -#include "hw/omap_lcd_template.h" -#define DEPTH 15 -#include "hw/omap_lcd_template.h" -#define DEPTH 16 -#include "hw/omap_lcd_template.h" -#define DEPTH 32 -#include "hw/omap_lcd_template.h" - -static draw_line_func draw_line_table2[33] = { - [0 ... 32] = NULL, - [8] = draw_line2_8, - [15] = draw_line2_15, - [16] = draw_line2_16, - [32] = draw_line2_32, -}, draw_line_table4[33] = { - [0 ... 32] = NULL, - [8] = draw_line4_8, - [15] = draw_line4_15, - [16] = draw_line4_16, - [32] = draw_line4_32, -}, draw_line_table8[33] = { - [0 ... 32] = NULL, - [8] = draw_line8_8, - [15] = draw_line8_15, - [16] = draw_line8_16, - [32] = draw_line8_32, -}, draw_line_table12[33] = { - [0 ... 32] = NULL, - [8] = draw_line12_8, - [15] = draw_line12_15, - [16] = draw_line12_16, - [32] = draw_line12_32, -}, draw_line_table16[33] = { - [0 ... 32] = NULL, - [8] = draw_line16_8, - [15] = draw_line16_15, - [16] = draw_line16_16, - [32] = draw_line16_32, -}; - -static void omap_update_display(void *opaque) -{ - struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque; - DisplaySurface *surface = qemu_console_surface(omap_lcd->con); - draw_line_func draw_line; - int size, height, first, last; - int width, linesize, step, bpp, frame_offset; - hwaddr frame_base; - - if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable || - !surface_bits_per_pixel(surface)) { - return; - } - - frame_offset = 0; - if (omap_lcd->plm != 2) { - cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[ - omap_lcd->dma->current_frame], - (void *)omap_lcd->palette, 0x200); - switch (omap_lcd->palette[0] >> 12 & 7) { - case 3 ... 7: - frame_offset += 0x200; - break; - default: - frame_offset += 0x20; - } - } - - /* Colour depth */ - switch ((omap_lcd->palette[0] >> 12) & 7) { - case 1: - draw_line = draw_line_table2[surface_bits_per_pixel(surface)]; - bpp = 2; - break; - - case 2: - draw_line = draw_line_table4[surface_bits_per_pixel(surface)]; - bpp = 4; - break; - - case 3: - draw_line = draw_line_table8[surface_bits_per_pixel(surface)]; - bpp = 8; - break; - - case 4 ... 7: - if (!omap_lcd->tft) - draw_line = draw_line_table12[surface_bits_per_pixel(surface)]; - else - draw_line = draw_line_table16[surface_bits_per_pixel(surface)]; - bpp = 16; - break; - - default: - /* Unsupported at the moment. */ - return; - } - - /* Resolution */ - width = omap_lcd->width; - if (width != surface_width(surface) || - omap_lcd->height != surface_height(surface)) { - qemu_console_resize(omap_lcd->con, - omap_lcd->width, omap_lcd->height); - surface = qemu_console_surface(omap_lcd->con); - omap_lcd->invalidate = 1; - } - - if (omap_lcd->dma->current_frame == 0) - size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top; - else - size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top; - - if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) { - omap_lcd->sync_error = 1; - omap_lcd_interrupts(omap_lcd); - omap_lcd->enable = 0; - return; - } - - /* Content */ - frame_base = omap_lcd->dma->phys_framebuffer[ - omap_lcd->dma->current_frame] + frame_offset; - omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame; - if (omap_lcd->dma->interrupts & 1) - qemu_irq_raise(omap_lcd->dma->irq); - if (omap_lcd->dma->dual) - omap_lcd->dma->current_frame ^= 1; - - if (!surface_bits_per_pixel(surface)) { - return; - } - - first = 0; - height = omap_lcd->height; - if (omap_lcd->subpanel & (1 << 31)) { - if (omap_lcd->subpanel & (1 << 29)) - first = (omap_lcd->subpanel >> 16) & 0x3ff; - else - height = (omap_lcd->subpanel >> 16) & 0x3ff; - /* TODO: fill the rest of the panel with DPD */ - } - - step = width * bpp >> 3; - linesize = surface_stride(surface); - framebuffer_update_display(surface, omap_lcd->sysmem, - frame_base, width, height, - step, linesize, 0, - omap_lcd->invalidate, - draw_line, omap_lcd->palette, - &first, &last); - if (first >= 0) { - dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1); - } - omap_lcd->invalidate = 0; -} - -static void omap_ppm_save(const char *filename, uint8_t *data, - int w, int h, int linesize, Error **errp) -{ - FILE *f; - uint8_t *d, *d1; - unsigned int v; - int ret, y, x, bpp; - - f = fopen(filename, "wb"); - if (!f) { - error_setg(errp, "failed to open file '%s': %s", filename, - strerror(errno)); - return; - } - ret = fprintf(f, "P6\n%d %d\n%d\n", w, h, 255); - if (ret < 0) { - goto write_err; - } - d1 = data; - bpp = linesize / w; - for (y = 0; y < h; y ++) { - d = d1; - for (x = 0; x < w; x ++) { - v = *(uint32_t *) d; - switch (bpp) { - case 2: - ret = fputc((v >> 8) & 0xf8, f); - if (ret == EOF) { - goto write_err; - } - ret = fputc((v >> 3) & 0xfc, f); - if (ret == EOF) { - goto write_err; - } - ret = fputc((v << 3) & 0xf8, f); - if (ret == EOF) { - goto write_err; - } - break; - case 3: - case 4: - default: - ret = fputc((v >> 16) & 0xff, f); - if (ret == EOF) { - goto write_err; - } - ret = fputc((v >> 8) & 0xff, f); - if (ret == EOF) { - goto write_err; - } - ret = fputc((v) & 0xff, f); - if (ret == EOF) { - goto write_err; - } - break; - } - d += bpp; - } - d1 += linesize; - } -out: - fclose(f); - return; - -write_err: - error_setg(errp, "failed to write to file '%s': %s", filename, - strerror(errno)); - unlink(filename); - goto out; -} - -static void omap_screen_dump(void *opaque, const char *filename, bool cswitch, - Error **errp) -{ - struct omap_lcd_panel_s *omap_lcd = opaque; - DisplaySurface *surface = qemu_console_surface(omap_lcd->con); - - omap_update_display(opaque); - if (omap_lcd && surface_data(surface)) - omap_ppm_save(filename, surface_data(surface), - omap_lcd->width, omap_lcd->height, - surface_stride(surface), errp); -} - -static void omap_invalidate_display(void *opaque) { - struct omap_lcd_panel_s *omap_lcd = opaque; - omap_lcd->invalidate = 1; -} - -static void omap_lcd_update(struct omap_lcd_panel_s *s) { - if (!s->enable) { - s->dma->current_frame = -1; - s->sync_error = 0; - if (s->plm != 1) - s->frame_done = 1; - omap_lcd_interrupts(s); - return; - } - - if (s->dma->current_frame == -1) { - s->frame_done = 0; - s->palette_done = 0; - s->dma->current_frame = 0; - } - - if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu, - s->dma->src_f1_top) || - !s->dma->mpu->port[ - s->dma->src].addr_valid(s->dma->mpu, - s->dma->src_f1_bottom) || - (s->dma->dual && - (!s->dma->mpu->port[ - s->dma->src].addr_valid(s->dma->mpu, - s->dma->src_f2_top) || - !s->dma->mpu->port[ - s->dma->src].addr_valid(s->dma->mpu, - s->dma->src_f2_bottom)))) { - s->dma->condition |= 1 << 2; - if (s->dma->interrupts & (1 << 1)) - qemu_irq_raise(s->dma->irq); - s->enable = 0; - return; - } - - s->dma->phys_framebuffer[0] = s->dma->src_f1_top; - s->dma->phys_framebuffer[1] = s->dma->src_f2_top; - - if (s->plm != 2 && !s->palette_done) { - cpu_physical_memory_read( - s->dma->phys_framebuffer[s->dma->current_frame], - (void *)s->palette, 0x200); - s->palette_done = 1; - omap_lcd_interrupts(s); - } -} - -static uint64_t omap_lcdc_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; - - switch (addr) { - case 0x00: /* LCD_CONTROL */ - return (s->tft << 23) | (s->plm << 20) | - (s->tft << 7) | (s->interrupts << 3) | - (s->mono << 1) | s->enable | s->ctrl | 0xfe000c34; - - case 0x04: /* LCD_TIMING0 */ - return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f; - - case 0x08: /* LCD_TIMING1 */ - return (s->timing[1] << 10) | (s->height - 1); - - case 0x0c: /* LCD_TIMING2 */ - return s->timing[2] | 0xfc000000; - - case 0x10: /* LCD_STATUS */ - return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done; - - case 0x14: /* LCD_SUBPANEL */ - return s->subpanel; - - default: - break; - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_lcdc_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque; - - switch (addr) { - case 0x00: /* LCD_CONTROL */ - s->plm = (value >> 20) & 3; - s->tft = (value >> 7) & 1; - s->interrupts = (value >> 3) & 3; - s->mono = (value >> 1) & 1; - s->ctrl = value & 0x01cff300; - if (s->enable != (value & 1)) { - s->enable = value & 1; - omap_lcd_update(s); - } - break; - - case 0x04: /* LCD_TIMING0 */ - s->timing[0] = value >> 10; - s->width = (value & 0x3ff) + 1; - break; - - case 0x08: /* LCD_TIMING1 */ - s->timing[1] = value >> 10; - s->height = (value & 0x3ff) + 1; - break; - - case 0x0c: /* LCD_TIMING2 */ - s->timing[2] = value; - break; - - case 0x10: /* LCD_STATUS */ - break; - - case 0x14: /* LCD_SUBPANEL */ - s->subpanel = value & 0xa1ffffff; - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_lcdc_ops = { - .read = omap_lcdc_read, - .write = omap_lcdc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void omap_lcdc_reset(struct omap_lcd_panel_s *s) -{ - s->dma->current_frame = -1; - s->plm = 0; - s->tft = 0; - s->mono = 0; - s->enable = 0; - s->width = 0; - s->height = 0; - s->interrupts = 0; - s->timing[0] = 0; - s->timing[1] = 0; - s->timing[2] = 0; - s->subpanel = 0; - s->palette_done = 0; - s->frame_done = 0; - s->sync_error = 0; - s->invalidate = 1; - s->subpanel = 0; - s->ctrl = 0; -} - -struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq, - struct omap_dma_lcd_channel_s *dma, - omap_clk clk) -{ - struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) - g_malloc0(sizeof(struct omap_lcd_panel_s)); - - s->irq = irq; - s->dma = dma; - s->sysmem = sysmem; - omap_lcdc_reset(s); - - memory_region_init_io(&s->iomem, &omap_lcdc_ops, s, "omap.lcdc", 0x100); - memory_region_add_subregion(sysmem, base, &s->iomem); - - s->con = graphic_console_init(omap_update_display, - omap_invalidate_display, - omap_screen_dump, NULL, s); - - return s; -} diff --git a/hw/pxa2xx_lcd.c b/hw/pxa2xx_lcd.c deleted file mode 100644 index ee59bc2de3..0000000000 --- a/hw/pxa2xx_lcd.c +++ /dev/null @@ -1,1058 +0,0 @@ -/* - * Intel XScale PXA255/270 LCDC emulation. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * This code is licensed under the GPLv2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "hw/hw.h" -#include "ui/console.h" -#include "hw/arm/pxa.h" -#include "ui/pixel_ops.h" -/* FIXME: For graphic_rotate. Should probably be done in common code. */ -#include "sysemu/sysemu.h" -#include "hw/framebuffer.h" - -struct DMAChannel { - uint32_t branch; - uint8_t up; - uint8_t palette[1024]; - uint8_t pbuffer[1024]; - void (*redraw)(PXA2xxLCDState *s, hwaddr addr, - int *miny, int *maxy); - - uint32_t descriptor; - uint32_t source; - uint32_t id; - uint32_t command; -}; - -struct PXA2xxLCDState { - MemoryRegion *sysmem; - MemoryRegion iomem; - qemu_irq irq; - int irqlevel; - - int invalidated; - QemuConsole *con; - drawfn *line_fn[2]; - int dest_width; - int xres, yres; - int pal_for; - int transp; - enum { - pxa_lcdc_2bpp = 1, - pxa_lcdc_4bpp = 2, - pxa_lcdc_8bpp = 3, - pxa_lcdc_16bpp = 4, - pxa_lcdc_18bpp = 5, - pxa_lcdc_18pbpp = 6, - pxa_lcdc_19bpp = 7, - pxa_lcdc_19pbpp = 8, - pxa_lcdc_24bpp = 9, - pxa_lcdc_25bpp = 10, - } bpp; - - uint32_t control[6]; - uint32_t status[2]; - uint32_t ovl1c[2]; - uint32_t ovl2c[2]; - uint32_t ccr; - uint32_t cmdcr; - uint32_t trgbr; - uint32_t tcr; - uint32_t liidr; - uint8_t bscntr; - - struct DMAChannel dma_ch[7]; - - qemu_irq vsync_cb; - int orientation; -}; - -typedef struct QEMU_PACKED { - uint32_t fdaddr; - uint32_t fsaddr; - uint32_t fidr; - uint32_t ldcmd; -} PXAFrameDescriptor; - -#define LCCR0 0x000 /* LCD Controller Control register 0 */ -#define LCCR1 0x004 /* LCD Controller Control register 1 */ -#define LCCR2 0x008 /* LCD Controller Control register 2 */ -#define LCCR3 0x00c /* LCD Controller Control register 3 */ -#define LCCR4 0x010 /* LCD Controller Control register 4 */ -#define LCCR5 0x014 /* LCD Controller Control register 5 */ - -#define FBR0 0x020 /* DMA Channel 0 Frame Branch register */ -#define FBR1 0x024 /* DMA Channel 1 Frame Branch register */ -#define FBR2 0x028 /* DMA Channel 2 Frame Branch register */ -#define FBR3 0x02c /* DMA Channel 3 Frame Branch register */ -#define FBR4 0x030 /* DMA Channel 4 Frame Branch register */ -#define FBR5 0x110 /* DMA Channel 5 Frame Branch register */ -#define FBR6 0x114 /* DMA Channel 6 Frame Branch register */ - -#define LCSR1 0x034 /* LCD Controller Status register 1 */ -#define LCSR0 0x038 /* LCD Controller Status register 0 */ -#define LIIDR 0x03c /* LCD Controller Interrupt ID register */ - -#define TRGBR 0x040 /* TMED RGB Seed register */ -#define TCR 0x044 /* TMED Control register */ - -#define OVL1C1 0x050 /* Overlay 1 Control register 1 */ -#define OVL1C2 0x060 /* Overlay 1 Control register 2 */ -#define OVL2C1 0x070 /* Overlay 2 Control register 1 */ -#define OVL2C2 0x080 /* Overlay 2 Control register 2 */ -#define CCR 0x090 /* Cursor Control register */ - -#define CMDCR 0x100 /* Command Control register */ -#define PRSR 0x104 /* Panel Read Status register */ - -#define PXA_LCDDMA_CHANS 7 -#define DMA_FDADR 0x00 /* Frame Descriptor Address register */ -#define DMA_FSADR 0x04 /* Frame Source Address register */ -#define DMA_FIDR 0x08 /* Frame ID register */ -#define DMA_LDCMD 0x0c /* Command register */ - -/* LCD Buffer Strength Control register */ -#define BSCNTR 0x04000054 - -/* Bitfield masks */ -#define LCCR0_ENB (1 << 0) -#define LCCR0_CMS (1 << 1) -#define LCCR0_SDS (1 << 2) -#define LCCR0_LDM (1 << 3) -#define LCCR0_SOFM0 (1 << 4) -#define LCCR0_IUM (1 << 5) -#define LCCR0_EOFM0 (1 << 6) -#define LCCR0_PAS (1 << 7) -#define LCCR0_DPD (1 << 9) -#define LCCR0_DIS (1 << 10) -#define LCCR0_QDM (1 << 11) -#define LCCR0_PDD (0xff << 12) -#define LCCR0_BSM0 (1 << 20) -#define LCCR0_OUM (1 << 21) -#define LCCR0_LCDT (1 << 22) -#define LCCR0_RDSTM (1 << 23) -#define LCCR0_CMDIM (1 << 24) -#define LCCR0_OUC (1 << 25) -#define LCCR0_LDDALT (1 << 26) -#define LCCR1_PPL(x) ((x) & 0x3ff) -#define LCCR2_LPP(x) ((x) & 0x3ff) -#define LCCR3_API (15 << 16) -#define LCCR3_BPP(x) ((((x) >> 24) & 7) | (((x) >> 26) & 8)) -#define LCCR3_PDFOR(x) (((x) >> 30) & 3) -#define LCCR4_K1(x) (((x) >> 0) & 7) -#define LCCR4_K2(x) (((x) >> 3) & 7) -#define LCCR4_K3(x) (((x) >> 6) & 7) -#define LCCR4_PALFOR(x) (((x) >> 15) & 3) -#define LCCR5_SOFM(ch) (1 << (ch - 1)) -#define LCCR5_EOFM(ch) (1 << (ch + 7)) -#define LCCR5_BSM(ch) (1 << (ch + 15)) -#define LCCR5_IUM(ch) (1 << (ch + 23)) -#define OVLC1_EN (1 << 31) -#define CCR_CEN (1 << 31) -#define FBR_BRA (1 << 0) -#define FBR_BINT (1 << 1) -#define FBR_SRCADDR (0xfffffff << 4) -#define LCSR0_LDD (1 << 0) -#define LCSR0_SOF0 (1 << 1) -#define LCSR0_BER (1 << 2) -#define LCSR0_ABC (1 << 3) -#define LCSR0_IU0 (1 << 4) -#define LCSR0_IU1 (1 << 5) -#define LCSR0_OU (1 << 6) -#define LCSR0_QD (1 << 7) -#define LCSR0_EOF0 (1 << 8) -#define LCSR0_BS0 (1 << 9) -#define LCSR0_SINT (1 << 10) -#define LCSR0_RDST (1 << 11) -#define LCSR0_CMDINT (1 << 12) -#define LCSR0_BERCH(x) (((x) & 7) << 28) -#define LCSR1_SOF(ch) (1 << (ch - 1)) -#define LCSR1_EOF(ch) (1 << (ch + 7)) -#define LCSR1_BS(ch) (1 << (ch + 15)) -#define LCSR1_IU(ch) (1 << (ch + 23)) -#define LDCMD_LENGTH(x) ((x) & 0x001ffffc) -#define LDCMD_EOFINT (1 << 21) -#define LDCMD_SOFINT (1 << 22) -#define LDCMD_PAL (1 << 26) - -/* Route internal interrupt lines to the global IC */ -static void pxa2xx_lcdc_int_update(PXA2xxLCDState *s) -{ - int level = 0; - level |= (s->status[0] & LCSR0_LDD) && !(s->control[0] & LCCR0_LDM); - level |= (s->status[0] & LCSR0_SOF0) && !(s->control[0] & LCCR0_SOFM0); - level |= (s->status[0] & LCSR0_IU0) && !(s->control[0] & LCCR0_IUM); - level |= (s->status[0] & LCSR0_IU1) && !(s->control[5] & LCCR5_IUM(1)); - level |= (s->status[0] & LCSR0_OU) && !(s->control[0] & LCCR0_OUM); - level |= (s->status[0] & LCSR0_QD) && !(s->control[0] & LCCR0_QDM); - level |= (s->status[0] & LCSR0_EOF0) && !(s->control[0] & LCCR0_EOFM0); - level |= (s->status[0] & LCSR0_BS0) && !(s->control[0] & LCCR0_BSM0); - level |= (s->status[0] & LCSR0_RDST) && !(s->control[0] & LCCR0_RDSTM); - level |= (s->status[0] & LCSR0_CMDINT) && !(s->control[0] & LCCR0_CMDIM); - level |= (s->status[1] & ~s->control[5]); - - qemu_set_irq(s->irq, !!level); - s->irqlevel = level; -} - -/* Set Branch Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_bs_set(PXA2xxLCDState *s, int ch) -{ - int unmasked; - if (ch == 0) { - s->status[0] |= LCSR0_BS0; - unmasked = !(s->control[0] & LCCR0_BSM0); - } else { - s->status[1] |= LCSR1_BS(ch); - unmasked = !(s->control[5] & LCCR5_BSM(ch)); - } - - if (unmasked) { - if (s->irqlevel) - s->status[0] |= LCSR0_SINT; - else - s->liidr = s->dma_ch[ch].id; - } -} - -/* Set Start Of Frame Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_sof_set(PXA2xxLCDState *s, int ch) -{ - int unmasked; - if (!(s->dma_ch[ch].command & LDCMD_SOFINT)) - return; - - if (ch == 0) { - s->status[0] |= LCSR0_SOF0; - unmasked = !(s->control[0] & LCCR0_SOFM0); - } else { - s->status[1] |= LCSR1_SOF(ch); - unmasked = !(s->control[5] & LCCR5_SOFM(ch)); - } - - if (unmasked) { - if (s->irqlevel) - s->status[0] |= LCSR0_SINT; - else - s->liidr = s->dma_ch[ch].id; - } -} - -/* Set End Of Frame Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_eof_set(PXA2xxLCDState *s, int ch) -{ - int unmasked; - if (!(s->dma_ch[ch].command & LDCMD_EOFINT)) - return; - - if (ch == 0) { - s->status[0] |= LCSR0_EOF0; - unmasked = !(s->control[0] & LCCR0_EOFM0); - } else { - s->status[1] |= LCSR1_EOF(ch); - unmasked = !(s->control[5] & LCCR5_EOFM(ch)); - } - - if (unmasked) { - if (s->irqlevel) - s->status[0] |= LCSR0_SINT; - else - s->liidr = s->dma_ch[ch].id; - } -} - -/* Set Bus Error Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_ber_set(PXA2xxLCDState *s, int ch) -{ - s->status[0] |= LCSR0_BERCH(ch) | LCSR0_BER; - if (s->irqlevel) - s->status[0] |= LCSR0_SINT; - else - s->liidr = s->dma_ch[ch].id; -} - -/* Set Read Status interrupt high and poke associated registers */ -static inline void pxa2xx_dma_rdst_set(PXA2xxLCDState *s) -{ - s->status[0] |= LCSR0_RDST; - if (s->irqlevel && !(s->control[0] & LCCR0_RDSTM)) - s->status[0] |= LCSR0_SINT; -} - -/* Load new Frame Descriptors from DMA */ -static void pxa2xx_descriptor_load(PXA2xxLCDState *s) -{ - PXAFrameDescriptor desc; - hwaddr descptr; - int i; - - for (i = 0; i < PXA_LCDDMA_CHANS; i ++) { - s->dma_ch[i].source = 0; - - if (!s->dma_ch[i].up) - continue; - - if (s->dma_ch[i].branch & FBR_BRA) { - descptr = s->dma_ch[i].branch & FBR_SRCADDR; - if (s->dma_ch[i].branch & FBR_BINT) - pxa2xx_dma_bs_set(s, i); - s->dma_ch[i].branch &= ~FBR_BRA; - } else - descptr = s->dma_ch[i].descriptor; - - if (!((descptr >= PXA2XX_SDRAM_BASE && descptr + - sizeof(desc) <= PXA2XX_SDRAM_BASE + ram_size) || - (descptr >= PXA2XX_INTERNAL_BASE && descptr + sizeof(desc) <= - PXA2XX_INTERNAL_BASE + PXA2XX_INTERNAL_SIZE))) { - continue; - } - - cpu_physical_memory_read(descptr, (void *)&desc, sizeof(desc)); - s->dma_ch[i].descriptor = tswap32(desc.fdaddr); - s->dma_ch[i].source = tswap32(desc.fsaddr); - s->dma_ch[i].id = tswap32(desc.fidr); - s->dma_ch[i].command = tswap32(desc.ldcmd); - } -} - -static uint64_t pxa2xx_lcdc_read(void *opaque, hwaddr offset, - unsigned size) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - int ch; - - switch (offset) { - case LCCR0: - return s->control[0]; - case LCCR1: - return s->control[1]; - case LCCR2: - return s->control[2]; - case LCCR3: - return s->control[3]; - case LCCR4: - return s->control[4]; - case LCCR5: - return s->control[5]; - - case OVL1C1: - return s->ovl1c[0]; - case OVL1C2: - return s->ovl1c[1]; - case OVL2C1: - return s->ovl2c[0]; - case OVL2C2: - return s->ovl2c[1]; - - case CCR: - return s->ccr; - - case CMDCR: - return s->cmdcr; - - case TRGBR: - return s->trgbr; - case TCR: - return s->tcr; - - case 0x200 ... 0x1000: /* DMA per-channel registers */ - ch = (offset - 0x200) >> 4; - if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS)) - goto fail; - - switch (offset & 0xf) { - case DMA_FDADR: - return s->dma_ch[ch].descriptor; - case DMA_FSADR: - return s->dma_ch[ch].source; - case DMA_FIDR: - return s->dma_ch[ch].id; - case DMA_LDCMD: - return s->dma_ch[ch].command; - default: - goto fail; - } - - case FBR0: - return s->dma_ch[0].branch; - case FBR1: - return s->dma_ch[1].branch; - case FBR2: - return s->dma_ch[2].branch; - case FBR3: - return s->dma_ch[3].branch; - case FBR4: - return s->dma_ch[4].branch; - case FBR5: - return s->dma_ch[5].branch; - case FBR6: - return s->dma_ch[6].branch; - - case BSCNTR: - return s->bscntr; - - case PRSR: - return 0; - - case LCSR0: - return s->status[0]; - case LCSR1: - return s->status[1]; - case LIIDR: - return s->liidr; - - default: - fail: - hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); - } - - return 0; -} - -static void pxa2xx_lcdc_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - int ch; - - switch (offset) { - case LCCR0: - /* ACK Quick Disable done */ - if ((s->control[0] & LCCR0_ENB) && !(value & LCCR0_ENB)) - s->status[0] |= LCSR0_QD; - - if (!(s->control[0] & LCCR0_LCDT) && (value & LCCR0_LCDT)) - printf("%s: internal frame buffer unsupported\n", __FUNCTION__); - - if ((s->control[3] & LCCR3_API) && - (value & LCCR0_ENB) && !(value & LCCR0_LCDT)) - s->status[0] |= LCSR0_ABC; - - s->control[0] = value & 0x07ffffff; - pxa2xx_lcdc_int_update(s); - - s->dma_ch[0].up = !!(value & LCCR0_ENB); - s->dma_ch[1].up = (s->ovl1c[0] & OVLC1_EN) || (value & LCCR0_SDS); - break; - - case LCCR1: - s->control[1] = value; - break; - - case LCCR2: - s->control[2] = value; - break; - - case LCCR3: - s->control[3] = value & 0xefffffff; - s->bpp = LCCR3_BPP(value); - break; - - case LCCR4: - s->control[4] = value & 0x83ff81ff; - break; - - case LCCR5: - s->control[5] = value & 0x3f3f3f3f; - break; - - case OVL1C1: - if (!(s->ovl1c[0] & OVLC1_EN) && (value & OVLC1_EN)) - printf("%s: Overlay 1 not supported\n", __FUNCTION__); - - s->ovl1c[0] = value & 0x80ffffff; - s->dma_ch[1].up = (value & OVLC1_EN) || (s->control[0] & LCCR0_SDS); - break; - - case OVL1C2: - s->ovl1c[1] = value & 0x000fffff; - break; - - case OVL2C1: - if (!(s->ovl2c[0] & OVLC1_EN) && (value & OVLC1_EN)) - printf("%s: Overlay 2 not supported\n", __FUNCTION__); - - s->ovl2c[0] = value & 0x80ffffff; - s->dma_ch[2].up = !!(value & OVLC1_EN); - s->dma_ch[3].up = !!(value & OVLC1_EN); - s->dma_ch[4].up = !!(value & OVLC1_EN); - break; - - case OVL2C2: - s->ovl2c[1] = value & 0x007fffff; - break; - - case CCR: - if (!(s->ccr & CCR_CEN) && (value & CCR_CEN)) - printf("%s: Hardware cursor unimplemented\n", __FUNCTION__); - - s->ccr = value & 0x81ffffe7; - s->dma_ch[5].up = !!(value & CCR_CEN); - break; - - case CMDCR: - s->cmdcr = value & 0xff; - break; - - case TRGBR: - s->trgbr = value & 0x00ffffff; - break; - - case TCR: - s->tcr = value & 0x7fff; - break; - - case 0x200 ... 0x1000: /* DMA per-channel registers */ - ch = (offset - 0x200) >> 4; - if (!(ch >= 0 && ch < PXA_LCDDMA_CHANS)) - goto fail; - - switch (offset & 0xf) { - case DMA_FDADR: - s->dma_ch[ch].descriptor = value & 0xfffffff0; - break; - - default: - goto fail; - } - break; - - case FBR0: - s->dma_ch[0].branch = value & 0xfffffff3; - break; - case FBR1: - s->dma_ch[1].branch = value & 0xfffffff3; - break; - case FBR2: - s->dma_ch[2].branch = value & 0xfffffff3; - break; - case FBR3: - s->dma_ch[3].branch = value & 0xfffffff3; - break; - case FBR4: - s->dma_ch[4].branch = value & 0xfffffff3; - break; - case FBR5: - s->dma_ch[5].branch = value & 0xfffffff3; - break; - case FBR6: - s->dma_ch[6].branch = value & 0xfffffff3; - break; - - case BSCNTR: - s->bscntr = value & 0xf; - break; - - case PRSR: - break; - - case LCSR0: - s->status[0] &= ~(value & 0xfff); - if (value & LCSR0_BER) - s->status[0] &= ~LCSR0_BERCH(7); - break; - - case LCSR1: - s->status[1] &= ~(value & 0x3e3f3f); - break; - - default: - fail: - hw_error("%s: Bad offset " REG_FMT "\n", __FUNCTION__, offset); - } -} - -static const MemoryRegionOps pxa2xx_lcdc_ops = { - .read = pxa2xx_lcdc_read, - .write = pxa2xx_lcdc_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* Load new palette for a given DMA channel, convert to internal format */ -static void pxa2xx_palette_parse(PXA2xxLCDState *s, int ch, int bpp) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i, n, format, r, g, b, alpha; - uint32_t *dest; - uint8_t *src; - s->pal_for = LCCR4_PALFOR(s->control[4]); - format = s->pal_for; - - switch (bpp) { - case pxa_lcdc_2bpp: - n = 4; - break; - case pxa_lcdc_4bpp: - n = 16; - break; - case pxa_lcdc_8bpp: - n = 256; - break; - default: - format = 0; - return; - } - - src = (uint8_t *) s->dma_ch[ch].pbuffer; - dest = (uint32_t *) s->dma_ch[ch].palette; - alpha = r = g = b = 0; - - for (i = 0; i < n; i ++) { - switch (format) { - case 0: /* 16 bpp, no transparency */ - alpha = 0; - if (s->control[0] & LCCR0_CMS) { - r = g = b = *(uint16_t *) src & 0xff; - } - else { - r = (*(uint16_t *) src & 0xf800) >> 8; - g = (*(uint16_t *) src & 0x07e0) >> 3; - b = (*(uint16_t *) src & 0x001f) << 3; - } - src += 2; - break; - case 1: /* 16 bpp plus transparency */ - alpha = *(uint16_t *) src & (1 << 24); - if (s->control[0] & LCCR0_CMS) - r = g = b = *(uint16_t *) src & 0xff; - else { - r = (*(uint16_t *) src & 0xf800) >> 8; - g = (*(uint16_t *) src & 0x07e0) >> 3; - b = (*(uint16_t *) src & 0x001f) << 3; - } - src += 2; - break; - case 2: /* 18 bpp plus transparency */ - alpha = *(uint32_t *) src & (1 << 24); - if (s->control[0] & LCCR0_CMS) - r = g = b = *(uint32_t *) src & 0xff; - else { - r = (*(uint32_t *) src & 0xf80000) >> 16; - g = (*(uint32_t *) src & 0x00fc00) >> 8; - b = (*(uint32_t *) src & 0x0000f8); - } - src += 4; - break; - case 3: /* 24 bpp plus transparency */ - alpha = *(uint32_t *) src & (1 << 24); - if (s->control[0] & LCCR0_CMS) - r = g = b = *(uint32_t *) src & 0xff; - else { - r = (*(uint32_t *) src & 0xff0000) >> 16; - g = (*(uint32_t *) src & 0x00ff00) >> 8; - b = (*(uint32_t *) src & 0x0000ff); - } - src += 4; - break; - } - switch (surface_bits_per_pixel(surface)) { - case 8: - *dest = rgb_to_pixel8(r, g, b) | alpha; - break; - case 15: - *dest = rgb_to_pixel15(r, g, b) | alpha; - break; - case 16: - *dest = rgb_to_pixel16(r, g, b) | alpha; - break; - case 24: - *dest = rgb_to_pixel24(r, g, b) | alpha; - break; - case 32: - *dest = rgb_to_pixel32(r, g, b) | alpha; - break; - } - dest ++; - } -} - -static void pxa2xx_lcdc_dma0_redraw_rot0(PXA2xxLCDState *s, - hwaddr addr, int *miny, int *maxy) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width, dest_width; - drawfn fn = NULL; - if (s->dest_width) - fn = s->line_fn[s->transp][s->bpp]; - if (!fn) - return; - - src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ - if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) - src_width *= 3; - else if (s->bpp > pxa_lcdc_16bpp) - src_width *= 4; - else if (s->bpp > pxa_lcdc_8bpp) - src_width *= 2; - - dest_width = s->xres * s->dest_width; - *miny = 0; - framebuffer_update_display(surface, s->sysmem, - addr, s->xres, s->yres, - src_width, dest_width, s->dest_width, - s->invalidated, - fn, s->dma_ch[0].palette, miny, maxy); -} - -static void pxa2xx_lcdc_dma0_redraw_rot90(PXA2xxLCDState *s, - hwaddr addr, int *miny, int *maxy) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width, dest_width; - drawfn fn = NULL; - if (s->dest_width) - fn = s->line_fn[s->transp][s->bpp]; - if (!fn) - return; - - src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ - if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) - src_width *= 3; - else if (s->bpp > pxa_lcdc_16bpp) - src_width *= 4; - else if (s->bpp > pxa_lcdc_8bpp) - src_width *= 2; - - dest_width = s->yres * s->dest_width; - *miny = 0; - framebuffer_update_display(surface, s->sysmem, - addr, s->xres, s->yres, - src_width, s->dest_width, -dest_width, - s->invalidated, - fn, s->dma_ch[0].palette, - miny, maxy); -} - -static void pxa2xx_lcdc_dma0_redraw_rot180(PXA2xxLCDState *s, - hwaddr addr, int *miny, int *maxy) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width, dest_width; - drawfn fn = NULL; - if (s->dest_width) { - fn = s->line_fn[s->transp][s->bpp]; - } - if (!fn) { - return; - } - - src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ - if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) { - src_width *= 3; - } else if (s->bpp > pxa_lcdc_16bpp) { - src_width *= 4; - } else if (s->bpp > pxa_lcdc_8bpp) { - src_width *= 2; - } - - dest_width = s->xres * s->dest_width; - *miny = 0; - framebuffer_update_display(surface, s->sysmem, - addr, s->xres, s->yres, - src_width, -dest_width, -s->dest_width, - s->invalidated, - fn, s->dma_ch[0].palette, miny, maxy); -} - -static void pxa2xx_lcdc_dma0_redraw_rot270(PXA2xxLCDState *s, - hwaddr addr, int *miny, int *maxy) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int src_width, dest_width; - drawfn fn = NULL; - if (s->dest_width) { - fn = s->line_fn[s->transp][s->bpp]; - } - if (!fn) { - return; - } - - src_width = (s->xres + 3) & ~3; /* Pad to a 4 pixels multiple */ - if (s->bpp == pxa_lcdc_19pbpp || s->bpp == pxa_lcdc_18pbpp) { - src_width *= 3; - } else if (s->bpp > pxa_lcdc_16bpp) { - src_width *= 4; - } else if (s->bpp > pxa_lcdc_8bpp) { - src_width *= 2; - } - - dest_width = s->yres * s->dest_width; - *miny = 0; - framebuffer_update_display(surface, s->sysmem, - addr, s->xres, s->yres, - src_width, -s->dest_width, dest_width, - s->invalidated, - fn, s->dma_ch[0].palette, - miny, maxy); -} - -static void pxa2xx_lcdc_resize(PXA2xxLCDState *s) -{ - int width, height; - if (!(s->control[0] & LCCR0_ENB)) - return; - - width = LCCR1_PPL(s->control[1]) + 1; - height = LCCR2_LPP(s->control[2]) + 1; - - if (width != s->xres || height != s->yres) { - if (s->orientation == 90 || s->orientation == 270) { - qemu_console_resize(s->con, height, width); - } else { - qemu_console_resize(s->con, width, height); - } - s->invalidated = 1; - s->xres = width; - s->yres = height; - } -} - -static void pxa2xx_update_display(void *opaque) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - hwaddr fbptr; - int miny, maxy; - int ch; - if (!(s->control[0] & LCCR0_ENB)) - return; - - pxa2xx_descriptor_load(s); - - pxa2xx_lcdc_resize(s); - miny = s->yres; - maxy = 0; - s->transp = s->dma_ch[2].up || s->dma_ch[3].up; - /* Note: With overlay planes the order depends on LCCR0 bit 25. */ - for (ch = 0; ch < PXA_LCDDMA_CHANS; ch ++) - if (s->dma_ch[ch].up) { - if (!s->dma_ch[ch].source) { - pxa2xx_dma_ber_set(s, ch); - continue; - } - fbptr = s->dma_ch[ch].source; - if (!((fbptr >= PXA2XX_SDRAM_BASE && - fbptr <= PXA2XX_SDRAM_BASE + ram_size) || - (fbptr >= PXA2XX_INTERNAL_BASE && - fbptr <= PXA2XX_INTERNAL_BASE + PXA2XX_INTERNAL_SIZE))) { - pxa2xx_dma_ber_set(s, ch); - continue; - } - - if (s->dma_ch[ch].command & LDCMD_PAL) { - cpu_physical_memory_read(fbptr, s->dma_ch[ch].pbuffer, - MAX(LDCMD_LENGTH(s->dma_ch[ch].command), - sizeof(s->dma_ch[ch].pbuffer))); - pxa2xx_palette_parse(s, ch, s->bpp); - } else { - /* Do we need to reparse palette */ - if (LCCR4_PALFOR(s->control[4]) != s->pal_for) - pxa2xx_palette_parse(s, ch, s->bpp); - - /* ACK frame start */ - pxa2xx_dma_sof_set(s, ch); - - s->dma_ch[ch].redraw(s, fbptr, &miny, &maxy); - s->invalidated = 0; - - /* ACK frame completed */ - pxa2xx_dma_eof_set(s, ch); - } - } - - if (s->control[0] & LCCR0_DIS) { - /* ACK last frame completed */ - s->control[0] &= ~LCCR0_ENB; - s->status[0] |= LCSR0_LDD; - } - - if (miny >= 0) { - switch (s->orientation) { - case 0: - dpy_gfx_update(s->con, 0, miny, s->xres, maxy - miny + 1); - break; - case 90: - dpy_gfx_update(s->con, miny, 0, maxy - miny + 1, s->xres); - break; - case 180: - maxy = s->yres - maxy - 1; - miny = s->yres - miny - 1; - dpy_gfx_update(s->con, 0, maxy, s->xres, miny - maxy + 1); - break; - case 270: - maxy = s->yres - maxy - 1; - miny = s->yres - miny - 1; - dpy_gfx_update(s->con, maxy, 0, miny - maxy + 1, s->xres); - break; - } - } - pxa2xx_lcdc_int_update(s); - - qemu_irq_raise(s->vsync_cb); -} - -static void pxa2xx_invalidate_display(void *opaque) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - s->invalidated = 1; -} - -static void pxa2xx_lcdc_orientation(void *opaque, int angle) -{ - PXA2xxLCDState *s = (PXA2xxLCDState *) opaque; - - switch (angle) { - case 0: - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot0; - break; - case 90: - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot90; - break; - case 180: - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot180; - break; - case 270: - s->dma_ch[0].redraw = pxa2xx_lcdc_dma0_redraw_rot270; - break; - } - - s->orientation = angle; - s->xres = s->yres = -1; - pxa2xx_lcdc_resize(s); -} - -static const VMStateDescription vmstate_dma_channel = { - .name = "dma_channel", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(branch, struct DMAChannel), - VMSTATE_UINT8(up, struct DMAChannel), - VMSTATE_BUFFER(pbuffer, struct DMAChannel), - VMSTATE_UINT32(descriptor, struct DMAChannel), - VMSTATE_UINT32(source, struct DMAChannel), - VMSTATE_UINT32(id, struct DMAChannel), - VMSTATE_UINT32(command, struct DMAChannel), - VMSTATE_END_OF_LIST() - } -}; - -static int pxa2xx_lcdc_post_load(void *opaque, int version_id) -{ - PXA2xxLCDState *s = opaque; - - s->bpp = LCCR3_BPP(s->control[3]); - s->xres = s->yres = s->pal_for = -1; - - return 0; -} - -static const VMStateDescription vmstate_pxa2xx_lcdc = { - .name = "pxa2xx_lcdc", - .version_id = 0, - .minimum_version_id = 0, - .minimum_version_id_old = 0, - .post_load = pxa2xx_lcdc_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT32(irqlevel, PXA2xxLCDState), - VMSTATE_INT32(transp, PXA2xxLCDState), - VMSTATE_UINT32_ARRAY(control, PXA2xxLCDState, 6), - VMSTATE_UINT32_ARRAY(status, PXA2xxLCDState, 2), - VMSTATE_UINT32_ARRAY(ovl1c, PXA2xxLCDState, 2), - VMSTATE_UINT32_ARRAY(ovl2c, PXA2xxLCDState, 2), - VMSTATE_UINT32(ccr, PXA2xxLCDState), - VMSTATE_UINT32(cmdcr, PXA2xxLCDState), - VMSTATE_UINT32(trgbr, PXA2xxLCDState), - VMSTATE_UINT32(tcr, PXA2xxLCDState), - VMSTATE_UINT32(liidr, PXA2xxLCDState), - VMSTATE_UINT8(bscntr, PXA2xxLCDState), - VMSTATE_STRUCT_ARRAY(dma_ch, PXA2xxLCDState, 7, 0, - vmstate_dma_channel, struct DMAChannel), - VMSTATE_END_OF_LIST() - } -}; - -#define BITS 8 -#include "hw/pxa2xx_template.h" -#define BITS 15 -#include "hw/pxa2xx_template.h" -#define BITS 16 -#include "hw/pxa2xx_template.h" -#define BITS 24 -#include "hw/pxa2xx_template.h" -#define BITS 32 -#include "hw/pxa2xx_template.h" - -PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem, - hwaddr base, qemu_irq irq) -{ - PXA2xxLCDState *s; - DisplaySurface *surface; - - s = (PXA2xxLCDState *) g_malloc0(sizeof(PXA2xxLCDState)); - s->invalidated = 1; - s->irq = irq; - s->sysmem = sysmem; - - pxa2xx_lcdc_orientation(s, graphic_rotate); - - memory_region_init_io(&s->iomem, &pxa2xx_lcdc_ops, s, - "pxa2xx-lcd-controller", 0x00100000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - s->con = graphic_console_init(pxa2xx_update_display, - pxa2xx_invalidate_display, - NULL, NULL, s); - surface = qemu_console_surface(s->con); - - switch (surface_bits_per_pixel(surface)) { - case 0: - s->dest_width = 0; - break; - case 8: - s->line_fn[0] = pxa2xx_draw_fn_8; - s->line_fn[1] = pxa2xx_draw_fn_8t; - s->dest_width = 1; - break; - case 15: - s->line_fn[0] = pxa2xx_draw_fn_15; - s->line_fn[1] = pxa2xx_draw_fn_15t; - s->dest_width = 2; - break; - case 16: - s->line_fn[0] = pxa2xx_draw_fn_16; - s->line_fn[1] = pxa2xx_draw_fn_16t; - s->dest_width = 2; - break; - case 24: - s->line_fn[0] = pxa2xx_draw_fn_24; - s->line_fn[1] = pxa2xx_draw_fn_24t; - s->dest_width = 3; - break; - case 32: - s->line_fn[0] = pxa2xx_draw_fn_32; - s->line_fn[1] = pxa2xx_draw_fn_32t; - s->dest_width = 4; - break; - default: - fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__); - exit(1); - } - - vmstate_register(NULL, 0, &vmstate_pxa2xx_lcdc, s); - - return s; -} - -void pxa2xx_lcd_vsync_notifier(PXA2xxLCDState *s, qemu_irq handler) -{ - s->vsync_cb = handler; -} diff --git a/hw/qxl-logger.c b/hw/qxl-logger.c deleted file mode 100644 index 84f9aa1eda..0000000000 --- a/hw/qxl-logger.c +++ /dev/null @@ -1,275 +0,0 @@ -/* - * qxl command logging -- for debug purposes - * - * Copyright (C) 2010 Red Hat, Inc. - * - * maintained by Gerd Hoffmann - * - * 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 or - * (at your option) version 3 of the License. - * - * 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, see . - */ - -#include "qemu/timer.h" -#include "hw/qxl.h" - -static const char *qxl_type[] = { - [ QXL_CMD_NOP ] = "nop", - [ QXL_CMD_DRAW ] = "draw", - [ QXL_CMD_UPDATE ] = "update", - [ QXL_CMD_CURSOR ] = "cursor", - [ QXL_CMD_MESSAGE ] = "message", - [ QXL_CMD_SURFACE ] = "surface", -}; - -static const char *qxl_draw_type[] = { - [ QXL_DRAW_NOP ] = "nop", - [ QXL_DRAW_FILL ] = "fill", - [ QXL_DRAW_OPAQUE ] = "opaque", - [ QXL_DRAW_COPY ] = "copy", - [ QXL_COPY_BITS ] = "copy-bits", - [ QXL_DRAW_BLEND ] = "blend", - [ QXL_DRAW_BLACKNESS ] = "blackness", - [ QXL_DRAW_WHITENESS ] = "whitemess", - [ QXL_DRAW_INVERS ] = "invers", - [ QXL_DRAW_ROP3 ] = "rop3", - [ QXL_DRAW_STROKE ] = "stroke", - [ QXL_DRAW_TEXT ] = "text", - [ QXL_DRAW_TRANSPARENT ] = "transparent", - [ QXL_DRAW_ALPHA_BLEND ] = "alpha-blend", -}; - -static const char *qxl_draw_effect[] = { - [ QXL_EFFECT_BLEND ] = "blend", - [ QXL_EFFECT_OPAQUE ] = "opaque", - [ QXL_EFFECT_REVERT_ON_DUP ] = "revert-on-dup", - [ QXL_EFFECT_BLACKNESS_ON_DUP ] = "blackness-on-dup", - [ QXL_EFFECT_WHITENESS_ON_DUP ] = "whiteness-on-dup", - [ QXL_EFFECT_NOP_ON_DUP ] = "nop-on-dup", - [ QXL_EFFECT_NOP ] = "nop", - [ QXL_EFFECT_OPAQUE_BRUSH ] = "opaque-brush", -}; - -static const char *qxl_surface_cmd[] = { - [ QXL_SURFACE_CMD_CREATE ] = "create", - [ QXL_SURFACE_CMD_DESTROY ] = "destroy", -}; - -static const char *spice_surface_fmt[] = { - [ SPICE_SURFACE_FMT_INVALID ] = "invalid", - [ SPICE_SURFACE_FMT_1_A ] = "alpha/1", - [ SPICE_SURFACE_FMT_8_A ] = "alpha/8", - [ SPICE_SURFACE_FMT_16_555 ] = "555/16", - [ SPICE_SURFACE_FMT_16_565 ] = "565/16", - [ SPICE_SURFACE_FMT_32_xRGB ] = "xRGB/32", - [ SPICE_SURFACE_FMT_32_ARGB ] = "ARGB/32", -}; - -static const char *qxl_cursor_cmd[] = { - [ QXL_CURSOR_SET ] = "set", - [ QXL_CURSOR_MOVE ] = "move", - [ QXL_CURSOR_HIDE ] = "hide", - [ QXL_CURSOR_TRAIL ] = "trail", -}; - -static const char *spice_cursor_type[] = { - [ SPICE_CURSOR_TYPE_ALPHA ] = "alpha", - [ SPICE_CURSOR_TYPE_MONO ] = "mono", - [ SPICE_CURSOR_TYPE_COLOR4 ] = "color4", - [ SPICE_CURSOR_TYPE_COLOR8 ] = "color8", - [ SPICE_CURSOR_TYPE_COLOR16 ] = "color16", - [ SPICE_CURSOR_TYPE_COLOR24 ] = "color24", - [ SPICE_CURSOR_TYPE_COLOR32 ] = "color32", -}; - -static const char *qxl_v2n(const char *n[], size_t l, int v) -{ - if (v >= l || !n[v]) { - return "???"; - } - return n[v]; -} -#define qxl_name(_list, _value) qxl_v2n(_list, ARRAY_SIZE(_list), _value) - -static int qxl_log_image(PCIQXLDevice *qxl, QXLPHYSICAL addr, int group_id) -{ - QXLImage *image; - QXLImageDescriptor *desc; - - image = qxl_phys2virt(qxl, addr, group_id); - if (!image) { - return 1; - } - desc = &image->descriptor; - fprintf(stderr, " (id %" PRIx64 " type %d flags %d width %d height %d", - desc->id, desc->type, desc->flags, desc->width, desc->height); - switch (desc->type) { - case SPICE_IMAGE_TYPE_BITMAP: - fprintf(stderr, ", fmt %d flags %d x %d y %d stride %d" - " palette %" PRIx64 " data %" PRIx64, - image->bitmap.format, image->bitmap.flags, - image->bitmap.x, image->bitmap.y, - image->bitmap.stride, - image->bitmap.palette, image->bitmap.data); - break; - } - fprintf(stderr, ")"); - return 0; -} - -static void qxl_log_rect(QXLRect *rect) -{ - fprintf(stderr, " %dx%d+%d+%d", - rect->right - rect->left, - rect->bottom - rect->top, - rect->left, rect->top); -} - -static int qxl_log_cmd_draw_copy(PCIQXLDevice *qxl, QXLCopy *copy, - int group_id) -{ - int ret; - - fprintf(stderr, " src %" PRIx64, - copy->src_bitmap); - ret = qxl_log_image(qxl, copy->src_bitmap, group_id); - if (ret != 0) { - return ret; - } - fprintf(stderr, " area"); - qxl_log_rect(©->src_area); - fprintf(stderr, " rop %d", copy->rop_descriptor); - return 0; -} - -static int qxl_log_cmd_draw(PCIQXLDevice *qxl, QXLDrawable *draw, int group_id) -{ - fprintf(stderr, ": surface_id %d type %s effect %s", - draw->surface_id, - qxl_name(qxl_draw_type, draw->type), - qxl_name(qxl_draw_effect, draw->effect)); - switch (draw->type) { - case QXL_DRAW_COPY: - return qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id); - break; - } - return 0; -} - -static int qxl_log_cmd_draw_compat(PCIQXLDevice *qxl, QXLCompatDrawable *draw, - int group_id) -{ - fprintf(stderr, ": type %s effect %s", - qxl_name(qxl_draw_type, draw->type), - qxl_name(qxl_draw_effect, draw->effect)); - if (draw->bitmap_offset) { - fprintf(stderr, ": bitmap %d", - draw->bitmap_offset); - qxl_log_rect(&draw->bitmap_area); - } - switch (draw->type) { - case QXL_DRAW_COPY: - return qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id); - break; - } - return 0; -} - -static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd) -{ - fprintf(stderr, ": %s id %d", - qxl_name(qxl_surface_cmd, cmd->type), - cmd->surface_id); - if (cmd->type == QXL_SURFACE_CMD_CREATE) { - fprintf(stderr, " size %dx%d stride %d format %s (count %d, max %d)", - cmd->u.surface_create.width, - cmd->u.surface_create.height, - cmd->u.surface_create.stride, - qxl_name(spice_surface_fmt, cmd->u.surface_create.format), - qxl->guest_surfaces.count, qxl->guest_surfaces.max); - } - if (cmd->type == QXL_SURFACE_CMD_DESTROY) { - fprintf(stderr, " (count %d)", qxl->guest_surfaces.count); - } -} - -int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id) -{ - QXLCursor *cursor; - - fprintf(stderr, ": %s", - qxl_name(qxl_cursor_cmd, cmd->type)); - switch (cmd->type) { - case QXL_CURSOR_SET: - fprintf(stderr, " +%d+%d visible %s, shape @ 0x%" PRIx64, - cmd->u.set.position.x, - cmd->u.set.position.y, - cmd->u.set.visible ? "yes" : "no", - cmd->u.set.shape); - cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id); - if (!cursor) { - return 1; - } - fprintf(stderr, " type %s size %dx%d hot-spot +%d+%d" - " unique 0x%" PRIx64 " data-size %d", - qxl_name(spice_cursor_type, cursor->header.type), - cursor->header.width, cursor->header.height, - cursor->header.hot_spot_x, cursor->header.hot_spot_y, - cursor->header.unique, cursor->data_size); - break; - case QXL_CURSOR_MOVE: - fprintf(stderr, " +%d+%d", cmd->u.position.x, cmd->u.position.y); - break; - } - return 0; -} - -int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext) -{ - bool compat = ext->flags & QXL_COMMAND_FLAG_COMPAT; - void *data; - int ret; - - if (!qxl->cmdlog) { - return 0; - } - fprintf(stderr, "%" PRId64 " qxl-%d/%s:", qemu_get_clock_ns(vm_clock), - qxl->id, ring); - fprintf(stderr, " cmd @ 0x%" PRIx64 " %s%s", ext->cmd.data, - qxl_name(qxl_type, ext->cmd.type), - compat ? "(compat)" : ""); - - data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); - if (!data) { - return 1; - } - switch (ext->cmd.type) { - case QXL_CMD_DRAW: - if (!compat) { - ret = qxl_log_cmd_draw(qxl, data, ext->group_id); - } else { - ret = qxl_log_cmd_draw_compat(qxl, data, ext->group_id); - } - if (ret) { - return ret; - } - break; - case QXL_CMD_SURFACE: - qxl_log_cmd_surface(qxl, data); - break; - case QXL_CMD_CURSOR: - qxl_log_cmd_cursor(qxl, data, ext->group_id); - break; - } - fprintf(stderr, "\n"); - return 0; -} diff --git a/hw/qxl-render.c b/hw/qxl-render.c deleted file mode 100644 index 8cd9be434d..0000000000 --- a/hw/qxl-render.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * qxl local rendering (aka display on sdl/vnc) - * - * Copyright (C) 2010 Red Hat, Inc. - * - * maintained by Gerd Hoffmann - * - * 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 or - * (at your option) version 3 of the License. - * - * 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, see . - */ - -#include "hw/qxl.h" - -static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) -{ - DisplaySurface *surface = qemu_console_surface(qxl->vga.con); - uint8_t *dst = surface_data(surface); - uint8_t *src; - int len, i; - - if (is_buffer_shared(surface)) { - return; - } - if (!qxl->guest_primary.data) { - trace_qxl_render_blit_guest_primary_initialized(); - qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); - } - trace_qxl_render_blit(qxl->guest_primary.qxl_stride, - rect->left, rect->right, rect->top, rect->bottom); - src = qxl->guest_primary.data; - if (qxl->guest_primary.qxl_stride < 0) { - /* qxl surface is upside down, walk src scanlines - * in reverse order to flip it */ - src += (qxl->guest_primary.surface.height - rect->top - 1) * - qxl->guest_primary.abs_stride; - } else { - src += rect->top * qxl->guest_primary.abs_stride; - } - dst += rect->top * qxl->guest_primary.abs_stride; - src += rect->left * qxl->guest_primary.bytes_pp; - dst += rect->left * qxl->guest_primary.bytes_pp; - len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp; - - for (i = rect->top; i < rect->bottom; i++) { - memcpy(dst, src, len); - dst += qxl->guest_primary.abs_stride; - src += qxl->guest_primary.qxl_stride; - } -} - -void qxl_render_resize(PCIQXLDevice *qxl) -{ - QXLSurfaceCreate *sc = &qxl->guest_primary.surface; - - qxl->guest_primary.qxl_stride = sc->stride; - qxl->guest_primary.abs_stride = abs(sc->stride); - qxl->guest_primary.resized++; - switch (sc->format) { - case SPICE_SURFACE_FMT_16_555: - qxl->guest_primary.bytes_pp = 2; - qxl->guest_primary.bits_pp = 15; - break; - case SPICE_SURFACE_FMT_16_565: - qxl->guest_primary.bytes_pp = 2; - qxl->guest_primary.bits_pp = 16; - break; - case SPICE_SURFACE_FMT_32_xRGB: - case SPICE_SURFACE_FMT_32_ARGB: - qxl->guest_primary.bytes_pp = 4; - qxl->guest_primary.bits_pp = 32; - break; - default: - fprintf(stderr, "%s: unhandled format: %x\n", __FUNCTION__, - qxl->guest_primary.surface.format); - qxl->guest_primary.bytes_pp = 4; - qxl->guest_primary.bits_pp = 32; - break; - } -} - -static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area) -{ - area->left = 0; - area->right = qxl->guest_primary.surface.width; - area->top = 0; - area->bottom = qxl->guest_primary.surface.height; -} - -static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) -{ - VGACommonState *vga = &qxl->vga; - DisplaySurface *surface; - int i; - - if (qxl->guest_primary.resized) { - qxl->guest_primary.resized = 0; - qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram); - qxl_set_rect_to_surface(qxl, &qxl->dirty[0]); - qxl->num_dirty_rects = 1; - trace_qxl_render_guest_primary_resized( - qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height, - qxl->guest_primary.qxl_stride, - qxl->guest_primary.bytes_pp, - qxl->guest_primary.bits_pp); - if (qxl->guest_primary.qxl_stride > 0) { - surface = qemu_create_displaysurface_from - (qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height, - qxl->guest_primary.bits_pp, - qxl->guest_primary.abs_stride, - qxl->guest_primary.data, - false); - } else { - surface = qemu_create_displaysurface - (qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height); - } - dpy_gfx_replace_surface(vga->con, surface); - } - for (i = 0; i < qxl->num_dirty_rects; i++) { - if (qemu_spice_rect_is_empty(qxl->dirty+i)) { - break; - } - qxl_blit(qxl, qxl->dirty+i); - dpy_gfx_update(vga->con, - qxl->dirty[i].left, qxl->dirty[i].top, - qxl->dirty[i].right - qxl->dirty[i].left, - qxl->dirty[i].bottom - qxl->dirty[i].top); - } - qxl->num_dirty_rects = 0; -} - -/* - * use ssd.lock to protect render_update_cookie_num. - * qxl_render_update is called by io thread or vcpu thread, and the completion - * callbacks are called by spice_server thread, defering to bh called from the - * io thread. - */ -void qxl_render_update(PCIQXLDevice *qxl) -{ - QXLCookie *cookie; - - qemu_mutex_lock(&qxl->ssd.lock); - - if (!runstate_is_running() || !qxl->guest_primary.commands) { - qxl_render_update_area_unlocked(qxl); - qemu_mutex_unlock(&qxl->ssd.lock); - return; - } - - qxl->guest_primary.commands = 0; - qxl->render_update_cookie_num++; - qemu_mutex_unlock(&qxl->ssd.lock); - cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA, - 0); - qxl_set_rect_to_surface(qxl, &cookie->u.render.area); - qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL, - 0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie); -} - -void qxl_render_update_area_bh(void *opaque) -{ - PCIQXLDevice *qxl = opaque; - - qemu_mutex_lock(&qxl->ssd.lock); - qxl_render_update_area_unlocked(qxl); - qemu_mutex_unlock(&qxl->ssd.lock); -} - -void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie) -{ - qemu_mutex_lock(&qxl->ssd.lock); - trace_qxl_render_update_area_done(cookie); - qemu_bh_schedule(qxl->update_area_bh); - qxl->render_update_cookie_num--; - qemu_mutex_unlock(&qxl->ssd.lock); - g_free(cookie); -} - -static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor) -{ - QEMUCursor *c; - uint8_t *image, *mask; - size_t size; - - c = cursor_alloc(cursor->header.width, cursor->header.height); - c->hot_x = cursor->header.hot_spot_x; - c->hot_y = cursor->header.hot_spot_y; - switch (cursor->header.type) { - case SPICE_CURSOR_TYPE_ALPHA: - size = cursor->header.width * cursor->header.height * sizeof(uint32_t); - memcpy(c->data, cursor->chunk.data, size); - if (qxl->debug > 2) { - cursor_print_ascii_art(c, "qxl/alpha"); - } - break; - case SPICE_CURSOR_TYPE_MONO: - mask = cursor->chunk.data; - image = mask + cursor_get_mono_bpl(c) * c->width; - cursor_set_mono(c, 0xffffff, 0x000000, image, 1, mask); - if (qxl->debug > 2) { - cursor_print_ascii_art(c, "qxl/mono"); - } - break; - default: - fprintf(stderr, "%s: not implemented: type %d\n", - __FUNCTION__, cursor->header.type); - goto fail; - } - return c; - -fail: - cursor_put(c); - return NULL; -} - - -/* called from spice server thread context only */ -int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) -{ - QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); - QXLCursor *cursor; - QEMUCursor *c; - - if (!cmd) { - return 1; - } - - if (!dpy_cursor_define_supported(qxl->vga.con)) { - return 0; - } - - if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) { - fprintf(stderr, "%s", __FUNCTION__); - qxl_log_cmd_cursor(qxl, cmd, ext->group_id); - fprintf(stderr, "\n"); - } - switch (cmd->type) { - case QXL_CURSOR_SET: - cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id); - if (!cursor) { - return 1; - } - if (cursor->chunk.data_size != cursor->data_size) { - fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__); - return 1; - } - c = qxl_cursor(qxl, cursor); - if (c == NULL) { - c = cursor_builtin_left_ptr(); - } - qemu_mutex_lock(&qxl->ssd.lock); - if (qxl->ssd.cursor) { - cursor_put(qxl->ssd.cursor); - } - qxl->ssd.cursor = c; - qxl->ssd.mouse_x = cmd->u.set.position.x; - qxl->ssd.mouse_y = cmd->u.set.position.y; - qemu_mutex_unlock(&qxl->ssd.lock); - break; - case QXL_CURSOR_MOVE: - qemu_mutex_lock(&qxl->ssd.lock); - qxl->ssd.mouse_x = cmd->u.position.x; - qxl->ssd.mouse_y = cmd->u.position.y; - qemu_mutex_unlock(&qxl->ssd.lock); - break; - } - return 0; -} diff --git a/hw/qxl.c b/hw/qxl.c deleted file mode 100644 index b66b41442a..0000000000 --- a/hw/qxl.c +++ /dev/null @@ -1,2365 +0,0 @@ -/* - * Copyright (C) 2010 Red Hat, Inc. - * - * written by Yaniv Kamay, Izik Eidus, Gerd Hoffmann - * maintained by Gerd Hoffmann - * - * 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 or - * (at your option) version 3 of the License. - * - * 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, see . - */ - -#include - -#include "qemu-common.h" -#include "qemu/timer.h" -#include "qemu/queue.h" -#include "monitor/monitor.h" -#include "sysemu/sysemu.h" -#include "trace.h" - -#include "hw/qxl.h" - -/* - * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as - * such can be changed by the guest, so to avoid a guest trigerrable - * abort we just qxl_set_guest_bug and set the return to NULL. Still - * it may happen as a result of emulator bug as well. - */ -#undef SPICE_RING_PROD_ITEM -#define SPICE_RING_PROD_ITEM(qxl, r, ret) { \ - uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \ - if (prod >= ARRAY_SIZE((r)->items)) { \ - qxl_set_guest_bug(qxl, "SPICE_RING_PROD_ITEM indices mismatch " \ - "%u >= %zu", prod, ARRAY_SIZE((r)->items)); \ - ret = NULL; \ - } else { \ - ret = &(r)->items[prod].el; \ - } \ - } - -#undef SPICE_RING_CONS_ITEM -#define SPICE_RING_CONS_ITEM(qxl, r, ret) { \ - uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \ - if (cons >= ARRAY_SIZE((r)->items)) { \ - qxl_set_guest_bug(qxl, "SPICE_RING_CONS_ITEM indices mismatch " \ - "%u >= %zu", cons, ARRAY_SIZE((r)->items)); \ - ret = NULL; \ - } else { \ - ret = &(r)->items[cons].el; \ - } \ - } - -#undef ALIGN -#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1)) - -#define PIXEL_SIZE 0.2936875 //1280x1024 is 14.8" x 11.9" - -#define QXL_MODE(_x, _y, _b, _o) \ - { .x_res = _x, \ - .y_res = _y, \ - .bits = _b, \ - .stride = (_x) * (_b) / 8, \ - .x_mili = PIXEL_SIZE * (_x), \ - .y_mili = PIXEL_SIZE * (_y), \ - .orientation = _o, \ - } - -#define QXL_MODE_16_32(x_res, y_res, orientation) \ - QXL_MODE(x_res, y_res, 16, orientation), \ - QXL_MODE(x_res, y_res, 32, orientation) - -#define QXL_MODE_EX(x_res, y_res) \ - QXL_MODE_16_32(x_res, y_res, 0), \ - QXL_MODE_16_32(x_res, y_res, 1) - -static QXLMode qxl_modes[] = { - QXL_MODE_EX(640, 480), - QXL_MODE_EX(800, 480), - QXL_MODE_EX(800, 600), - QXL_MODE_EX(832, 624), - QXL_MODE_EX(960, 640), - QXL_MODE_EX(1024, 600), - QXL_MODE_EX(1024, 768), - QXL_MODE_EX(1152, 864), - QXL_MODE_EX(1152, 870), - QXL_MODE_EX(1280, 720), - QXL_MODE_EX(1280, 760), - QXL_MODE_EX(1280, 768), - QXL_MODE_EX(1280, 800), - QXL_MODE_EX(1280, 960), - QXL_MODE_EX(1280, 1024), - QXL_MODE_EX(1360, 768), - QXL_MODE_EX(1366, 768), - QXL_MODE_EX(1400, 1050), - QXL_MODE_EX(1440, 900), - QXL_MODE_EX(1600, 900), - QXL_MODE_EX(1600, 1200), - QXL_MODE_EX(1680, 1050), - QXL_MODE_EX(1920, 1080), - /* these modes need more than 8 MB video memory */ - QXL_MODE_EX(1920, 1200), - QXL_MODE_EX(1920, 1440), - QXL_MODE_EX(2048, 1536), - QXL_MODE_EX(2560, 1440), - QXL_MODE_EX(2560, 1600), - /* these modes need more than 16 MB video memory */ - QXL_MODE_EX(2560, 2048), - QXL_MODE_EX(2800, 2100), - QXL_MODE_EX(3200, 2400), -}; - -static void qxl_send_events(PCIQXLDevice *d, uint32_t events); -static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async); -static void qxl_reset_memslots(PCIQXLDevice *d); -static void qxl_reset_surfaces(PCIQXLDevice *d); -static void qxl_ring_set_dirty(PCIQXLDevice *qxl); - -void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...) -{ - trace_qxl_set_guest_bug(qxl->id); - qxl_send_events(qxl, QXL_INTERRUPT_ERROR); - qxl->guest_bug = 1; - if (qxl->guestdebug) { - va_list ap; - va_start(ap, msg); - fprintf(stderr, "qxl-%d: guest bug: ", qxl->id); - vfprintf(stderr, msg, ap); - fprintf(stderr, "\n"); - va_end(ap); - } -} - -static void qxl_clear_guest_bug(PCIQXLDevice *qxl) -{ - qxl->guest_bug = 0; -} - -void qxl_spice_update_area(PCIQXLDevice *qxl, uint32_t surface_id, - struct QXLRect *area, struct QXLRect *dirty_rects, - uint32_t num_dirty_rects, - uint32_t clear_dirty_region, - qxl_async_io async, struct QXLCookie *cookie) -{ - trace_qxl_spice_update_area(qxl->id, surface_id, area->left, area->right, - area->top, area->bottom); - trace_qxl_spice_update_area_rest(qxl->id, num_dirty_rects, - clear_dirty_region); - if (async == QXL_SYNC) { - qxl->ssd.worker->update_area(qxl->ssd.worker, surface_id, area, - dirty_rects, num_dirty_rects, clear_dirty_region); - } else { - assert(cookie != NULL); - spice_qxl_update_area_async(&qxl->ssd.qxl, surface_id, area, - clear_dirty_region, (uintptr_t)cookie); - } -} - -static void qxl_spice_destroy_surface_wait_complete(PCIQXLDevice *qxl, - uint32_t id) -{ - trace_qxl_spice_destroy_surface_wait_complete(qxl->id, id); - qemu_mutex_lock(&qxl->track_lock); - qxl->guest_surfaces.cmds[id] = 0; - qxl->guest_surfaces.count--; - qemu_mutex_unlock(&qxl->track_lock); -} - -static void qxl_spice_destroy_surface_wait(PCIQXLDevice *qxl, uint32_t id, - qxl_async_io async) -{ - QXLCookie *cookie; - - trace_qxl_spice_destroy_surface_wait(qxl->id, id, async); - if (async) { - cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_DESTROY_SURFACE_ASYNC); - cookie->u.surface_id = id; - spice_qxl_destroy_surface_async(&qxl->ssd.qxl, id, (uintptr_t)cookie); - } else { - qxl->ssd.worker->destroy_surface_wait(qxl->ssd.worker, id); - qxl_spice_destroy_surface_wait_complete(qxl, id); - } -} - -static void qxl_spice_flush_surfaces_async(PCIQXLDevice *qxl) -{ - trace_qxl_spice_flush_surfaces_async(qxl->id, qxl->guest_surfaces.count, - qxl->num_free_res); - spice_qxl_flush_surfaces_async(&qxl->ssd.qxl, - (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_FLUSH_SURFACES_ASYNC)); -} - -void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext, - uint32_t count) -{ - trace_qxl_spice_loadvm_commands(qxl->id, ext, count); - qxl->ssd.worker->loadvm_commands(qxl->ssd.worker, ext, count); -} - -void qxl_spice_oom(PCIQXLDevice *qxl) -{ - trace_qxl_spice_oom(qxl->id); - qxl->ssd.worker->oom(qxl->ssd.worker); -} - -void qxl_spice_reset_memslots(PCIQXLDevice *qxl) -{ - trace_qxl_spice_reset_memslots(qxl->id); - qxl->ssd.worker->reset_memslots(qxl->ssd.worker); -} - -static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl) -{ - trace_qxl_spice_destroy_surfaces_complete(qxl->id); - qemu_mutex_lock(&qxl->track_lock); - memset(qxl->guest_surfaces.cmds, 0, - sizeof(qxl->guest_surfaces.cmds) * qxl->ssd.num_surfaces); - qxl->guest_surfaces.count = 0; - qemu_mutex_unlock(&qxl->track_lock); -} - -static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async) -{ - trace_qxl_spice_destroy_surfaces(qxl->id, async); - if (async) { - spice_qxl_destroy_surfaces_async(&qxl->ssd.qxl, - (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_DESTROY_ALL_SURFACES_ASYNC)); - } else { - qxl->ssd.worker->destroy_surfaces(qxl->ssd.worker); - qxl_spice_destroy_surfaces_complete(qxl); - } -} - -static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay) -{ - trace_qxl_spice_monitors_config(qxl->id); - if (replay) { - /* - * don't use QXL_COOKIE_TYPE_IO: - * - we are not running yet (post_load), we will assert - * in send_events - * - this is not a guest io, but a reply, so async_io isn't set. - */ - spice_qxl_monitors_config_async(&qxl->ssd.qxl, - qxl->guest_monitors_config, - MEMSLOT_GROUP_GUEST, - (uintptr_t)qxl_cookie_new( - QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG, - 0)); - } else { - qxl->guest_monitors_config = qxl->ram->monitors_config; - spice_qxl_monitors_config_async(&qxl->ssd.qxl, - qxl->ram->monitors_config, - MEMSLOT_GROUP_GUEST, - (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_MONITORS_CONFIG_ASYNC)); - } -} - -void qxl_spice_reset_image_cache(PCIQXLDevice *qxl) -{ - trace_qxl_spice_reset_image_cache(qxl->id); - qxl->ssd.worker->reset_image_cache(qxl->ssd.worker); -} - -void qxl_spice_reset_cursor(PCIQXLDevice *qxl) -{ - trace_qxl_spice_reset_cursor(qxl->id); - qxl->ssd.worker->reset_cursor(qxl->ssd.worker); - qemu_mutex_lock(&qxl->track_lock); - qxl->guest_cursor = 0; - qemu_mutex_unlock(&qxl->track_lock); - if (qxl->ssd.cursor) { - cursor_put(qxl->ssd.cursor); - } - qxl->ssd.cursor = cursor_builtin_hidden(); -} - - -static inline uint32_t msb_mask(uint32_t val) -{ - uint32_t mask; - - do { - mask = ~(val - 1) & val; - val &= ~mask; - } while (mask < val); - - return mask; -} - -static ram_addr_t qxl_rom_size(void) -{ - uint32_t required_rom_size = sizeof(QXLRom) + sizeof(QXLModes) + - sizeof(qxl_modes); - uint32_t rom_size = 8192; /* two pages */ - - required_rom_size = MAX(required_rom_size, TARGET_PAGE_SIZE); - required_rom_size = msb_mask(required_rom_size * 2 - 1); - assert(required_rom_size <= rom_size); - return rom_size; -} - -static void init_qxl_rom(PCIQXLDevice *d) -{ - QXLRom *rom = memory_region_get_ram_ptr(&d->rom_bar); - QXLModes *modes = (QXLModes *)(rom + 1); - uint32_t ram_header_size; - uint32_t surface0_area_size; - uint32_t num_pages; - uint32_t fb; - int i, n; - - memset(rom, 0, d->rom_size); - - rom->magic = cpu_to_le32(QXL_ROM_MAGIC); - rom->id = cpu_to_le32(d->id); - rom->log_level = cpu_to_le32(d->guestdebug); - rom->modes_offset = cpu_to_le32(sizeof(QXLRom)); - - rom->slot_gen_bits = MEMSLOT_GENERATION_BITS; - rom->slot_id_bits = MEMSLOT_SLOT_BITS; - rom->slots_start = 1; - rom->slots_end = NUM_MEMSLOTS - 1; - rom->n_surfaces = cpu_to_le32(d->ssd.num_surfaces); - - for (i = 0, n = 0; i < ARRAY_SIZE(qxl_modes); i++) { - fb = qxl_modes[i].y_res * qxl_modes[i].stride; - if (fb > d->vgamem_size) { - continue; - } - modes->modes[n].id = cpu_to_le32(i); - modes->modes[n].x_res = cpu_to_le32(qxl_modes[i].x_res); - modes->modes[n].y_res = cpu_to_le32(qxl_modes[i].y_res); - modes->modes[n].bits = cpu_to_le32(qxl_modes[i].bits); - modes->modes[n].stride = cpu_to_le32(qxl_modes[i].stride); - modes->modes[n].x_mili = cpu_to_le32(qxl_modes[i].x_mili); - modes->modes[n].y_mili = cpu_to_le32(qxl_modes[i].y_mili); - modes->modes[n].orientation = cpu_to_le32(qxl_modes[i].orientation); - n++; - } - modes->n_modes = cpu_to_le32(n); - - ram_header_size = ALIGN(sizeof(QXLRam), 4096); - surface0_area_size = ALIGN(d->vgamem_size, 4096); - num_pages = d->vga.vram_size; - num_pages -= ram_header_size; - num_pages -= surface0_area_size; - num_pages = num_pages / TARGET_PAGE_SIZE; - - rom->draw_area_offset = cpu_to_le32(0); - rom->surface0_area_size = cpu_to_le32(surface0_area_size); - rom->pages_offset = cpu_to_le32(surface0_area_size); - rom->num_pages = cpu_to_le32(num_pages); - rom->ram_header_offset = cpu_to_le32(d->vga.vram_size - ram_header_size); - - d->shadow_rom = *rom; - d->rom = rom; - d->modes = modes; -} - -static void init_qxl_ram(PCIQXLDevice *d) -{ - uint8_t *buf; - uint64_t *item; - - buf = d->vga.vram_ptr; - d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset)); - d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC); - d->ram->int_pending = cpu_to_le32(0); - d->ram->int_mask = cpu_to_le32(0); - d->ram->update_surface = 0; - SPICE_RING_INIT(&d->ram->cmd_ring); - SPICE_RING_INIT(&d->ram->cursor_ring); - SPICE_RING_INIT(&d->ram->release_ring); - SPICE_RING_PROD_ITEM(d, &d->ram->release_ring, item); - assert(item); - *item = 0; - qxl_ring_set_dirty(d); -} - -/* can be called from spice server thread context */ -static void qxl_set_dirty(MemoryRegion *mr, ram_addr_t addr, ram_addr_t end) -{ - memory_region_set_dirty(mr, addr, end - addr); -} - -static void qxl_rom_set_dirty(PCIQXLDevice *qxl) -{ - qxl_set_dirty(&qxl->rom_bar, 0, qxl->rom_size); -} - -/* called from spice server thread context only */ -static void qxl_ram_set_dirty(PCIQXLDevice *qxl, void *ptr) -{ - void *base = qxl->vga.vram_ptr; - intptr_t offset; - - offset = ptr - base; - offset &= ~(TARGET_PAGE_SIZE-1); - assert(offset < qxl->vga.vram_size); - qxl_set_dirty(&qxl->vga.vram, offset, offset + TARGET_PAGE_SIZE); -} - -/* can be called from spice server thread context */ -static void qxl_ring_set_dirty(PCIQXLDevice *qxl) -{ - ram_addr_t addr = qxl->shadow_rom.ram_header_offset; - ram_addr_t end = qxl->vga.vram_size; - qxl_set_dirty(&qxl->vga.vram, addr, end); -} - -/* - * keep track of some command state, for savevm/loadvm. - * called from spice server thread context only - */ -static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) -{ - switch (le32_to_cpu(ext->cmd.type)) { - case QXL_CMD_SURFACE: - { - QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); - - if (!cmd) { - return 1; - } - uint32_t id = le32_to_cpu(cmd->surface_id); - - if (id >= qxl->ssd.num_surfaces) { - qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE id %d >= %d", id, - qxl->ssd.num_surfaces); - return 1; - } - if (cmd->type == QXL_SURFACE_CMD_CREATE && - (cmd->u.surface_create.stride & 0x03) != 0) { - qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE stride = %d %% 4 != 0\n", - cmd->u.surface_create.stride); - return 1; - } - qemu_mutex_lock(&qxl->track_lock); - if (cmd->type == QXL_SURFACE_CMD_CREATE) { - qxl->guest_surfaces.cmds[id] = ext->cmd.data; - qxl->guest_surfaces.count++; - if (qxl->guest_surfaces.max < qxl->guest_surfaces.count) - qxl->guest_surfaces.max = qxl->guest_surfaces.count; - } - if (cmd->type == QXL_SURFACE_CMD_DESTROY) { - qxl->guest_surfaces.cmds[id] = 0; - qxl->guest_surfaces.count--; - } - qemu_mutex_unlock(&qxl->track_lock); - break; - } - case QXL_CMD_CURSOR: - { - QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); - - if (!cmd) { - return 1; - } - if (cmd->type == QXL_CURSOR_SET) { - qemu_mutex_lock(&qxl->track_lock); - qxl->guest_cursor = ext->cmd.data; - qemu_mutex_unlock(&qxl->track_lock); - } - break; - } - } - return 0; -} - -/* spice display interface callbacks */ - -static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - - trace_qxl_interface_attach_worker(qxl->id); - qxl->ssd.worker = qxl_worker; -} - -static void interface_set_compression_level(QXLInstance *sin, int level) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - - trace_qxl_interface_set_compression_level(qxl->id, level); - qxl->shadow_rom.compression_level = cpu_to_le32(level); - qxl->rom->compression_level = cpu_to_le32(level); - qxl_rom_set_dirty(qxl); -} - -static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - - trace_qxl_interface_set_mm_time(qxl->id, mm_time); - qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time); - qxl->rom->mm_clock = cpu_to_le32(mm_time); - qxl_rom_set_dirty(qxl); -} - -static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - - trace_qxl_interface_get_init_info(qxl->id); - info->memslot_gen_bits = MEMSLOT_GENERATION_BITS; - info->memslot_id_bits = MEMSLOT_SLOT_BITS; - info->num_memslots = NUM_MEMSLOTS; - info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; - info->internal_groupslot_id = 0; - info->qxl_ram_size = le32_to_cpu(qxl->shadow_rom.num_pages) << TARGET_PAGE_BITS; - info->n_surfaces = qxl->ssd.num_surfaces; -} - -static const char *qxl_mode_to_string(int mode) -{ - switch (mode) { - case QXL_MODE_COMPAT: - return "compat"; - case QXL_MODE_NATIVE: - return "native"; - case QXL_MODE_UNDEFINED: - return "undefined"; - case QXL_MODE_VGA: - return "vga"; - } - return "INVALID"; -} - -static const char *io_port_to_string(uint32_t io_port) -{ - if (io_port >= QXL_IO_RANGE_SIZE) { - return "out of range"; - } - static const char *io_port_to_string[QXL_IO_RANGE_SIZE + 1] = { - [QXL_IO_NOTIFY_CMD] = "QXL_IO_NOTIFY_CMD", - [QXL_IO_NOTIFY_CURSOR] = "QXL_IO_NOTIFY_CURSOR", - [QXL_IO_UPDATE_AREA] = "QXL_IO_UPDATE_AREA", - [QXL_IO_UPDATE_IRQ] = "QXL_IO_UPDATE_IRQ", - [QXL_IO_NOTIFY_OOM] = "QXL_IO_NOTIFY_OOM", - [QXL_IO_RESET] = "QXL_IO_RESET", - [QXL_IO_SET_MODE] = "QXL_IO_SET_MODE", - [QXL_IO_LOG] = "QXL_IO_LOG", - [QXL_IO_MEMSLOT_ADD] = "QXL_IO_MEMSLOT_ADD", - [QXL_IO_MEMSLOT_DEL] = "QXL_IO_MEMSLOT_DEL", - [QXL_IO_DETACH_PRIMARY] = "QXL_IO_DETACH_PRIMARY", - [QXL_IO_ATTACH_PRIMARY] = "QXL_IO_ATTACH_PRIMARY", - [QXL_IO_CREATE_PRIMARY] = "QXL_IO_CREATE_PRIMARY", - [QXL_IO_DESTROY_PRIMARY] = "QXL_IO_DESTROY_PRIMARY", - [QXL_IO_DESTROY_SURFACE_WAIT] = "QXL_IO_DESTROY_SURFACE_WAIT", - [QXL_IO_DESTROY_ALL_SURFACES] = "QXL_IO_DESTROY_ALL_SURFACES", - [QXL_IO_UPDATE_AREA_ASYNC] = "QXL_IO_UPDATE_AREA_ASYNC", - [QXL_IO_MEMSLOT_ADD_ASYNC] = "QXL_IO_MEMSLOT_ADD_ASYNC", - [QXL_IO_CREATE_PRIMARY_ASYNC] = "QXL_IO_CREATE_PRIMARY_ASYNC", - [QXL_IO_DESTROY_PRIMARY_ASYNC] = "QXL_IO_DESTROY_PRIMARY_ASYNC", - [QXL_IO_DESTROY_SURFACE_ASYNC] = "QXL_IO_DESTROY_SURFACE_ASYNC", - [QXL_IO_DESTROY_ALL_SURFACES_ASYNC] - = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC", - [QXL_IO_FLUSH_SURFACES_ASYNC] = "QXL_IO_FLUSH_SURFACES_ASYNC", - [QXL_IO_FLUSH_RELEASE] = "QXL_IO_FLUSH_RELEASE", - [QXL_IO_MONITORS_CONFIG_ASYNC] = "QXL_IO_MONITORS_CONFIG_ASYNC", - }; - return io_port_to_string[io_port]; -} - -/* called from spice server thread context only */ -static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - SimpleSpiceUpdate *update; - QXLCommandRing *ring; - QXLCommand *cmd; - int notify, ret; - - trace_qxl_ring_command_check(qxl->id, qxl_mode_to_string(qxl->mode)); - - switch (qxl->mode) { - case QXL_MODE_VGA: - ret = false; - qemu_mutex_lock(&qxl->ssd.lock); - update = QTAILQ_FIRST(&qxl->ssd.updates); - if (update != NULL) { - QTAILQ_REMOVE(&qxl->ssd.updates, update, next); - *ext = update->ext; - ret = true; - } - qemu_mutex_unlock(&qxl->ssd.lock); - if (ret) { - trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode)); - qxl_log_command(qxl, "vga", ext); - } - return ret; - case QXL_MODE_COMPAT: - case QXL_MODE_NATIVE: - case QXL_MODE_UNDEFINED: - ring = &qxl->ram->cmd_ring; - if (qxl->guest_bug || SPICE_RING_IS_EMPTY(ring)) { - return false; - } - SPICE_RING_CONS_ITEM(qxl, ring, cmd); - if (!cmd) { - return false; - } - ext->cmd = *cmd; - ext->group_id = MEMSLOT_GROUP_GUEST; - ext->flags = qxl->cmdflags; - SPICE_RING_POP(ring, notify); - qxl_ring_set_dirty(qxl); - if (notify) { - qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY); - } - qxl->guest_primary.commands++; - qxl_track_command(qxl, ext); - qxl_log_command(qxl, "cmd", ext); - trace_qxl_ring_command_get(qxl->id, qxl_mode_to_string(qxl->mode)); - return true; - default: - return false; - } -} - -/* called from spice server thread context only */ -static int interface_req_cmd_notification(QXLInstance *sin) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - int wait = 1; - - trace_qxl_ring_command_req_notification(qxl->id); - switch (qxl->mode) { - case QXL_MODE_COMPAT: - case QXL_MODE_NATIVE: - case QXL_MODE_UNDEFINED: - SPICE_RING_CONS_WAIT(&qxl->ram->cmd_ring, wait); - qxl_ring_set_dirty(qxl); - break; - default: - /* nothing */ - break; - } - return wait; -} - -/* called from spice server thread context only */ -static inline void qxl_push_free_res(PCIQXLDevice *d, int flush) -{ - QXLReleaseRing *ring = &d->ram->release_ring; - uint64_t *item; - int notify; - -#define QXL_FREE_BUNCH_SIZE 32 - - if (ring->prod - ring->cons + 1 == ring->num_items) { - /* ring full -- can't push */ - return; - } - if (!flush && d->oom_running) { - /* collect everything from oom handler before pushing */ - return; - } - if (!flush && d->num_free_res < QXL_FREE_BUNCH_SIZE) { - /* collect a bit more before pushing */ - return; - } - - SPICE_RING_PUSH(ring, notify); - trace_qxl_ring_res_push(d->id, qxl_mode_to_string(d->mode), - d->guest_surfaces.count, d->num_free_res, - d->last_release, notify ? "yes" : "no"); - trace_qxl_ring_res_push_rest(d->id, ring->prod - ring->cons, - ring->num_items, ring->prod, ring->cons); - if (notify) { - qxl_send_events(d, QXL_INTERRUPT_DISPLAY); - } - SPICE_RING_PROD_ITEM(d, ring, item); - if (!item) { - return; - } - *item = 0; - d->num_free_res = 0; - d->last_release = NULL; - qxl_ring_set_dirty(d); -} - -/* called from spice server thread context only */ -static void interface_release_resource(QXLInstance *sin, - struct QXLReleaseInfoExt ext) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - QXLReleaseRing *ring; - uint64_t *item, id; - - if (ext.group_id == MEMSLOT_GROUP_HOST) { - /* host group -> vga mode update request */ - qemu_spice_destroy_update(&qxl->ssd, (void *)(intptr_t)ext.info->id); - return; - } - - /* - * ext->info points into guest-visible memory - * pci bar 0, $command.release_info - */ - ring = &qxl->ram->release_ring; - SPICE_RING_PROD_ITEM(qxl, ring, item); - if (!item) { - return; - } - if (*item == 0) { - /* stick head into the ring */ - id = ext.info->id; - ext.info->next = 0; - qxl_ram_set_dirty(qxl, &ext.info->next); - *item = id; - qxl_ring_set_dirty(qxl); - } else { - /* append item to the list */ - qxl->last_release->next = ext.info->id; - qxl_ram_set_dirty(qxl, &qxl->last_release->next); - ext.info->next = 0; - qxl_ram_set_dirty(qxl, &ext.info->next); - } - qxl->last_release = ext.info; - qxl->num_free_res++; - trace_qxl_ring_res_put(qxl->id, qxl->num_free_res); - qxl_push_free_res(qxl, 0); -} - -/* called from spice server thread context only */ -static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - QXLCursorRing *ring; - QXLCommand *cmd; - int notify; - - trace_qxl_ring_cursor_check(qxl->id, qxl_mode_to_string(qxl->mode)); - - switch (qxl->mode) { - case QXL_MODE_COMPAT: - case QXL_MODE_NATIVE: - case QXL_MODE_UNDEFINED: - ring = &qxl->ram->cursor_ring; - if (SPICE_RING_IS_EMPTY(ring)) { - return false; - } - SPICE_RING_CONS_ITEM(qxl, ring, cmd); - if (!cmd) { - return false; - } - ext->cmd = *cmd; - ext->group_id = MEMSLOT_GROUP_GUEST; - ext->flags = qxl->cmdflags; - SPICE_RING_POP(ring, notify); - qxl_ring_set_dirty(qxl); - if (notify) { - qxl_send_events(qxl, QXL_INTERRUPT_CURSOR); - } - qxl->guest_primary.commands++; - qxl_track_command(qxl, ext); - qxl_log_command(qxl, "csr", ext); - if (qxl->id == 0) { - qxl_render_cursor(qxl, ext); - } - trace_qxl_ring_cursor_get(qxl->id, qxl_mode_to_string(qxl->mode)); - return true; - default: - return false; - } -} - -/* called from spice server thread context only */ -static int interface_req_cursor_notification(QXLInstance *sin) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - int wait = 1; - - trace_qxl_ring_cursor_req_notification(qxl->id); - switch (qxl->mode) { - case QXL_MODE_COMPAT: - case QXL_MODE_NATIVE: - case QXL_MODE_UNDEFINED: - SPICE_RING_CONS_WAIT(&qxl->ram->cursor_ring, wait); - qxl_ring_set_dirty(qxl); - break; - default: - /* nothing */ - break; - } - return wait; -} - -/* called from spice server thread context */ -static void interface_notify_update(QXLInstance *sin, uint32_t update_id) -{ - /* - * Called by spice-server as a result of a QXL_CMD_UPDATE which is not in - * use by xf86-video-qxl and is defined out in the qxl windows driver. - * Probably was at some earlier version that is prior to git start (2009), - * and is still guest trigerrable. - */ - fprintf(stderr, "%s: deprecated\n", __func__); -} - -/* called from spice server thread context only */ -static int interface_flush_resources(QXLInstance *sin) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - int ret; - - ret = qxl->num_free_res; - if (ret) { - qxl_push_free_res(qxl, 1); - } - return ret; -} - -static void qxl_create_guest_primary_complete(PCIQXLDevice *d); - -/* called from spice server thread context only */ -static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie) -{ - uint32_t current_async; - - qemu_mutex_lock(&qxl->async_lock); - current_async = qxl->current_async; - qxl->current_async = QXL_UNDEFINED_IO; - qemu_mutex_unlock(&qxl->async_lock); - - trace_qxl_interface_async_complete_io(qxl->id, current_async, cookie); - if (!cookie) { - fprintf(stderr, "qxl: %s: error, cookie is NULL\n", __func__); - return; - } - if (cookie && current_async != cookie->io) { - fprintf(stderr, - "qxl: %s: error: current_async = %d != %" - PRId64 " = cookie->io\n", __func__, current_async, cookie->io); - } - switch (current_async) { - case QXL_IO_MEMSLOT_ADD_ASYNC: - case QXL_IO_DESTROY_PRIMARY_ASYNC: - case QXL_IO_UPDATE_AREA_ASYNC: - case QXL_IO_FLUSH_SURFACES_ASYNC: - case QXL_IO_MONITORS_CONFIG_ASYNC: - break; - case QXL_IO_CREATE_PRIMARY_ASYNC: - qxl_create_guest_primary_complete(qxl); - break; - case QXL_IO_DESTROY_ALL_SURFACES_ASYNC: - qxl_spice_destroy_surfaces_complete(qxl); - break; - case QXL_IO_DESTROY_SURFACE_ASYNC: - qxl_spice_destroy_surface_wait_complete(qxl, cookie->u.surface_id); - break; - default: - fprintf(stderr, "qxl: %s: unexpected current_async %d\n", __func__, - current_async); - } - qxl_send_events(qxl, QXL_INTERRUPT_IO_CMD); -} - -/* called from spice server thread context only */ -static void interface_update_area_complete(QXLInstance *sin, - uint32_t surface_id, - QXLRect *dirty, uint32_t num_updated_rects) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - int i; - int qxl_i; - - qemu_mutex_lock(&qxl->ssd.lock); - if (surface_id != 0 || !qxl->render_update_cookie_num) { - qemu_mutex_unlock(&qxl->ssd.lock); - return; - } - trace_qxl_interface_update_area_complete(qxl->id, surface_id, dirty->left, - dirty->right, dirty->top, dirty->bottom); - trace_qxl_interface_update_area_complete_rest(qxl->id, num_updated_rects); - if (qxl->num_dirty_rects + num_updated_rects > QXL_NUM_DIRTY_RECTS) { - /* - * overflow - treat this as a full update. Not expected to be common. - */ - trace_qxl_interface_update_area_complete_overflow(qxl->id, - QXL_NUM_DIRTY_RECTS); - qxl->guest_primary.resized = 1; - } - if (qxl->guest_primary.resized) { - /* - * Don't bother copying or scheduling the bh since we will flip - * the whole area anyway on completion of the update_area async call - */ - qemu_mutex_unlock(&qxl->ssd.lock); - return; - } - qxl_i = qxl->num_dirty_rects; - for (i = 0; i < num_updated_rects; i++) { - qxl->dirty[qxl_i++] = dirty[i]; - } - qxl->num_dirty_rects += num_updated_rects; - trace_qxl_interface_update_area_complete_schedule_bh(qxl->id, - qxl->num_dirty_rects); - qemu_bh_schedule(qxl->update_area_bh); - qemu_mutex_unlock(&qxl->ssd.lock); -} - -/* called from spice server thread context only */ -static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - QXLCookie *cookie = (QXLCookie *)(uintptr_t)cookie_token; - - switch (cookie->type) { - case QXL_COOKIE_TYPE_IO: - interface_async_complete_io(qxl, cookie); - g_free(cookie); - break; - case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA: - qxl_render_update_area_done(qxl, cookie); - break; - case QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG: - break; - default: - fprintf(stderr, "qxl: %s: unexpected cookie type %d\n", - __func__, cookie->type); - g_free(cookie); - } -} - -/* called from spice server thread context only */ -static void interface_set_client_capabilities(QXLInstance *sin, - uint8_t client_present, - uint8_t caps[58]) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - - if (qxl->revision < 4) { - trace_qxl_set_client_capabilities_unsupported_by_revision(qxl->id, - qxl->revision); - return; - } - - if (runstate_check(RUN_STATE_INMIGRATE) || - runstate_check(RUN_STATE_POSTMIGRATE)) { - return; - } - - qxl->shadow_rom.client_present = client_present; - memcpy(qxl->shadow_rom.client_capabilities, caps, - sizeof(qxl->shadow_rom.client_capabilities)); - qxl->rom->client_present = client_present; - memcpy(qxl->rom->client_capabilities, caps, - sizeof(qxl->rom->client_capabilities)); - qxl_rom_set_dirty(qxl); - - qxl_send_events(qxl, QXL_INTERRUPT_CLIENT); -} - -static uint32_t qxl_crc32(const uint8_t *p, unsigned len) -{ - /* - * zlib xors the seed with 0xffffffff, and xors the result - * again with 0xffffffff; Both are not done with linux's crc32, - * which we want to be compatible with, so undo that. - */ - return crc32(0xffffffff, p, len) ^ 0xffffffff; -} - -/* called from main context only */ -static int interface_client_monitors_config(QXLInstance *sin, - VDAgentMonitorsConfig *monitors_config) -{ - PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); - QXLRom *rom = memory_region_get_ram_ptr(&qxl->rom_bar); - int i; - - if (qxl->revision < 4) { - trace_qxl_client_monitors_config_unsupported_by_device(qxl->id, - qxl->revision); - return 0; - } - /* - * Older windows drivers set int_mask to 0 when their ISR is called, - * then later set it to ~0. So it doesn't relate to the actual interrupts - * handled. However, they are old, so clearly they don't support this - * interrupt - */ - if (qxl->ram->int_mask == 0 || qxl->ram->int_mask == ~0 || - !(qxl->ram->int_mask & QXL_INTERRUPT_CLIENT_MONITORS_CONFIG)) { - trace_qxl_client_monitors_config_unsupported_by_guest(qxl->id, - qxl->ram->int_mask, - monitors_config); - return 0; - } - if (!monitors_config) { - return 1; - } - memset(&rom->client_monitors_config, 0, - sizeof(rom->client_monitors_config)); - rom->client_monitors_config.count = monitors_config->num_of_monitors; - /* monitors_config->flags ignored */ - if (rom->client_monitors_config.count >= - ARRAY_SIZE(rom->client_monitors_config.heads)) { - trace_qxl_client_monitors_config_capped(qxl->id, - monitors_config->num_of_monitors, - ARRAY_SIZE(rom->client_monitors_config.heads)); - rom->client_monitors_config.count = - ARRAY_SIZE(rom->client_monitors_config.heads); - } - for (i = 0 ; i < rom->client_monitors_config.count ; ++i) { - VDAgentMonConfig *monitor = &monitors_config->monitors[i]; - QXLURect *rect = &rom->client_monitors_config.heads[i]; - /* monitor->depth ignored */ - rect->left = monitor->x; - rect->top = monitor->y; - rect->right = monitor->x + monitor->width; - rect->bottom = monitor->y + monitor->height; - } - rom->client_monitors_config_crc = qxl_crc32( - (const uint8_t *)&rom->client_monitors_config, - sizeof(rom->client_monitors_config)); - trace_qxl_client_monitors_config_crc(qxl->id, - sizeof(rom->client_monitors_config), - rom->client_monitors_config_crc); - - trace_qxl_interrupt_client_monitors_config(qxl->id, - rom->client_monitors_config.count, - rom->client_monitors_config.heads); - qxl_send_events(qxl, QXL_INTERRUPT_CLIENT_MONITORS_CONFIG); - return 1; -} - -static const QXLInterface qxl_interface = { - .base.type = SPICE_INTERFACE_QXL, - .base.description = "qxl gpu", - .base.major_version = SPICE_INTERFACE_QXL_MAJOR, - .base.minor_version = SPICE_INTERFACE_QXL_MINOR, - - .attache_worker = interface_attach_worker, - .set_compression_level = interface_set_compression_level, - .set_mm_time = interface_set_mm_time, - .get_init_info = interface_get_init_info, - - /* the callbacks below are called from spice server thread context */ - .get_command = interface_get_command, - .req_cmd_notification = interface_req_cmd_notification, - .release_resource = interface_release_resource, - .get_cursor_command = interface_get_cursor_command, - .req_cursor_notification = interface_req_cursor_notification, - .notify_update = interface_notify_update, - .flush_resources = interface_flush_resources, - .async_complete = interface_async_complete, - .update_area_complete = interface_update_area_complete, - .set_client_capabilities = interface_set_client_capabilities, - .client_monitors_config = interface_client_monitors_config, -}; - -static void qxl_enter_vga_mode(PCIQXLDevice *d) -{ - if (d->mode == QXL_MODE_VGA) { - return; - } - trace_qxl_enter_vga_mode(d->id); - qemu_spice_create_host_primary(&d->ssd); - d->mode = QXL_MODE_VGA; - vga_dirty_log_start(&d->vga); - vga_hw_update(); -} - -static void qxl_exit_vga_mode(PCIQXLDevice *d) -{ - if (d->mode != QXL_MODE_VGA) { - return; - } - trace_qxl_exit_vga_mode(d->id); - vga_dirty_log_stop(&d->vga); - qxl_destroy_primary(d, QXL_SYNC); -} - -static void qxl_update_irq(PCIQXLDevice *d) -{ - uint32_t pending = le32_to_cpu(d->ram->int_pending); - uint32_t mask = le32_to_cpu(d->ram->int_mask); - int level = !!(pending & mask); - qemu_set_irq(d->pci.irq[0], level); - qxl_ring_set_dirty(d); -} - -static void qxl_check_state(PCIQXLDevice *d) -{ - QXLRam *ram = d->ram; - int spice_display_running = qemu_spice_display_is_running(&d->ssd); - - assert(!spice_display_running || SPICE_RING_IS_EMPTY(&ram->cmd_ring)); - assert(!spice_display_running || SPICE_RING_IS_EMPTY(&ram->cursor_ring)); -} - -static void qxl_reset_state(PCIQXLDevice *d) -{ - QXLRom *rom = d->rom; - - qxl_check_state(d); - d->shadow_rom.update_id = cpu_to_le32(0); - *rom = d->shadow_rom; - qxl_rom_set_dirty(d); - init_qxl_ram(d); - d->num_free_res = 0; - d->last_release = NULL; - memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty)); -} - -static void qxl_soft_reset(PCIQXLDevice *d) -{ - trace_qxl_soft_reset(d->id); - qxl_check_state(d); - qxl_clear_guest_bug(d); - d->current_async = QXL_UNDEFINED_IO; - - if (d->id == 0) { - qxl_enter_vga_mode(d); - } else { - d->mode = QXL_MODE_UNDEFINED; - } -} - -static void qxl_hard_reset(PCIQXLDevice *d, int loadvm) -{ - trace_qxl_hard_reset(d->id, loadvm); - - qxl_spice_reset_cursor(d); - qxl_spice_reset_image_cache(d); - qxl_reset_surfaces(d); - qxl_reset_memslots(d); - - /* pre loadvm reset must not touch QXLRam. This lives in - * device memory, is migrated together with RAM and thus - * already loaded at this point */ - if (!loadvm) { - qxl_reset_state(d); - } - qemu_spice_create_host_memslot(&d->ssd); - qxl_soft_reset(d); -} - -static void qxl_reset_handler(DeviceState *dev) -{ - PCIQXLDevice *d = DO_UPCAST(PCIQXLDevice, pci.qdev, dev); - - qxl_hard_reset(d, 0); -} - -static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) -{ - VGACommonState *vga = opaque; - PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga); - - trace_qxl_io_write_vga(qxl->id, qxl_mode_to_string(qxl->mode), addr, val); - if (qxl->mode != QXL_MODE_VGA) { - qxl_destroy_primary(qxl, QXL_SYNC); - qxl_soft_reset(qxl); - } - vga_ioport_write(opaque, addr, val); -} - -static const MemoryRegionPortio qxl_vga_portio_list[] = { - { 0x04, 2, 1, .read = vga_ioport_read, - .write = qxl_vga_ioport_write }, /* 3b4 */ - { 0x0a, 1, 1, .read = vga_ioport_read, - .write = qxl_vga_ioport_write }, /* 3ba */ - { 0x10, 16, 1, .read = vga_ioport_read, - .write = qxl_vga_ioport_write }, /* 3c0 */ - { 0x24, 2, 1, .read = vga_ioport_read, - .write = qxl_vga_ioport_write }, /* 3d4 */ - { 0x2a, 1, 1, .read = vga_ioport_read, - .write = qxl_vga_ioport_write }, /* 3da */ - PORTIO_END_OF_LIST(), -}; - -static int qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta, - qxl_async_io async) -{ - static const int regions[] = { - QXL_RAM_RANGE_INDEX, - QXL_VRAM_RANGE_INDEX, - QXL_VRAM64_RANGE_INDEX, - }; - uint64_t guest_start; - uint64_t guest_end; - int pci_region; - pcibus_t pci_start; - pcibus_t pci_end; - intptr_t virt_start; - QXLDevMemSlot memslot; - int i; - - guest_start = le64_to_cpu(d->guest_slots[slot_id].slot.mem_start); - guest_end = le64_to_cpu(d->guest_slots[slot_id].slot.mem_end); - - trace_qxl_memslot_add_guest(d->id, slot_id, guest_start, guest_end); - - if (slot_id >= NUM_MEMSLOTS) { - qxl_set_guest_bug(d, "%s: slot_id >= NUM_MEMSLOTS %d >= %d", __func__, - slot_id, NUM_MEMSLOTS); - return 1; - } - if (guest_start > guest_end) { - qxl_set_guest_bug(d, "%s: guest_start > guest_end 0x%" PRIx64 - " > 0x%" PRIx64, __func__, guest_start, guest_end); - return 1; - } - - for (i = 0; i < ARRAY_SIZE(regions); i++) { - pci_region = regions[i]; - pci_start = d->pci.io_regions[pci_region].addr; - pci_end = pci_start + d->pci.io_regions[pci_region].size; - /* mapped? */ - if (pci_start == -1) { - continue; - } - /* start address in range ? */ - if (guest_start < pci_start || guest_start > pci_end) { - continue; - } - /* end address in range ? */ - if (guest_end > pci_end) { - continue; - } - /* passed */ - break; - } - if (i == ARRAY_SIZE(regions)) { - qxl_set_guest_bug(d, "%s: finished loop without match", __func__); - return 1; - } - - switch (pci_region) { - case QXL_RAM_RANGE_INDEX: - virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vga.vram); - break; - case QXL_VRAM_RANGE_INDEX: - case 4 /* vram 64bit */: - virt_start = (intptr_t)memory_region_get_ram_ptr(&d->vram_bar); - break; - default: - /* should not happen */ - qxl_set_guest_bug(d, "%s: pci_region = %d", __func__, pci_region); - return 1; - } - - memslot.slot_id = slot_id; - memslot.slot_group_id = MEMSLOT_GROUP_GUEST; /* guest group */ - memslot.virt_start = virt_start + (guest_start - pci_start); - memslot.virt_end = virt_start + (guest_end - pci_start); - memslot.addr_delta = memslot.virt_start - delta; - memslot.generation = d->rom->slot_generation = 0; - qxl_rom_set_dirty(d); - - qemu_spice_add_memslot(&d->ssd, &memslot, async); - d->guest_slots[slot_id].ptr = (void*)memslot.virt_start; - d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start; - d->guest_slots[slot_id].delta = delta; - d->guest_slots[slot_id].active = 1; - return 0; -} - -static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id) -{ - qemu_spice_del_memslot(&d->ssd, MEMSLOT_GROUP_HOST, slot_id); - d->guest_slots[slot_id].active = 0; -} - -static void qxl_reset_memslots(PCIQXLDevice *d) -{ - qxl_spice_reset_memslots(d); - memset(&d->guest_slots, 0, sizeof(d->guest_slots)); -} - -static void qxl_reset_surfaces(PCIQXLDevice *d) -{ - trace_qxl_reset_surfaces(d->id); - d->mode = QXL_MODE_UNDEFINED; - qxl_spice_destroy_surfaces(d, QXL_SYNC); -} - -/* can be also called from spice server thread context */ -void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) -{ - uint64_t phys = le64_to_cpu(pqxl); - uint32_t slot = (phys >> (64 - 8)) & 0xff; - uint64_t offset = phys & 0xffffffffffff; - - switch (group_id) { - case MEMSLOT_GROUP_HOST: - return (void *)(intptr_t)offset; - case MEMSLOT_GROUP_GUEST: - if (slot >= NUM_MEMSLOTS) { - qxl_set_guest_bug(qxl, "slot too large %d >= %d", slot, - NUM_MEMSLOTS); - return NULL; - } - if (!qxl->guest_slots[slot].active) { - qxl_set_guest_bug(qxl, "inactive slot %d\n", slot); - return NULL; - } - if (offset < qxl->guest_slots[slot].delta) { - qxl_set_guest_bug(qxl, - "slot %d offset %"PRIu64" < delta %"PRIu64"\n", - slot, offset, qxl->guest_slots[slot].delta); - return NULL; - } - offset -= qxl->guest_slots[slot].delta; - if (offset > qxl->guest_slots[slot].size) { - qxl_set_guest_bug(qxl, - "slot %d offset %"PRIu64" > size %"PRIu64"\n", - slot, offset, qxl->guest_slots[slot].size); - return NULL; - } - return qxl->guest_slots[slot].ptr + offset; - } - return NULL; -} - -static void qxl_create_guest_primary_complete(PCIQXLDevice *qxl) -{ - /* for local rendering */ - qxl_render_resize(qxl); -} - -static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, - qxl_async_io async) -{ - QXLDevSurfaceCreate surface; - QXLSurfaceCreate *sc = &qxl->guest_primary.surface; - int size; - int requested_height = le32_to_cpu(sc->height); - int requested_stride = le32_to_cpu(sc->stride); - - size = abs(requested_stride) * requested_height; - if (size > qxl->vgamem_size) { - qxl_set_guest_bug(qxl, "%s: requested primary larger then framebuffer" - " size", __func__); - return; - } - - if (qxl->mode == QXL_MODE_NATIVE) { - qxl_set_guest_bug(qxl, "%s: nop since already in QXL_MODE_NATIVE", - __func__); - } - qxl_exit_vga_mode(qxl); - - surface.format = le32_to_cpu(sc->format); - surface.height = le32_to_cpu(sc->height); - surface.mem = le64_to_cpu(sc->mem); - surface.position = le32_to_cpu(sc->position); - surface.stride = le32_to_cpu(sc->stride); - surface.width = le32_to_cpu(sc->width); - surface.type = le32_to_cpu(sc->type); - surface.flags = le32_to_cpu(sc->flags); - trace_qxl_create_guest_primary(qxl->id, sc->width, sc->height, sc->mem, - sc->format, sc->position); - trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type, - sc->flags); - - if ((surface.stride & 0x3) != 0) { - qxl_set_guest_bug(qxl, "primary surface stride = %d %% 4 != 0", - surface.stride); - return; - } - - surface.mouse_mode = true; - surface.group_id = MEMSLOT_GROUP_GUEST; - if (loadvm) { - surface.flags |= QXL_SURF_FLAG_KEEP_DATA; - } - - qxl->mode = QXL_MODE_NATIVE; - qxl->cmdflags = 0; - qemu_spice_create_primary_surface(&qxl->ssd, 0, &surface, async); - - if (async == QXL_SYNC) { - qxl_create_guest_primary_complete(qxl); - } -} - -/* return 1 if surface destoy was initiated (in QXL_ASYNC case) or - * done (in QXL_SYNC case), 0 otherwise. */ -static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async) -{ - if (d->mode == QXL_MODE_UNDEFINED) { - return 0; - } - trace_qxl_destroy_primary(d->id); - d->mode = QXL_MODE_UNDEFINED; - qemu_spice_destroy_primary_surface(&d->ssd, 0, async); - qxl_spice_reset_cursor(d); - return 1; -} - -static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) -{ - pcibus_t start = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr; - pcibus_t end = d->pci.io_regions[QXL_RAM_RANGE_INDEX].size + start; - QXLMode *mode = d->modes->modes + modenr; - uint64_t devmem = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr; - QXLMemSlot slot = { - .mem_start = start, - .mem_end = end - }; - QXLSurfaceCreate surface = { - .width = mode->x_res, - .height = mode->y_res, - .stride = -mode->x_res * 4, - .format = SPICE_SURFACE_FMT_32_xRGB, - .flags = loadvm ? QXL_SURF_FLAG_KEEP_DATA : 0, - .mouse_mode = true, - .mem = devmem + d->shadow_rom.draw_area_offset, - }; - - trace_qxl_set_mode(d->id, modenr, mode->x_res, mode->y_res, mode->bits, - devmem); - if (!loadvm) { - qxl_hard_reset(d, 0); - } - - d->guest_slots[0].slot = slot; - assert(qxl_add_memslot(d, 0, devmem, QXL_SYNC) == 0); - - d->guest_primary.surface = surface; - qxl_create_guest_primary(d, 0, QXL_SYNC); - - d->mode = QXL_MODE_COMPAT; - d->cmdflags = QXL_COMMAND_FLAG_COMPAT; - if (mode->bits == 16) { - d->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP; - } - d->shadow_rom.mode = cpu_to_le32(modenr); - d->rom->mode = cpu_to_le32(modenr); - qxl_rom_set_dirty(d); -} - -static void ioport_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - PCIQXLDevice *d = opaque; - uint32_t io_port = addr; - qxl_async_io async = QXL_SYNC; - uint32_t orig_io_port = io_port; - - if (d->guest_bug && io_port != QXL_IO_RESET) { - return; - } - - if (d->revision <= QXL_REVISION_STABLE_V10 && - io_port > QXL_IO_FLUSH_RELEASE) { - qxl_set_guest_bug(d, "unsupported io %d for revision %d\n", - io_port, d->revision); - return; - } - - switch (io_port) { - case QXL_IO_RESET: - case QXL_IO_SET_MODE: - case QXL_IO_MEMSLOT_ADD: - case QXL_IO_MEMSLOT_DEL: - case QXL_IO_CREATE_PRIMARY: - case QXL_IO_UPDATE_IRQ: - case QXL_IO_LOG: - case QXL_IO_MEMSLOT_ADD_ASYNC: - case QXL_IO_CREATE_PRIMARY_ASYNC: - break; - default: - if (d->mode != QXL_MODE_VGA) { - break; - } - trace_qxl_io_unexpected_vga_mode(d->id, - addr, val, io_port_to_string(io_port)); - /* be nice to buggy guest drivers */ - if (io_port >= QXL_IO_UPDATE_AREA_ASYNC && - io_port < QXL_IO_RANGE_SIZE) { - qxl_send_events(d, QXL_INTERRUPT_IO_CMD); - } - return; - } - - /* we change the io_port to avoid ifdeffery in the main switch */ - orig_io_port = io_port; - switch (io_port) { - case QXL_IO_UPDATE_AREA_ASYNC: - io_port = QXL_IO_UPDATE_AREA; - goto async_common; - case QXL_IO_MEMSLOT_ADD_ASYNC: - io_port = QXL_IO_MEMSLOT_ADD; - goto async_common; - case QXL_IO_CREATE_PRIMARY_ASYNC: - io_port = QXL_IO_CREATE_PRIMARY; - goto async_common; - case QXL_IO_DESTROY_PRIMARY_ASYNC: - io_port = QXL_IO_DESTROY_PRIMARY; - goto async_common; - case QXL_IO_DESTROY_SURFACE_ASYNC: - io_port = QXL_IO_DESTROY_SURFACE_WAIT; - goto async_common; - case QXL_IO_DESTROY_ALL_SURFACES_ASYNC: - io_port = QXL_IO_DESTROY_ALL_SURFACES; - goto async_common; - case QXL_IO_FLUSH_SURFACES_ASYNC: - case QXL_IO_MONITORS_CONFIG_ASYNC: -async_common: - async = QXL_ASYNC; - qemu_mutex_lock(&d->async_lock); - if (d->current_async != QXL_UNDEFINED_IO) { - qxl_set_guest_bug(d, "%d async started before last (%d) complete", - io_port, d->current_async); - qemu_mutex_unlock(&d->async_lock); - return; - } - d->current_async = orig_io_port; - qemu_mutex_unlock(&d->async_lock); - break; - default: - break; - } - trace_qxl_io_write(d->id, qxl_mode_to_string(d->mode), addr, val, size, - async); - - switch (io_port) { - case QXL_IO_UPDATE_AREA: - { - QXLCookie *cookie = NULL; - QXLRect update = d->ram->update_area; - - if (d->ram->update_surface > d->ssd.num_surfaces) { - qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: invalid surface id %d\n", - d->ram->update_surface); - break; - } - if (update.left >= update.right || update.top >= update.bottom || - update.left < 0 || update.top < 0) { - qxl_set_guest_bug(d, - "QXL_IO_UPDATE_AREA: invalid area (%ux%u)x(%ux%u)\n", - update.left, update.top, update.right, update.bottom); - break; - } - if (async == QXL_ASYNC) { - cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO, - QXL_IO_UPDATE_AREA_ASYNC); - cookie->u.area = update; - } - qxl_spice_update_area(d, d->ram->update_surface, - cookie ? &cookie->u.area : &update, - NULL, 0, 0, async, cookie); - break; - } - case QXL_IO_NOTIFY_CMD: - qemu_spice_wakeup(&d->ssd); - break; - case QXL_IO_NOTIFY_CURSOR: - qemu_spice_wakeup(&d->ssd); - break; - case QXL_IO_UPDATE_IRQ: - qxl_update_irq(d); - break; - case QXL_IO_NOTIFY_OOM: - if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) { - break; - } - d->oom_running = 1; - qxl_spice_oom(d); - d->oom_running = 0; - break; - case QXL_IO_SET_MODE: - qxl_set_mode(d, val, 0); - break; - case QXL_IO_LOG: - trace_qxl_io_log(d->id, d->ram->log_buf); - if (d->guestdebug) { - fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id, - qemu_get_clock_ns(vm_clock), d->ram->log_buf); - } - break; - case QXL_IO_RESET: - qxl_hard_reset(d, 0); - break; - case QXL_IO_MEMSLOT_ADD: - if (val >= NUM_MEMSLOTS) { - qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_ADD: val out of range"); - break; - } - if (d->guest_slots[val].active) { - qxl_set_guest_bug(d, - "QXL_IO_MEMSLOT_ADD: memory slot already active"); - break; - } - d->guest_slots[val].slot = d->ram->mem_slot; - qxl_add_memslot(d, val, 0, async); - break; - case QXL_IO_MEMSLOT_DEL: - if (val >= NUM_MEMSLOTS) { - qxl_set_guest_bug(d, "QXL_IO_MEMSLOT_DEL: val out of range"); - break; - } - qxl_del_memslot(d, val); - break; - case QXL_IO_CREATE_PRIMARY: - if (val != 0) { - qxl_set_guest_bug(d, "QXL_IO_CREATE_PRIMARY (async=%d): val != 0", - async); - goto cancel_async; - } - d->guest_primary.surface = d->ram->create_surface; - qxl_create_guest_primary(d, 0, async); - break; - case QXL_IO_DESTROY_PRIMARY: - if (val != 0) { - qxl_set_guest_bug(d, "QXL_IO_DESTROY_PRIMARY (async=%d): val != 0", - async); - goto cancel_async; - } - if (!qxl_destroy_primary(d, async)) { - trace_qxl_io_destroy_primary_ignored(d->id, - qxl_mode_to_string(d->mode)); - goto cancel_async; - } - break; - case QXL_IO_DESTROY_SURFACE_WAIT: - if (val >= d->ssd.num_surfaces) { - qxl_set_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):" - "%" PRIu64 " >= NUM_SURFACES", async, val); - goto cancel_async; - } - qxl_spice_destroy_surface_wait(d, val, async); - break; - case QXL_IO_FLUSH_RELEASE: { - QXLReleaseRing *ring = &d->ram->release_ring; - if (ring->prod - ring->cons + 1 == ring->num_items) { - fprintf(stderr, - "ERROR: no flush, full release ring [p%d,%dc]\n", - ring->prod, ring->cons); - } - qxl_push_free_res(d, 1 /* flush */); - break; - } - case QXL_IO_FLUSH_SURFACES_ASYNC: - qxl_spice_flush_surfaces_async(d); - break; - case QXL_IO_DESTROY_ALL_SURFACES: - d->mode = QXL_MODE_UNDEFINED; - qxl_spice_destroy_surfaces(d, async); - break; - case QXL_IO_MONITORS_CONFIG_ASYNC: - qxl_spice_monitors_config_async(d, 0); - break; - default: - qxl_set_guest_bug(d, "%s: unexpected ioport=0x%x\n", __func__, io_port); - } - return; -cancel_async: - if (async) { - qxl_send_events(d, QXL_INTERRUPT_IO_CMD); - qemu_mutex_lock(&d->async_lock); - d->current_async = QXL_UNDEFINED_IO; - qemu_mutex_unlock(&d->async_lock); - } -} - -static uint64_t ioport_read(void *opaque, hwaddr addr, - unsigned size) -{ - PCIQXLDevice *qxl = opaque; - - trace_qxl_io_read_unexpected(qxl->id); - return 0xff; -} - -static const MemoryRegionOps qxl_io_ops = { - .read = ioport_read, - .write = ioport_write, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static void pipe_read(void *opaque) -{ - PCIQXLDevice *d = opaque; - char dummy; - int len; - - do { - len = read(d->pipe[0], &dummy, sizeof(dummy)); - } while (len == sizeof(dummy)); - qxl_update_irq(d); -} - -static void qxl_send_events(PCIQXLDevice *d, uint32_t events) -{ - uint32_t old_pending; - uint32_t le_events = cpu_to_le32(events); - - trace_qxl_send_events(d->id, events); - if (!qemu_spice_display_is_running(&d->ssd)) { - /* spice-server tracks guest running state and should not do this */ - fprintf(stderr, "%s: spice-server bug: guest stopped, ignoring\n", - __func__); - trace_qxl_send_events_vm_stopped(d->id, events); - return; - } - old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events); - if ((old_pending & le_events) == le_events) { - return; - } - if (qemu_thread_is_self(&d->main)) { - qxl_update_irq(d); - } else { - if (write(d->pipe[1], d, 1) != 1) { - dprint(d, 1, "%s: write to pipe failed\n", __func__); - } - } -} - -static void init_pipe_signaling(PCIQXLDevice *d) -{ - if (pipe(d->pipe) < 0) { - fprintf(stderr, "%s:%s: qxl pipe creation failed\n", - __FILE__, __func__); - exit(1); - } - fcntl(d->pipe[0], F_SETFL, O_NONBLOCK); - fcntl(d->pipe[1], F_SETFL, O_NONBLOCK); - fcntl(d->pipe[0], F_SETOWN, getpid()); - - qemu_thread_get_self(&d->main); - qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d); -} - -/* graphics console */ - -static void qxl_hw_update(void *opaque) -{ - PCIQXLDevice *qxl = opaque; - VGACommonState *vga = &qxl->vga; - - switch (qxl->mode) { - case QXL_MODE_VGA: - vga->update(vga); - break; - case QXL_MODE_COMPAT: - case QXL_MODE_NATIVE: - qxl_render_update(qxl); - break; - default: - break; - } -} - -static void qxl_hw_invalidate(void *opaque) -{ - PCIQXLDevice *qxl = opaque; - VGACommonState *vga = &qxl->vga; - - vga->invalidate(vga); -} - -static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch, - Error **errp) -{ - PCIQXLDevice *qxl = opaque; - VGACommonState *vga = &qxl->vga; - - switch (qxl->mode) { - case QXL_MODE_COMPAT: - case QXL_MODE_NATIVE: - qxl_render_update(qxl); - ppm_save(filename, qxl->ssd.ds, errp); - break; - case QXL_MODE_VGA: - vga->screen_dump(vga, filename, cswitch, errp); - break; - default: - break; - } -} - -static void qxl_hw_text_update(void *opaque, console_ch_t *chardata) -{ - PCIQXLDevice *qxl = opaque; - VGACommonState *vga = &qxl->vga; - - if (qxl->mode == QXL_MODE_VGA) { - vga->text_update(vga, chardata); - return; - } -} - -static void qxl_dirty_surfaces(PCIQXLDevice *qxl) -{ - uintptr_t vram_start; - int i; - - if (qxl->mode != QXL_MODE_NATIVE && qxl->mode != QXL_MODE_COMPAT) { - return; - } - - /* dirty the primary surface */ - qxl_set_dirty(&qxl->vga.vram, qxl->shadow_rom.draw_area_offset, - qxl->shadow_rom.surface0_area_size); - - vram_start = (uintptr_t)memory_region_get_ram_ptr(&qxl->vram_bar); - - /* dirty the off-screen surfaces */ - for (i = 0; i < qxl->ssd.num_surfaces; i++) { - QXLSurfaceCmd *cmd; - intptr_t surface_offset; - int surface_size; - - if (qxl->guest_surfaces.cmds[i] == 0) { - continue; - } - - cmd = qxl_phys2virt(qxl, qxl->guest_surfaces.cmds[i], - MEMSLOT_GROUP_GUEST); - assert(cmd); - assert(cmd->type == QXL_SURFACE_CMD_CREATE); - surface_offset = (intptr_t)qxl_phys2virt(qxl, - cmd->u.surface_create.data, - MEMSLOT_GROUP_GUEST); - assert(surface_offset); - surface_offset -= vram_start; - surface_size = cmd->u.surface_create.height * - abs(cmd->u.surface_create.stride); - trace_qxl_surfaces_dirty(qxl->id, i, (int)surface_offset, surface_size); - qxl_set_dirty(&qxl->vram_bar, surface_offset, surface_size); - } -} - -static void qxl_vm_change_state_handler(void *opaque, int running, - RunState state) -{ - PCIQXLDevice *qxl = opaque; - - if (running) { - /* - * if qxl_send_events was called from spice server context before - * migration ended, qxl_update_irq for these events might not have been - * called - */ - qxl_update_irq(qxl); - } else { - /* make sure surfaces are saved before migration */ - qxl_dirty_surfaces(qxl); - } -} - -/* display change listener */ - -static void display_update(DisplayChangeListener *dcl, - int x, int y, int w, int h) -{ - PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl); - - if (qxl->mode == QXL_MODE_VGA) { - qemu_spice_display_update(&qxl->ssd, x, y, w, h); - } -} - -static void display_switch(DisplayChangeListener *dcl, - struct DisplaySurface *surface) -{ - PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl); - - qxl->ssd.ds = surface; - if (qxl->mode == QXL_MODE_VGA) { - qemu_spice_display_switch(&qxl->ssd, surface); - } -} - -static void display_refresh(DisplayChangeListener *dcl) -{ - PCIQXLDevice *qxl = container_of(dcl, PCIQXLDevice, ssd.dcl); - - if (qxl->mode == QXL_MODE_VGA) { - qemu_spice_display_refresh(&qxl->ssd); - } else { - qemu_mutex_lock(&qxl->ssd.lock); - qemu_spice_cursor_refresh_unlocked(&qxl->ssd); - qemu_mutex_unlock(&qxl->ssd.lock); - } -} - -static DisplayChangeListenerOps display_listener_ops = { - .dpy_name = "spice/qxl", - .dpy_gfx_update = display_update, - .dpy_gfx_switch = display_switch, - .dpy_refresh = display_refresh, -}; - -static void qxl_init_ramsize(PCIQXLDevice *qxl) -{ - /* vga mode framebuffer / primary surface (bar 0, first part) */ - if (qxl->vgamem_size_mb < 8) { - qxl->vgamem_size_mb = 8; - } - qxl->vgamem_size = qxl->vgamem_size_mb * 1024 * 1024; - - /* vga ram (bar 0, total) */ - if (qxl->ram_size_mb != -1) { - qxl->vga.vram_size = qxl->ram_size_mb * 1024 * 1024; - } - if (qxl->vga.vram_size < qxl->vgamem_size * 2) { - qxl->vga.vram_size = qxl->vgamem_size * 2; - } - - /* vram32 (surfaces, 32bit, bar 1) */ - if (qxl->vram32_size_mb != -1) { - qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024; - } - if (qxl->vram32_size < 4096) { - qxl->vram32_size = 4096; - } - - /* vram (surfaces, 64bit, bar 4+5) */ - if (qxl->vram_size_mb != -1) { - qxl->vram_size = qxl->vram_size_mb * 1024 * 1024; - } - if (qxl->vram_size < qxl->vram32_size) { - qxl->vram_size = qxl->vram32_size; - } - - if (qxl->revision == 1) { - qxl->vram32_size = 4096; - qxl->vram_size = 4096; - } - qxl->vgamem_size = msb_mask(qxl->vgamem_size * 2 - 1); - qxl->vga.vram_size = msb_mask(qxl->vga.vram_size * 2 - 1); - qxl->vram32_size = msb_mask(qxl->vram32_size * 2 - 1); - qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1); -} - -static int qxl_init_common(PCIQXLDevice *qxl) -{ - uint8_t* config = qxl->pci.config; - uint32_t pci_device_rev; - uint32_t io_size; - - qxl->mode = QXL_MODE_UNDEFINED; - qxl->generation = 1; - qxl->num_memslots = NUM_MEMSLOTS; - qemu_mutex_init(&qxl->track_lock); - qemu_mutex_init(&qxl->async_lock); - qxl->current_async = QXL_UNDEFINED_IO; - qxl->guest_bug = 0; - - switch (qxl->revision) { - case 1: /* spice 0.4 -- qxl-1 */ - pci_device_rev = QXL_REVISION_STABLE_V04; - io_size = 8; - break; - case 2: /* spice 0.6 -- qxl-2 */ - pci_device_rev = QXL_REVISION_STABLE_V06; - io_size = 16; - break; - case 3: /* qxl-3 */ - pci_device_rev = QXL_REVISION_STABLE_V10; - io_size = 32; /* PCI region size must be pow2 */ - break; - case 4: /* qxl-4 */ - pci_device_rev = QXL_REVISION_STABLE_V12; - io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1); - break; - default: - error_report("Invalid revision %d for qxl device (max %d)", - qxl->revision, QXL_DEFAULT_REVISION); - return -1; - } - - pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev); - pci_set_byte(&config[PCI_INTERRUPT_PIN], 1); - - qxl->rom_size = qxl_rom_size(); - memory_region_init_ram(&qxl->rom_bar, "qxl.vrom", qxl->rom_size); - vmstate_register_ram(&qxl->rom_bar, &qxl->pci.qdev); - init_qxl_rom(qxl); - init_qxl_ram(qxl); - - qxl->guest_surfaces.cmds = g_new0(QXLPHYSICAL, qxl->ssd.num_surfaces); - memory_region_init_ram(&qxl->vram_bar, "qxl.vram", qxl->vram_size); - vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev); - memory_region_init_alias(&qxl->vram32_bar, "qxl.vram32", &qxl->vram_bar, - 0, qxl->vram32_size); - - memory_region_init_io(&qxl->io_bar, &qxl_io_ops, qxl, - "qxl-ioports", io_size); - if (qxl->id == 0) { - vga_dirty_log_start(&qxl->vga); - } - memory_region_set_flush_coalesced(&qxl->io_bar); - - - pci_register_bar(&qxl->pci, QXL_IO_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_IO, &qxl->io_bar); - - pci_register_bar(&qxl->pci, QXL_ROM_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->rom_bar); - - pci_register_bar(&qxl->pci, QXL_RAM_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vga.vram); - - pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_MEMORY, &qxl->vram32_bar); - - if (qxl->vram32_size < qxl->vram_size) { - /* - * Make the 64bit vram bar show up only in case it is - * configured to be larger than the 32bit vram bar. - */ - pci_register_bar(&qxl->pci, QXL_VRAM64_RANGE_INDEX, - PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_TYPE_64 | - PCI_BASE_ADDRESS_MEM_PREFETCH, - &qxl->vram_bar); - } - - /* print pci bar details */ - dprint(qxl, 1, "ram/%s: %d MB [region 0]\n", - qxl->id == 0 ? "pri" : "sec", - qxl->vga.vram_size / (1024*1024)); - dprint(qxl, 1, "vram/32: %d MB [region 1]\n", - qxl->vram32_size / (1024*1024)); - dprint(qxl, 1, "vram/64: %d MB %s\n", - qxl->vram_size / (1024*1024), - qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]"); - - qxl->ssd.qxl.base.sif = &qxl_interface.base; - qxl->ssd.qxl.id = qxl->id; - if (qemu_spice_add_interface(&qxl->ssd.qxl.base) != 0) { - error_report("qxl interface %d.%d not supported by spice-server", - SPICE_INTERFACE_QXL_MAJOR, SPICE_INTERFACE_QXL_MINOR); - return -1; - } - qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); - - init_pipe_signaling(qxl); - qxl_reset_state(qxl); - - qxl->update_area_bh = qemu_bh_new(qxl_render_update_area_bh, qxl); - - return 0; -} - -static int qxl_init_primary(PCIDevice *dev) -{ - PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev); - VGACommonState *vga = &qxl->vga; - PortioList *qxl_vga_port_list = g_new(PortioList, 1); - DisplayState *ds; - int rc; - - qxl->id = 0; - qxl_init_ramsize(qxl); - vga->vram_size_mb = qxl->vga.vram_size >> 20; - vga_common_init(vga); - vga_init(vga, pci_address_space(dev), pci_address_space_io(dev), false); - portio_list_init(qxl_vga_port_list, qxl_vga_portio_list, vga, "vga"); - portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0); - - vga->con = graphic_console_init(qxl_hw_update, qxl_hw_invalidate, - qxl_hw_screen_dump, qxl_hw_text_update, - qxl); - qxl->ssd.con = vga->con, - qemu_spice_display_init_common(&qxl->ssd); - - rc = qxl_init_common(qxl); - if (rc != 0) { - return rc; - } - - qxl->ssd.dcl.ops = &display_listener_ops; - ds = qemu_console_displaystate(vga->con); - register_displaychangelistener(ds, &qxl->ssd.dcl); - return rc; -} - -static int qxl_init_secondary(PCIDevice *dev) -{ - static int device_id = 1; - PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev); - - qxl->id = device_id++; - qxl_init_ramsize(qxl); - memory_region_init_ram(&qxl->vga.vram, "qxl.vgavram", qxl->vga.vram_size); - vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev); - qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram); - - return qxl_init_common(qxl); -} - -static void qxl_pre_save(void *opaque) -{ - PCIQXLDevice* d = opaque; - uint8_t *ram_start = d->vga.vram_ptr; - - trace_qxl_pre_save(d->id); - if (d->last_release == NULL) { - d->last_release_offset = 0; - } else { - d->last_release_offset = (uint8_t *)d->last_release - ram_start; - } - assert(d->last_release_offset < d->vga.vram_size); -} - -static int qxl_pre_load(void *opaque) -{ - PCIQXLDevice* d = opaque; - - trace_qxl_pre_load(d->id); - qxl_hard_reset(d, 1); - qxl_exit_vga_mode(d); - return 0; -} - -static void qxl_create_memslots(PCIQXLDevice *d) -{ - int i; - - for (i = 0; i < NUM_MEMSLOTS; i++) { - if (!d->guest_slots[i].active) { - continue; - } - qxl_add_memslot(d, i, 0, QXL_SYNC); - } -} - -static int qxl_post_load(void *opaque, int version) -{ - PCIQXLDevice* d = opaque; - uint8_t *ram_start = d->vga.vram_ptr; - QXLCommandExt *cmds; - int in, out, newmode; - - assert(d->last_release_offset < d->vga.vram_size); - if (d->last_release_offset == 0) { - d->last_release = NULL; - } else { - d->last_release = (QXLReleaseInfo *)(ram_start + d->last_release_offset); - } - - d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset); - - trace_qxl_post_load(d->id, qxl_mode_to_string(d->mode)); - newmode = d->mode; - d->mode = QXL_MODE_UNDEFINED; - - switch (newmode) { - case QXL_MODE_UNDEFINED: - qxl_create_memslots(d); - break; - case QXL_MODE_VGA: - qxl_create_memslots(d); - qxl_enter_vga_mode(d); - break; - case QXL_MODE_NATIVE: - qxl_create_memslots(d); - qxl_create_guest_primary(d, 1, QXL_SYNC); - - /* replay surface-create and cursor-set commands */ - cmds = g_malloc0(sizeof(QXLCommandExt) * (d->ssd.num_surfaces + 1)); - for (in = 0, out = 0; in < d->ssd.num_surfaces; in++) { - if (d->guest_surfaces.cmds[in] == 0) { - continue; - } - cmds[out].cmd.data = d->guest_surfaces.cmds[in]; - cmds[out].cmd.type = QXL_CMD_SURFACE; - cmds[out].group_id = MEMSLOT_GROUP_GUEST; - out++; - } - if (d->guest_cursor) { - cmds[out].cmd.data = d->guest_cursor; - cmds[out].cmd.type = QXL_CMD_CURSOR; - cmds[out].group_id = MEMSLOT_GROUP_GUEST; - out++; - } - qxl_spice_loadvm_commands(d, cmds, out); - g_free(cmds); - if (d->guest_monitors_config) { - qxl_spice_monitors_config_async(d, 1); - } - break; - case QXL_MODE_COMPAT: - /* note: no need to call qxl_create_memslots, qxl_set_mode - * creates the mem slot. */ - qxl_set_mode(d, d->shadow_rom.mode, 1); - break; - } - return 0; -} - -#define QXL_SAVE_VERSION 21 - -static bool qxl_monitors_config_needed(void *opaque) -{ - PCIQXLDevice *qxl = opaque; - - return qxl->guest_monitors_config != 0; -} - - -static VMStateDescription qxl_memslot = { - .name = "qxl-memslot", - .version_id = QXL_SAVE_VERSION, - .minimum_version_id = QXL_SAVE_VERSION, - .fields = (VMStateField[]) { - VMSTATE_UINT64(slot.mem_start, struct guest_slots), - VMSTATE_UINT64(slot.mem_end, struct guest_slots), - VMSTATE_UINT32(active, struct guest_slots), - VMSTATE_END_OF_LIST() - } -}; - -static VMStateDescription qxl_surface = { - .name = "qxl-surface", - .version_id = QXL_SAVE_VERSION, - .minimum_version_id = QXL_SAVE_VERSION, - .fields = (VMStateField[]) { - VMSTATE_UINT32(width, QXLSurfaceCreate), - VMSTATE_UINT32(height, QXLSurfaceCreate), - VMSTATE_INT32(stride, QXLSurfaceCreate), - VMSTATE_UINT32(format, QXLSurfaceCreate), - VMSTATE_UINT32(position, QXLSurfaceCreate), - VMSTATE_UINT32(mouse_mode, QXLSurfaceCreate), - VMSTATE_UINT32(flags, QXLSurfaceCreate), - VMSTATE_UINT32(type, QXLSurfaceCreate), - VMSTATE_UINT64(mem, QXLSurfaceCreate), - VMSTATE_END_OF_LIST() - } -}; - -static VMStateDescription qxl_vmstate_monitors_config = { - .name = "qxl/monitors-config", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT64(guest_monitors_config, PCIQXLDevice), - VMSTATE_END_OF_LIST() - }, -}; - -static VMStateDescription qxl_vmstate = { - .name = "qxl", - .version_id = QXL_SAVE_VERSION, - .minimum_version_id = QXL_SAVE_VERSION, - .pre_save = qxl_pre_save, - .pre_load = qxl_pre_load, - .post_load = qxl_post_load, - .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(pci, PCIQXLDevice), - VMSTATE_STRUCT(vga, PCIQXLDevice, 0, vmstate_vga_common, VGACommonState), - VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice), - VMSTATE_UINT32(num_free_res, PCIQXLDevice), - VMSTATE_UINT32(last_release_offset, PCIQXLDevice), - VMSTATE_UINT32(mode, PCIQXLDevice), - VMSTATE_UINT32(ssd.unique, PCIQXLDevice), - VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice), - VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, 0, - qxl_memslot, struct guest_slots), - VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0, - qxl_surface, QXLSurfaceCreate), - VMSTATE_INT32_EQUAL(ssd.num_surfaces, PCIQXLDevice), - VMSTATE_VARRAY_INT32(guest_surfaces.cmds, PCIQXLDevice, - ssd.num_surfaces, 0, - vmstate_info_uint64, uint64_t), - VMSTATE_UINT64(guest_cursor, PCIQXLDevice), - VMSTATE_END_OF_LIST() - }, - .subsections = (VMStateSubsection[]) { - { - .vmsd = &qxl_vmstate_monitors_config, - .needed = qxl_monitors_config_needed, - }, { - /* empty */ - } - } -}; - -static Property qxl_properties[] = { - DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram32_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, - QXL_DEFAULT_REVISION), - DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), - DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), - DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), - DEFINE_PROP_UINT32("ram_size_mb", PCIQXLDevice, ram_size_mb, -1), - DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1), - DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1), - DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16), - DEFINE_PROP_INT32("surfaces", PCIQXLDevice, ssd.num_surfaces, 1024), - DEFINE_PROP_END_OF_LIST(), -}; - -static void qxl_primary_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->no_hotplug = 1; - k->init = qxl_init_primary; - k->romfile = "vgabios-qxl.bin"; - k->vendor_id = REDHAT_PCI_VENDOR_ID; - k->device_id = QXL_DEVICE_ID_STABLE; - k->class_id = PCI_CLASS_DISPLAY_VGA; - dc->desc = "Spice QXL GPU (primary, vga compatible)"; - dc->reset = qxl_reset_handler; - dc->vmsd = &qxl_vmstate; - dc->props = qxl_properties; -} - -static const TypeInfo qxl_primary_info = { - .name = "qxl-vga", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIQXLDevice), - .class_init = qxl_primary_class_init, -}; - -static void qxl_secondary_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = qxl_init_secondary; - k->vendor_id = REDHAT_PCI_VENDOR_ID; - k->device_id = QXL_DEVICE_ID_STABLE; - k->class_id = PCI_CLASS_DISPLAY_OTHER; - dc->desc = "Spice QXL GPU (secondary)"; - dc->reset = qxl_reset_handler; - dc->vmsd = &qxl_vmstate; - dc->props = qxl_properties; -} - -static const TypeInfo qxl_secondary_info = { - .name = "qxl", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIQXLDevice), - .class_init = qxl_secondary_class_init, -}; - -static void qxl_register_types(void) -{ - type_register_static(&qxl_primary_info); - type_register_static(&qxl_secondary_info); -} - -type_init(qxl_register_types) diff --git a/hw/sh4/Makefile.objs b/hw/sh4/Makefile.objs index efbb9ebef1..c9b0416b07 100644 --- a/hw/sh4/Makefile.objs +++ b/hw/sh4/Makefile.objs @@ -1,4 +1,4 @@ -obj-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o +obj-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/sm501.c b/hw/sm501.c deleted file mode 100644 index d9fcead719..0000000000 --- a/hw/sm501.c +++ /dev/null @@ -1,1450 +0,0 @@ -/* - * QEMU SM501 Device - * - * Copyright (c) 2008 Shin-ichiro KAWASAKI - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include -#include "hw/hw.h" -#include "hw/char/serial.h" -#include "ui/console.h" -#include "hw/arm/devices.h" -#include "hw/sysbus.h" -#include "hw/qdev-addr.h" -#include "qemu/range.h" -#include "ui/pixel_ops.h" - -/* - * Status: 2010/05/07 - * - Minimum implementation for Linux console : mmio regs and CRT layer. - * - 2D grapihcs acceleration partially supported : only fill rectangle. - * - * TODO: - * - Panel support - * - Touch panel support - * - USB support - * - UART support - * - More 2D graphics engine support - * - Performance tuning - */ - -//#define DEBUG_SM501 -//#define DEBUG_BITBLT - -#ifdef DEBUG_SM501 -#define SM501_DPRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) -#else -#define SM501_DPRINTF(fmt, ...) do {} while(0) -#endif - - -#define MMIO_BASE_OFFSET 0x3e00000 - -/* SM501 register definitions taken from "linux/include/linux/sm501-regs.h" */ - -/* System Configuration area */ -/* System config base */ -#define SM501_SYS_CONFIG (0x000000) - -/* config 1 */ -#define SM501_SYSTEM_CONTROL (0x000000) - -#define SM501_SYSCTRL_PANEL_TRISTATE (1<<0) -#define SM501_SYSCTRL_MEM_TRISTATE (1<<1) -#define SM501_SYSCTRL_CRT_TRISTATE (1<<2) - -#define SM501_SYSCTRL_PCI_SLAVE_BURST_MASK (3<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_1 (0<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_2 (1<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_4 (2<<4) -#define SM501_SYSCTRL_PCI_SLAVE_BURST_8 (3<<4) - -#define SM501_SYSCTRL_PCI_CLOCK_RUN_EN (1<<6) -#define SM501_SYSCTRL_PCI_RETRY_DISABLE (1<<7) -#define SM501_SYSCTRL_PCI_SUBSYS_LOCK (1<<11) -#define SM501_SYSCTRL_PCI_BURST_READ_EN (1<<15) - -/* miscellaneous control */ - -#define SM501_MISC_CONTROL (0x000004) - -#define SM501_MISC_BUS_SH (0x0) -#define SM501_MISC_BUS_PCI (0x1) -#define SM501_MISC_BUS_XSCALE (0x2) -#define SM501_MISC_BUS_NEC (0x6) -#define SM501_MISC_BUS_MASK (0x7) - -#define SM501_MISC_VR_62MB (1<<3) -#define SM501_MISC_CDR_RESET (1<<7) -#define SM501_MISC_USB_LB (1<<8) -#define SM501_MISC_USB_SLAVE (1<<9) -#define SM501_MISC_BL_1 (1<<10) -#define SM501_MISC_MC (1<<11) -#define SM501_MISC_DAC_POWER (1<<12) -#define SM501_MISC_IRQ_INVERT (1<<16) -#define SM501_MISC_SH (1<<17) - -#define SM501_MISC_HOLD_EMPTY (0<<18) -#define SM501_MISC_HOLD_8 (1<<18) -#define SM501_MISC_HOLD_16 (2<<18) -#define SM501_MISC_HOLD_24 (3<<18) -#define SM501_MISC_HOLD_32 (4<<18) -#define SM501_MISC_HOLD_MASK (7<<18) - -#define SM501_MISC_FREQ_12 (1<<24) -#define SM501_MISC_PNL_24BIT (1<<25) -#define SM501_MISC_8051_LE (1<<26) - - - -#define SM501_GPIO31_0_CONTROL (0x000008) -#define SM501_GPIO63_32_CONTROL (0x00000C) -#define SM501_DRAM_CONTROL (0x000010) - -/* command list */ -#define SM501_ARBTRTN_CONTROL (0x000014) - -/* command list */ -#define SM501_COMMAND_LIST_STATUS (0x000024) - -/* interrupt debug */ -#define SM501_RAW_IRQ_STATUS (0x000028) -#define SM501_RAW_IRQ_CLEAR (0x000028) -#define SM501_IRQ_STATUS (0x00002C) -#define SM501_IRQ_MASK (0x000030) -#define SM501_DEBUG_CONTROL (0x000034) - -/* power management */ -#define SM501_POWERMODE_P2X_SRC (1<<29) -#define SM501_POWERMODE_V2X_SRC (1<<20) -#define SM501_POWERMODE_M_SRC (1<<12) -#define SM501_POWERMODE_M1_SRC (1<<4) - -#define SM501_CURRENT_GATE (0x000038) -#define SM501_CURRENT_CLOCK (0x00003C) -#define SM501_POWER_MODE_0_GATE (0x000040) -#define SM501_POWER_MODE_0_CLOCK (0x000044) -#define SM501_POWER_MODE_1_GATE (0x000048) -#define SM501_POWER_MODE_1_CLOCK (0x00004C) -#define SM501_SLEEP_MODE_GATE (0x000050) -#define SM501_POWER_MODE_CONTROL (0x000054) - -/* power gates for units within the 501 */ -#define SM501_GATE_HOST (0) -#define SM501_GATE_MEMORY (1) -#define SM501_GATE_DISPLAY (2) -#define SM501_GATE_2D_ENGINE (3) -#define SM501_GATE_CSC (4) -#define SM501_GATE_ZVPORT (5) -#define SM501_GATE_GPIO (6) -#define SM501_GATE_UART0 (7) -#define SM501_GATE_UART1 (8) -#define SM501_GATE_SSP (10) -#define SM501_GATE_USB_HOST (11) -#define SM501_GATE_USB_GADGET (12) -#define SM501_GATE_UCONTROLLER (17) -#define SM501_GATE_AC97 (18) - -/* panel clock */ -#define SM501_CLOCK_P2XCLK (24) -/* crt clock */ -#define SM501_CLOCK_V2XCLK (16) -/* main clock */ -#define SM501_CLOCK_MCLK (8) -/* SDRAM controller clock */ -#define SM501_CLOCK_M1XCLK (0) - -/* config 2 */ -#define SM501_PCI_MASTER_BASE (0x000058) -#define SM501_ENDIAN_CONTROL (0x00005C) -#define SM501_DEVICEID (0x000060) -/* 0x050100A0 */ - -#define SM501_DEVICEID_SM501 (0x05010000) -#define SM501_DEVICEID_IDMASK (0xffff0000) -#define SM501_DEVICEID_REVMASK (0x000000ff) - -#define SM501_PLLCLOCK_COUNT (0x000064) -#define SM501_MISC_TIMING (0x000068) -#define SM501_CURRENT_SDRAM_CLOCK (0x00006C) - -#define SM501_PROGRAMMABLE_PLL_CONTROL (0x000074) - -/* GPIO base */ -#define SM501_GPIO (0x010000) -#define SM501_GPIO_DATA_LOW (0x00) -#define SM501_GPIO_DATA_HIGH (0x04) -#define SM501_GPIO_DDR_LOW (0x08) -#define SM501_GPIO_DDR_HIGH (0x0C) -#define SM501_GPIO_IRQ_SETUP (0x10) -#define SM501_GPIO_IRQ_STATUS (0x14) -#define SM501_GPIO_IRQ_RESET (0x14) - -/* I2C controller base */ -#define SM501_I2C (0x010040) -#define SM501_I2C_BYTE_COUNT (0x00) -#define SM501_I2C_CONTROL (0x01) -#define SM501_I2C_STATUS (0x02) -#define SM501_I2C_RESET (0x02) -#define SM501_I2C_SLAVE_ADDRESS (0x03) -#define SM501_I2C_DATA (0x04) - -/* SSP base */ -#define SM501_SSP (0x020000) - -/* Uart 0 base */ -#define SM501_UART0 (0x030000) - -/* Uart 1 base */ -#define SM501_UART1 (0x030020) - -/* USB host port base */ -#define SM501_USB_HOST (0x040000) - -/* USB slave/gadget base */ -#define SM501_USB_GADGET (0x060000) - -/* USB slave/gadget data port base */ -#define SM501_USB_GADGET_DATA (0x070000) - -/* Display controller/video engine base */ -#define SM501_DC (0x080000) - -/* common defines for the SM501 address registers */ -#define SM501_ADDR_FLIP (1<<31) -#define SM501_ADDR_EXT (1<<27) -#define SM501_ADDR_CS1 (1<<26) -#define SM501_ADDR_MASK (0x3f << 26) - -#define SM501_FIFO_MASK (0x3 << 16) -#define SM501_FIFO_1 (0x0 << 16) -#define SM501_FIFO_3 (0x1 << 16) -#define SM501_FIFO_7 (0x2 << 16) -#define SM501_FIFO_11 (0x3 << 16) - -/* common registers for panel and the crt */ -#define SM501_OFF_DC_H_TOT (0x000) -#define SM501_OFF_DC_V_TOT (0x008) -#define SM501_OFF_DC_H_SYNC (0x004) -#define SM501_OFF_DC_V_SYNC (0x00C) - -#define SM501_DC_PANEL_CONTROL (0x000) - -#define SM501_DC_PANEL_CONTROL_FPEN (1<<27) -#define SM501_DC_PANEL_CONTROL_BIAS (1<<26) -#define SM501_DC_PANEL_CONTROL_DATA (1<<25) -#define SM501_DC_PANEL_CONTROL_VDD (1<<24) -#define SM501_DC_PANEL_CONTROL_DP (1<<23) - -#define SM501_DC_PANEL_CONTROL_TFT_888 (0<<21) -#define SM501_DC_PANEL_CONTROL_TFT_333 (1<<21) -#define SM501_DC_PANEL_CONTROL_TFT_444 (2<<21) - -#define SM501_DC_PANEL_CONTROL_DE (1<<20) - -#define SM501_DC_PANEL_CONTROL_LCD_TFT (0<<18) -#define SM501_DC_PANEL_CONTROL_LCD_STN8 (1<<18) -#define SM501_DC_PANEL_CONTROL_LCD_STN12 (2<<18) - -#define SM501_DC_PANEL_CONTROL_CP (1<<14) -#define SM501_DC_PANEL_CONTROL_VSP (1<<13) -#define SM501_DC_PANEL_CONTROL_HSP (1<<12) -#define SM501_DC_PANEL_CONTROL_CK (1<<9) -#define SM501_DC_PANEL_CONTROL_TE (1<<8) -#define SM501_DC_PANEL_CONTROL_VPD (1<<7) -#define SM501_DC_PANEL_CONTROL_VP (1<<6) -#define SM501_DC_PANEL_CONTROL_HPD (1<<5) -#define SM501_DC_PANEL_CONTROL_HP (1<<4) -#define SM501_DC_PANEL_CONTROL_GAMMA (1<<3) -#define SM501_DC_PANEL_CONTROL_EN (1<<2) - -#define SM501_DC_PANEL_CONTROL_8BPP (0<<0) -#define SM501_DC_PANEL_CONTROL_16BPP (1<<0) -#define SM501_DC_PANEL_CONTROL_32BPP (2<<0) - - -#define SM501_DC_PANEL_PANNING_CONTROL (0x004) -#define SM501_DC_PANEL_COLOR_KEY (0x008) -#define SM501_DC_PANEL_FB_ADDR (0x00C) -#define SM501_DC_PANEL_FB_OFFSET (0x010) -#define SM501_DC_PANEL_FB_WIDTH (0x014) -#define SM501_DC_PANEL_FB_HEIGHT (0x018) -#define SM501_DC_PANEL_TL_LOC (0x01C) -#define SM501_DC_PANEL_BR_LOC (0x020) -#define SM501_DC_PANEL_H_TOT (0x024) -#define SM501_DC_PANEL_H_SYNC (0x028) -#define SM501_DC_PANEL_V_TOT (0x02C) -#define SM501_DC_PANEL_V_SYNC (0x030) -#define SM501_DC_PANEL_CUR_LINE (0x034) - -#define SM501_DC_VIDEO_CONTROL (0x040) -#define SM501_DC_VIDEO_FB0_ADDR (0x044) -#define SM501_DC_VIDEO_FB_WIDTH (0x048) -#define SM501_DC_VIDEO_FB0_LAST_ADDR (0x04C) -#define SM501_DC_VIDEO_TL_LOC (0x050) -#define SM501_DC_VIDEO_BR_LOC (0x054) -#define SM501_DC_VIDEO_SCALE (0x058) -#define SM501_DC_VIDEO_INIT_SCALE (0x05C) -#define SM501_DC_VIDEO_YUV_CONSTANTS (0x060) -#define SM501_DC_VIDEO_FB1_ADDR (0x064) -#define SM501_DC_VIDEO_FB1_LAST_ADDR (0x068) - -#define SM501_DC_VIDEO_ALPHA_CONTROL (0x080) -#define SM501_DC_VIDEO_ALPHA_FB_ADDR (0x084) -#define SM501_DC_VIDEO_ALPHA_FB_OFFSET (0x088) -#define SM501_DC_VIDEO_ALPHA_FB_LAST_ADDR (0x08C) -#define SM501_DC_VIDEO_ALPHA_TL_LOC (0x090) -#define SM501_DC_VIDEO_ALPHA_BR_LOC (0x094) -#define SM501_DC_VIDEO_ALPHA_SCALE (0x098) -#define SM501_DC_VIDEO_ALPHA_INIT_SCALE (0x09C) -#define SM501_DC_VIDEO_ALPHA_CHROMA_KEY (0x0A0) -#define SM501_DC_VIDEO_ALPHA_COLOR_LOOKUP (0x0A4) - -#define SM501_DC_PANEL_HWC_BASE (0x0F0) -#define SM501_DC_PANEL_HWC_ADDR (0x0F0) -#define SM501_DC_PANEL_HWC_LOC (0x0F4) -#define SM501_DC_PANEL_HWC_COLOR_1_2 (0x0F8) -#define SM501_DC_PANEL_HWC_COLOR_3 (0x0FC) - -#define SM501_HWC_EN (1<<31) - -#define SM501_OFF_HWC_ADDR (0x00) -#define SM501_OFF_HWC_LOC (0x04) -#define SM501_OFF_HWC_COLOR_1_2 (0x08) -#define SM501_OFF_HWC_COLOR_3 (0x0C) - -#define SM501_DC_ALPHA_CONTROL (0x100) -#define SM501_DC_ALPHA_FB_ADDR (0x104) -#define SM501_DC_ALPHA_FB_OFFSET (0x108) -#define SM501_DC_ALPHA_TL_LOC (0x10C) -#define SM501_DC_ALPHA_BR_LOC (0x110) -#define SM501_DC_ALPHA_CHROMA_KEY (0x114) -#define SM501_DC_ALPHA_COLOR_LOOKUP (0x118) - -#define SM501_DC_CRT_CONTROL (0x200) - -#define SM501_DC_CRT_CONTROL_TVP (1<<15) -#define SM501_DC_CRT_CONTROL_CP (1<<14) -#define SM501_DC_CRT_CONTROL_VSP (1<<13) -#define SM501_DC_CRT_CONTROL_HSP (1<<12) -#define SM501_DC_CRT_CONTROL_VS (1<<11) -#define SM501_DC_CRT_CONTROL_BLANK (1<<10) -#define SM501_DC_CRT_CONTROL_SEL (1<<9) -#define SM501_DC_CRT_CONTROL_TE (1<<8) -#define SM501_DC_CRT_CONTROL_PIXEL_MASK (0xF << 4) -#define SM501_DC_CRT_CONTROL_GAMMA (1<<3) -#define SM501_DC_CRT_CONTROL_ENABLE (1<<2) - -#define SM501_DC_CRT_CONTROL_8BPP (0<<0) -#define SM501_DC_CRT_CONTROL_16BPP (1<<0) -#define SM501_DC_CRT_CONTROL_32BPP (2<<0) - -#define SM501_DC_CRT_FB_ADDR (0x204) -#define SM501_DC_CRT_FB_OFFSET (0x208) -#define SM501_DC_CRT_H_TOT (0x20C) -#define SM501_DC_CRT_H_SYNC (0x210) -#define SM501_DC_CRT_V_TOT (0x214) -#define SM501_DC_CRT_V_SYNC (0x218) -#define SM501_DC_CRT_SIGNATURE_ANALYZER (0x21C) -#define SM501_DC_CRT_CUR_LINE (0x220) -#define SM501_DC_CRT_MONITOR_DETECT (0x224) - -#define SM501_DC_CRT_HWC_BASE (0x230) -#define SM501_DC_CRT_HWC_ADDR (0x230) -#define SM501_DC_CRT_HWC_LOC (0x234) -#define SM501_DC_CRT_HWC_COLOR_1_2 (0x238) -#define SM501_DC_CRT_HWC_COLOR_3 (0x23C) - -#define SM501_DC_PANEL_PALETTE (0x400) - -#define SM501_DC_VIDEO_PALETTE (0x800) - -#define SM501_DC_CRT_PALETTE (0xC00) - -/* Zoom Video port base */ -#define SM501_ZVPORT (0x090000) - -/* AC97/I2S base */ -#define SM501_AC97 (0x0A0000) - -/* 8051 micro controller base */ -#define SM501_UCONTROLLER (0x0B0000) - -/* 8051 micro controller SRAM base */ -#define SM501_UCONTROLLER_SRAM (0x0C0000) - -/* DMA base */ -#define SM501_DMA (0x0D0000) - -/* 2d engine base */ -#define SM501_2D_ENGINE (0x100000) -#define SM501_2D_SOURCE (0x00) -#define SM501_2D_DESTINATION (0x04) -#define SM501_2D_DIMENSION (0x08) -#define SM501_2D_CONTROL (0x0C) -#define SM501_2D_PITCH (0x10) -#define SM501_2D_FOREGROUND (0x14) -#define SM501_2D_BACKGROUND (0x18) -#define SM501_2D_STRETCH (0x1C) -#define SM501_2D_COLOR_COMPARE (0x20) -#define SM501_2D_COLOR_COMPARE_MASK (0x24) -#define SM501_2D_MASK (0x28) -#define SM501_2D_CLIP_TL (0x2C) -#define SM501_2D_CLIP_BR (0x30) -#define SM501_2D_MONO_PATTERN_LOW (0x34) -#define SM501_2D_MONO_PATTERN_HIGH (0x38) -#define SM501_2D_WINDOW_WIDTH (0x3C) -#define SM501_2D_SOURCE_BASE (0x40) -#define SM501_2D_DESTINATION_BASE (0x44) -#define SM501_2D_ALPHA (0x48) -#define SM501_2D_WRAP (0x4C) -#define SM501_2D_STATUS (0x50) - -#define SM501_CSC_Y_SOURCE_BASE (0xC8) -#define SM501_CSC_CONSTANTS (0xCC) -#define SM501_CSC_Y_SOURCE_X (0xD0) -#define SM501_CSC_Y_SOURCE_Y (0xD4) -#define SM501_CSC_U_SOURCE_BASE (0xD8) -#define SM501_CSC_V_SOURCE_BASE (0xDC) -#define SM501_CSC_SOURCE_DIMENSION (0xE0) -#define SM501_CSC_SOURCE_PITCH (0xE4) -#define SM501_CSC_DESTINATION (0xE8) -#define SM501_CSC_DESTINATION_DIMENSION (0xEC) -#define SM501_CSC_DESTINATION_PITCH (0xF0) -#define SM501_CSC_SCALE_FACTOR (0xF4) -#define SM501_CSC_DESTINATION_BASE (0xF8) -#define SM501_CSC_CONTROL (0xFC) - -/* 2d engine data port base */ -#define SM501_2D_ENGINE_DATA (0x110000) - -/* end of register definitions */ - -#define SM501_HWC_WIDTH (64) -#define SM501_HWC_HEIGHT (64) - -/* SM501 local memory size taken from "linux/drivers/mfd/sm501.c" */ -static const uint32_t sm501_mem_local_size[] = { - [0] = 4*1024*1024, - [1] = 8*1024*1024, - [2] = 16*1024*1024, - [3] = 32*1024*1024, - [4] = 64*1024*1024, - [5] = 2*1024*1024, -}; -#define get_local_mem_size(s) sm501_mem_local_size[(s)->local_mem_size_index] - -typedef struct SM501State { - /* graphic console status */ - QemuConsole *con; - - /* status & internal resources */ - hwaddr base; - uint32_t local_mem_size_index; - uint8_t * local_mem; - MemoryRegion local_mem_region; - uint32_t last_width; - uint32_t last_height; - - /* mmio registers */ - uint32_t system_control; - uint32_t misc_control; - uint32_t gpio_31_0_control; - uint32_t gpio_63_32_control; - uint32_t dram_control; - uint32_t irq_mask; - uint32_t misc_timing; - uint32_t power_mode_control; - - uint32_t uart0_ier; - uint32_t uart0_lcr; - uint32_t uart0_mcr; - uint32_t uart0_scr; - - uint8_t dc_palette[0x400 * 3]; - - uint32_t dc_panel_control; - uint32_t dc_panel_panning_control; - uint32_t dc_panel_fb_addr; - uint32_t dc_panel_fb_offset; - uint32_t dc_panel_fb_width; - uint32_t dc_panel_fb_height; - uint32_t dc_panel_tl_location; - uint32_t dc_panel_br_location; - uint32_t dc_panel_h_total; - uint32_t dc_panel_h_sync; - uint32_t dc_panel_v_total; - uint32_t dc_panel_v_sync; - - uint32_t dc_panel_hwc_addr; - uint32_t dc_panel_hwc_location; - uint32_t dc_panel_hwc_color_1_2; - uint32_t dc_panel_hwc_color_3; - - uint32_t dc_crt_control; - uint32_t dc_crt_fb_addr; - uint32_t dc_crt_fb_offset; - uint32_t dc_crt_h_total; - uint32_t dc_crt_h_sync; - uint32_t dc_crt_v_total; - uint32_t dc_crt_v_sync; - - uint32_t dc_crt_hwc_addr; - uint32_t dc_crt_hwc_location; - uint32_t dc_crt_hwc_color_1_2; - uint32_t dc_crt_hwc_color_3; - - uint32_t twoD_source; - uint32_t twoD_destination; - uint32_t twoD_dimension; - uint32_t twoD_control; - uint32_t twoD_pitch; - uint32_t twoD_foreground; - uint32_t twoD_stretch; - uint32_t twoD_color_compare_mask; - uint32_t twoD_mask; - uint32_t twoD_window_width; - uint32_t twoD_source_base; - uint32_t twoD_destination_base; - -} SM501State; - -static uint32_t get_local_mem_size_index(uint32_t size) -{ - uint32_t norm_size = 0; - int i, index = 0; - - for (i = 0; i < ARRAY_SIZE(sm501_mem_local_size); i++) { - uint32_t new_size = sm501_mem_local_size[i]; - if (new_size >= size) { - if (norm_size == 0 || norm_size > new_size) { - norm_size = new_size; - index = i; - } - } - } - - return index; -} - -/** - * Check the availability of hardware cursor. - * @param crt 0 for PANEL, 1 for CRT. - */ -static inline int is_hwc_enabled(SM501State *state, int crt) -{ - uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr; - return addr & 0x80000000; -} - -/** - * Get the address which holds cursor pattern data. - * @param crt 0 for PANEL, 1 for CRT. - */ -static inline uint32_t get_hwc_address(SM501State *state, int crt) -{ - uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr; - return (addr & 0x03FFFFF0)/* >> 4*/; -} - -/** - * Get the cursor position in y coordinate. - * @param crt 0 for PANEL, 1 for CRT. - */ -static inline uint32_t get_hwc_y(SM501State *state, int crt) -{ - uint32_t location = crt ? state->dc_crt_hwc_location - : state->dc_panel_hwc_location; - return (location & 0x07FF0000) >> 16; -} - -/** - * Get the cursor position in x coordinate. - * @param crt 0 for PANEL, 1 for CRT. - */ -static inline uint32_t get_hwc_x(SM501State *state, int crt) -{ - uint32_t location = crt ? state->dc_crt_hwc_location - : state->dc_panel_hwc_location; - return location & 0x000007FF; -} - -/** - * Get the cursor position in x coordinate. - * @param crt 0 for PANEL, 1 for CRT. - * @param index 0, 1, 2 or 3 which specifies color of corsor dot. - */ -static inline uint16_t get_hwc_color(SM501State *state, int crt, int index) -{ - uint32_t color_reg = 0; - uint16_t color_565 = 0; - - if (index == 0) { - return 0; - } - - switch (index) { - case 1: - case 2: - color_reg = crt ? state->dc_crt_hwc_color_1_2 - : state->dc_panel_hwc_color_1_2; - break; - case 3: - color_reg = crt ? state->dc_crt_hwc_color_3 - : state->dc_panel_hwc_color_3; - break; - default: - printf("invalid hw cursor color.\n"); - abort(); - } - - switch (index) { - case 1: - case 3: - color_565 = (uint16_t)(color_reg & 0xFFFF); - break; - case 2: - color_565 = (uint16_t)((color_reg >> 16) & 0xFFFF); - break; - } - return color_565; -} - -static int within_hwc_y_range(SM501State *state, int y, int crt) -{ - int hwc_y = get_hwc_y(state, crt); - return (hwc_y <= y && y < hwc_y + SM501_HWC_HEIGHT); -} - -static void sm501_2d_operation(SM501State * s) -{ - /* obtain operation parameters */ - int operation = (s->twoD_control >> 16) & 0x1f; - int rtl = s->twoD_control & 0x8000000; - int src_x = (s->twoD_source >> 16) & 0x01FFF; - int src_y = s->twoD_source & 0xFFFF; - int dst_x = (s->twoD_destination >> 16) & 0x01FFF; - int dst_y = s->twoD_destination & 0xFFFF; - int operation_width = (s->twoD_dimension >> 16) & 0x1FFF; - int operation_height = s->twoD_dimension & 0xFFFF; - uint32_t color = s->twoD_foreground; - int format_flags = (s->twoD_stretch >> 20) & 0x3; - int addressing = (s->twoD_stretch >> 16) & 0xF; - - /* get frame buffer info */ - uint8_t * src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); - uint8_t * dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF); - int src_width = (s->dc_crt_h_total & 0x00000FFF) + 1; - int dst_width = (s->dc_crt_h_total & 0x00000FFF) + 1; - - if (addressing != 0x0) { - printf("%s: only XY addressing is supported.\n", __func__); - abort(); - } - - if ((s->twoD_source_base & 0x08000000) || - (s->twoD_destination_base & 0x08000000)) { - printf("%s: only local memory is supported.\n", __func__); - abort(); - } - - switch (operation) { - case 0x00: /* copy area */ -#define COPY_AREA(_bpp, _pixel_type, rtl) { \ - int y, x, index_d, index_s; \ - for (y = 0; y < operation_height; y++) { \ - for (x = 0; x < operation_width; x++) { \ - if (rtl) { \ - index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ - index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ - } else { \ - index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ - index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ - } \ - *(_pixel_type*)&dst[index_d] = *(_pixel_type*)&src[index_s];\ - } \ - } \ - } - switch (format_flags) { - case 0: - COPY_AREA(1, uint8_t, rtl); - break; - case 1: - COPY_AREA(2, uint16_t, rtl); - break; - case 2: - COPY_AREA(4, uint32_t, rtl); - break; - } - break; - - case 0x01: /* fill rectangle */ -#define FILL_RECT(_bpp, _pixel_type) { \ - int y, x; \ - for (y = 0; y < operation_height; y++) { \ - for (x = 0; x < operation_width; x++) { \ - int index = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ - *(_pixel_type*)&dst[index] = (_pixel_type)color; \ - } \ - } \ - } - - switch (format_flags) { - case 0: - FILL_RECT(1, uint8_t); - break; - case 1: - FILL_RECT(2, uint16_t); - break; - case 2: - FILL_RECT(4, uint32_t); - break; - } - break; - - default: - printf("non-implemented SM501 2D operation. %d\n", operation); - abort(); - break; - } -} - -static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, - unsigned size) -{ - SM501State * s = (SM501State *)opaque; - uint32_t ret = 0; - SM501_DPRINTF("sm501 system config regs : read addr=%x\n", (int)addr); - - switch(addr) { - case SM501_SYSTEM_CONTROL: - ret = s->system_control; - break; - case SM501_MISC_CONTROL: - ret = s->misc_control; - break; - case SM501_GPIO31_0_CONTROL: - ret = s->gpio_31_0_control; - break; - case SM501_GPIO63_32_CONTROL: - ret = s->gpio_63_32_control; - break; - case SM501_DEVICEID: - ret = 0x050100A0; - break; - case SM501_DRAM_CONTROL: - ret = (s->dram_control & 0x07F107C0) | s->local_mem_size_index << 13; - break; - case SM501_IRQ_MASK: - ret = s->irq_mask; - break; - case SM501_MISC_TIMING: - /* TODO : simulate gate control */ - ret = s->misc_timing; - break; - case SM501_CURRENT_GATE: - /* TODO : simulate gate control */ - ret = 0x00021807; - break; - case SM501_CURRENT_CLOCK: - ret = 0x2A1A0A09; - break; - case SM501_POWER_MODE_CONTROL: - ret = s->power_mode_control; - break; - - default: - printf("sm501 system config : not implemented register read." - " addr=%x\n", (int)addr); - abort(); - } - - return ret; -} - -static void sm501_system_config_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - SM501State * s = (SM501State *)opaque; - SM501_DPRINTF("sm501 system config regs : write addr=%x, val=%x\n", - (uint32_t)addr, (uint32_t)value); - - switch(addr) { - case SM501_SYSTEM_CONTROL: - s->system_control = value & 0xE300B8F7; - break; - case SM501_MISC_CONTROL: - s->misc_control = value & 0xFF7FFF20; - break; - case SM501_GPIO31_0_CONTROL: - s->gpio_31_0_control = value; - break; - case SM501_GPIO63_32_CONTROL: - s->gpio_63_32_control = value; - break; - case SM501_DRAM_CONTROL: - s->local_mem_size_index = (value >> 13) & 0x7; - /* rODO : check validity of size change */ - s->dram_control |= value & 0x7FFFFFC3; - break; - case SM501_IRQ_MASK: - s->irq_mask = value; - break; - case SM501_MISC_TIMING: - s->misc_timing = value & 0xF31F1FFF; - break; - case SM501_POWER_MODE_0_GATE: - case SM501_POWER_MODE_1_GATE: - case SM501_POWER_MODE_0_CLOCK: - case SM501_POWER_MODE_1_CLOCK: - /* TODO : simulate gate & clock control */ - break; - case SM501_POWER_MODE_CONTROL: - s->power_mode_control = value & 0x00000003; - break; - - default: - printf("sm501 system config : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (uint32_t)value); - abort(); - } -} - -static const MemoryRegionOps sm501_system_config_ops = { - .read = sm501_system_config_read, - .write = sm501_system_config_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint32_t sm501_palette_read(void *opaque, hwaddr addr) -{ - SM501State * s = (SM501State *)opaque; - SM501_DPRINTF("sm501 palette read addr=%x\n", (int)addr); - - /* TODO : consider BYTE/WORD access */ - /* TODO : consider endian */ - - assert(range_covers_byte(0, 0x400 * 3, addr)); - return *(uint32_t*)&s->dc_palette[addr]; -} - -static void sm501_palette_write(void *opaque, - hwaddr addr, uint32_t value) -{ - SM501State * s = (SM501State *)opaque; - SM501_DPRINTF("sm501 palette write addr=%x, val=%x\n", - (int)addr, value); - - /* TODO : consider BYTE/WORD access */ - /* TODO : consider endian */ - - assert(range_covers_byte(0, 0x400 * 3, addr)); - *(uint32_t*)&s->dc_palette[addr] = value; -} - -static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, - unsigned size) -{ - SM501State * s = (SM501State *)opaque; - uint32_t ret = 0; - SM501_DPRINTF("sm501 disp ctrl regs : read addr=%x\n", (int)addr); - - switch(addr) { - - case SM501_DC_PANEL_CONTROL: - ret = s->dc_panel_control; - break; - case SM501_DC_PANEL_PANNING_CONTROL: - ret = s->dc_panel_panning_control; - break; - case SM501_DC_PANEL_FB_ADDR: - ret = s->dc_panel_fb_addr; - break; - case SM501_DC_PANEL_FB_OFFSET: - ret = s->dc_panel_fb_offset; - break; - case SM501_DC_PANEL_FB_WIDTH: - ret = s->dc_panel_fb_width; - break; - case SM501_DC_PANEL_FB_HEIGHT: - ret = s->dc_panel_fb_height; - break; - case SM501_DC_PANEL_TL_LOC: - ret = s->dc_panel_tl_location; - break; - case SM501_DC_PANEL_BR_LOC: - ret = s->dc_panel_br_location; - break; - - case SM501_DC_PANEL_H_TOT: - ret = s->dc_panel_h_total; - break; - case SM501_DC_PANEL_H_SYNC: - ret = s->dc_panel_h_sync; - break; - case SM501_DC_PANEL_V_TOT: - ret = s->dc_panel_v_total; - break; - case SM501_DC_PANEL_V_SYNC: - ret = s->dc_panel_v_sync; - break; - - case SM501_DC_CRT_CONTROL: - ret = s->dc_crt_control; - break; - case SM501_DC_CRT_FB_ADDR: - ret = s->dc_crt_fb_addr; - break; - case SM501_DC_CRT_FB_OFFSET: - ret = s->dc_crt_fb_offset; - break; - case SM501_DC_CRT_H_TOT: - ret = s->dc_crt_h_total; - break; - case SM501_DC_CRT_H_SYNC: - ret = s->dc_crt_h_sync; - break; - case SM501_DC_CRT_V_TOT: - ret = s->dc_crt_v_total; - break; - case SM501_DC_CRT_V_SYNC: - ret = s->dc_crt_v_sync; - break; - - case SM501_DC_CRT_HWC_ADDR: - ret = s->dc_crt_hwc_addr; - break; - case SM501_DC_CRT_HWC_LOC: - ret = s->dc_crt_hwc_location; - break; - case SM501_DC_CRT_HWC_COLOR_1_2: - ret = s->dc_crt_hwc_color_1_2; - break; - case SM501_DC_CRT_HWC_COLOR_3: - ret = s->dc_crt_hwc_color_3; - break; - - case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4: - ret = sm501_palette_read(opaque, addr - SM501_DC_PANEL_PALETTE); - break; - - default: - printf("sm501 disp ctrl : not implemented register read." - " addr=%x\n", (int)addr); - abort(); - } - - return ret; -} - -static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - SM501State * s = (SM501State *)opaque; - SM501_DPRINTF("sm501 disp ctrl regs : write addr=%x, val=%x\n", - (unsigned)addr, (unsigned)value); - - switch(addr) { - case SM501_DC_PANEL_CONTROL: - s->dc_panel_control = value & 0x0FFF73FF; - break; - case SM501_DC_PANEL_PANNING_CONTROL: - s->dc_panel_panning_control = value & 0xFF3FFF3F; - break; - case SM501_DC_PANEL_FB_ADDR: - s->dc_panel_fb_addr = value & 0x8FFFFFF0; - break; - case SM501_DC_PANEL_FB_OFFSET: - s->dc_panel_fb_offset = value & 0x3FF03FF0; - break; - case SM501_DC_PANEL_FB_WIDTH: - s->dc_panel_fb_width = value & 0x0FFF0FFF; - break; - case SM501_DC_PANEL_FB_HEIGHT: - s->dc_panel_fb_height = value & 0x0FFF0FFF; - break; - case SM501_DC_PANEL_TL_LOC: - s->dc_panel_tl_location = value & 0x07FF07FF; - break; - case SM501_DC_PANEL_BR_LOC: - s->dc_panel_br_location = value & 0x07FF07FF; - break; - - case SM501_DC_PANEL_H_TOT: - s->dc_panel_h_total = value & 0x0FFF0FFF; - break; - case SM501_DC_PANEL_H_SYNC: - s->dc_panel_h_sync = value & 0x00FF0FFF; - break; - case SM501_DC_PANEL_V_TOT: - s->dc_panel_v_total = value & 0x0FFF0FFF; - break; - case SM501_DC_PANEL_V_SYNC: - s->dc_panel_v_sync = value & 0x003F0FFF; - break; - - case SM501_DC_PANEL_HWC_ADDR: - s->dc_panel_hwc_addr = value & 0x8FFFFFF0; - break; - case SM501_DC_PANEL_HWC_LOC: - s->dc_panel_hwc_location = value & 0x0FFF0FFF; - break; - case SM501_DC_PANEL_HWC_COLOR_1_2: - s->dc_panel_hwc_color_1_2 = value; - break; - case SM501_DC_PANEL_HWC_COLOR_3: - s->dc_panel_hwc_color_3 = value & 0x0000FFFF; - break; - - case SM501_DC_CRT_CONTROL: - s->dc_crt_control = value & 0x0003FFFF; - break; - case SM501_DC_CRT_FB_ADDR: - s->dc_crt_fb_addr = value & 0x8FFFFFF0; - break; - case SM501_DC_CRT_FB_OFFSET: - s->dc_crt_fb_offset = value & 0x3FF03FF0; - break; - case SM501_DC_CRT_H_TOT: - s->dc_crt_h_total = value & 0x0FFF0FFF; - break; - case SM501_DC_CRT_H_SYNC: - s->dc_crt_h_sync = value & 0x00FF0FFF; - break; - case SM501_DC_CRT_V_TOT: - s->dc_crt_v_total = value & 0x0FFF0FFF; - break; - case SM501_DC_CRT_V_SYNC: - s->dc_crt_v_sync = value & 0x003F0FFF; - break; - - case SM501_DC_CRT_HWC_ADDR: - s->dc_crt_hwc_addr = value & 0x8FFFFFF0; - break; - case SM501_DC_CRT_HWC_LOC: - s->dc_crt_hwc_location = value & 0x0FFF0FFF; - break; - case SM501_DC_CRT_HWC_COLOR_1_2: - s->dc_crt_hwc_color_1_2 = value; - break; - case SM501_DC_CRT_HWC_COLOR_3: - s->dc_crt_hwc_color_3 = value & 0x0000FFFF; - break; - - case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4: - sm501_palette_write(opaque, addr - SM501_DC_PANEL_PALETTE, value); - break; - - default: - printf("sm501 disp ctrl : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (unsigned)value); - abort(); - } -} - -static const MemoryRegionOps sm501_disp_ctrl_ops = { - .read = sm501_disp_ctrl_read, - .write = sm501_disp_ctrl_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static uint64_t sm501_2d_engine_read(void *opaque, hwaddr addr, - unsigned size) -{ - SM501State * s = (SM501State *)opaque; - uint32_t ret = 0; - SM501_DPRINTF("sm501 2d engine regs : read addr=%x\n", (int)addr); - - switch(addr) { - case SM501_2D_SOURCE_BASE: - ret = s->twoD_source_base; - break; - default: - printf("sm501 disp ctrl : not implemented register read." - " addr=%x\n", (int)addr); - abort(); - } - - return ret; -} - -static void sm501_2d_engine_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - SM501State * s = (SM501State *)opaque; - SM501_DPRINTF("sm501 2d engine regs : write addr=%x, val=%x\n", - (unsigned)addr, (unsigned)value); - - switch(addr) { - case SM501_2D_SOURCE: - s->twoD_source = value; - break; - case SM501_2D_DESTINATION: - s->twoD_destination = value; - break; - case SM501_2D_DIMENSION: - s->twoD_dimension = value; - break; - case SM501_2D_CONTROL: - s->twoD_control = value; - - /* do 2d operation if start flag is set. */ - if (value & 0x80000000) { - sm501_2d_operation(s); - s->twoD_control &= ~0x80000000; /* start flag down */ - } - - break; - case SM501_2D_PITCH: - s->twoD_pitch = value; - break; - case SM501_2D_FOREGROUND: - s->twoD_foreground = value; - break; - case SM501_2D_STRETCH: - s->twoD_stretch = value; - break; - case SM501_2D_COLOR_COMPARE_MASK: - s->twoD_color_compare_mask = value; - break; - case SM501_2D_MASK: - s->twoD_mask = value; - break; - case SM501_2D_WINDOW_WIDTH: - s->twoD_window_width = value; - break; - case SM501_2D_SOURCE_BASE: - s->twoD_source_base = value; - break; - case SM501_2D_DESTINATION_BASE: - s->twoD_destination_base = value; - break; - default: - printf("sm501 2d engine : not implemented register write." - " addr=%x, val=%x\n", (int)addr, (unsigned)value); - abort(); - } -} - -static const MemoryRegionOps sm501_2d_engine_ops = { - .read = sm501_2d_engine_read, - .write = sm501_2d_engine_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -/* draw line functions for all console modes */ - -typedef void draw_line_func(uint8_t *d, const uint8_t *s, - int width, const uint32_t *pal); - -typedef void draw_hwc_line_func(SM501State * s, int crt, uint8_t * palette, - int c_y, uint8_t *d, int width); - -#define DEPTH 8 -#include "hw/sm501_template.h" - -#define DEPTH 15 -#include "hw/sm501_template.h" - -#define BGR_FORMAT -#define DEPTH 15 -#include "hw/sm501_template.h" - -#define DEPTH 16 -#include "hw/sm501_template.h" - -#define BGR_FORMAT -#define DEPTH 16 -#include "hw/sm501_template.h" - -#define DEPTH 32 -#include "hw/sm501_template.h" - -#define BGR_FORMAT -#define DEPTH 32 -#include "hw/sm501_template.h" - -static draw_line_func * draw_line8_funcs[] = { - draw_line8_8, - draw_line8_15, - draw_line8_16, - draw_line8_32, - draw_line8_32bgr, - draw_line8_15bgr, - draw_line8_16bgr, -}; - -static draw_line_func * draw_line16_funcs[] = { - draw_line16_8, - draw_line16_15, - draw_line16_16, - draw_line16_32, - draw_line16_32bgr, - draw_line16_15bgr, - draw_line16_16bgr, -}; - -static draw_line_func * draw_line32_funcs[] = { - draw_line32_8, - draw_line32_15, - draw_line32_16, - draw_line32_32, - draw_line32_32bgr, - draw_line32_15bgr, - draw_line32_16bgr, -}; - -static draw_hwc_line_func * draw_hwc_line_funcs[] = { - draw_hwc_line_8, - draw_hwc_line_15, - draw_hwc_line_16, - draw_hwc_line_32, - draw_hwc_line_32bgr, - draw_hwc_line_15bgr, - draw_hwc_line_16bgr, -}; - -static inline int get_depth_index(DisplaySurface *surface) -{ - switch (surface_bits_per_pixel(surface)) { - default: - case 8: - return 0; - case 15: - return 1; - case 16: - return 2; - case 32: - if (is_surface_bgr(surface)) { - return 4; - } else { - return 3; - } - } -} - -static void sm501_draw_crt(SM501State * s) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int y; - int width = (s->dc_crt_h_total & 0x00000FFF) + 1; - int height = (s->dc_crt_v_total & 0x00000FFF) + 1; - - uint8_t * src = s->local_mem; - int src_bpp = 0; - int dst_bpp = surface_bytes_per_pixel(surface); - uint32_t * palette = (uint32_t *)&s->dc_palette[SM501_DC_CRT_PALETTE - - SM501_DC_PANEL_PALETTE]; - uint8_t hwc_palette[3 * 3]; - int ds_depth_index = get_depth_index(surface); - draw_line_func * draw_line = NULL; - draw_hwc_line_func * draw_hwc_line = NULL; - int full_update = 0; - int y_start = -1; - ram_addr_t page_min = ~0l; - ram_addr_t page_max = 0l; - ram_addr_t offset = 0; - - /* choose draw_line function */ - switch (s->dc_crt_control & 3) { - case SM501_DC_CRT_CONTROL_8BPP: - src_bpp = 1; - draw_line = draw_line8_funcs[ds_depth_index]; - break; - case SM501_DC_CRT_CONTROL_16BPP: - src_bpp = 2; - draw_line = draw_line16_funcs[ds_depth_index]; - break; - case SM501_DC_CRT_CONTROL_32BPP: - src_bpp = 4; - draw_line = draw_line32_funcs[ds_depth_index]; - break; - default: - printf("sm501 draw crt : invalid DC_CRT_CONTROL=%x.\n", - s->dc_crt_control); - abort(); - break; - } - - /* set up to draw hardware cursor */ - if (is_hwc_enabled(s, 1)) { - int i; - - /* get cursor palette */ - for (i = 0; i < 3; i++) { - uint16_t rgb565 = get_hwc_color(s, 1, i + 1); - hwc_palette[i * 3 + 0] = (rgb565 & 0xf800) >> 8; /* red */ - hwc_palette[i * 3 + 1] = (rgb565 & 0x07e0) >> 3; /* green */ - hwc_palette[i * 3 + 2] = (rgb565 & 0x001f) << 3; /* blue */ - } - - /* choose cursor draw line function */ - draw_hwc_line = draw_hwc_line_funcs[ds_depth_index]; - } - - /* adjust console size */ - if (s->last_width != width || s->last_height != height) { - qemu_console_resize(s->con, width, height); - surface = qemu_console_surface(s->con); - s->last_width = width; - s->last_height = height; - full_update = 1; - } - - /* draw each line according to conditions */ - for (y = 0; y < height; y++) { - int update_hwc = draw_hwc_line ? within_hwc_y_range(s, y, 1) : 0; - int update = full_update || update_hwc; - ram_addr_t page0 = offset; - ram_addr_t page1 = offset + width * src_bpp - 1; - - /* check dirty flags for each line */ - update = memory_region_get_dirty(&s->local_mem_region, page0, - page1 - page0, DIRTY_MEMORY_VGA); - - /* draw line and change status */ - if (update) { - uint8_t *d = surface_data(surface); - d += y * width * dst_bpp; - - /* draw graphics layer */ - draw_line(d, src, width, palette); - - /* draw haredware cursor */ - if (update_hwc) { - draw_hwc_line(s, 1, hwc_palette, y - get_hwc_y(s, 1), d, width); - } - - if (y_start < 0) - y_start = y; - if (page0 < page_min) - page_min = page0; - if (page1 > page_max) - page_max = page1; - } else { - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(s->con, 0, y_start, width, y - y_start); - y_start = -1; - } - } - - src += width * src_bpp; - offset += width * src_bpp; - } - - /* complete flush to display */ - if (y_start >= 0) - dpy_gfx_update(s->con, 0, y_start, width, y - y_start); - - /* clear dirty flags */ - if (page_min != ~0l) { - memory_region_reset_dirty(&s->local_mem_region, - page_min, page_max + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - } -} - -static void sm501_update_display(void *opaque) -{ - SM501State * s = (SM501State *)opaque; - - if (s->dc_crt_control & SM501_DC_CRT_CONTROL_ENABLE) - sm501_draw_crt(s); -} - -void sm501_init(MemoryRegion *address_space_mem, uint32_t base, - uint32_t local_mem_bytes, qemu_irq irq, CharDriverState *chr) -{ - SM501State * s; - DeviceState *dev; - MemoryRegion *sm501_system_config = g_new(MemoryRegion, 1); - MemoryRegion *sm501_disp_ctrl = g_new(MemoryRegion, 1); - MemoryRegion *sm501_2d_engine = g_new(MemoryRegion, 1); - - /* allocate management data region */ - s = (SM501State *)g_malloc0(sizeof(SM501State)); - s->base = base; - s->local_mem_size_index - = get_local_mem_size_index(local_mem_bytes); - SM501_DPRINTF("local mem size=%x. index=%d\n", get_local_mem_size(s), - s->local_mem_size_index); - s->system_control = 0x00100000; - s->misc_control = 0x00001000; /* assumes SH, active=low */ - s->dc_panel_control = 0x00010000; - s->dc_crt_control = 0x00010000; - - /* allocate local memory */ - memory_region_init_ram(&s->local_mem_region, "sm501.local", - local_mem_bytes); - vmstate_register_ram_global(&s->local_mem_region); - s->local_mem = memory_region_get_ram_ptr(&s->local_mem_region); - memory_region_add_subregion(address_space_mem, base, &s->local_mem_region); - - /* map mmio */ - memory_region_init_io(sm501_system_config, &sm501_system_config_ops, s, - "sm501-system-config", 0x6c); - memory_region_add_subregion(address_space_mem, base + MMIO_BASE_OFFSET, - sm501_system_config); - memory_region_init_io(sm501_disp_ctrl, &sm501_disp_ctrl_ops, s, - "sm501-disp-ctrl", 0x1000); - memory_region_add_subregion(address_space_mem, - base + MMIO_BASE_OFFSET + SM501_DC, - sm501_disp_ctrl); - memory_region_init_io(sm501_2d_engine, &sm501_2d_engine_ops, s, - "sm501-2d-engine", 0x54); - memory_region_add_subregion(address_space_mem, - base + MMIO_BASE_OFFSET + SM501_2D_ENGINE, - sm501_2d_engine); - - /* bridge to usb host emulation module */ - dev = qdev_create(NULL, "sysbus-ohci"); - qdev_prop_set_uint32(dev, "num-ports", 2); - qdev_prop_set_taddr(dev, "dma-offset", base); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, - base + MMIO_BASE_OFFSET + SM501_USB_HOST); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - - /* bridge to serial emulation module */ - if (chr) { - serial_mm_init(address_space_mem, - base + MMIO_BASE_OFFSET + SM501_UART0, 2, - NULL, /* TODO : chain irq to IRL */ - 115200, chr, DEVICE_NATIVE_ENDIAN); - } - - /* create qemu graphic console */ - s->con = graphic_console_init(sm501_update_display, NULL, - NULL, NULL, s); -} diff --git a/hw/sparc/Makefile.objs b/hw/sparc/Makefile.objs index b2a921e7e1..2354616968 100644 --- a/hw/sparc/Makefile.objs +++ b/hw/sparc/Makefile.objs @@ -1,4 +1,4 @@ -obj-y = tcx.o sun4m_iommu.o slavio_intctl.o +obj-y = sun4m_iommu.o slavio_intctl.o obj-y += slavio_timer.o slavio_misc.o sparc32_dma.o obj-y += eccmemctl.o sbi.o sun4c_intctl.o diff --git a/hw/tc6393xb.c b/hw/tc6393xb.c deleted file mode 100644 index 2d5fa89e9b..0000000000 --- a/hw/tc6393xb.c +++ /dev/null @@ -1,593 +0,0 @@ -/* - * Toshiba TC6393XB I/O Controller. - * Found in Sharp Zaurus SL-6000 (tosa) or some - * Toshiba e-Series PDAs. - * - * Most features are currently unsupported!!! - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ -#include "hw/hw.h" -#include "hw/arm/devices.h" -#include "hw/block/flash.h" -#include "ui/console.h" -#include "ui/pixel_ops.h" -#include "sysemu/blockdev.h" - -#define IRQ_TC6393_NAND 0 -#define IRQ_TC6393_MMC 1 -#define IRQ_TC6393_OHCI 2 -#define IRQ_TC6393_SERIAL 3 -#define IRQ_TC6393_FB 4 - -#define TC6393XB_NR_IRQS 8 - -#define TC6393XB_GPIOS 16 - -#define SCR_REVID 0x08 /* b Revision ID */ -#define SCR_ISR 0x50 /* b Interrupt Status */ -#define SCR_IMR 0x52 /* b Interrupt Mask */ -#define SCR_IRR 0x54 /* b Interrupt Routing */ -#define SCR_GPER 0x60 /* w GP Enable */ -#define SCR_GPI_SR(i) (0x64 + (i)) /* b3 GPI Status */ -#define SCR_GPI_IMR(i) (0x68 + (i)) /* b3 GPI INT Mask */ -#define SCR_GPI_EDER(i) (0x6c + (i)) /* b3 GPI Edge Detect Enable */ -#define SCR_GPI_LIR(i) (0x70 + (i)) /* b3 GPI Level Invert */ -#define SCR_GPO_DSR(i) (0x78 + (i)) /* b3 GPO Data Set */ -#define SCR_GPO_DOECR(i) (0x7c + (i)) /* b3 GPO Data OE Control */ -#define SCR_GP_IARCR(i) (0x80 + (i)) /* b3 GP Internal Active Register Control */ -#define SCR_GP_IARLCR(i) (0x84 + (i)) /* b3 GP INTERNAL Active Register Level Control */ -#define SCR_GPI_BCR(i) (0x88 + (i)) /* b3 GPI Buffer Control */ -#define SCR_GPA_IARCR 0x8c /* w GPa Internal Active Register Control */ -#define SCR_GPA_IARLCR 0x90 /* w GPa Internal Active Register Level Control */ -#define SCR_GPA_BCR 0x94 /* w GPa Buffer Control */ -#define SCR_CCR 0x98 /* w Clock Control */ -#define SCR_PLL2CR 0x9a /* w PLL2 Control */ -#define SCR_PLL1CR 0x9c /* l PLL1 Control */ -#define SCR_DIARCR 0xa0 /* b Device Internal Active Register Control */ -#define SCR_DBOCR 0xa1 /* b Device Buffer Off Control */ -#define SCR_FER 0xe0 /* b Function Enable */ -#define SCR_MCR 0xe4 /* w Mode Control */ -#define SCR_CONFIG 0xfc /* b Configuration Control */ -#define SCR_DEBUG 0xff /* b Debug */ - -#define NAND_CFG_COMMAND 0x04 /* w Command */ -#define NAND_CFG_BASE 0x10 /* l Control Base Address */ -#define NAND_CFG_INTP 0x3d /* b Interrupt Pin */ -#define NAND_CFG_INTE 0x48 /* b Int Enable */ -#define NAND_CFG_EC 0x4a /* b Event Control */ -#define NAND_CFG_ICC 0x4c /* b Internal Clock Control */ -#define NAND_CFG_ECCC 0x5b /* b ECC Control */ -#define NAND_CFG_NFTC 0x60 /* b NAND Flash Transaction Control */ -#define NAND_CFG_NFM 0x61 /* b NAND Flash Monitor */ -#define NAND_CFG_NFPSC 0x62 /* b NAND Flash Power Supply Control */ -#define NAND_CFG_NFDC 0x63 /* b NAND Flash Detect Control */ - -#define NAND_DATA 0x00 /* l Data */ -#define NAND_MODE 0x04 /* b Mode */ -#define NAND_STATUS 0x05 /* b Status */ -#define NAND_ISR 0x06 /* b Interrupt Status */ -#define NAND_IMR 0x07 /* b Interrupt Mask */ - -#define NAND_MODE_WP 0x80 -#define NAND_MODE_CE 0x10 -#define NAND_MODE_ALE 0x02 -#define NAND_MODE_CLE 0x01 -#define NAND_MODE_ECC_MASK 0x60 -#define NAND_MODE_ECC_EN 0x20 -#define NAND_MODE_ECC_READ 0x40 -#define NAND_MODE_ECC_RST 0x60 - -struct TC6393xbState { - MemoryRegion iomem; - qemu_irq irq; - qemu_irq *sub_irqs; - struct { - uint8_t ISR; - uint8_t IMR; - uint8_t IRR; - uint16_t GPER; - uint8_t GPI_SR[3]; - uint8_t GPI_IMR[3]; - uint8_t GPI_EDER[3]; - uint8_t GPI_LIR[3]; - uint8_t GP_IARCR[3]; - uint8_t GP_IARLCR[3]; - uint8_t GPI_BCR[3]; - uint16_t GPA_IARCR; - uint16_t GPA_IARLCR; - uint16_t CCR; - uint16_t PLL2CR; - uint32_t PLL1CR; - uint8_t DIARCR; - uint8_t DBOCR; - uint8_t FER; - uint16_t MCR; - uint8_t CONFIG; - uint8_t DEBUG; - } scr; - uint32_t gpio_dir; - uint32_t gpio_level; - uint32_t prev_level; - qemu_irq handler[TC6393XB_GPIOS]; - qemu_irq *gpio_in; - - struct { - uint8_t mode; - uint8_t isr; - uint8_t imr; - } nand; - int nand_enable; - uint32_t nand_phys; - DeviceState *flash; - ECCState ecc; - - QemuConsole *con; - MemoryRegion vram; - uint16_t *vram_ptr; - uint32_t scr_width, scr_height; /* in pixels */ - qemu_irq l3v; - unsigned blank : 1, - blanked : 1; -}; - -qemu_irq *tc6393xb_gpio_in_get(TC6393xbState *s) -{ - return s->gpio_in; -} - -static void tc6393xb_gpio_set(void *opaque, int line, int level) -{ -// TC6393xbState *s = opaque; - - if (line > TC6393XB_GPIOS) { - printf("%s: No GPIO pin %i\n", __FUNCTION__, line); - return; - } - - // FIXME: how does the chip reflect the GPIO input level change? -} - -void tc6393xb_gpio_out_set(TC6393xbState *s, int line, - qemu_irq handler) -{ - if (line >= TC6393XB_GPIOS) { - fprintf(stderr, "TC6393xb: no GPIO pin %d\n", line); - return; - } - - s->handler[line] = handler; -} - -static void tc6393xb_gpio_handler_update(TC6393xbState *s) -{ - uint32_t level, diff; - int bit; - - level = s->gpio_level & s->gpio_dir; - - for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { - bit = ffs(diff) - 1; - qemu_set_irq(s->handler[bit], (level >> bit) & 1); - } - - s->prev_level = level; -} - -qemu_irq tc6393xb_l3v_get(TC6393xbState *s) -{ - return s->l3v; -} - -static void tc6393xb_l3v(void *opaque, int line, int level) -{ - TC6393xbState *s = opaque; - s->blank = !level; - fprintf(stderr, "L3V: %d\n", level); -} - -static void tc6393xb_sub_irq(void *opaque, int line, int level) { - TC6393xbState *s = opaque; - uint8_t isr = s->scr.ISR; - if (level) - isr |= 1 << line; - else - isr &= ~(1 << line); - s->scr.ISR = isr; - qemu_set_irq(s->irq, isr & s->scr.IMR); -} - -#define SCR_REG_B(N) \ - case SCR_ ##N: return s->scr.N -#define SCR_REG_W(N) \ - case SCR_ ##N: return s->scr.N; \ - case SCR_ ##N + 1: return s->scr.N >> 8; -#define SCR_REG_L(N) \ - case SCR_ ##N: return s->scr.N; \ - case SCR_ ##N + 1: return s->scr.N >> 8; \ - case SCR_ ##N + 2: return s->scr.N >> 16; \ - case SCR_ ##N + 3: return s->scr.N >> 24; -#define SCR_REG_A(N) \ - case SCR_ ##N(0): return s->scr.N[0]; \ - case SCR_ ##N(1): return s->scr.N[1]; \ - case SCR_ ##N(2): return s->scr.N[2] - -static uint32_t tc6393xb_scr_readb(TC6393xbState *s, hwaddr addr) -{ - switch (addr) { - case SCR_REVID: - return 3; - case SCR_REVID+1: - return 0; - SCR_REG_B(ISR); - SCR_REG_B(IMR); - SCR_REG_B(IRR); - SCR_REG_W(GPER); - SCR_REG_A(GPI_SR); - SCR_REG_A(GPI_IMR); - SCR_REG_A(GPI_EDER); - SCR_REG_A(GPI_LIR); - case SCR_GPO_DSR(0): - case SCR_GPO_DSR(1): - case SCR_GPO_DSR(2): - return (s->gpio_level >> ((addr - SCR_GPO_DSR(0)) * 8)) & 0xff; - case SCR_GPO_DOECR(0): - case SCR_GPO_DOECR(1): - case SCR_GPO_DOECR(2): - return (s->gpio_dir >> ((addr - SCR_GPO_DOECR(0)) * 8)) & 0xff; - SCR_REG_A(GP_IARCR); - SCR_REG_A(GP_IARLCR); - SCR_REG_A(GPI_BCR); - SCR_REG_W(GPA_IARCR); - SCR_REG_W(GPA_IARLCR); - SCR_REG_W(CCR); - SCR_REG_W(PLL2CR); - SCR_REG_L(PLL1CR); - SCR_REG_B(DIARCR); - SCR_REG_B(DBOCR); - SCR_REG_B(FER); - SCR_REG_W(MCR); - SCR_REG_B(CONFIG); - SCR_REG_B(DEBUG); - } - fprintf(stderr, "tc6393xb_scr: unhandled read at %08x\n", (uint32_t) addr); - return 0; -} -#undef SCR_REG_B -#undef SCR_REG_W -#undef SCR_REG_L -#undef SCR_REG_A - -#define SCR_REG_B(N) \ - case SCR_ ##N: s->scr.N = value; return; -#define SCR_REG_W(N) \ - case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \ - case SCR_ ##N + 1: s->scr.N = (s->scr.N & 0xff) | (value << 8); return -#define SCR_REG_L(N) \ - case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \ - case SCR_ ##N + 1: s->scr.N = (s->scr.N & ~(0xff << 8)) | (value & (0xff << 8)); return; \ - case SCR_ ##N + 2: s->scr.N = (s->scr.N & ~(0xff << 16)) | (value & (0xff << 16)); return; \ - case SCR_ ##N + 3: s->scr.N = (s->scr.N & ~(0xff << 24)) | (value & (0xff << 24)); return; -#define SCR_REG_A(N) \ - case SCR_ ##N(0): s->scr.N[0] = value; return; \ - case SCR_ ##N(1): s->scr.N[1] = value; return; \ - case SCR_ ##N(2): s->scr.N[2] = value; return - -static void tc6393xb_scr_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) -{ - switch (addr) { - SCR_REG_B(ISR); - SCR_REG_B(IMR); - SCR_REG_B(IRR); - SCR_REG_W(GPER); - SCR_REG_A(GPI_SR); - SCR_REG_A(GPI_IMR); - SCR_REG_A(GPI_EDER); - SCR_REG_A(GPI_LIR); - case SCR_GPO_DSR(0): - case SCR_GPO_DSR(1): - case SCR_GPO_DSR(2): - s->gpio_level = (s->gpio_level & ~(0xff << ((addr - SCR_GPO_DSR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DSR(0))*8)); - tc6393xb_gpio_handler_update(s); - return; - case SCR_GPO_DOECR(0): - case SCR_GPO_DOECR(1): - case SCR_GPO_DOECR(2): - s->gpio_dir = (s->gpio_dir & ~(0xff << ((addr - SCR_GPO_DOECR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DOECR(0))*8)); - tc6393xb_gpio_handler_update(s); - return; - SCR_REG_A(GP_IARCR); - SCR_REG_A(GP_IARLCR); - SCR_REG_A(GPI_BCR); - SCR_REG_W(GPA_IARCR); - SCR_REG_W(GPA_IARLCR); - SCR_REG_W(CCR); - SCR_REG_W(PLL2CR); - SCR_REG_L(PLL1CR); - SCR_REG_B(DIARCR); - SCR_REG_B(DBOCR); - SCR_REG_B(FER); - SCR_REG_W(MCR); - SCR_REG_B(CONFIG); - SCR_REG_B(DEBUG); - } - fprintf(stderr, "tc6393xb_scr: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); -} -#undef SCR_REG_B -#undef SCR_REG_W -#undef SCR_REG_L -#undef SCR_REG_A - -static void tc6393xb_nand_irq(TC6393xbState *s) { - qemu_set_irq(s->sub_irqs[IRQ_TC6393_NAND], - (s->nand.imr & 0x80) && (s->nand.imr & s->nand.isr)); -} - -static uint32_t tc6393xb_nand_cfg_readb(TC6393xbState *s, hwaddr addr) { - switch (addr) { - case NAND_CFG_COMMAND: - return s->nand_enable ? 2 : 0; - case NAND_CFG_BASE: - case NAND_CFG_BASE + 1: - case NAND_CFG_BASE + 2: - case NAND_CFG_BASE + 3: - return s->nand_phys >> (addr - NAND_CFG_BASE); - } - fprintf(stderr, "tc6393xb_nand_cfg: unhandled read at %08x\n", (uint32_t) addr); - return 0; -} -static void tc6393xb_nand_cfg_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) { - switch (addr) { - case NAND_CFG_COMMAND: - s->nand_enable = (value & 0x2); - return; - case NAND_CFG_BASE: - case NAND_CFG_BASE + 1: - case NAND_CFG_BASE + 2: - case NAND_CFG_BASE + 3: - s->nand_phys &= ~(0xff << ((addr - NAND_CFG_BASE) * 8)); - s->nand_phys |= (value & 0xff) << ((addr - NAND_CFG_BASE) * 8); - return; - } - fprintf(stderr, "tc6393xb_nand_cfg: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); -} - -static uint32_t tc6393xb_nand_readb(TC6393xbState *s, hwaddr addr) { - switch (addr) { - case NAND_DATA + 0: - case NAND_DATA + 1: - case NAND_DATA + 2: - case NAND_DATA + 3: - return nand_getio(s->flash); - case NAND_MODE: - return s->nand.mode; - case NAND_STATUS: - return 0x14; - case NAND_ISR: - return s->nand.isr; - case NAND_IMR: - return s->nand.imr; - } - fprintf(stderr, "tc6393xb_nand: unhandled read at %08x\n", (uint32_t) addr); - return 0; -} -static void tc6393xb_nand_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) { -// fprintf(stderr, "tc6393xb_nand: write at %08x: %02x\n", -// (uint32_t) addr, value & 0xff); - switch (addr) { - case NAND_DATA + 0: - case NAND_DATA + 1: - case NAND_DATA + 2: - case NAND_DATA + 3: - nand_setio(s->flash, value); - s->nand.isr |= 1; - tc6393xb_nand_irq(s); - return; - case NAND_MODE: - s->nand.mode = value; - nand_setpins(s->flash, - value & NAND_MODE_CLE, - value & NAND_MODE_ALE, - !(value & NAND_MODE_CE), - value & NAND_MODE_WP, - 0); // FIXME: gnd - switch (value & NAND_MODE_ECC_MASK) { - case NAND_MODE_ECC_RST: - ecc_reset(&s->ecc); - break; - case NAND_MODE_ECC_READ: - // FIXME - break; - case NAND_MODE_ECC_EN: - ecc_reset(&s->ecc); - } - return; - case NAND_ISR: - s->nand.isr = value; - tc6393xb_nand_irq(s); - return; - case NAND_IMR: - s->nand.imr = value; - tc6393xb_nand_irq(s); - return; - } - fprintf(stderr, "tc6393xb_nand: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); -} - -#define BITS 8 -#include "hw/tc6393xb_template.h" -#define BITS 15 -#include "hw/tc6393xb_template.h" -#define BITS 16 -#include "hw/tc6393xb_template.h" -#define BITS 24 -#include "hw/tc6393xb_template.h" -#define BITS 32 -#include "hw/tc6393xb_template.h" - -static void tc6393xb_draw_graphic(TC6393xbState *s, int full_update) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - - switch (surface_bits_per_pixel(surface)) { - case 8: - tc6393xb_draw_graphic8(s); - break; - case 15: - tc6393xb_draw_graphic15(s); - break; - case 16: - tc6393xb_draw_graphic16(s); - break; - case 24: - tc6393xb_draw_graphic24(s); - break; - case 32: - tc6393xb_draw_graphic32(s); - break; - default: - printf("tc6393xb: unknown depth %d\n", - surface_bits_per_pixel(surface)); - return; - } - - dpy_gfx_update(s->con, 0, 0, s->scr_width, s->scr_height); -} - -static void tc6393xb_draw_blank(TC6393xbState *s, int full_update) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i, w; - uint8_t *d; - - if (!full_update) - return; - - w = s->scr_width * surface_bytes_per_pixel(surface); - d = surface_data(surface); - for(i = 0; i < s->scr_height; i++) { - memset(d, 0, w); - d += surface_stride(surface); - } - - dpy_gfx_update(s->con, 0, 0, s->scr_width, s->scr_height); -} - -static void tc6393xb_update_display(void *opaque) -{ - TC6393xbState *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - int full_update; - - if (s->scr_width == 0 || s->scr_height == 0) - return; - - full_update = 0; - if (s->blanked != s->blank) { - s->blanked = s->blank; - full_update = 1; - } - if (s->scr_width != surface_width(surface) || - s->scr_height != surface_height(surface)) { - qemu_console_resize(s->con, s->scr_width, s->scr_height); - full_update = 1; - } - if (s->blanked) - tc6393xb_draw_blank(s, full_update); - else - tc6393xb_draw_graphic(s, full_update); -} - - -static uint64_t tc6393xb_readb(void *opaque, hwaddr addr, - unsigned size) -{ - TC6393xbState *s = opaque; - - switch (addr >> 8) { - case 0: - return tc6393xb_scr_readb(s, addr & 0xff); - case 1: - return tc6393xb_nand_cfg_readb(s, addr & 0xff); - }; - - if ((addr &~0xff) == s->nand_phys && s->nand_enable) { -// return tc6393xb_nand_readb(s, addr & 0xff); - uint8_t d = tc6393xb_nand_readb(s, addr & 0xff); -// fprintf(stderr, "tc6393xb_nand: read at %08x: %02hhx\n", (uint32_t) addr, d); - return d; - } - -// fprintf(stderr, "tc6393xb: unhandled read at %08x\n", (uint32_t) addr); - return 0; -} - -static void tc6393xb_writeb(void *opaque, hwaddr addr, - uint64_t value, unsigned size) { - TC6393xbState *s = opaque; - - switch (addr >> 8) { - case 0: - tc6393xb_scr_writeb(s, addr & 0xff, value); - return; - case 1: - tc6393xb_nand_cfg_writeb(s, addr & 0xff, value); - return; - }; - - if ((addr &~0xff) == s->nand_phys && s->nand_enable) - tc6393xb_nand_writeb(s, addr & 0xff, value); - else - fprintf(stderr, "tc6393xb: unhandled write at %08x: %02x\n", - (uint32_t) addr, (int)value & 0xff); -} - -TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq) -{ - TC6393xbState *s; - DriveInfo *nand; - static const MemoryRegionOps tc6393xb_ops = { - .read = tc6393xb_readb, - .write = tc6393xb_writeb, - .endianness = DEVICE_NATIVE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - }; - - s = (TC6393xbState *) g_malloc0(sizeof(TC6393xbState)); - s->irq = irq; - s->gpio_in = qemu_allocate_irqs(tc6393xb_gpio_set, s, TC6393XB_GPIOS); - - s->l3v = *qemu_allocate_irqs(tc6393xb_l3v, s, 1); - s->blanked = 1; - - s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS); - - nand = drive_get(IF_MTD, 0, 0); - s->flash = nand_init(nand ? nand->bdrv : NULL, NAND_MFR_TOSHIBA, 0x76); - - memory_region_init_io(&s->iomem, &tc6393xb_ops, s, "tc6393xb", 0x10000); - memory_region_add_subregion(sysmem, base, &s->iomem); - - memory_region_init_ram(&s->vram, "tc6393xb.vram", 0x100000); - vmstate_register_ram_global(&s->vram); - s->vram_ptr = memory_region_get_ram_ptr(&s->vram); - memory_region_add_subregion(sysmem, base + 0x100000, &s->vram); - s->scr_width = 480; - s->scr_height = 640; - s->con = graphic_console_init(tc6393xb_update_display, - NULL, /* invalidate */ - NULL, /* screen_dump */ - NULL, /* text_update */ - s); - - return s; -} diff --git a/hw/tcx.c b/hw/tcx.c deleted file mode 100644 index c44068e7c9..0000000000 --- a/hw/tcx.c +++ /dev/null @@ -1,739 +0,0 @@ -/* - * QEMU TCX Frame buffer - * - * Copyright (c) 2003-2005 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "qemu-common.h" -#include "ui/console.h" -#include "ui/pixel_ops.h" -#include "hw/sysbus.h" -#include "hw/qdev-addr.h" - -#define MAXX 1024 -#define MAXY 768 -#define TCX_DAC_NREGS 16 -#define TCX_THC_NREGS_8 0x081c -#define TCX_THC_NREGS_24 0x1000 -#define TCX_TEC_NREGS 0x1000 - -typedef struct TCXState { - SysBusDevice busdev; - QemuConsole *con; - uint8_t *vram; - uint32_t *vram24, *cplane; - MemoryRegion vram_mem; - MemoryRegion vram_8bit; - MemoryRegion vram_24bit; - MemoryRegion vram_cplane; - MemoryRegion dac; - MemoryRegion tec; - MemoryRegion thc24; - MemoryRegion thc8; - ram_addr_t vram24_offset, cplane_offset; - uint32_t vram_size; - uint32_t palette[256]; - uint8_t r[256], g[256], b[256]; - uint16_t width, height, depth; - uint8_t dac_index, dac_state; -} TCXState; - -static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch, - Error **errp); -static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch, - Error **errp); - -static void tcx_set_dirty(TCXState *s) -{ - memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY); -} - -static void tcx24_set_dirty(TCXState *s) -{ - memory_region_set_dirty(&s->vram_mem, s->vram24_offset, MAXX * MAXY * 4); - memory_region_set_dirty(&s->vram_mem, s->cplane_offset, MAXX * MAXY * 4); -} - -static void update_palette_entries(TCXState *s, int start, int end) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i; - - for (i = start; i < end; i++) { - switch (surface_bits_per_pixel(surface)) { - default: - case 8: - s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]); - break; - case 15: - s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]); - break; - case 16: - s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]); - break; - case 32: - if (is_surface_bgr(surface)) { - s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); - } else { - s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); - } - break; - } - } - if (s->depth == 24) { - tcx24_set_dirty(s); - } else { - tcx_set_dirty(s); - } -} - -static void tcx_draw_line32(TCXState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int x; - uint8_t val; - uint32_t *p = (uint32_t *)d; - - for(x = 0; x < width; x++) { - val = *s++; - *p++ = s1->palette[val]; - } -} - -static void tcx_draw_line16(TCXState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int x; - uint8_t val; - uint16_t *p = (uint16_t *)d; - - for(x = 0; x < width; x++) { - val = *s++; - *p++ = s1->palette[val]; - } -} - -static void tcx_draw_line8(TCXState *s1, uint8_t *d, - const uint8_t *s, int width) -{ - int x; - uint8_t val; - - for(x = 0; x < width; x++) { - val = *s++; - *d++ = s1->palette[val]; - } -} - -/* - XXX Could be much more optimal: - * detect if line/page/whole screen is in 24 bit mode - * if destination is also BGR, use memcpy - */ -static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, - const uint8_t *s, int width, - const uint32_t *cplane, - const uint32_t *s24) -{ - DisplaySurface *surface = qemu_console_surface(s1->con); - int x, bgr, r, g, b; - uint8_t val, *p8; - uint32_t *p = (uint32_t *)d; - uint32_t dval; - - bgr = is_surface_bgr(surface); - for(x = 0; x < width; x++, s++, s24++) { - if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) { - // 24-bit direct, BGR order - p8 = (uint8_t *)s24; - p8++; - b = *p8++; - g = *p8++; - r = *p8; - if (bgr) - dval = rgb_to_pixel32bgr(r, g, b); - else - dval = rgb_to_pixel32(r, g, b); - } else { - val = *s; - dval = s1->palette[val]; - } - *p++ = dval; - } -} - -static inline int check_dirty(TCXState *s, ram_addr_t page, ram_addr_t page24, - ram_addr_t cpage) -{ - int ret; - - ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4, - DIRTY_MEMORY_VGA); - ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4, - DIRTY_MEMORY_VGA); - return ret; -} - -static inline void reset_dirty(TCXState *ts, ram_addr_t page_min, - ram_addr_t page_max, ram_addr_t page24, - ram_addr_t cpage) -{ - memory_region_reset_dirty(&ts->vram_mem, - page_min, page_max + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - memory_region_reset_dirty(&ts->vram_mem, - page24 + page_min * 4, - page24 + page_max * 4 + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - memory_region_reset_dirty(&ts->vram_mem, - cpage + page_min * 4, - cpage + page_max * 4 + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); -} - -/* Fixed line length 1024 allows us to do nice tricks not possible on - VGA... */ -static void tcx_update_display(void *opaque) -{ - TCXState *ts = opaque; - DisplaySurface *surface = qemu_console_surface(ts->con); - ram_addr_t page, page_min, page_max; - int y, y_start, dd, ds; - uint8_t *d, *s; - void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width); - - if (surface_bits_per_pixel(surface) == 0) { - return; - } - - page = 0; - y_start = -1; - page_min = -1; - page_max = 0; - d = surface_data(surface); - s = ts->vram; - dd = surface_stride(surface); - ds = 1024; - - switch (surface_bits_per_pixel(surface)) { - case 32: - f = tcx_draw_line32; - break; - case 15: - case 16: - f = tcx_draw_line16; - break; - default: - case 8: - f = tcx_draw_line8; - break; - case 0: - return; - } - - for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) { - if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA)) { - if (y_start < 0) - y_start = y; - if (page < page_min) - page_min = page; - if (page > page_max) - page_max = page; - f(ts, d, s, ts->width); - d += dd; - s += ds; - f(ts, d, s, ts->width); - d += dd; - s += ds; - f(ts, d, s, ts->width); - d += dd; - s += ds; - f(ts, d, s, ts->width); - d += dd; - s += ds; - } else { - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(ts->con, 0, y_start, - ts->width, y - y_start); - y_start = -1; - } - d += dd * 4; - s += ds * 4; - } - } - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(ts->con, 0, y_start, - ts->width, y - y_start); - } - /* reset modified pages */ - if (page_max >= page_min) { - memory_region_reset_dirty(&ts->vram_mem, - page_min, page_max + TARGET_PAGE_SIZE, - DIRTY_MEMORY_VGA); - } -} - -static void tcx24_update_display(void *opaque) -{ - TCXState *ts = opaque; - DisplaySurface *surface = qemu_console_surface(ts->con); - ram_addr_t page, page_min, page_max, cpage, page24; - int y, y_start, dd, ds; - uint8_t *d, *s; - uint32_t *cptr, *s24; - - if (surface_bits_per_pixel(surface) != 32) { - return; - } - - page = 0; - page24 = ts->vram24_offset; - cpage = ts->cplane_offset; - y_start = -1; - page_min = -1; - page_max = 0; - d = surface_data(surface); - s = ts->vram; - s24 = ts->vram24; - cptr = ts->cplane; - dd = surface_stride(surface); - ds = 1024; - - for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE, - page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) { - if (check_dirty(ts, page, page24, cpage)) { - if (y_start < 0) - y_start = y; - if (page < page_min) - page_min = page; - if (page > page_max) - page_max = page; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - d += dd; - s += ds; - cptr += ds; - s24 += ds; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - d += dd; - s += ds; - cptr += ds; - s24 += ds; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - d += dd; - s += ds; - cptr += ds; - s24 += ds; - tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); - d += dd; - s += ds; - cptr += ds; - s24 += ds; - } else { - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(ts->con, 0, y_start, - ts->width, y - y_start); - y_start = -1; - } - d += dd * 4; - s += ds * 4; - cptr += ds * 4; - s24 += ds * 4; - } - } - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(ts->con, 0, y_start, - ts->width, y - y_start); - } - /* reset modified pages */ - if (page_max >= page_min) { - reset_dirty(ts, page_min, page_max, page24, cpage); - } -} - -static void tcx_invalidate_display(void *opaque) -{ - TCXState *s = opaque; - - tcx_set_dirty(s); - qemu_console_resize(s->con, s->width, s->height); -} - -static void tcx24_invalidate_display(void *opaque) -{ - TCXState *s = opaque; - - tcx_set_dirty(s); - tcx24_set_dirty(s); - qemu_console_resize(s->con, s->width, s->height); -} - -static int vmstate_tcx_post_load(void *opaque, int version_id) -{ - TCXState *s = opaque; - - update_palette_entries(s, 0, 256); - if (s->depth == 24) { - tcx24_set_dirty(s); - } else { - tcx_set_dirty(s); - } - - return 0; -} - -static const VMStateDescription vmstate_tcx = { - .name ="tcx", - .version_id = 4, - .minimum_version_id = 4, - .minimum_version_id_old = 4, - .post_load = vmstate_tcx_post_load, - .fields = (VMStateField []) { - VMSTATE_UINT16(height, TCXState), - VMSTATE_UINT16(width, TCXState), - VMSTATE_UINT16(depth, TCXState), - VMSTATE_BUFFER(r, TCXState), - VMSTATE_BUFFER(g, TCXState), - VMSTATE_BUFFER(b, TCXState), - VMSTATE_UINT8(dac_index, TCXState), - VMSTATE_UINT8(dac_state, TCXState), - VMSTATE_END_OF_LIST() - } -}; - -static void tcx_reset(DeviceState *d) -{ - TCXState *s = container_of(d, TCXState, busdev.qdev); - - /* Initialize palette */ - memset(s->r, 0, 256); - memset(s->g, 0, 256); - memset(s->b, 0, 256); - s->r[255] = s->g[255] = s->b[255] = 255; - update_palette_entries(s, 0, 256); - memset(s->vram, 0, MAXX*MAXY); - memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4), - DIRTY_MEMORY_VGA); - s->dac_index = 0; - s->dac_state = 0; -} - -static uint64_t tcx_dac_readl(void *opaque, hwaddr addr, - unsigned size) -{ - return 0; -} - -static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - TCXState *s = opaque; - - switch (addr) { - case 0: - s->dac_index = val >> 24; - s->dac_state = 0; - break; - case 4: - switch (s->dac_state) { - case 0: - s->r[s->dac_index] = val >> 24; - update_palette_entries(s, s->dac_index, s->dac_index + 1); - s->dac_state++; - break; - case 1: - s->g[s->dac_index] = val >> 24; - update_palette_entries(s, s->dac_index, s->dac_index + 1); - s->dac_state++; - break; - case 2: - s->b[s->dac_index] = val >> 24; - update_palette_entries(s, s->dac_index, s->dac_index + 1); - s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement - default: - s->dac_state = 0; - break; - } - break; - default: - break; - } -} - -static const MemoryRegionOps tcx_dac_ops = { - .read = tcx_dac_readl, - .write = tcx_dac_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static uint64_t dummy_readl(void *opaque, hwaddr addr, - unsigned size) -{ - return 0; -} - -static void dummy_writel(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ -} - -static const MemoryRegionOps dummy_ops = { - .read = dummy_readl, - .write = dummy_writel, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static int tcx_init1(SysBusDevice *dev) -{ - TCXState *s = FROM_SYSBUS(TCXState, dev); - ram_addr_t vram_offset = 0; - int size; - uint8_t *vram_base; - - memory_region_init_ram(&s->vram_mem, "tcx.vram", - s->vram_size * (1 + 4 + 4)); - vmstate_register_ram_global(&s->vram_mem); - vram_base = memory_region_get_ram_ptr(&s->vram_mem); - - /* 8-bit plane */ - s->vram = vram_base; - size = s->vram_size; - memory_region_init_alias(&s->vram_8bit, "tcx.vram.8bit", - &s->vram_mem, vram_offset, size); - sysbus_init_mmio(dev, &s->vram_8bit); - vram_offset += size; - vram_base += size; - - /* DAC */ - memory_region_init_io(&s->dac, &tcx_dac_ops, s, "tcx.dac", TCX_DAC_NREGS); - sysbus_init_mmio(dev, &s->dac); - - /* TEC (dummy) */ - memory_region_init_io(&s->tec, &dummy_ops, s, "tcx.tec", TCX_TEC_NREGS); - sysbus_init_mmio(dev, &s->tec); - /* THC: NetBSD writes here even with 8-bit display: dummy */ - memory_region_init_io(&s->thc24, &dummy_ops, s, "tcx.thc24", - TCX_THC_NREGS_24); - sysbus_init_mmio(dev, &s->thc24); - - if (s->depth == 24) { - /* 24-bit plane */ - size = s->vram_size * 4; - s->vram24 = (uint32_t *)vram_base; - s->vram24_offset = vram_offset; - memory_region_init_alias(&s->vram_24bit, "tcx.vram.24bit", - &s->vram_mem, vram_offset, size); - sysbus_init_mmio(dev, &s->vram_24bit); - vram_offset += size; - vram_base += size; - - /* Control plane */ - size = s->vram_size * 4; - s->cplane = (uint32_t *)vram_base; - s->cplane_offset = vram_offset; - memory_region_init_alias(&s->vram_cplane, "tcx.vram.cplane", - &s->vram_mem, vram_offset, size); - sysbus_init_mmio(dev, &s->vram_cplane); - - s->con = graphic_console_init(tcx24_update_display, - tcx24_invalidate_display, - tcx24_screen_dump, NULL, s); - } else { - /* THC 8 bit (dummy) */ - memory_region_init_io(&s->thc8, &dummy_ops, s, "tcx.thc8", - TCX_THC_NREGS_8); - sysbus_init_mmio(dev, &s->thc8); - - s->con = graphic_console_init(tcx_update_display, - tcx_invalidate_display, - tcx_screen_dump, NULL, s); - } - - qemu_console_resize(s->con, s->width, s->height); - return 0; -} - -static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch, - Error **errp) -{ - TCXState *s = opaque; - FILE *f; - uint8_t *d, *d1, v; - int ret, y, x; - - f = fopen(filename, "wb"); - if (!f) { - error_setg(errp, "failed to open file '%s': %s", filename, - strerror(errno)); - return; - } - ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); - if (ret < 0) { - goto write_err; - } - d1 = s->vram; - for(y = 0; y < s->height; y++) { - d = d1; - for(x = 0; x < s->width; x++) { - v = *d; - ret = fputc(s->r[v], f); - if (ret == EOF) { - goto write_err; - } - ret = fputc(s->g[v], f); - if (ret == EOF) { - goto write_err; - } - ret = fputc(s->b[v], f); - if (ret == EOF) { - goto write_err; - } - d++; - } - d1 += MAXX; - } - -out: - fclose(f); - return; - -write_err: - error_setg(errp, "failed to write to file '%s': %s", filename, - strerror(errno)); - unlink(filename); - goto out; -} - -static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch, - Error **errp) -{ - TCXState *s = opaque; - FILE *f; - uint8_t *d, *d1, v; - uint32_t *s24, *cptr, dval; - int ret, y, x; - - f = fopen(filename, "wb"); - if (!f) { - error_setg(errp, "failed to open file '%s': %s", filename, - strerror(errno)); - return; - } - ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); - if (ret < 0) { - goto write_err; - } - d1 = s->vram; - s24 = s->vram24; - cptr = s->cplane; - for(y = 0; y < s->height; y++) { - d = d1; - for(x = 0; x < s->width; x++, d++, s24++) { - if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct - dval = *s24 & 0x00ffffff; - ret = fputc((dval >> 16) & 0xff, f); - if (ret == EOF) { - goto write_err; - } - ret = fputc((dval >> 8) & 0xff, f); - if (ret == EOF) { - goto write_err; - } - ret = fputc(dval & 0xff, f); - if (ret == EOF) { - goto write_err; - } - } else { - v = *d; - ret = fputc(s->r[v], f); - if (ret == EOF) { - goto write_err; - } - ret = fputc(s->g[v], f); - if (ret == EOF) { - goto write_err; - } - ret = fputc(s->b[v], f); - if (ret == EOF) { - goto write_err; - } - } - } - d1 += MAXX; - } - -out: - fclose(f); - return; - -write_err: - error_setg(errp, "failed to write to file '%s': %s", filename, - strerror(errno)); - unlink(filename); - goto out; -} - -static Property tcx_properties[] = { - DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1), - DEFINE_PROP_UINT16("width", TCXState, width, -1), - DEFINE_PROP_UINT16("height", TCXState, height, -1), - DEFINE_PROP_UINT16("depth", TCXState, depth, -1), - DEFINE_PROP_END_OF_LIST(), -}; - -static void tcx_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = tcx_init1; - dc->reset = tcx_reset; - dc->vmsd = &vmstate_tcx; - dc->props = tcx_properties; -} - -static const TypeInfo tcx_info = { - .name = "SUNW,tcx", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(TCXState), - .class_init = tcx_class_init, -}; - -static void tcx_register_types(void) -{ - type_register_static(&tcx_info); -} - -type_init(tcx_register_types) diff --git a/hw/vga.c b/hw/vga.c deleted file mode 100644 index dc31fd574c..0000000000 --- a/hw/vga.c +++ /dev/null @@ -1,2457 +0,0 @@ -/* - * QEMU VGA Emulator. - * - * Copyright (c) 2003 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "hw/hw.h" -#include "hw/vga.h" -#include "ui/console.h" -#include "hw/i386/pc.h" -#include "hw/pci/pci.h" -#include "hw/vga_int.h" -#include "ui/pixel_ops.h" -#include "qemu/timer.h" -#include "hw/xen/xen.h" -#include "trace.h" - -//#define DEBUG_VGA -//#define DEBUG_VGA_MEM -//#define DEBUG_VGA_REG - -//#define DEBUG_BOCHS_VBE - -/* 16 state changes per vertical frame @60 Hz */ -#define VGA_TEXT_CURSOR_PERIOD_MS (1000 * 2 * 16 / 60) - -/* - * Video Graphics Array (VGA) - * - * Chipset docs for original IBM VGA: - * http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf - * - * FreeVGA site: - * http://www.osdever.net/FreeVGA/home.htm - * - * Standard VGA features and Bochs VBE extensions are implemented. - */ - -/* force some bits to zero */ -const uint8_t sr_mask[8] = { - 0x03, - 0x3d, - 0x0f, - 0x3f, - 0x0e, - 0x00, - 0x00, - 0xff, -}; - -const uint8_t gr_mask[16] = { - 0x0f, /* 0x00 */ - 0x0f, /* 0x01 */ - 0x0f, /* 0x02 */ - 0x1f, /* 0x03 */ - 0x03, /* 0x04 */ - 0x7b, /* 0x05 */ - 0x0f, /* 0x06 */ - 0x0f, /* 0x07 */ - 0xff, /* 0x08 */ - 0x00, /* 0x09 */ - 0x00, /* 0x0a */ - 0x00, /* 0x0b */ - 0x00, /* 0x0c */ - 0x00, /* 0x0d */ - 0x00, /* 0x0e */ - 0x00, /* 0x0f */ -}; - -#define cbswap_32(__x) \ -((uint32_t)( \ - (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ - (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ - (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ - (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) - -#ifdef HOST_WORDS_BIGENDIAN -#define PAT(x) cbswap_32(x) -#else -#define PAT(x) (x) -#endif - -#ifdef HOST_WORDS_BIGENDIAN -#define BIG 1 -#else -#define BIG 0 -#endif - -#ifdef HOST_WORDS_BIGENDIAN -#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff) -#else -#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff) -#endif - -static const uint32_t mask16[16] = { - PAT(0x00000000), - PAT(0x000000ff), - PAT(0x0000ff00), - PAT(0x0000ffff), - PAT(0x00ff0000), - PAT(0x00ff00ff), - PAT(0x00ffff00), - PAT(0x00ffffff), - PAT(0xff000000), - PAT(0xff0000ff), - PAT(0xff00ff00), - PAT(0xff00ffff), - PAT(0xffff0000), - PAT(0xffff00ff), - PAT(0xffffff00), - PAT(0xffffffff), -}; - -#undef PAT - -#ifdef HOST_WORDS_BIGENDIAN -#define PAT(x) (x) -#else -#define PAT(x) cbswap_32(x) -#endif - -static const uint32_t dmask16[16] = { - PAT(0x00000000), - PAT(0x000000ff), - PAT(0x0000ff00), - PAT(0x0000ffff), - PAT(0x00ff0000), - PAT(0x00ff00ff), - PAT(0x00ffff00), - PAT(0x00ffffff), - PAT(0xff000000), - PAT(0xff0000ff), - PAT(0xff00ff00), - PAT(0xff00ffff), - PAT(0xffff0000), - PAT(0xffff00ff), - PAT(0xffffff00), - PAT(0xffffffff), -}; - -static const uint32_t dmask4[4] = { - PAT(0x00000000), - PAT(0x0000ffff), - PAT(0xffff0000), - PAT(0xffffffff), -}; - -static uint32_t expand4[256]; -static uint16_t expand2[256]; -static uint8_t expand4to8[16]; - -static void vga_screen_dump(void *opaque, const char *filename, bool cswitch, - Error **errp); - -static void vga_update_memory_access(VGACommonState *s) -{ - MemoryRegion *region, *old_region = s->chain4_alias; - hwaddr base, offset, size; - - s->chain4_alias = NULL; - - if ((s->sr[VGA_SEQ_PLANE_WRITE] & VGA_SR02_ALL_PLANES) == - VGA_SR02_ALL_PLANES && s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { - offset = 0; - switch ((s->gr[VGA_GFX_MISC] >> 2) & 3) { - case 0: - base = 0xa0000; - size = 0x20000; - break; - case 1: - base = 0xa0000; - size = 0x10000; - offset = s->bank_offset; - break; - case 2: - base = 0xb0000; - size = 0x8000; - break; - case 3: - default: - base = 0xb8000; - size = 0x8000; - break; - } - base += isa_mem_base; - region = g_malloc(sizeof(*region)); - memory_region_init_alias(region, "vga.chain4", &s->vram, offset, size); - memory_region_add_subregion_overlap(s->legacy_address_space, base, - region, 2); - s->chain4_alias = region; - } - if (old_region) { - memory_region_del_subregion(s->legacy_address_space, old_region); - memory_region_destroy(old_region); - g_free(old_region); - s->plane_updated = 0xf; - } -} - -static void vga_dumb_update_retrace_info(VGACommonState *s) -{ - (void) s; -} - -static void vga_precise_update_retrace_info(VGACommonState *s) -{ - int htotal_chars; - int hretr_start_char; - int hretr_skew_chars; - int hretr_end_char; - - int vtotal_lines; - int vretr_start_line; - int vretr_end_line; - - int dots; -#if 0 - int div2, sldiv2; -#endif - int clocking_mode; - int clock_sel; - const int clk_hz[] = {25175000, 28322000, 25175000, 25175000}; - int64_t chars_per_sec; - struct vga_precise_retrace *r = &s->retrace_info.precise; - - htotal_chars = s->cr[VGA_CRTC_H_TOTAL] + 5; - hretr_start_char = s->cr[VGA_CRTC_H_SYNC_START]; - hretr_skew_chars = (s->cr[VGA_CRTC_H_SYNC_END] >> 5) & 3; - hretr_end_char = s->cr[VGA_CRTC_H_SYNC_END] & 0x1f; - - vtotal_lines = (s->cr[VGA_CRTC_V_TOTAL] | - (((s->cr[VGA_CRTC_OVERFLOW] & 1) | - ((s->cr[VGA_CRTC_OVERFLOW] >> 4) & 2)) << 8)) + 2; - vretr_start_line = s->cr[VGA_CRTC_V_SYNC_START] | - ((((s->cr[VGA_CRTC_OVERFLOW] >> 2) & 1) | - ((s->cr[VGA_CRTC_OVERFLOW] >> 6) & 2)) << 8); - vretr_end_line = s->cr[VGA_CRTC_V_SYNC_END] & 0xf; - - clocking_mode = (s->sr[VGA_SEQ_CLOCK_MODE] >> 3) & 1; - clock_sel = (s->msr >> 2) & 3; - dots = (s->msr & 1) ? 8 : 9; - - chars_per_sec = clk_hz[clock_sel] / dots; - - htotal_chars <<= clocking_mode; - - r->total_chars = vtotal_lines * htotal_chars; - if (r->freq) { - r->ticks_per_char = get_ticks_per_sec() / (r->total_chars * r->freq); - } else { - r->ticks_per_char = get_ticks_per_sec() / chars_per_sec; - } - - r->vstart = vretr_start_line; - r->vend = r->vstart + vretr_end_line + 1; - - r->hstart = hretr_start_char + hretr_skew_chars; - r->hend = r->hstart + hretr_end_char + 1; - r->htotal = htotal_chars; - -#if 0 - div2 = (s->cr[VGA_CRTC_MODE] >> 2) & 1; - sldiv2 = (s->cr[VGA_CRTC_MODE] >> 3) & 1; - printf ( - "hz=%f\n" - "htotal = %d\n" - "hretr_start = %d\n" - "hretr_skew = %d\n" - "hretr_end = %d\n" - "vtotal = %d\n" - "vretr_start = %d\n" - "vretr_end = %d\n" - "div2 = %d sldiv2 = %d\n" - "clocking_mode = %d\n" - "clock_sel = %d %d\n" - "dots = %d\n" - "ticks/char = %" PRId64 "\n" - "\n", - (double) get_ticks_per_sec() / (r->ticks_per_char * r->total_chars), - htotal_chars, - hretr_start_char, - hretr_skew_chars, - hretr_end_char, - vtotal_lines, - vretr_start_line, - vretr_end_line, - div2, sldiv2, - clocking_mode, - clock_sel, - clk_hz[clock_sel], - dots, - r->ticks_per_char - ); -#endif -} - -static uint8_t vga_precise_retrace(VGACommonState *s) -{ - struct vga_precise_retrace *r = &s->retrace_info.precise; - uint8_t val = s->st01 & ~(ST01_V_RETRACE | ST01_DISP_ENABLE); - - if (r->total_chars) { - int cur_line, cur_line_char, cur_char; - int64_t cur_tick; - - cur_tick = qemu_get_clock_ns(vm_clock); - - cur_char = (cur_tick / r->ticks_per_char) % r->total_chars; - cur_line = cur_char / r->htotal; - - if (cur_line >= r->vstart && cur_line <= r->vend) { - val |= ST01_V_RETRACE | ST01_DISP_ENABLE; - } else { - cur_line_char = cur_char % r->htotal; - if (cur_line_char >= r->hstart && cur_line_char <= r->hend) { - val |= ST01_DISP_ENABLE; - } - } - - return val; - } else { - return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE); - } -} - -static uint8_t vga_dumb_retrace(VGACommonState *s) -{ - return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE); -} - -int vga_ioport_invalid(VGACommonState *s, uint32_t addr) -{ - if (s->msr & VGA_MIS_COLOR) { - /* Color */ - return (addr >= 0x3b0 && addr <= 0x3bf); - } else { - /* Monochrome */ - return (addr >= 0x3d0 && addr <= 0x3df); - } -} - -uint32_t vga_ioport_read(void *opaque, uint32_t addr) -{ - VGACommonState *s = opaque; - int val, index; - - qemu_flush_coalesced_mmio_buffer(); - - if (vga_ioport_invalid(s, addr)) { - val = 0xff; - } else { - switch(addr) { - case VGA_ATT_W: - if (s->ar_flip_flop == 0) { - val = s->ar_index; - } else { - val = 0; - } - break; - case VGA_ATT_R: - index = s->ar_index & 0x1f; - if (index < VGA_ATT_C) { - val = s->ar[index]; - } else { - val = 0; - } - break; - case VGA_MIS_W: - val = s->st00; - break; - case VGA_SEQ_I: - val = s->sr_index; - break; - case VGA_SEQ_D: - val = s->sr[s->sr_index]; -#ifdef DEBUG_VGA_REG - printf("vga: read SR%x = 0x%02x\n", s->sr_index, val); -#endif - break; - case VGA_PEL_IR: - val = s->dac_state; - break; - case VGA_PEL_IW: - val = s->dac_write_index; - break; - case VGA_PEL_D: - val = s->palette[s->dac_read_index * 3 + s->dac_sub_index]; - if (++s->dac_sub_index == 3) { - s->dac_sub_index = 0; - s->dac_read_index++; - } - break; - case VGA_FTC_R: - val = s->fcr; - break; - case VGA_MIS_R: - val = s->msr; - break; - case VGA_GFX_I: - val = s->gr_index; - break; - case VGA_GFX_D: - val = s->gr[s->gr_index]; -#ifdef DEBUG_VGA_REG - printf("vga: read GR%x = 0x%02x\n", s->gr_index, val); -#endif - break; - case VGA_CRT_IM: - case VGA_CRT_IC: - val = s->cr_index; - break; - case VGA_CRT_DM: - case VGA_CRT_DC: - val = s->cr[s->cr_index]; -#ifdef DEBUG_VGA_REG - printf("vga: read CR%x = 0x%02x\n", s->cr_index, val); -#endif - break; - case VGA_IS1_RM: - case VGA_IS1_RC: - /* just toggle to fool polling */ - val = s->st01 = s->retrace(s); - s->ar_flip_flop = 0; - break; - default: - val = 0x00; - break; - } - } -#if defined(DEBUG_VGA) - printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val); -#endif - return val; -} - -void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) -{ - VGACommonState *s = opaque; - int index; - - qemu_flush_coalesced_mmio_buffer(); - - /* check port range access depending on color/monochrome mode */ - if (vga_ioport_invalid(s, addr)) { - return; - } -#ifdef DEBUG_VGA - printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val); -#endif - - switch(addr) { - case VGA_ATT_W: - if (s->ar_flip_flop == 0) { - val &= 0x3f; - s->ar_index = val; - } else { - index = s->ar_index & 0x1f; - switch(index) { - case VGA_ATC_PALETTE0 ... VGA_ATC_PALETTEF: - s->ar[index] = val & 0x3f; - break; - case VGA_ATC_MODE: - s->ar[index] = val & ~0x10; - break; - case VGA_ATC_OVERSCAN: - s->ar[index] = val; - break; - case VGA_ATC_PLANE_ENABLE: - s->ar[index] = val & ~0xc0; - break; - case VGA_ATC_PEL: - s->ar[index] = val & ~0xf0; - break; - case VGA_ATC_COLOR_PAGE: - s->ar[index] = val & ~0xf0; - break; - default: - break; - } - } - s->ar_flip_flop ^= 1; - break; - case VGA_MIS_W: - s->msr = val & ~0x10; - s->update_retrace_info(s); - break; - case VGA_SEQ_I: - s->sr_index = val & 7; - break; - case VGA_SEQ_D: -#ifdef DEBUG_VGA_REG - printf("vga: write SR%x = 0x%02x\n", s->sr_index, val); -#endif - s->sr[s->sr_index] = val & sr_mask[s->sr_index]; - if (s->sr_index == VGA_SEQ_CLOCK_MODE) { - s->update_retrace_info(s); - } - vga_update_memory_access(s); - break; - case VGA_PEL_IR: - s->dac_read_index = val; - s->dac_sub_index = 0; - s->dac_state = 3; - break; - case VGA_PEL_IW: - s->dac_write_index = val; - s->dac_sub_index = 0; - s->dac_state = 0; - break; - case VGA_PEL_D: - s->dac_cache[s->dac_sub_index] = val; - if (++s->dac_sub_index == 3) { - memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3); - s->dac_sub_index = 0; - s->dac_write_index++; - } - break; - case VGA_GFX_I: - s->gr_index = val & 0x0f; - break; - case VGA_GFX_D: -#ifdef DEBUG_VGA_REG - printf("vga: write GR%x = 0x%02x\n", s->gr_index, val); -#endif - s->gr[s->gr_index] = val & gr_mask[s->gr_index]; - vga_update_memory_access(s); - break; - case VGA_CRT_IM: - case VGA_CRT_IC: - s->cr_index = val; - break; - case VGA_CRT_DM: - case VGA_CRT_DC: -#ifdef DEBUG_VGA_REG - printf("vga: write CR%x = 0x%02x\n", s->cr_index, val); -#endif - /* handle CR0-7 protection */ - if ((s->cr[VGA_CRTC_V_SYNC_END] & VGA_CR11_LOCK_CR0_CR7) && - s->cr_index <= VGA_CRTC_OVERFLOW) { - /* can always write bit 4 of CR7 */ - if (s->cr_index == VGA_CRTC_OVERFLOW) { - s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x10) | - (val & 0x10); - } - return; - } - s->cr[s->cr_index] = val; - - switch(s->cr_index) { - case VGA_CRTC_H_TOTAL: - case VGA_CRTC_H_SYNC_START: - case VGA_CRTC_H_SYNC_END: - case VGA_CRTC_V_TOTAL: - case VGA_CRTC_OVERFLOW: - case VGA_CRTC_V_SYNC_END: - case VGA_CRTC_MODE: - s->update_retrace_info(s); - break; - } - break; - case VGA_IS1_RM: - case VGA_IS1_RC: - s->fcr = val & 0x10; - break; - } -} - -static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr) -{ - VGACommonState *s = opaque; - uint32_t val; - val = s->vbe_index; - return val; -} - -uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr) -{ - VGACommonState *s = opaque; - uint32_t val; - - if (s->vbe_index < VBE_DISPI_INDEX_NB) { - if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) { - switch(s->vbe_index) { - /* XXX: do not hardcode ? */ - case VBE_DISPI_INDEX_XRES: - val = VBE_DISPI_MAX_XRES; - break; - case VBE_DISPI_INDEX_YRES: - val = VBE_DISPI_MAX_YRES; - break; - case VBE_DISPI_INDEX_BPP: - val = VBE_DISPI_MAX_BPP; - break; - default: - val = s->vbe_regs[s->vbe_index]; - break; - } - } else { - val = s->vbe_regs[s->vbe_index]; - } - } else if (s->vbe_index == VBE_DISPI_INDEX_VIDEO_MEMORY_64K) { - val = s->vram_size / (64 * 1024); - } else { - val = 0; - } -#ifdef DEBUG_BOCHS_VBE - printf("VBE: read index=0x%x val=0x%x\n", s->vbe_index, val); -#endif - return val; -} - -void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val) -{ - VGACommonState *s = opaque; - s->vbe_index = val; -} - -void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val) -{ - VGACommonState *s = opaque; - - if (s->vbe_index <= VBE_DISPI_INDEX_NB) { -#ifdef DEBUG_BOCHS_VBE - printf("VBE: write index=0x%x val=0x%x\n", s->vbe_index, val); -#endif - switch(s->vbe_index) { - case VBE_DISPI_INDEX_ID: - if (val == VBE_DISPI_ID0 || - val == VBE_DISPI_ID1 || - val == VBE_DISPI_ID2 || - val == VBE_DISPI_ID3 || - val == VBE_DISPI_ID4) { - s->vbe_regs[s->vbe_index] = val; - } - break; - case VBE_DISPI_INDEX_XRES: - if ((val <= VBE_DISPI_MAX_XRES) && ((val & 7) == 0)) { - s->vbe_regs[s->vbe_index] = val; - } - break; - case VBE_DISPI_INDEX_YRES: - if (val <= VBE_DISPI_MAX_YRES) { - s->vbe_regs[s->vbe_index] = val; - } - break; - case VBE_DISPI_INDEX_BPP: - if (val == 0) - val = 8; - if (val == 4 || val == 8 || val == 15 || - val == 16 || val == 24 || val == 32) { - s->vbe_regs[s->vbe_index] = val; - } - break; - case VBE_DISPI_INDEX_BANK: - if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) { - val &= (s->vbe_bank_mask >> 2); - } else { - val &= s->vbe_bank_mask; - } - s->vbe_regs[s->vbe_index] = val; - s->bank_offset = (val << 16); - vga_update_memory_access(s); - break; - case VBE_DISPI_INDEX_ENABLE: - if ((val & VBE_DISPI_ENABLED) && - !(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { - int h, shift_control; - - s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = - s->vbe_regs[VBE_DISPI_INDEX_XRES]; - s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = - s->vbe_regs[VBE_DISPI_INDEX_YRES]; - s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0; - s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0; - - if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) - s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1; - else - s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] * - ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); - s->vbe_start_addr = 0; - - /* clear the screen (should be done in BIOS) */ - if (!(val & VBE_DISPI_NOCLEARMEM)) { - memset(s->vram_ptr, 0, - s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset); - } - - /* we initialize the VGA graphic mode (should be done - in BIOS) */ - /* graphic mode + memory map 1 */ - s->gr[VGA_GFX_MISC] = (s->gr[VGA_GFX_MISC] & ~0x0c) | 0x04 | - VGA_GR06_GRAPHICS_MODE; - s->cr[VGA_CRTC_MODE] |= 3; /* no CGA modes */ - s->cr[VGA_CRTC_OFFSET] = s->vbe_line_offset >> 3; - /* width */ - s->cr[VGA_CRTC_H_DISP] = - (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1; - /* height (only meaningful if < 1024) */ - h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1; - s->cr[VGA_CRTC_V_DISP_END] = h; - s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x42) | - ((h >> 7) & 0x02) | ((h >> 3) & 0x40); - /* line compare to 1023 */ - s->cr[VGA_CRTC_LINE_COMPARE] = 0xff; - s->cr[VGA_CRTC_OVERFLOW] |= 0x10; - s->cr[VGA_CRTC_MAX_SCAN] |= 0x40; - - if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) { - shift_control = 0; - s->sr[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */ - } else { - shift_control = 2; - /* set chain 4 mode */ - s->sr[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M; - /* activate all planes */ - s->sr[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES; - } - s->gr[VGA_GFX_MODE] = (s->gr[VGA_GFX_MODE] & ~0x60) | - (shift_control << 5); - s->cr[VGA_CRTC_MAX_SCAN] &= ~0x9f; /* no double scan */ - } else { - /* XXX: the bios should do that */ - s->bank_offset = 0; - } - s->dac_8bit = (val & VBE_DISPI_8BIT_DAC) > 0; - s->vbe_regs[s->vbe_index] = val; - vga_update_memory_access(s); - break; - case VBE_DISPI_INDEX_VIRT_WIDTH: - { - int w, h, line_offset; - - if (val < s->vbe_regs[VBE_DISPI_INDEX_XRES]) - return; - w = val; - if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) - line_offset = w >> 1; - else - line_offset = w * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); - h = s->vram_size / line_offset; - /* XXX: support weird bochs semantics ? */ - if (h < s->vbe_regs[VBE_DISPI_INDEX_YRES]) - return; - s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = w; - s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h; - s->vbe_line_offset = line_offset; - } - break; - case VBE_DISPI_INDEX_X_OFFSET: - case VBE_DISPI_INDEX_Y_OFFSET: - { - int x; - s->vbe_regs[s->vbe_index] = val; - s->vbe_start_addr = s->vbe_line_offset * s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]; - x = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET]; - if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) - s->vbe_start_addr += x >> 1; - else - s->vbe_start_addr += x * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); - s->vbe_start_addr >>= 2; - } - break; - default: - break; - } - } -} - -/* called for accesses between 0xa0000 and 0xc0000 */ -uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr) -{ - int memory_map_mode, plane; - uint32_t ret; - - /* convert to VGA memory offset */ - memory_map_mode = (s->gr[VGA_GFX_MISC] >> 2) & 3; - addr &= 0x1ffff; - switch(memory_map_mode) { - case 0: - break; - case 1: - if (addr >= 0x10000) - return 0xff; - addr += s->bank_offset; - break; - case 2: - addr -= 0x10000; - if (addr >= 0x8000) - return 0xff; - break; - default: - case 3: - addr -= 0x18000; - if (addr >= 0x8000) - return 0xff; - break; - } - - if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { - /* chain 4 mode : simplest access */ - ret = s->vram_ptr[addr]; - } else if (s->gr[VGA_GFX_MODE] & 0x10) { - /* odd/even mode (aka text mode mapping) */ - plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1); - ret = s->vram_ptr[((addr & ~1) << 1) | plane]; - } else { - /* standard VGA latched access */ - s->latch = ((uint32_t *)s->vram_ptr)[addr]; - - if (!(s->gr[VGA_GFX_MODE] & 0x08)) { - /* read mode 0 */ - plane = s->gr[VGA_GFX_PLANE_READ]; - ret = GET_PLANE(s->latch, plane); - } else { - /* read mode 1 */ - ret = (s->latch ^ mask16[s->gr[VGA_GFX_COMPARE_VALUE]]) & - mask16[s->gr[VGA_GFX_COMPARE_MASK]]; - ret |= ret >> 16; - ret |= ret >> 8; - ret = (~ret) & 0xff; - } - } - return ret; -} - -/* called for accesses between 0xa0000 and 0xc0000 */ -void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) -{ - int memory_map_mode, plane, write_mode, b, func_select, mask; - uint32_t write_mask, bit_mask, set_mask; - -#ifdef DEBUG_VGA_MEM - printf("vga: [0x" TARGET_FMT_plx "] = 0x%02x\n", addr, val); -#endif - /* convert to VGA memory offset */ - memory_map_mode = (s->gr[VGA_GFX_MISC] >> 2) & 3; - addr &= 0x1ffff; - switch(memory_map_mode) { - case 0: - break; - case 1: - if (addr >= 0x10000) - return; - addr += s->bank_offset; - break; - case 2: - addr -= 0x10000; - if (addr >= 0x8000) - return; - break; - default: - case 3: - addr -= 0x18000; - if (addr >= 0x8000) - return; - break; - } - - if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { - /* chain 4 mode : simplest access */ - plane = addr & 3; - mask = (1 << plane); - if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) { - s->vram_ptr[addr] = val; -#ifdef DEBUG_VGA_MEM - printf("vga: chain4: [0x" TARGET_FMT_plx "]\n", addr); -#endif - s->plane_updated |= mask; /* only used to detect font change */ - memory_region_set_dirty(&s->vram, addr, 1); - } - } else if (s->gr[VGA_GFX_MODE] & 0x10) { - /* odd/even mode (aka text mode mapping) */ - plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1); - mask = (1 << plane); - if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) { - addr = ((addr & ~1) << 1) | plane; - s->vram_ptr[addr] = val; -#ifdef DEBUG_VGA_MEM - printf("vga: odd/even: [0x" TARGET_FMT_plx "]\n", addr); -#endif - s->plane_updated |= mask; /* only used to detect font change */ - memory_region_set_dirty(&s->vram, addr, 1); - } - } else { - /* standard VGA latched access */ - write_mode = s->gr[VGA_GFX_MODE] & 3; - switch(write_mode) { - default: - case 0: - /* rotate */ - b = s->gr[VGA_GFX_DATA_ROTATE] & 7; - val = ((val >> b) | (val << (8 - b))) & 0xff; - val |= val << 8; - val |= val << 16; - - /* apply set/reset mask */ - set_mask = mask16[s->gr[VGA_GFX_SR_ENABLE]]; - val = (val & ~set_mask) | - (mask16[s->gr[VGA_GFX_SR_VALUE]] & set_mask); - bit_mask = s->gr[VGA_GFX_BIT_MASK]; - break; - case 1: - val = s->latch; - goto do_write; - case 2: - val = mask16[val & 0x0f]; - bit_mask = s->gr[VGA_GFX_BIT_MASK]; - break; - case 3: - /* rotate */ - b = s->gr[VGA_GFX_DATA_ROTATE] & 7; - val = (val >> b) | (val << (8 - b)); - - bit_mask = s->gr[VGA_GFX_BIT_MASK] & val; - val = mask16[s->gr[VGA_GFX_SR_VALUE]]; - break; - } - - /* apply logical operation */ - func_select = s->gr[VGA_GFX_DATA_ROTATE] >> 3; - switch(func_select) { - case 0: - default: - /* nothing to do */ - break; - case 1: - /* and */ - val &= s->latch; - break; - case 2: - /* or */ - val |= s->latch; - break; - case 3: - /* xor */ - val ^= s->latch; - break; - } - - /* apply bit mask */ - bit_mask |= bit_mask << 8; - bit_mask |= bit_mask << 16; - val = (val & bit_mask) | (s->latch & ~bit_mask); - - do_write: - /* mask data according to sr[2] */ - mask = s->sr[VGA_SEQ_PLANE_WRITE]; - s->plane_updated |= mask; /* only used to detect font change */ - write_mask = mask16[mask]; - ((uint32_t *)s->vram_ptr)[addr] = - (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) | - (val & write_mask); -#ifdef DEBUG_VGA_MEM - printf("vga: latch: [0x" TARGET_FMT_plx "] mask=0x%08x val=0x%08x\n", - addr * 4, write_mask, val); -#endif - memory_region_set_dirty(&s->vram, addr << 2, sizeof(uint32_t)); - } -} - -typedef void vga_draw_glyph8_func(uint8_t *d, int linesize, - const uint8_t *font_ptr, int h, - uint32_t fgcol, uint32_t bgcol); -typedef void vga_draw_glyph9_func(uint8_t *d, int linesize, - const uint8_t *font_ptr, int h, - uint32_t fgcol, uint32_t bgcol, int dup9); -typedef void vga_draw_line_func(VGACommonState *s1, uint8_t *d, - const uint8_t *s, int width); - -#define DEPTH 8 -#include "hw/vga_template.h" - -#define DEPTH 15 -#include "hw/vga_template.h" - -#define BGR_FORMAT -#define DEPTH 15 -#include "hw/vga_template.h" - -#define DEPTH 16 -#include "hw/vga_template.h" - -#define BGR_FORMAT -#define DEPTH 16 -#include "hw/vga_template.h" - -#define DEPTH 32 -#include "hw/vga_template.h" - -#define BGR_FORMAT -#define DEPTH 32 -#include "hw/vga_template.h" - -static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b) -{ - unsigned int col; - col = rgb_to_pixel8(r, g, b); - col |= col << 8; - col |= col << 16; - return col; -} - -static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b) -{ - unsigned int col; - col = rgb_to_pixel15(r, g, b); - col |= col << 16; - return col; -} - -static unsigned int rgb_to_pixel15bgr_dup(unsigned int r, unsigned int g, - unsigned int b) -{ - unsigned int col; - col = rgb_to_pixel15bgr(r, g, b); - col |= col << 16; - return col; -} - -static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b) -{ - unsigned int col; - col = rgb_to_pixel16(r, g, b); - col |= col << 16; - return col; -} - -static unsigned int rgb_to_pixel16bgr_dup(unsigned int r, unsigned int g, - unsigned int b) -{ - unsigned int col; - col = rgb_to_pixel16bgr(r, g, b); - col |= col << 16; - return col; -} - -static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b) -{ - unsigned int col; - col = rgb_to_pixel32(r, g, b); - return col; -} - -static unsigned int rgb_to_pixel32bgr_dup(unsigned int r, unsigned int g, unsigned b) -{ - unsigned int col; - col = rgb_to_pixel32bgr(r, g, b); - return col; -} - -/* return true if the palette was modified */ -static int update_palette16(VGACommonState *s) -{ - int full_update, i; - uint32_t v, col, *palette; - - full_update = 0; - palette = s->last_palette; - for(i = 0; i < 16; i++) { - v = s->ar[i]; - if (s->ar[VGA_ATC_MODE] & 0x80) { - v = ((s->ar[VGA_ATC_COLOR_PAGE] & 0xf) << 4) | (v & 0xf); - } else { - v = ((s->ar[VGA_ATC_COLOR_PAGE] & 0xc) << 4) | (v & 0x3f); - } - v = v * 3; - col = s->rgb_to_pixel(c6_to_8(s->palette[v]), - c6_to_8(s->palette[v + 1]), - c6_to_8(s->palette[v + 2])); - if (col != palette[i]) { - full_update = 1; - palette[i] = col; - } - } - return full_update; -} - -/* return true if the palette was modified */ -static int update_palette256(VGACommonState *s) -{ - int full_update, i; - uint32_t v, col, *palette; - - full_update = 0; - palette = s->last_palette; - v = 0; - for(i = 0; i < 256; i++) { - if (s->dac_8bit) { - col = s->rgb_to_pixel(s->palette[v], - s->palette[v + 1], - s->palette[v + 2]); - } else { - col = s->rgb_to_pixel(c6_to_8(s->palette[v]), - c6_to_8(s->palette[v + 1]), - c6_to_8(s->palette[v + 2])); - } - if (col != palette[i]) { - full_update = 1; - palette[i] = col; - } - v += 3; - } - return full_update; -} - -static void vga_get_offsets(VGACommonState *s, - uint32_t *pline_offset, - uint32_t *pstart_addr, - uint32_t *pline_compare) -{ - uint32_t start_addr, line_offset, line_compare; - - if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { - line_offset = s->vbe_line_offset; - start_addr = s->vbe_start_addr; - line_compare = 65535; - } else { - /* compute line_offset in bytes */ - line_offset = s->cr[VGA_CRTC_OFFSET]; - line_offset <<= 3; - - /* starting address */ - start_addr = s->cr[VGA_CRTC_START_LO] | - (s->cr[VGA_CRTC_START_HI] << 8); - - /* line compare */ - line_compare = s->cr[VGA_CRTC_LINE_COMPARE] | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x10) << 4) | - ((s->cr[VGA_CRTC_MAX_SCAN] & 0x40) << 3); - } - *pline_offset = line_offset; - *pstart_addr = start_addr; - *pline_compare = line_compare; -} - -/* update start_addr and line_offset. Return TRUE if modified */ -static int update_basic_params(VGACommonState *s) -{ - int full_update; - uint32_t start_addr, line_offset, line_compare; - - full_update = 0; - - s->get_offsets(s, &line_offset, &start_addr, &line_compare); - - if (line_offset != s->line_offset || - start_addr != s->start_addr || - line_compare != s->line_compare) { - s->line_offset = line_offset; - s->start_addr = start_addr; - s->line_compare = line_compare; - full_update = 1; - } - return full_update; -} - -#define NB_DEPTHS 7 - -static inline int get_depth_index(DisplaySurface *s) -{ - switch (surface_bits_per_pixel(s)) { - default: - case 8: - return 0; - case 15: - return 1; - case 16: - return 2; - case 32: - if (is_surface_bgr(s)) { - return 4; - } else { - return 3; - } - } -} - -static vga_draw_glyph8_func * const vga_draw_glyph8_table[NB_DEPTHS] = { - vga_draw_glyph8_8, - vga_draw_glyph8_16, - vga_draw_glyph8_16, - vga_draw_glyph8_32, - vga_draw_glyph8_32, - vga_draw_glyph8_16, - vga_draw_glyph8_16, -}; - -static vga_draw_glyph8_func * const vga_draw_glyph16_table[NB_DEPTHS] = { - vga_draw_glyph16_8, - vga_draw_glyph16_16, - vga_draw_glyph16_16, - vga_draw_glyph16_32, - vga_draw_glyph16_32, - vga_draw_glyph16_16, - vga_draw_glyph16_16, -}; - -static vga_draw_glyph9_func * const vga_draw_glyph9_table[NB_DEPTHS] = { - vga_draw_glyph9_8, - vga_draw_glyph9_16, - vga_draw_glyph9_16, - vga_draw_glyph9_32, - vga_draw_glyph9_32, - vga_draw_glyph9_16, - vga_draw_glyph9_16, -}; - -static const uint8_t cursor_glyph[32 * 4] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; - -static void vga_get_text_resolution(VGACommonState *s, int *pwidth, int *pheight, - int *pcwidth, int *pcheight) -{ - int width, cwidth, height, cheight; - - /* total width & height */ - cheight = (s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1; - cwidth = 8; - if (!(s->sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_CHAR_CLK_8DOTS)) { - cwidth = 9; - } - if (s->sr[VGA_SEQ_CLOCK_MODE] & 0x08) { - cwidth = 16; /* NOTE: no 18 pixel wide */ - } - width = (s->cr[VGA_CRTC_H_DISP] + 1); - if (s->cr[VGA_CRTC_V_TOTAL] == 100) { - /* ugly hack for CGA 160x100x16 - explain me the logic */ - height = 100; - } else { - height = s->cr[VGA_CRTC_V_DISP_END] | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x02) << 7) | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x40) << 3); - height = (height + 1) / cheight; - } - - *pwidth = width; - *pheight = height; - *pcwidth = cwidth; - *pcheight = cheight; -} - -typedef unsigned int rgb_to_pixel_dup_func(unsigned int r, unsigned int g, unsigned b); - -static rgb_to_pixel_dup_func * const rgb_to_pixel_dup_table[NB_DEPTHS] = { - rgb_to_pixel8_dup, - rgb_to_pixel15_dup, - rgb_to_pixel16_dup, - rgb_to_pixel32_dup, - rgb_to_pixel32bgr_dup, - rgb_to_pixel15bgr_dup, - rgb_to_pixel16bgr_dup, -}; - -/* - * Text mode update - * Missing: - * - double scan - * - double width - * - underline - * - flashing - */ -static void vga_draw_text(VGACommonState *s, int full_update) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr; - int cx_min, cx_max, linesize, x_incr, line, line1; - uint32_t offset, fgcol, bgcol, v, cursor_offset; - uint8_t *d1, *d, *src, *dest, *cursor_ptr; - const uint8_t *font_ptr, *font_base[2]; - int dup9, line_offset, depth_index; - uint32_t *palette; - uint32_t *ch_attr_ptr; - vga_draw_glyph8_func *vga_draw_glyph8; - vga_draw_glyph9_func *vga_draw_glyph9; - int64_t now = qemu_get_clock_ms(vm_clock); - - /* compute font data address (in plane 2) */ - v = s->sr[VGA_SEQ_CHARACTER_MAP]; - offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2; - if (offset != s->font_offsets[0]) { - s->font_offsets[0] = offset; - full_update = 1; - } - font_base[0] = s->vram_ptr + offset; - - offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2; - font_base[1] = s->vram_ptr + offset; - if (offset != s->font_offsets[1]) { - s->font_offsets[1] = offset; - full_update = 1; - } - if (s->plane_updated & (1 << 2) || s->chain4_alias) { - /* if the plane 2 was modified since the last display, it - indicates the font may have been modified */ - s->plane_updated = 0; - full_update = 1; - } - full_update |= update_basic_params(s); - - line_offset = s->line_offset; - - vga_get_text_resolution(s, &width, &height, &cw, &cheight); - if ((height * width) <= 1) { - /* better than nothing: exit if transient size is too small */ - return; - } - if ((height * width) > CH_ATTR_SIZE) { - /* better than nothing: exit if transient size is too big */ - return; - } - - if (width != s->last_width || height != s->last_height || - cw != s->last_cw || cheight != s->last_ch || s->last_depth) { - s->last_scr_width = width * cw; - s->last_scr_height = height * cheight; - qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height); - surface = qemu_console_surface(s->con); - dpy_text_resize(s->con, width, height); - s->last_depth = 0; - s->last_width = width; - s->last_height = height; - s->last_ch = cheight; - s->last_cw = cw; - full_update = 1; - } - s->rgb_to_pixel = - rgb_to_pixel_dup_table[get_depth_index(surface)]; - full_update |= update_palette16(s); - palette = s->last_palette; - x_incr = cw * surface_bytes_per_pixel(surface); - - if (full_update) { - s->full_update_text = 1; - } - if (s->full_update_gfx) { - s->full_update_gfx = 0; - full_update |= 1; - } - - cursor_offset = ((s->cr[VGA_CRTC_CURSOR_HI] << 8) | - s->cr[VGA_CRTC_CURSOR_LO]) - s->start_addr; - if (cursor_offset != s->cursor_offset || - s->cr[VGA_CRTC_CURSOR_START] != s->cursor_start || - s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end) { - /* if the cursor position changed, we update the old and new - chars */ - if (s->cursor_offset < CH_ATTR_SIZE) - s->last_ch_attr[s->cursor_offset] = -1; - if (cursor_offset < CH_ATTR_SIZE) - s->last_ch_attr[cursor_offset] = -1; - s->cursor_offset = cursor_offset; - s->cursor_start = s->cr[VGA_CRTC_CURSOR_START]; - s->cursor_end = s->cr[VGA_CRTC_CURSOR_END]; - } - cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4; - if (now >= s->cursor_blink_time) { - s->cursor_blink_time = now + VGA_TEXT_CURSOR_PERIOD_MS / 2; - s->cursor_visible_phase = !s->cursor_visible_phase; - } - - depth_index = get_depth_index(surface); - if (cw == 16) - vga_draw_glyph8 = vga_draw_glyph16_table[depth_index]; - else - vga_draw_glyph8 = vga_draw_glyph8_table[depth_index]; - vga_draw_glyph9 = vga_draw_glyph9_table[depth_index]; - - dest = surface_data(surface); - linesize = surface_stride(surface); - ch_attr_ptr = s->last_ch_attr; - line = 0; - offset = s->start_addr * 4; - for(cy = 0; cy < height; cy++) { - d1 = dest; - src = s->vram_ptr + offset; - cx_min = width; - cx_max = -1; - for(cx = 0; cx < width; cx++) { - ch_attr = *(uint16_t *)src; - if (full_update || ch_attr != *ch_attr_ptr || src == cursor_ptr) { - if (cx < cx_min) - cx_min = cx; - if (cx > cx_max) - cx_max = cx; - *ch_attr_ptr = ch_attr; -#ifdef HOST_WORDS_BIGENDIAN - ch = ch_attr >> 8; - cattr = ch_attr & 0xff; -#else - ch = ch_attr & 0xff; - cattr = ch_attr >> 8; -#endif - font_ptr = font_base[(cattr >> 3) & 1]; - font_ptr += 32 * 4 * ch; - bgcol = palette[cattr >> 4]; - fgcol = palette[cattr & 0x0f]; - if (cw != 9) { - vga_draw_glyph8(d1, linesize, - font_ptr, cheight, fgcol, bgcol); - } else { - dup9 = 0; - if (ch >= 0xb0 && ch <= 0xdf && - (s->ar[VGA_ATC_MODE] & 0x04)) { - dup9 = 1; - } - vga_draw_glyph9(d1, linesize, - font_ptr, cheight, fgcol, bgcol, dup9); - } - if (src == cursor_ptr && - !(s->cr[VGA_CRTC_CURSOR_START] & 0x20) && - s->cursor_visible_phase) { - int line_start, line_last, h; - /* draw the cursor */ - line_start = s->cr[VGA_CRTC_CURSOR_START] & 0x1f; - line_last = s->cr[VGA_CRTC_CURSOR_END] & 0x1f; - /* XXX: check that */ - if (line_last > cheight - 1) - line_last = cheight - 1; - if (line_last >= line_start && line_start < cheight) { - h = line_last - line_start + 1; - d = d1 + linesize * line_start; - if (cw != 9) { - vga_draw_glyph8(d, linesize, - cursor_glyph, h, fgcol, bgcol); - } else { - vga_draw_glyph9(d, linesize, - cursor_glyph, h, fgcol, bgcol, 1); - } - } - } - } - d1 += x_incr; - src += 4; - ch_attr_ptr++; - } - if (cx_max != -1) { - dpy_gfx_update(s->con, cx_min * cw, cy * cheight, - (cx_max - cx_min + 1) * cw, cheight); - } - dest += linesize * cheight; - line1 = line + cheight; - offset += line_offset; - if (line < s->line_compare && line1 >= s->line_compare) { - offset = 0; - } - line = line1; - } -} - -enum { - VGA_DRAW_LINE2, - VGA_DRAW_LINE2D2, - VGA_DRAW_LINE4, - VGA_DRAW_LINE4D2, - VGA_DRAW_LINE8D2, - VGA_DRAW_LINE8, - VGA_DRAW_LINE15, - VGA_DRAW_LINE16, - VGA_DRAW_LINE24, - VGA_DRAW_LINE32, - VGA_DRAW_LINE_NB, -}; - -static vga_draw_line_func * const vga_draw_line_table[NB_DEPTHS * VGA_DRAW_LINE_NB] = { - vga_draw_line2_8, - vga_draw_line2_16, - vga_draw_line2_16, - vga_draw_line2_32, - vga_draw_line2_32, - vga_draw_line2_16, - vga_draw_line2_16, - - vga_draw_line2d2_8, - vga_draw_line2d2_16, - vga_draw_line2d2_16, - vga_draw_line2d2_32, - vga_draw_line2d2_32, - vga_draw_line2d2_16, - vga_draw_line2d2_16, - - vga_draw_line4_8, - vga_draw_line4_16, - vga_draw_line4_16, - vga_draw_line4_32, - vga_draw_line4_32, - vga_draw_line4_16, - vga_draw_line4_16, - - vga_draw_line4d2_8, - vga_draw_line4d2_16, - vga_draw_line4d2_16, - vga_draw_line4d2_32, - vga_draw_line4d2_32, - vga_draw_line4d2_16, - vga_draw_line4d2_16, - - vga_draw_line8d2_8, - vga_draw_line8d2_16, - vga_draw_line8d2_16, - vga_draw_line8d2_32, - vga_draw_line8d2_32, - vga_draw_line8d2_16, - vga_draw_line8d2_16, - - vga_draw_line8_8, - vga_draw_line8_16, - vga_draw_line8_16, - vga_draw_line8_32, - vga_draw_line8_32, - vga_draw_line8_16, - vga_draw_line8_16, - - vga_draw_line15_8, - vga_draw_line15_15, - vga_draw_line15_16, - vga_draw_line15_32, - vga_draw_line15_32bgr, - vga_draw_line15_15bgr, - vga_draw_line15_16bgr, - - vga_draw_line16_8, - vga_draw_line16_15, - vga_draw_line16_16, - vga_draw_line16_32, - vga_draw_line16_32bgr, - vga_draw_line16_15bgr, - vga_draw_line16_16bgr, - - vga_draw_line24_8, - vga_draw_line24_15, - vga_draw_line24_16, - vga_draw_line24_32, - vga_draw_line24_32bgr, - vga_draw_line24_15bgr, - vga_draw_line24_16bgr, - - vga_draw_line32_8, - vga_draw_line32_15, - vga_draw_line32_16, - vga_draw_line32_32, - vga_draw_line32_32bgr, - vga_draw_line32_15bgr, - vga_draw_line32_16bgr, -}; - -static int vga_get_bpp(VGACommonState *s) -{ - int ret; - - if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { - ret = s->vbe_regs[VBE_DISPI_INDEX_BPP]; - } else { - ret = 0; - } - return ret; -} - -static void vga_get_resolution(VGACommonState *s, int *pwidth, int *pheight) -{ - int width, height; - - if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { - width = s->vbe_regs[VBE_DISPI_INDEX_XRES]; - height = s->vbe_regs[VBE_DISPI_INDEX_YRES]; - } else { - width = (s->cr[VGA_CRTC_H_DISP] + 1) * 8; - height = s->cr[VGA_CRTC_V_DISP_END] | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x02) << 7) | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x40) << 3); - height = (height + 1); - } - *pwidth = width; - *pheight = height; -} - -void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2) -{ - int y; - if (y1 >= VGA_MAX_HEIGHT) - return; - if (y2 >= VGA_MAX_HEIGHT) - y2 = VGA_MAX_HEIGHT; - for(y = y1; y < y2; y++) { - s->invalidated_y_table[y >> 5] |= 1 << (y & 0x1f); - } -} - -void vga_sync_dirty_bitmap(VGACommonState *s) -{ - memory_region_sync_dirty_bitmap(&s->vram); -} - -void vga_dirty_log_start(VGACommonState *s) -{ - memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA); -} - -void vga_dirty_log_stop(VGACommonState *s) -{ - memory_region_set_log(&s->vram, false, DIRTY_MEMORY_VGA); -} - -/* - * graphic modes - */ -static void vga_draw_graphic(VGACommonState *s, int full_update) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int y1, y, update, linesize, y_start, double_scan, mask, depth; - int width, height, shift_control, line_offset, bwidth, bits; - ram_addr_t page0, page1, page_min, page_max; - int disp_width, multi_scan, multi_run; - uint8_t *d; - uint32_t v, addr1, addr; - vga_draw_line_func *vga_draw_line; -#if defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) - static const bool byteswap = false; -#else - static const bool byteswap = true; -#endif - - full_update |= update_basic_params(s); - - if (!full_update) - vga_sync_dirty_bitmap(s); - - s->get_resolution(s, &width, &height); - disp_width = width; - - shift_control = (s->gr[VGA_GFX_MODE] >> 5) & 3; - double_scan = (s->cr[VGA_CRTC_MAX_SCAN] >> 7); - if (shift_control != 1) { - multi_scan = (((s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1) << double_scan) - - 1; - } else { - /* in CGA modes, multi_scan is ignored */ - /* XXX: is it correct ? */ - multi_scan = double_scan; - } - multi_run = multi_scan; - if (shift_control != s->shift_control || - double_scan != s->double_scan) { - full_update = 1; - s->shift_control = shift_control; - s->double_scan = double_scan; - } - - if (shift_control == 0) { - if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { - disp_width <<= 1; - } - } else if (shift_control == 1) { - if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { - disp_width <<= 1; - } - } - - depth = s->get_bpp(s); - if (s->line_offset != s->last_line_offset || - disp_width != s->last_width || - height != s->last_height || - s->last_depth != depth) { - if (depth == 32 || (depth == 16 && !byteswap)) { - surface = qemu_create_displaysurface_from(disp_width, - height, depth, s->line_offset, - s->vram_ptr + (s->start_addr * 4), byteswap); - dpy_gfx_replace_surface(s->con, surface); - } else { - qemu_console_resize(s->con, disp_width, height); - surface = qemu_console_surface(s->con); - } - s->last_scr_width = disp_width; - s->last_scr_height = height; - s->last_width = disp_width; - s->last_height = height; - s->last_line_offset = s->line_offset; - s->last_depth = depth; - full_update = 1; - } else if (is_buffer_shared(surface) && - (full_update || surface_data(surface) != s->vram_ptr - + (s->start_addr * 4))) { - DisplaySurface *surface; - surface = qemu_create_displaysurface_from(disp_width, - height, depth, s->line_offset, - s->vram_ptr + (s->start_addr * 4), byteswap); - dpy_gfx_replace_surface(s->con, surface); - } - - s->rgb_to_pixel = - rgb_to_pixel_dup_table[get_depth_index(surface)]; - - if (shift_control == 0) { - full_update |= update_palette16(s); - if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { - v = VGA_DRAW_LINE4D2; - } else { - v = VGA_DRAW_LINE4; - } - bits = 4; - } else if (shift_control == 1) { - full_update |= update_palette16(s); - if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { - v = VGA_DRAW_LINE2D2; - } else { - v = VGA_DRAW_LINE2; - } - bits = 4; - } else { - switch(s->get_bpp(s)) { - default: - case 0: - full_update |= update_palette256(s); - v = VGA_DRAW_LINE8D2; - bits = 4; - break; - case 8: - full_update |= update_palette256(s); - v = VGA_DRAW_LINE8; - bits = 8; - break; - case 15: - v = VGA_DRAW_LINE15; - bits = 16; - break; - case 16: - v = VGA_DRAW_LINE16; - bits = 16; - break; - case 24: - v = VGA_DRAW_LINE24; - bits = 24; - break; - case 32: - v = VGA_DRAW_LINE32; - bits = 32; - break; - } - } - vga_draw_line = vga_draw_line_table[v * NB_DEPTHS + - get_depth_index(surface)]; - - if (!is_buffer_shared(surface) && s->cursor_invalidate) { - s->cursor_invalidate(s); - } - - line_offset = s->line_offset; -#if 0 - printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n", - width, height, v, line_offset, s->cr[9], s->cr[VGA_CRTC_MODE], - s->line_compare, s->sr[VGA_SEQ_CLOCK_MODE]); -#endif - addr1 = (s->start_addr * 4); - bwidth = (width * bits + 7) / 8; - y_start = -1; - page_min = -1; - page_max = 0; - d = surface_data(surface); - linesize = surface_stride(surface); - y1 = 0; - for(y = 0; y < height; y++) { - addr = addr1; - if (!(s->cr[VGA_CRTC_MODE] & 1)) { - int shift; - /* CGA compatibility handling */ - shift = 14 + ((s->cr[VGA_CRTC_MODE] >> 6) & 1); - addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift); - } - if (!(s->cr[VGA_CRTC_MODE] & 2)) { - addr = (addr & ~0x8000) | ((y1 & 2) << 14); - } - update = full_update; - page0 = addr; - page1 = addr + bwidth - 1; - update |= memory_region_get_dirty(&s->vram, page0, page1 - page0, - DIRTY_MEMORY_VGA); - /* explicit invalidation for the hardware cursor */ - update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1; - if (update) { - if (y_start < 0) - y_start = y; - if (page0 < page_min) - page_min = page0; - if (page1 > page_max) - page_max = page1; - if (!(is_buffer_shared(surface))) { - vga_draw_line(s, d, s->vram_ptr + addr, width); - if (s->cursor_draw_line) - s->cursor_draw_line(s, d, y); - } - } else { - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(s->con, 0, y_start, - disp_width, y - y_start); - y_start = -1; - } - } - if (!multi_run) { - mask = (s->cr[VGA_CRTC_MODE] & 3) ^ 3; - if ((y1 & mask) == mask) - addr1 += line_offset; - y1++; - multi_run = multi_scan; - } else { - multi_run--; - } - /* line compare acts on the displayed lines */ - if (y == s->line_compare) - addr1 = 0; - d += linesize; - } - if (y_start >= 0) { - /* flush to display */ - dpy_gfx_update(s->con, 0, y_start, - disp_width, y - y_start); - } - /* reset modified pages */ - if (page_max >= page_min) { - memory_region_reset_dirty(&s->vram, - page_min, - page_max - page_min, - DIRTY_MEMORY_VGA); - } - memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4); -} - -static void vga_draw_blank(VGACommonState *s, int full_update) -{ - DisplaySurface *surface = qemu_console_surface(s->con); - int i, w, val; - uint8_t *d; - - if (!full_update) - return; - if (s->last_scr_width <= 0 || s->last_scr_height <= 0) - return; - - s->rgb_to_pixel = - rgb_to_pixel_dup_table[get_depth_index(surface)]; - if (surface_bits_per_pixel(surface) == 8) { - val = s->rgb_to_pixel(0, 0, 0); - } else { - val = 0; - } - w = s->last_scr_width * surface_bytes_per_pixel(surface); - d = surface_data(surface); - for(i = 0; i < s->last_scr_height; i++) { - memset(d, val, w); - d += surface_stride(surface); - } - dpy_gfx_update(s->con, 0, 0, - s->last_scr_width, s->last_scr_height); -} - -#define GMODE_TEXT 0 -#define GMODE_GRAPH 1 -#define GMODE_BLANK 2 - -static void vga_update_display(void *opaque) -{ - VGACommonState *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - int full_update, graphic_mode; - - qemu_flush_coalesced_mmio_buffer(); - - if (surface_bits_per_pixel(surface) == 0) { - /* nothing to do */ - } else { - full_update = 0; - if (!(s->ar_index & 0x20)) { - graphic_mode = GMODE_BLANK; - } else { - graphic_mode = s->gr[VGA_GFX_MISC] & VGA_GR06_GRAPHICS_MODE; - } - if (graphic_mode != s->graphic_mode) { - s->graphic_mode = graphic_mode; - s->cursor_blink_time = qemu_get_clock_ms(vm_clock); - full_update = 1; - } - switch(graphic_mode) { - case GMODE_TEXT: - vga_draw_text(s, full_update); - break; - case GMODE_GRAPH: - vga_draw_graphic(s, full_update); - break; - case GMODE_BLANK: - default: - vga_draw_blank(s, full_update); - break; - } - } -} - -/* force a full display refresh */ -static void vga_invalidate_display(void *opaque) -{ - VGACommonState *s = opaque; - - s->last_width = -1; - s->last_height = -1; -} - -void vga_common_reset(VGACommonState *s) -{ - s->sr_index = 0; - memset(s->sr, '\0', sizeof(s->sr)); - s->gr_index = 0; - memset(s->gr, '\0', sizeof(s->gr)); - s->ar_index = 0; - memset(s->ar, '\0', sizeof(s->ar)); - s->ar_flip_flop = 0; - s->cr_index = 0; - memset(s->cr, '\0', sizeof(s->cr)); - s->msr = 0; - s->fcr = 0; - s->st00 = 0; - s->st01 = 0; - s->dac_state = 0; - s->dac_sub_index = 0; - s->dac_read_index = 0; - s->dac_write_index = 0; - memset(s->dac_cache, '\0', sizeof(s->dac_cache)); - s->dac_8bit = 0; - memset(s->palette, '\0', sizeof(s->palette)); - s->bank_offset = 0; - s->vbe_index = 0; - memset(s->vbe_regs, '\0', sizeof(s->vbe_regs)); - s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID5; - s->vbe_start_addr = 0; - s->vbe_line_offset = 0; - s->vbe_bank_mask = (s->vram_size >> 16) - 1; - memset(s->font_offsets, '\0', sizeof(s->font_offsets)); - s->graphic_mode = -1; /* force full update */ - s->shift_control = 0; - s->double_scan = 0; - s->line_offset = 0; - s->line_compare = 0; - s->start_addr = 0; - s->plane_updated = 0; - s->last_cw = 0; - s->last_ch = 0; - s->last_width = 0; - s->last_height = 0; - s->last_scr_width = 0; - s->last_scr_height = 0; - s->cursor_start = 0; - s->cursor_end = 0; - s->cursor_offset = 0; - memset(s->invalidated_y_table, '\0', sizeof(s->invalidated_y_table)); - memset(s->last_palette, '\0', sizeof(s->last_palette)); - memset(s->last_ch_attr, '\0', sizeof(s->last_ch_attr)); - switch (vga_retrace_method) { - case VGA_RETRACE_DUMB: - break; - case VGA_RETRACE_PRECISE: - memset(&s->retrace_info, 0, sizeof (s->retrace_info)); - break; - } - vga_update_memory_access(s); -} - -static void vga_reset(void *opaque) -{ - VGACommonState *s = opaque; - vga_common_reset(s); -} - -#define TEXTMODE_X(x) ((x) % width) -#define TEXTMODE_Y(x) ((x) / width) -#define VMEM2CHTYPE(v) ((v & 0xff0007ff) | \ - ((v & 0x00000800) << 10) | ((v & 0x00007000) >> 1)) -/* relay text rendering to the display driver - * instead of doing a full vga_update_display() */ -static void vga_update_text(void *opaque, console_ch_t *chardata) -{ - VGACommonState *s = opaque; - int graphic_mode, i, cursor_offset, cursor_visible; - int cw, cheight, width, height, size, c_min, c_max; - uint32_t *src; - console_ch_t *dst, val; - char msg_buffer[80]; - int full_update = 0; - - qemu_flush_coalesced_mmio_buffer(); - - if (!(s->ar_index & 0x20)) { - graphic_mode = GMODE_BLANK; - } else { - graphic_mode = s->gr[VGA_GFX_MISC] & VGA_GR06_GRAPHICS_MODE; - } - if (graphic_mode != s->graphic_mode) { - s->graphic_mode = graphic_mode; - full_update = 1; - } - if (s->last_width == -1) { - s->last_width = 0; - full_update = 1; - } - - switch (graphic_mode) { - case GMODE_TEXT: - /* TODO: update palette */ - full_update |= update_basic_params(s); - - /* total width & height */ - cheight = (s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1; - cw = 8; - if (!(s->sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_CHAR_CLK_8DOTS)) { - cw = 9; - } - if (s->sr[VGA_SEQ_CLOCK_MODE] & 0x08) { - cw = 16; /* NOTE: no 18 pixel wide */ - } - width = (s->cr[VGA_CRTC_H_DISP] + 1); - if (s->cr[VGA_CRTC_V_TOTAL] == 100) { - /* ugly hack for CGA 160x100x16 - explain me the logic */ - height = 100; - } else { - height = s->cr[VGA_CRTC_V_DISP_END] | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x02) << 7) | - ((s->cr[VGA_CRTC_OVERFLOW] & 0x40) << 3); - height = (height + 1) / cheight; - } - - size = (height * width); - if (size > CH_ATTR_SIZE) { - if (!full_update) - return; - - snprintf(msg_buffer, sizeof(msg_buffer), "%i x %i Text mode", - width, height); - break; - } - - if (width != s->last_width || height != s->last_height || - cw != s->last_cw || cheight != s->last_ch) { - s->last_scr_width = width * cw; - s->last_scr_height = height * cheight; - qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height); - dpy_text_resize(s->con, width, height); - s->last_depth = 0; - s->last_width = width; - s->last_height = height; - s->last_ch = cheight; - s->last_cw = cw; - full_update = 1; - } - - if (full_update) { - s->full_update_gfx = 1; - } - if (s->full_update_text) { - s->full_update_text = 0; - full_update |= 1; - } - - /* Update "hardware" cursor */ - cursor_offset = ((s->cr[VGA_CRTC_CURSOR_HI] << 8) | - s->cr[VGA_CRTC_CURSOR_LO]) - s->start_addr; - if (cursor_offset != s->cursor_offset || - s->cr[VGA_CRTC_CURSOR_START] != s->cursor_start || - s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end || full_update) { - cursor_visible = !(s->cr[VGA_CRTC_CURSOR_START] & 0x20); - if (cursor_visible && cursor_offset < size && cursor_offset >= 0) - dpy_text_cursor(s->con, - TEXTMODE_X(cursor_offset), - TEXTMODE_Y(cursor_offset)); - else - dpy_text_cursor(s->con, -1, -1); - s->cursor_offset = cursor_offset; - s->cursor_start = s->cr[VGA_CRTC_CURSOR_START]; - s->cursor_end = s->cr[VGA_CRTC_CURSOR_END]; - } - - src = (uint32_t *) s->vram_ptr + s->start_addr; - dst = chardata; - - if (full_update) { - for (i = 0; i < size; src ++, dst ++, i ++) - console_write_ch(dst, VMEM2CHTYPE(le32_to_cpu(*src))); - - dpy_text_update(s->con, 0, 0, width, height); - } else { - c_max = 0; - - for (i = 0; i < size; src ++, dst ++, i ++) { - console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src))); - if (*dst != val) { - *dst = val; - c_max = i; - break; - } - } - c_min = i; - for (; i < size; src ++, dst ++, i ++) { - console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src))); - if (*dst != val) { - *dst = val; - c_max = i; - } - } - - if (c_min <= c_max) { - i = TEXTMODE_Y(c_min); - dpy_text_update(s->con, 0, i, width, TEXTMODE_Y(c_max) - i + 1); - } - } - - return; - case GMODE_GRAPH: - if (!full_update) - return; - - s->get_resolution(s, &width, &height); - snprintf(msg_buffer, sizeof(msg_buffer), "%i x %i Graphic mode", - width, height); - break; - case GMODE_BLANK: - default: - if (!full_update) - return; - - snprintf(msg_buffer, sizeof(msg_buffer), "VGA Blank mode"); - break; - } - - /* Display a message */ - s->last_width = 60; - s->last_height = height = 3; - dpy_text_cursor(s->con, -1, -1); - dpy_text_resize(s->con, s->last_width, height); - - for (dst = chardata, i = 0; i < s->last_width * height; i ++) - console_write_ch(dst ++, ' '); - - size = strlen(msg_buffer); - width = (s->last_width - size) / 2; - dst = chardata + s->last_width + width; - for (i = 0; i < size; i ++) - console_write_ch(dst ++, 0x00200100 | msg_buffer[i]); - - dpy_text_update(s->con, 0, 0, s->last_width, height); -} - -static uint64_t vga_mem_read(void *opaque, hwaddr addr, - unsigned size) -{ - VGACommonState *s = opaque; - - return vga_mem_readb(s, addr); -} - -static void vga_mem_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VGACommonState *s = opaque; - - return vga_mem_writeb(s, addr, data); -} - -const MemoryRegionOps vga_mem_ops = { - .read = vga_mem_read, - .write = vga_mem_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -static int vga_common_post_load(void *opaque, int version_id) -{ - VGACommonState *s = opaque; - - /* force refresh */ - s->graphic_mode = -1; - return 0; -} - -const VMStateDescription vmstate_vga_common = { - .name = "vga", - .version_id = 2, - .minimum_version_id = 2, - .minimum_version_id_old = 2, - .post_load = vga_common_post_load, - .fields = (VMStateField []) { - VMSTATE_UINT32(latch, VGACommonState), - VMSTATE_UINT8(sr_index, VGACommonState), - VMSTATE_PARTIAL_BUFFER(sr, VGACommonState, 8), - VMSTATE_UINT8(gr_index, VGACommonState), - VMSTATE_PARTIAL_BUFFER(gr, VGACommonState, 16), - VMSTATE_UINT8(ar_index, VGACommonState), - VMSTATE_BUFFER(ar, VGACommonState), - VMSTATE_INT32(ar_flip_flop, VGACommonState), - VMSTATE_UINT8(cr_index, VGACommonState), - VMSTATE_BUFFER(cr, VGACommonState), - VMSTATE_UINT8(msr, VGACommonState), - VMSTATE_UINT8(fcr, VGACommonState), - VMSTATE_UINT8(st00, VGACommonState), - VMSTATE_UINT8(st01, VGACommonState), - - VMSTATE_UINT8(dac_state, VGACommonState), - VMSTATE_UINT8(dac_sub_index, VGACommonState), - VMSTATE_UINT8(dac_read_index, VGACommonState), - VMSTATE_UINT8(dac_write_index, VGACommonState), - VMSTATE_BUFFER(dac_cache, VGACommonState), - VMSTATE_BUFFER(palette, VGACommonState), - - VMSTATE_INT32(bank_offset, VGACommonState), - VMSTATE_UINT8_EQUAL(is_vbe_vmstate, VGACommonState), - VMSTATE_UINT16(vbe_index, VGACommonState), - VMSTATE_UINT16_ARRAY(vbe_regs, VGACommonState, VBE_DISPI_INDEX_NB), - VMSTATE_UINT32(vbe_start_addr, VGACommonState), - VMSTATE_UINT32(vbe_line_offset, VGACommonState), - VMSTATE_UINT32(vbe_bank_mask, VGACommonState), - VMSTATE_END_OF_LIST() - } -}; - -void vga_common_init(VGACommonState *s) -{ - int i, j, v, b; - - for(i = 0;i < 256; i++) { - v = 0; - for(j = 0; j < 8; j++) { - v |= ((i >> j) & 1) << (j * 4); - } - expand4[i] = v; - - v = 0; - for(j = 0; j < 4; j++) { - v |= ((i >> (2 * j)) & 3) << (j * 4); - } - expand2[i] = v; - } - for(i = 0; i < 16; i++) { - v = 0; - for(j = 0; j < 4; j++) { - b = ((i >> j) & 1); - v |= b << (2 * j); - v |= b << (2 * j + 1); - } - expand4to8[i] = v; - } - - /* valid range: 1 MB -> 256 MB */ - s->vram_size = 1024 * 1024; - while (s->vram_size < (s->vram_size_mb << 20) && - s->vram_size < (256 << 20)) { - s->vram_size <<= 1; - } - s->vram_size_mb = s->vram_size >> 20; - - s->is_vbe_vmstate = 1; - memory_region_init_ram(&s->vram, "vga.vram", s->vram_size); - vmstate_register_ram_global(&s->vram); - xen_register_framebuffer(&s->vram); - s->vram_ptr = memory_region_get_ram_ptr(&s->vram); - s->get_bpp = vga_get_bpp; - s->get_offsets = vga_get_offsets; - s->get_resolution = vga_get_resolution; - s->update = vga_update_display; - s->invalidate = vga_invalidate_display; - s->screen_dump = vga_screen_dump; - s->text_update = vga_update_text; - switch (vga_retrace_method) { - case VGA_RETRACE_DUMB: - s->retrace = vga_dumb_retrace; - s->update_retrace_info = vga_dumb_update_retrace_info; - break; - - case VGA_RETRACE_PRECISE: - s->retrace = vga_precise_retrace; - s->update_retrace_info = vga_precise_update_retrace_info; - break; - } - vga_dirty_log_start(s); -} - -static const MemoryRegionPortio vga_portio_list[] = { - { 0x04, 2, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3b4 */ - { 0x0a, 1, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3ba */ - { 0x10, 16, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3c0 */ - { 0x24, 2, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3d4 */ - { 0x2a, 1, 1, .read = vga_ioport_read, .write = vga_ioport_write }, /* 3da */ - PORTIO_END_OF_LIST(), -}; - -static const MemoryRegionPortio vbe_portio_list[] = { - { 0, 1, 2, .read = vbe_ioport_read_index, .write = vbe_ioport_write_index }, -# ifdef TARGET_I386 - { 1, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data }, -# endif - { 2, 1, 2, .read = vbe_ioport_read_data, .write = vbe_ioport_write_data }, - PORTIO_END_OF_LIST(), -}; - -/* Used by both ISA and PCI */ -MemoryRegion *vga_init_io(VGACommonState *s, - const MemoryRegionPortio **vga_ports, - const MemoryRegionPortio **vbe_ports) -{ - MemoryRegion *vga_mem; - - *vga_ports = vga_portio_list; - *vbe_ports = vbe_portio_list; - - vga_mem = g_malloc(sizeof(*vga_mem)); - memory_region_init_io(vga_mem, &vga_mem_ops, s, - "vga-lowmem", 0x20000); - memory_region_set_flush_coalesced(vga_mem); - - return vga_mem; -} - -void vga_init(VGACommonState *s, MemoryRegion *address_space, - MemoryRegion *address_space_io, bool init_vga_ports) -{ - MemoryRegion *vga_io_memory; - const MemoryRegionPortio *vga_ports, *vbe_ports; - PortioList *vga_port_list = g_new(PortioList, 1); - PortioList *vbe_port_list = g_new(PortioList, 1); - - qemu_register_reset(vga_reset, s); - - s->bank_offset = 0; - - s->legacy_address_space = address_space; - - vga_io_memory = vga_init_io(s, &vga_ports, &vbe_ports); - memory_region_add_subregion_overlap(address_space, - isa_mem_base + 0x000a0000, - vga_io_memory, - 1); - memory_region_set_coalescing(vga_io_memory); - if (init_vga_ports) { - portio_list_init(vga_port_list, vga_ports, s, "vga"); - portio_list_add(vga_port_list, address_space_io, 0x3b0); - } - if (vbe_ports) { - portio_list_init(vbe_port_list, vbe_ports, s, "vbe"); - portio_list_add(vbe_port_list, address_space_io, 0x1ce); - } -} - -void vga_init_vbe(VGACommonState *s, MemoryRegion *system_memory) -{ - /* With pc-0.12 and below we map both the PCI BAR and the fixed VBE region, - * so use an alias to avoid double-mapping the same region. - */ - memory_region_init_alias(&s->vram_vbe, "vram.vbe", - &s->vram, 0, memory_region_size(&s->vram)); - /* XXX: use optimized standard vga accesses */ - memory_region_add_subregion(system_memory, - VBE_DISPI_LFB_PHYSICAL_ADDRESS, - &s->vram_vbe); - s->vbe_mapped = 1; -} -/********************************************************/ -/* vga screen dump */ - -void ppm_save(const char *filename, struct DisplaySurface *ds, Error **errp) -{ - int width = pixman_image_get_width(ds->image); - int height = pixman_image_get_height(ds->image); - FILE *f; - int y; - int ret; - pixman_image_t *linebuf; - - trace_ppm_save(filename, ds); - f = fopen(filename, "wb"); - if (!f) { - error_setg(errp, "failed to open file '%s': %s", filename, - strerror(errno)); - return; - } - ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255); - if (ret < 0) { - linebuf = NULL; - goto write_err; - } - linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width); - for (y = 0; y < height; y++) { - qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y); - clearerr(f); - ret = fwrite(pixman_image_get_data(linebuf), 1, - pixman_image_get_stride(linebuf), f); - (void)ret; - if (ferror(f)) { - goto write_err; - } - } - -out: - qemu_pixman_image_unref(linebuf); - fclose(f); - return; - -write_err: - error_setg(errp, "failed to write to file '%s': %s", filename, - strerror(errno)); - unlink(filename); - goto out; -} - -/* save the vga display in a PPM image even if no display is - available */ -static void vga_screen_dump(void *opaque, const char *filename, bool cswitch, - Error **errp) -{ - VGACommonState *s = opaque; - DisplaySurface *surface = qemu_console_surface(s->con); - - if (cswitch) { - vga_invalidate_display(s); - } - vga_hw_update(); - ppm_save(filename, surface, errp); -} -- cgit v1.2.1