aboutsummaryrefslogtreecommitdiff
path: root/hw/display
diff options
context:
space:
mode:
Diffstat (limited to 'hw/display')
-rw-r--r--hw/display/Makefile.objs34
-rw-r--r--hw/display/ads7846.c177
-rw-r--r--hw/display/blizzard.c1004
-rw-r--r--hw/display/blizzard_template.h136
-rw-r--r--hw/display/cirrus_vga.c3021
-rw-r--r--hw/display/cirrus_vga_rop.h208
-rw-r--r--hw/display/cirrus_vga_rop2.h281
-rw-r--r--hw/display/cirrus_vga_template.h102
-rw-r--r--hw/display/exynos4210_fimd.c1930
-rw-r--r--hw/display/framebuffer.c110
-rw-r--r--hw/display/framebuffer.h25
-rw-r--r--hw/display/g364fb.c617
-rw-r--r--hw/display/jazz_led.c304
-rw-r--r--hw/display/milkymist-tmu2.c490
-rw-r--r--hw/display/milkymist-vgafb.c335
-rw-r--r--hw/display/milkymist-vgafb_template.h74
-rw-r--r--hw/display/omap_dss.c1086
-rw-r--r--hw/display/omap_lcd_template.h175
-rw-r--r--hw/display/omap_lcdc.c493
-rw-r--r--hw/display/pl110.c533
-rw-r--r--hw/display/pl110_template.h395
-rw-r--r--hw/display/pxa2xx_lcd.c1058
-rw-r--r--hw/display/pxa2xx_template.h435
-rw-r--r--hw/display/qxl-logger.c275
-rw-r--r--hw/display/qxl-render.c280
-rw-r--r--hw/display/qxl.c2365
-rw-r--r--hw/display/qxl.h165
-rw-r--r--hw/display/sm501.c1450
-rw-r--r--hw/display/sm501_template.h145
-rw-r--r--hw/display/ssd0303.c322
-rw-r--r--hw/display/ssd0323.c373
-rw-r--r--hw/display/tc6393xb.c593
-rw-r--r--hw/display/tc6393xb_template.h68
-rw-r--r--hw/display/tcx.c739
-rw-r--r--hw/display/vga-isa-mm.c144
-rw-r--r--hw/display/vga-isa.c101
-rw-r--r--hw/display/vga-pci.c215
-rw-r--r--hw/display/vga.c2457
-rw-r--r--hw/display/vga.h159
-rw-r--r--hw/display/vga_int.h218
-rw-r--r--hw/display/vga_template.h459
-rw-r--r--hw/display/vmware_vga.c1282
-rw-r--r--hw/display/xenfb.c1021
43 files changed, 25854 insertions, 0 deletions
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
new file mode 100644
index 0000000000..3f7027d46b
--- /dev/null
+++ b/hw/display/Makefile.objs
@@ -0,0 +1,34 @@
+common-obj-$(CONFIG_ADS7846) += ads7846.o
+common-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o
+common-obj-$(CONFIG_G364FB) += g364fb.o
+common-obj-$(CONFIG_JAZZ_LED) += jazz_led.o
+common-obj-$(CONFIG_PL110) += pl110.o
+common-obj-$(CONFIG_SSD0303) += ssd0303.o
+common-obj-$(CONFIG_SSD0323) += ssd0323.o
+common-obj-$(CONFIG_XEN_BACKEND) += xenfb.o
+
+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/ads7846.c b/hw/display/ads7846.c
new file mode 100644
index 0000000000..5da3dc5b2c
--- /dev/null
+++ b/hw/display/ads7846.c
@@ -0,0 +1,177 @@
+/*
+ * TI ADS7846 / TSC2046 chip emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * 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/ssi.h"
+#include "ui/console.h"
+
+typedef struct {
+ SSISlave ssidev;
+ qemu_irq interrupt;
+
+ int input[8];
+ int pressure;
+ int noise;
+
+ int cycle;
+ int output;
+} ADS7846State;
+
+/* Control-byte bitfields */
+#define CB_PD0 (1 << 0)
+#define CB_PD1 (1 << 1)
+#define CB_SER (1 << 2)
+#define CB_MODE (1 << 3)
+#define CB_A0 (1 << 4)
+#define CB_A1 (1 << 5)
+#define CB_A2 (1 << 6)
+#define CB_START (1 << 7)
+
+#define X_AXIS_DMAX 3470
+#define X_AXIS_MIN 290
+#define Y_AXIS_DMAX 3450
+#define Y_AXIS_MIN 200
+
+#define ADS_VBAT 2000
+#define ADS_VAUX 2000
+#define ADS_TEMP0 2000
+#define ADS_TEMP1 3000
+#define ADS_XPOS(x, y) (X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15))
+#define ADS_YPOS(x, y) (Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15))
+#define ADS_Z1POS(x, y) 600
+#define ADS_Z2POS(x, y) (600 + 6000 / ADS_XPOS(x, y))
+
+static void ads7846_int_update(ADS7846State *s)
+{
+ if (s->interrupt)
+ qemu_set_irq(s->interrupt, s->pressure == 0);
+}
+
+static uint32_t ads7846_transfer(SSISlave *dev, uint32_t value)
+{
+ ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev);
+
+ switch (s->cycle ++) {
+ case 0:
+ if (!(value & CB_START)) {
+ s->cycle = 0;
+ break;
+ }
+
+ s->output = s->input[(value >> 4) & 7];
+
+ /* Imitate the ADC noise, some drivers expect this. */
+ s->noise = (s->noise + 3) & 7;
+ switch ((value >> 4) & 7) {
+ case 1: s->output += s->noise ^ 2; break;
+ case 3: s->output += s->noise ^ 0; break;
+ case 4: s->output += s->noise ^ 7; break;
+ case 5: s->output += s->noise ^ 5; break;
+ }
+
+ if (value & CB_MODE)
+ s->output >>= 4; /* 8 bits instead of 12 */
+
+ break;
+ case 1:
+ s->cycle = 0;
+ break;
+ }
+ return s->output;
+}
+
+static void ads7846_ts_event(void *opaque,
+ int x, int y, int z, int buttons_state)
+{
+ ADS7846State *s = opaque;
+
+ if (buttons_state) {
+ x = 0x7fff - x;
+ s->input[1] = ADS_XPOS(x, y);
+ s->input[3] = ADS_Z1POS(x, y);
+ s->input[4] = ADS_Z2POS(x, y);
+ s->input[5] = ADS_YPOS(x, y);
+ }
+
+ if (s->pressure == !buttons_state) {
+ s->pressure = !!buttons_state;
+
+ ads7846_int_update(s);
+ }
+}
+
+static int ads7856_post_load(void *opaque, int version_id)
+{
+ ADS7846State *s = opaque;
+
+ s->pressure = 0;
+ ads7846_int_update(s);
+ return 0;
+}
+
+static const VMStateDescription vmstate_ads7846 = {
+ .name = "ads7846",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = ads7856_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_SSI_SLAVE(ssidev, ADS7846State),
+ VMSTATE_INT32_ARRAY(input, ADS7846State, 8),
+ VMSTATE_INT32(noise, ADS7846State),
+ VMSTATE_INT32(cycle, ADS7846State),
+ VMSTATE_INT32(output, ADS7846State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int ads7846_init(SSISlave *dev)
+{
+ ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev);
+
+ qdev_init_gpio_out(&dev->qdev, &s->interrupt, 1);
+
+ s->input[0] = ADS_TEMP0; /* TEMP0 */
+ s->input[2] = ADS_VBAT; /* VBAT */
+ s->input[6] = ADS_VAUX; /* VAUX */
+ s->input[7] = ADS_TEMP1; /* TEMP1 */
+
+ /* We want absolute coordinates */
+ qemu_add_mouse_event_handler(ads7846_ts_event, s, 1,
+ "QEMU ADS7846-driven Touchscreen");
+
+ ads7846_int_update(s);
+
+ vmstate_register(NULL, -1, &vmstate_ads7846, s);
+ return 0;
+}
+
+static void ads7846_class_init(ObjectClass *klass, void *data)
+{
+ SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+ k->init = ads7846_init;
+ k->transfer = ads7846_transfer;
+}
+
+static const TypeInfo ads7846_info = {
+ .name = "ads7846",
+ .parent = TYPE_SSI_SLAVE,
+ .instance_size = sizeof(ADS7846State),
+ .class_init = ads7846_class_init,
+};
+
+static void ads7846_register_types(void)
+{
+ type_register_static(&ads7846_info);
+}
+
+type_init(ads7846_register_types)
diff --git a/hw/display/blizzard.c b/hw/display/blizzard.c
new file mode 100644
index 0000000000..175c5cdd00
--- /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 <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu-common.h"
+#include "ui/console.h"
+#include "hw/arm/devices.h"
+#include "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 "blizzard_template.h"
+#define DEPTH 15
+#include "blizzard_template.h"
+#define DEPTH 16
+#include "blizzard_template.h"
+#define DEPTH 24
+#include "blizzard_template.h"
+#define DEPTH 32
+#include "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/blizzard_template.h b/hw/display/blizzard_template.h
new file mode 100644
index 0000000000..a8a8899478
--- /dev/null
+++ b/hw/display/blizzard_template.h
@@ -0,0 +1,136 @@
+/*
+ * QEMU Epson S1D13744/S1D13745 templates
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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 <http://www.gnu.org/licenses/>.
+ */
+
+#define SKIP_PIXEL(to) to += deststep
+#if DEPTH == 8
+# define PIXEL_TYPE uint8_t
+# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
+# define COPY_PIXEL1(to, from) *to ++ = from
+#elif DEPTH == 15 || DEPTH == 16
+# define PIXEL_TYPE uint16_t
+# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
+# define COPY_PIXEL1(to, from) *to ++ = from
+#elif DEPTH == 24
+# define PIXEL_TYPE uint8_t
+# define COPY_PIXEL(to, from) \
+ to[0] = from; to[1] = (from) >> 8; to[2] = (from) >> 16; SKIP_PIXEL(to)
+# define COPY_PIXEL1(to, from) \
+ *to ++ = from; *to ++ = (from) >> 8; *to ++ = (from) >> 16
+#elif DEPTH == 32
+# define PIXEL_TYPE uint32_t
+# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
+# define COPY_PIXEL1(to, from) *to ++ = from
+#else
+# error unknown bit depth
+#endif
+
+#ifdef HOST_WORDS_BIGENDIAN
+# define SWAP_WORDS 1
+#endif
+
+static void glue(blizzard_draw_line16_, DEPTH)(PIXEL_TYPE *dest,
+ const uint16_t *src, unsigned int width)
+{
+#if !defined(SWAP_WORDS) && DEPTH == 16
+ memcpy(dest, src, width);
+#else
+ uint16_t data;
+ unsigned int r, g, b;
+ const uint16_t *end = (const void *) src + width;
+ while (src < end) {
+ data = *src ++;
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ r = (data & 0x1f) << 3;
+ data >>= 5;
+ COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b));
+ }
+#endif
+}
+
+static void glue(blizzard_draw_line24mode1_, DEPTH)(PIXEL_TYPE *dest,
+ const uint8_t *src, unsigned int width)
+{
+ /* TODO: check if SDL 24-bit planes are not in the same format and
+ * if so, use memcpy */
+ unsigned int r[2], g[2], b[2];
+ const uint8_t *end = src + width;
+ while (src < end) {
+ g[0] = *src ++;
+ r[0] = *src ++;
+ r[1] = *src ++;
+ b[0] = *src ++;
+ COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[0], g[0], b[0]));
+ b[1] = *src ++;
+ g[1] = *src ++;
+ COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r[1], g[1], b[1]));
+ }
+}
+
+static void glue(blizzard_draw_line24mode2_, DEPTH)(PIXEL_TYPE *dest,
+ const uint8_t *src, unsigned int width)
+{
+ unsigned int r, g, b;
+ const uint8_t *end = src + width;
+ while (src < end) {
+ r = *src ++;
+ src ++;
+ b = *src ++;
+ g = *src ++;
+ COPY_PIXEL1(dest, glue(rgb_to_pixel, DEPTH)(r, g, b));
+ }
+}
+
+/* No rotation */
+static blizzard_fn_t glue(blizzard_draw_fn_, DEPTH)[0x10] = {
+ NULL,
+ /* RGB 5:6:5*/
+ (blizzard_fn_t) glue(blizzard_draw_line16_, DEPTH),
+ /* RGB 6:6:6 mode 1 */
+ (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH),
+ /* RGB 8:8:8 mode 1 */
+ (blizzard_fn_t) glue(blizzard_draw_line24mode1_, DEPTH),
+ NULL, NULL,
+ /* RGB 6:6:6 mode 2 */
+ (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH),
+ /* RGB 8:8:8 mode 2 */
+ (blizzard_fn_t) glue(blizzard_draw_line24mode2_, DEPTH),
+ /* YUV 4:2:2 */
+ NULL,
+ /* YUV 4:2:0 */
+ NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+/* 90deg, 180deg and 270deg rotation */
+static blizzard_fn_t glue(blizzard_draw_fn_r_, DEPTH)[0x10] = {
+ /* TODO */
+ [0 ... 0xf] = NULL,
+};
+
+#undef DEPTH
+#undef SKIP_PIXEL
+#undef COPY_PIXEL
+#undef COPY_PIXEL1
+#undef PIXEL_TYPE
+
+#undef SWAP_WORDS
diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
new file mode 100644
index 0000000000..bf2181afd8
--- /dev/null
+++ b/hw/display/cirrus_vga.c
@@ -0,0 +1,3021 @@
+/*
+ * QEMU Cirrus CLGD 54xx VGA Emulator.
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ * Copyright (c) 2004 Makoto Suzuki (suzu)
+ *
+ * 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.
+ */
+/*
+ * Reference: Finn Thogersons' VGADOC4b
+ * available at http://home.worldonline.dk/~finth/
+ */
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "ui/console.h"
+#include "vga_int.h"
+#include "hw/loader.h"
+
+/*
+ * TODO:
+ * - destination write mask support not complete (bits 5..7)
+ * - optimize linear mappings
+ * - optimize bitblt functions
+ */
+
+//#define DEBUG_CIRRUS
+//#define DEBUG_BITBLT
+
+/***************************************
+ *
+ * definitions
+ *
+ ***************************************/
+
+// ID
+#define CIRRUS_ID_CLGD5422 (0x23<<2)
+#define CIRRUS_ID_CLGD5426 (0x24<<2)
+#define CIRRUS_ID_CLGD5424 (0x25<<2)
+#define CIRRUS_ID_CLGD5428 (0x26<<2)
+#define CIRRUS_ID_CLGD5430 (0x28<<2)
+#define CIRRUS_ID_CLGD5434 (0x2A<<2)
+#define CIRRUS_ID_CLGD5436 (0x2B<<2)
+#define CIRRUS_ID_CLGD5446 (0x2E<<2)
+
+// sequencer 0x07
+#define CIRRUS_SR7_BPP_VGA 0x00
+#define CIRRUS_SR7_BPP_SVGA 0x01
+#define CIRRUS_SR7_BPP_MASK 0x0e
+#define CIRRUS_SR7_BPP_8 0x00
+#define CIRRUS_SR7_BPP_16_DOUBLEVCLK 0x02
+#define CIRRUS_SR7_BPP_24 0x04
+#define CIRRUS_SR7_BPP_16 0x06
+#define CIRRUS_SR7_BPP_32 0x08
+#define CIRRUS_SR7_ISAADDR_MASK 0xe0
+
+// sequencer 0x0f
+#define CIRRUS_MEMSIZE_512k 0x08
+#define CIRRUS_MEMSIZE_1M 0x10
+#define CIRRUS_MEMSIZE_2M 0x18
+#define CIRRUS_MEMFLAGS_BANKSWITCH 0x80 // bank switching is enabled.
+
+// sequencer 0x12
+#define CIRRUS_CURSOR_SHOW 0x01
+#define CIRRUS_CURSOR_HIDDENPEL 0x02
+#define CIRRUS_CURSOR_LARGE 0x04 // 64x64 if set, 32x32 if clear
+
+// sequencer 0x17
+#define CIRRUS_BUSTYPE_VLBFAST 0x10
+#define CIRRUS_BUSTYPE_PCI 0x20
+#define CIRRUS_BUSTYPE_VLBSLOW 0x30
+#define CIRRUS_BUSTYPE_ISA 0x38
+#define CIRRUS_MMIO_ENABLE 0x04
+#define CIRRUS_MMIO_USE_PCIADDR 0x40 // 0xb8000 if cleared.
+#define CIRRUS_MEMSIZEEXT_DOUBLE 0x80
+
+// control 0x0b
+#define CIRRUS_BANKING_DUAL 0x01
+#define CIRRUS_BANKING_GRANULARITY_16K 0x20 // set:16k, clear:4k
+
+// control 0x30
+#define CIRRUS_BLTMODE_BACKWARDS 0x01
+#define CIRRUS_BLTMODE_MEMSYSDEST 0x02
+#define CIRRUS_BLTMODE_MEMSYSSRC 0x04
+#define CIRRUS_BLTMODE_TRANSPARENTCOMP 0x08
+#define CIRRUS_BLTMODE_PATTERNCOPY 0x40
+#define CIRRUS_BLTMODE_COLOREXPAND 0x80
+#define CIRRUS_BLTMODE_PIXELWIDTHMASK 0x30
+#define CIRRUS_BLTMODE_PIXELWIDTH8 0x00
+#define CIRRUS_BLTMODE_PIXELWIDTH16 0x10
+#define CIRRUS_BLTMODE_PIXELWIDTH24 0x20
+#define CIRRUS_BLTMODE_PIXELWIDTH32 0x30
+
+// control 0x31
+#define CIRRUS_BLT_BUSY 0x01
+#define CIRRUS_BLT_START 0x02
+#define CIRRUS_BLT_RESET 0x04
+#define CIRRUS_BLT_FIFOUSED 0x10
+#define CIRRUS_BLT_AUTOSTART 0x80
+
+// control 0x32
+#define CIRRUS_ROP_0 0x00
+#define CIRRUS_ROP_SRC_AND_DST 0x05
+#define CIRRUS_ROP_NOP 0x06
+#define CIRRUS_ROP_SRC_AND_NOTDST 0x09
+#define CIRRUS_ROP_NOTDST 0x0b
+#define CIRRUS_ROP_SRC 0x0d
+#define CIRRUS_ROP_1 0x0e
+#define CIRRUS_ROP_NOTSRC_AND_DST 0x50
+#define CIRRUS_ROP_SRC_XOR_DST 0x59
+#define CIRRUS_ROP_SRC_OR_DST 0x6d
+#define CIRRUS_ROP_NOTSRC_OR_NOTDST 0x90
+#define CIRRUS_ROP_SRC_NOTXOR_DST 0x95
+#define CIRRUS_ROP_SRC_OR_NOTDST 0xad
+#define CIRRUS_ROP_NOTSRC 0xd0
+#define CIRRUS_ROP_NOTSRC_OR_DST 0xd6
+#define CIRRUS_ROP_NOTSRC_AND_NOTDST 0xda
+
+#define CIRRUS_ROP_NOP_INDEX 2
+#define CIRRUS_ROP_SRC_INDEX 5
+
+// control 0x33
+#define CIRRUS_BLTMODEEXT_SOLIDFILL 0x04
+#define CIRRUS_BLTMODEEXT_COLOREXPINV 0x02
+#define CIRRUS_BLTMODEEXT_DWORDGRANULARITY 0x01
+
+// memory-mapped IO
+#define CIRRUS_MMIO_BLTBGCOLOR 0x00 // dword
+#define CIRRUS_MMIO_BLTFGCOLOR 0x04 // dword
+#define CIRRUS_MMIO_BLTWIDTH 0x08 // word
+#define CIRRUS_MMIO_BLTHEIGHT 0x0a // word
+#define CIRRUS_MMIO_BLTDESTPITCH 0x0c // word
+#define CIRRUS_MMIO_BLTSRCPITCH 0x0e // word
+#define CIRRUS_MMIO_BLTDESTADDR 0x10 // dword
+#define CIRRUS_MMIO_BLTSRCADDR 0x14 // dword
+#define CIRRUS_MMIO_BLTWRITEMASK 0x17 // byte
+#define CIRRUS_MMIO_BLTMODE 0x18 // byte
+#define CIRRUS_MMIO_BLTROP 0x1a // byte
+#define CIRRUS_MMIO_BLTMODEEXT 0x1b // byte
+#define CIRRUS_MMIO_BLTTRANSPARENTCOLOR 0x1c // word?
+#define CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK 0x20 // word?
+#define CIRRUS_MMIO_LINEARDRAW_START_X 0x24 // word
+#define CIRRUS_MMIO_LINEARDRAW_START_Y 0x26 // word
+#define CIRRUS_MMIO_LINEARDRAW_END_X 0x28 // word
+#define CIRRUS_MMIO_LINEARDRAW_END_Y 0x2a // word
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_INC 0x2c // byte
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ROLLOVER 0x2d // byte
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_MASK 0x2e // byte
+#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ACCUM 0x2f // byte
+#define CIRRUS_MMIO_BRESENHAM_K1 0x30 // word
+#define CIRRUS_MMIO_BRESENHAM_K3 0x32 // word
+#define CIRRUS_MMIO_BRESENHAM_ERROR 0x34 // word
+#define CIRRUS_MMIO_BRESENHAM_DELTA_MAJOR 0x36 // word
+#define CIRRUS_MMIO_BRESENHAM_DIRECTION 0x38 // byte
+#define CIRRUS_MMIO_LINEDRAW_MODE 0x39 // byte
+#define CIRRUS_MMIO_BLTSTATUS 0x40 // byte
+
+#define CIRRUS_PNPMMIO_SIZE 0x1000
+
+#define BLTUNSAFE(s) \
+ ( \
+ ( /* check dst is within bounds */ \
+ (s)->cirrus_blt_height * ABS((s)->cirrus_blt_dstpitch) \
+ + ((s)->cirrus_blt_dstaddr & (s)->cirrus_addr_mask) > \
+ (s)->vga.vram_size \
+ ) || \
+ ( /* check src is within bounds */ \
+ (s)->cirrus_blt_height * ABS((s)->cirrus_blt_srcpitch) \
+ + ((s)->cirrus_blt_srcaddr & (s)->cirrus_addr_mask) > \
+ (s)->vga.vram_size \
+ ) \
+ )
+
+struct CirrusVGAState;
+typedef void (*cirrus_bitblt_rop_t) (struct CirrusVGAState *s,
+ uint8_t * dst, const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight);
+typedef void (*cirrus_fill_t)(struct CirrusVGAState *s,
+ uint8_t *dst, int dst_pitch, int width, int height);
+
+typedef struct CirrusVGAState {
+ VGACommonState vga;
+
+ MemoryRegion cirrus_vga_io;
+ MemoryRegion cirrus_linear_io;
+ MemoryRegion cirrus_linear_bitblt_io;
+ MemoryRegion cirrus_mmio_io;
+ MemoryRegion pci_bar;
+ bool linear_vram; /* vga.vram mapped over cirrus_linear_io */
+ MemoryRegion low_mem_container; /* container for 0xa0000-0xc0000 */
+ MemoryRegion low_mem; /* always mapped, overridden by: */
+ MemoryRegion cirrus_bank[2]; /* aliases at 0xa0000-0xb0000 */
+ uint32_t cirrus_addr_mask;
+ uint32_t linear_mmio_mask;
+ uint8_t cirrus_shadow_gr0;
+ uint8_t cirrus_shadow_gr1;
+ uint8_t cirrus_hidden_dac_lockindex;
+ uint8_t cirrus_hidden_dac_data;
+ uint32_t cirrus_bank_base[2];
+ uint32_t cirrus_bank_limit[2];
+ uint8_t cirrus_hidden_palette[48];
+ uint32_t hw_cursor_x;
+ uint32_t hw_cursor_y;
+ int cirrus_blt_pixelwidth;
+ int cirrus_blt_width;
+ int cirrus_blt_height;
+ int cirrus_blt_dstpitch;
+ int cirrus_blt_srcpitch;
+ uint32_t cirrus_blt_fgcol;
+ uint32_t cirrus_blt_bgcol;
+ uint32_t cirrus_blt_dstaddr;
+ uint32_t cirrus_blt_srcaddr;
+ uint8_t cirrus_blt_mode;
+ uint8_t cirrus_blt_modeext;
+ cirrus_bitblt_rop_t cirrus_rop;
+#define CIRRUS_BLTBUFSIZE (2048 * 4) /* one line width */
+ uint8_t cirrus_bltbuf[CIRRUS_BLTBUFSIZE];
+ uint8_t *cirrus_srcptr;
+ uint8_t *cirrus_srcptr_end;
+ uint32_t cirrus_srccounter;
+ /* hwcursor display state */
+ int last_hw_cursor_size;
+ int last_hw_cursor_x;
+ int last_hw_cursor_y;
+ int last_hw_cursor_y_start;
+ int last_hw_cursor_y_end;
+ int real_vram_size; /* XXX: suppress that */
+ int device_id;
+ int bustype;
+} CirrusVGAState;
+
+typedef struct PCICirrusVGAState {
+ PCIDevice dev;
+ CirrusVGAState cirrus_vga;
+} PCICirrusVGAState;
+
+typedef struct ISACirrusVGAState {
+ ISADevice dev;
+ CirrusVGAState cirrus_vga;
+} ISACirrusVGAState;
+
+static uint8_t rop_to_index[256];
+
+/***************************************
+ *
+ * prototypes.
+ *
+ ***************************************/
+
+
+static void cirrus_bitblt_reset(CirrusVGAState *s);
+static void cirrus_update_memory_access(CirrusVGAState *s);
+
+/***************************************
+ *
+ * raster operations
+ *
+ ***************************************/
+
+static void cirrus_bitblt_rop_nop(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+}
+
+static void cirrus_bitblt_fill_nop(CirrusVGAState *s,
+ uint8_t *dst,
+ int dstpitch, int bltwidth,int bltheight)
+{
+}
+
+#define ROP_NAME 0
+#define ROP_FN(d, s) 0
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_and_dst
+#define ROP_FN(d, s) (s) & (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_and_notdst
+#define ROP_FN(d, s) (s) & (~(d))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notdst
+#define ROP_FN(d, s) ~(d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src
+#define ROP_FN(d, s) s
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME 1
+#define ROP_FN(d, s) ~0
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_and_dst
+#define ROP_FN(d, s) (~(s)) & (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_xor_dst
+#define ROP_FN(d, s) (s) ^ (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_or_dst
+#define ROP_FN(d, s) (s) | (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_or_notdst
+#define ROP_FN(d, s) (~(s)) | (~(d))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_notxor_dst
+#define ROP_FN(d, s) ~((s) ^ (d))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME src_or_notdst
+#define ROP_FN(d, s) (s) | (~(d))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc
+#define ROP_FN(d, s) (~(s))
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_or_dst
+#define ROP_FN(d, s) (~(s)) | (d)
+#include "cirrus_vga_rop.h"
+
+#define ROP_NAME notsrc_and_notdst
+#define ROP_FN(d, s) (~(s)) & (~(d))
+#include "cirrus_vga_rop.h"
+
+static const cirrus_bitblt_rop_t cirrus_fwd_rop[16] = {
+ cirrus_bitblt_rop_fwd_0,
+ cirrus_bitblt_rop_fwd_src_and_dst,
+ cirrus_bitblt_rop_nop,
+ cirrus_bitblt_rop_fwd_src_and_notdst,
+ cirrus_bitblt_rop_fwd_notdst,
+ cirrus_bitblt_rop_fwd_src,
+ cirrus_bitblt_rop_fwd_1,
+ cirrus_bitblt_rop_fwd_notsrc_and_dst,
+ cirrus_bitblt_rop_fwd_src_xor_dst,
+ cirrus_bitblt_rop_fwd_src_or_dst,
+ cirrus_bitblt_rop_fwd_notsrc_or_notdst,
+ cirrus_bitblt_rop_fwd_src_notxor_dst,
+ cirrus_bitblt_rop_fwd_src_or_notdst,
+ cirrus_bitblt_rop_fwd_notsrc,
+ cirrus_bitblt_rop_fwd_notsrc_or_dst,
+ cirrus_bitblt_rop_fwd_notsrc_and_notdst,
+};
+
+static const cirrus_bitblt_rop_t cirrus_bkwd_rop[16] = {
+ cirrus_bitblt_rop_bkwd_0,
+ cirrus_bitblt_rop_bkwd_src_and_dst,
+ cirrus_bitblt_rop_nop,
+ cirrus_bitblt_rop_bkwd_src_and_notdst,
+ cirrus_bitblt_rop_bkwd_notdst,
+ cirrus_bitblt_rop_bkwd_src,
+ cirrus_bitblt_rop_bkwd_1,
+ cirrus_bitblt_rop_bkwd_notsrc_and_dst,
+ cirrus_bitblt_rop_bkwd_src_xor_dst,
+ cirrus_bitblt_rop_bkwd_src_or_dst,
+ cirrus_bitblt_rop_bkwd_notsrc_or_notdst,
+ cirrus_bitblt_rop_bkwd_src_notxor_dst,
+ cirrus_bitblt_rop_bkwd_src_or_notdst,
+ cirrus_bitblt_rop_bkwd_notsrc,
+ cirrus_bitblt_rop_bkwd_notsrc_or_dst,
+ cirrus_bitblt_rop_bkwd_notsrc_and_notdst,
+};
+
+#define TRANSP_ROP(name) {\
+ name ## _8,\
+ name ## _16,\
+ }
+#define TRANSP_NOP(func) {\
+ func,\
+ func,\
+ }
+
+static const cirrus_bitblt_rop_t cirrus_fwd_transp_rop[16][2] = {
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_0),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_dst),
+ TRANSP_NOP(cirrus_bitblt_rop_nop),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_1),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_xor_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_notxor_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_bkwd_transp_rop[16][2] = {
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_0),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_dst),
+ TRANSP_NOP(cirrus_bitblt_rop_nop),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_1),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_xor_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_notxor_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_notdst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_dst),
+ TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_notdst),
+};
+
+#define ROP2(name) {\
+ name ## _8,\
+ name ## _16,\
+ name ## _24,\
+ name ## _32,\
+ }
+
+#define ROP_NOP2(func) {\
+ func,\
+ func,\
+ func,\
+ func,\
+ }
+
+static const cirrus_bitblt_rop_t cirrus_patternfill[16][4] = {
+ ROP2(cirrus_patternfill_0),
+ ROP2(cirrus_patternfill_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_patternfill_src_and_notdst),
+ ROP2(cirrus_patternfill_notdst),
+ ROP2(cirrus_patternfill_src),
+ ROP2(cirrus_patternfill_1),
+ ROP2(cirrus_patternfill_notsrc_and_dst),
+ ROP2(cirrus_patternfill_src_xor_dst),
+ ROP2(cirrus_patternfill_src_or_dst),
+ ROP2(cirrus_patternfill_notsrc_or_notdst),
+ ROP2(cirrus_patternfill_src_notxor_dst),
+ ROP2(cirrus_patternfill_src_or_notdst),
+ ROP2(cirrus_patternfill_notsrc),
+ ROP2(cirrus_patternfill_notsrc_or_dst),
+ ROP2(cirrus_patternfill_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_transp[16][4] = {
+ ROP2(cirrus_colorexpand_transp_0),
+ ROP2(cirrus_colorexpand_transp_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_colorexpand_transp_src_and_notdst),
+ ROP2(cirrus_colorexpand_transp_notdst),
+ ROP2(cirrus_colorexpand_transp_src),
+ ROP2(cirrus_colorexpand_transp_1),
+ ROP2(cirrus_colorexpand_transp_notsrc_and_dst),
+ ROP2(cirrus_colorexpand_transp_src_xor_dst),
+ ROP2(cirrus_colorexpand_transp_src_or_dst),
+ ROP2(cirrus_colorexpand_transp_notsrc_or_notdst),
+ ROP2(cirrus_colorexpand_transp_src_notxor_dst),
+ ROP2(cirrus_colorexpand_transp_src_or_notdst),
+ ROP2(cirrus_colorexpand_transp_notsrc),
+ ROP2(cirrus_colorexpand_transp_notsrc_or_dst),
+ ROP2(cirrus_colorexpand_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand[16][4] = {
+ ROP2(cirrus_colorexpand_0),
+ ROP2(cirrus_colorexpand_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_colorexpand_src_and_notdst),
+ ROP2(cirrus_colorexpand_notdst),
+ ROP2(cirrus_colorexpand_src),
+ ROP2(cirrus_colorexpand_1),
+ ROP2(cirrus_colorexpand_notsrc_and_dst),
+ ROP2(cirrus_colorexpand_src_xor_dst),
+ ROP2(cirrus_colorexpand_src_or_dst),
+ ROP2(cirrus_colorexpand_notsrc_or_notdst),
+ ROP2(cirrus_colorexpand_src_notxor_dst),
+ ROP2(cirrus_colorexpand_src_or_notdst),
+ ROP2(cirrus_colorexpand_notsrc),
+ ROP2(cirrus_colorexpand_notsrc_or_dst),
+ ROP2(cirrus_colorexpand_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern_transp[16][4] = {
+ ROP2(cirrus_colorexpand_pattern_transp_0),
+ ROP2(cirrus_colorexpand_pattern_transp_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_colorexpand_pattern_transp_src_and_notdst),
+ ROP2(cirrus_colorexpand_pattern_transp_notdst),
+ ROP2(cirrus_colorexpand_pattern_transp_src),
+ ROP2(cirrus_colorexpand_pattern_transp_1),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_src_xor_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_src_or_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_notdst),
+ ROP2(cirrus_colorexpand_pattern_transp_src_notxor_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_src_or_notdst),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_dst),
+ ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern[16][4] = {
+ ROP2(cirrus_colorexpand_pattern_0),
+ ROP2(cirrus_colorexpand_pattern_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_rop_nop),
+ ROP2(cirrus_colorexpand_pattern_src_and_notdst),
+ ROP2(cirrus_colorexpand_pattern_notdst),
+ ROP2(cirrus_colorexpand_pattern_src),
+ ROP2(cirrus_colorexpand_pattern_1),
+ ROP2(cirrus_colorexpand_pattern_notsrc_and_dst),
+ ROP2(cirrus_colorexpand_pattern_src_xor_dst),
+ ROP2(cirrus_colorexpand_pattern_src_or_dst),
+ ROP2(cirrus_colorexpand_pattern_notsrc_or_notdst),
+ ROP2(cirrus_colorexpand_pattern_src_notxor_dst),
+ ROP2(cirrus_colorexpand_pattern_src_or_notdst),
+ ROP2(cirrus_colorexpand_pattern_notsrc),
+ ROP2(cirrus_colorexpand_pattern_notsrc_or_dst),
+ ROP2(cirrus_colorexpand_pattern_notsrc_and_notdst),
+};
+
+static const cirrus_fill_t cirrus_fill[16][4] = {
+ ROP2(cirrus_fill_0),
+ ROP2(cirrus_fill_src_and_dst),
+ ROP_NOP2(cirrus_bitblt_fill_nop),
+ ROP2(cirrus_fill_src_and_notdst),
+ ROP2(cirrus_fill_notdst),
+ ROP2(cirrus_fill_src),
+ ROP2(cirrus_fill_1),
+ ROP2(cirrus_fill_notsrc_and_dst),
+ ROP2(cirrus_fill_src_xor_dst),
+ ROP2(cirrus_fill_src_or_dst),
+ ROP2(cirrus_fill_notsrc_or_notdst),
+ ROP2(cirrus_fill_src_notxor_dst),
+ ROP2(cirrus_fill_src_or_notdst),
+ ROP2(cirrus_fill_notsrc),
+ ROP2(cirrus_fill_notsrc_or_dst),
+ ROP2(cirrus_fill_notsrc_and_notdst),
+};
+
+static inline void cirrus_bitblt_fgcol(CirrusVGAState *s)
+{
+ unsigned int color;
+ switch (s->cirrus_blt_pixelwidth) {
+ case 1:
+ s->cirrus_blt_fgcol = s->cirrus_shadow_gr1;
+ break;
+ case 2:
+ color = s->cirrus_shadow_gr1 | (s->vga.gr[0x11] << 8);
+ s->cirrus_blt_fgcol = le16_to_cpu(color);
+ break;
+ case 3:
+ s->cirrus_blt_fgcol = s->cirrus_shadow_gr1 |
+ (s->vga.gr[0x11] << 8) | (s->vga.gr[0x13] << 16);
+ break;
+ default:
+ case 4:
+ color = s->cirrus_shadow_gr1 | (s->vga.gr[0x11] << 8) |
+ (s->vga.gr[0x13] << 16) | (s->vga.gr[0x15] << 24);
+ s->cirrus_blt_fgcol = le32_to_cpu(color);
+ break;
+ }
+}
+
+static inline void cirrus_bitblt_bgcol(CirrusVGAState *s)
+{
+ unsigned int color;
+ switch (s->cirrus_blt_pixelwidth) {
+ case 1:
+ s->cirrus_blt_bgcol = s->cirrus_shadow_gr0;
+ break;
+ case 2:
+ color = s->cirrus_shadow_gr0 | (s->vga.gr[0x10] << 8);
+ s->cirrus_blt_bgcol = le16_to_cpu(color);
+ break;
+ case 3:
+ s->cirrus_blt_bgcol = s->cirrus_shadow_gr0 |
+ (s->vga.gr[0x10] << 8) | (s->vga.gr[0x12] << 16);
+ break;
+ default:
+ case 4:
+ color = s->cirrus_shadow_gr0 | (s->vga.gr[0x10] << 8) |
+ (s->vga.gr[0x12] << 16) | (s->vga.gr[0x14] << 24);
+ s->cirrus_blt_bgcol = le32_to_cpu(color);
+ break;
+ }
+}
+
+static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin,
+ int off_pitch, int bytesperline,
+ int lines)
+{
+ int y;
+ int off_cur;
+ int off_cur_end;
+
+ for (y = 0; y < lines; y++) {
+ off_cur = off_begin;
+ off_cur_end = (off_cur + bytesperline) & s->cirrus_addr_mask;
+ memory_region_set_dirty(&s->vga.vram, off_cur, off_cur_end - off_cur);
+ off_begin += off_pitch;
+ }
+}
+
+static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s,
+ const uint8_t * src)
+{
+ uint8_t *dst;
+
+ dst = s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask);
+
+ if (BLTUNSAFE(s))
+ return 0;
+
+ (*s->cirrus_rop) (s, dst, src,
+ s->cirrus_blt_dstpitch, 0,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+ s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+ s->cirrus_blt_height);
+ return 1;
+}
+
+/* fill */
+
+static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop)
+{
+ cirrus_fill_t rop_func;
+
+ if (BLTUNSAFE(s))
+ return 0;
+ rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ rop_func(s, s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
+ s->cirrus_blt_dstpitch,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+ s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+ s->cirrus_blt_height);
+ cirrus_bitblt_reset(s);
+ return 1;
+}
+
+/***************************************
+ *
+ * bitblt (video-to-video)
+ *
+ ***************************************/
+
+static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s)
+{
+ return cirrus_bitblt_common_patterncopy(s,
+ s->vga.vram_ptr + ((s->cirrus_blt_srcaddr & ~7) &
+ s->cirrus_addr_mask));
+}
+
+static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
+{
+ int sx = 0, sy = 0;
+ int dx = 0, dy = 0;
+ int depth = 0;
+ int notify = 0;
+
+ /* make sure to only copy if it's a plain copy ROP */
+ if (*s->cirrus_rop == cirrus_bitblt_rop_fwd_src ||
+ *s->cirrus_rop == cirrus_bitblt_rop_bkwd_src) {
+
+ int width, height;
+
+ depth = s->vga.get_bpp(&s->vga) / 8;
+ s->vga.get_resolution(&s->vga, &width, &height);
+
+ /* extra x, y */
+ sx = (src % ABS(s->cirrus_blt_srcpitch)) / depth;
+ sy = (src / ABS(s->cirrus_blt_srcpitch));
+ dx = (dst % ABS(s->cirrus_blt_dstpitch)) / depth;
+ dy = (dst / ABS(s->cirrus_blt_dstpitch));
+
+ /* normalize width */
+ w /= depth;
+
+ /* if we're doing a backward copy, we have to adjust
+ our x/y to be the upper left corner (instead of the lower
+ right corner) */
+ if (s->cirrus_blt_dstpitch < 0) {
+ sx -= (s->cirrus_blt_width / depth) - 1;
+ dx -= (s->cirrus_blt_width / depth) - 1;
+ sy -= s->cirrus_blt_height - 1;
+ dy -= s->cirrus_blt_height - 1;
+ }
+
+ /* are we in the visible portion of memory? */
+ if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 &&
+ (sx + w) <= width && (sy + h) <= height &&
+ (dx + w) <= width && (dy + h) <= height) {
+ notify = 1;
+ }
+ }
+
+ /* we have to flush all pending changes so that the copy
+ is generated at the appropriate moment in time */
+ if (notify)
+ vga_hw_update();
+
+ (*s->cirrus_rop) (s, s->vga.vram_ptr +
+ (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
+ s->vga.vram_ptr +
+ (s->cirrus_blt_srcaddr & s->cirrus_addr_mask),
+ s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+
+ if (notify) {
+ qemu_console_copy(s->vga.con,
+ sx, sy, dx, dy,
+ s->cirrus_blt_width / depth,
+ s->cirrus_blt_height);
+ }
+
+ /* we don't have to notify the display that this portion has
+ changed since qemu_console_copy implies this */
+
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+ s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+ s->cirrus_blt_height);
+}
+
+static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
+{
+ if (BLTUNSAFE(s))
+ return 0;
+
+ cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->vga.start_addr,
+ s->cirrus_blt_srcaddr - s->vga.start_addr,
+ s->cirrus_blt_width, s->cirrus_blt_height);
+
+ return 1;
+}
+
+/***************************************
+ *
+ * bitblt (cpu-to-video)
+ *
+ ***************************************/
+
+static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s)
+{
+ int copy_count;
+ uint8_t *end_ptr;
+
+ if (s->cirrus_srccounter > 0) {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+ cirrus_bitblt_common_patterncopy(s, s->cirrus_bltbuf);
+ the_end:
+ s->cirrus_srccounter = 0;
+ cirrus_bitblt_reset(s);
+ } else {
+ /* at least one scan line */
+ do {
+ (*s->cirrus_rop)(s, s->vga.vram_ptr +
+ (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
+ s->cirrus_bltbuf, 0, 0, s->cirrus_blt_width, 1);
+ cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, 0,
+ s->cirrus_blt_width, 1);
+ s->cirrus_blt_dstaddr += s->cirrus_blt_dstpitch;
+ s->cirrus_srccounter -= s->cirrus_blt_srcpitch;
+ if (s->cirrus_srccounter <= 0)
+ goto the_end;
+ /* more bytes than needed can be transferred because of
+ word alignment, so we keep them for the next line */
+ /* XXX: keep alignment to speed up transfer */
+ end_ptr = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+ copy_count = s->cirrus_srcptr_end - end_ptr;
+ memmove(s->cirrus_bltbuf, end_ptr, copy_count);
+ s->cirrus_srcptr = s->cirrus_bltbuf + copy_count;
+ s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+ } while (s->cirrus_srcptr >= s->cirrus_srcptr_end);
+ }
+ }
+}
+
+/***************************************
+ *
+ * bitblt wrapper
+ *
+ ***************************************/
+
+static void cirrus_bitblt_reset(CirrusVGAState * s)
+{
+ int need_update;
+
+ s->vga.gr[0x31] &=
+ ~(CIRRUS_BLT_START | CIRRUS_BLT_BUSY | CIRRUS_BLT_FIFOUSED);
+ need_update = s->cirrus_srcptr != &s->cirrus_bltbuf[0]
+ || s->cirrus_srcptr_end != &s->cirrus_bltbuf[0];
+ s->cirrus_srcptr = &s->cirrus_bltbuf[0];
+ s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
+ s->cirrus_srccounter = 0;
+ if (!need_update)
+ return;
+ cirrus_update_memory_access(s);
+}
+
+static int cirrus_bitblt_cputovideo(CirrusVGAState * s)
+{
+ int w;
+
+ s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_MEMSYSSRC;
+ s->cirrus_srcptr = &s->cirrus_bltbuf[0];
+ s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
+
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+ s->cirrus_blt_srcpitch = 8;
+ } else {
+ /* XXX: check for 24 bpp */
+ s->cirrus_blt_srcpitch = 8 * 8 * s->cirrus_blt_pixelwidth;
+ }
+ s->cirrus_srccounter = s->cirrus_blt_srcpitch;
+ } else {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+ w = s->cirrus_blt_width / s->cirrus_blt_pixelwidth;
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY)
+ s->cirrus_blt_srcpitch = ((w + 31) >> 5);
+ else
+ s->cirrus_blt_srcpitch = ((w + 7) >> 3);
+ } else {
+ /* always align input size to 32 bits */
+ s->cirrus_blt_srcpitch = (s->cirrus_blt_width + 3) & ~3;
+ }
+ s->cirrus_srccounter = s->cirrus_blt_srcpitch * s->cirrus_blt_height;
+ }
+ s->cirrus_srcptr = s->cirrus_bltbuf;
+ s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+ cirrus_update_memory_access(s);
+ return 1;
+}
+
+static int cirrus_bitblt_videotocpu(CirrusVGAState * s)
+{
+ /* XXX */
+#ifdef DEBUG_BITBLT
+ printf("cirrus: bitblt (video to cpu) is not implemented yet\n");
+#endif
+ return 0;
+}
+
+static int cirrus_bitblt_videotovideo(CirrusVGAState * s)
+{
+ int ret;
+
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+ ret = cirrus_bitblt_videotovideo_patterncopy(s);
+ } else {
+ ret = cirrus_bitblt_videotovideo_copy(s);
+ }
+ if (ret)
+ cirrus_bitblt_reset(s);
+ return ret;
+}
+
+static void cirrus_bitblt_start(CirrusVGAState * s)
+{
+ uint8_t blt_rop;
+
+ s->vga.gr[0x31] |= CIRRUS_BLT_BUSY;
+
+ s->cirrus_blt_width = (s->vga.gr[0x20] | (s->vga.gr[0x21] << 8)) + 1;
+ s->cirrus_blt_height = (s->vga.gr[0x22] | (s->vga.gr[0x23] << 8)) + 1;
+ s->cirrus_blt_dstpitch = (s->vga.gr[0x24] | (s->vga.gr[0x25] << 8));
+ s->cirrus_blt_srcpitch = (s->vga.gr[0x26] | (s->vga.gr[0x27] << 8));
+ s->cirrus_blt_dstaddr =
+ (s->vga.gr[0x28] | (s->vga.gr[0x29] << 8) | (s->vga.gr[0x2a] << 16));
+ s->cirrus_blt_srcaddr =
+ (s->vga.gr[0x2c] | (s->vga.gr[0x2d] << 8) | (s->vga.gr[0x2e] << 16));
+ s->cirrus_blt_mode = s->vga.gr[0x30];
+ s->cirrus_blt_modeext = s->vga.gr[0x33];
+ blt_rop = s->vga.gr[0x32];
+
+#ifdef DEBUG_BITBLT
+ printf("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spitch=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n",
+ blt_rop,
+ s->cirrus_blt_mode,
+ s->cirrus_blt_modeext,
+ s->cirrus_blt_width,
+ s->cirrus_blt_height,
+ s->cirrus_blt_dstpitch,
+ s->cirrus_blt_srcpitch,
+ s->cirrus_blt_dstaddr,
+ s->cirrus_blt_srcaddr,
+ s->vga.gr[0x2f]);
+#endif
+
+ switch (s->cirrus_blt_mode & CIRRUS_BLTMODE_PIXELWIDTHMASK) {
+ case CIRRUS_BLTMODE_PIXELWIDTH8:
+ s->cirrus_blt_pixelwidth = 1;
+ break;
+ case CIRRUS_BLTMODE_PIXELWIDTH16:
+ s->cirrus_blt_pixelwidth = 2;
+ break;
+ case CIRRUS_BLTMODE_PIXELWIDTH24:
+ s->cirrus_blt_pixelwidth = 3;
+ break;
+ case CIRRUS_BLTMODE_PIXELWIDTH32:
+ s->cirrus_blt_pixelwidth = 4;
+ break;
+ default:
+#ifdef DEBUG_BITBLT
+ printf("cirrus: bitblt - pixel width is unknown\n");
+#endif
+ goto bitblt_ignore;
+ }
+ s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_PIXELWIDTHMASK;
+
+ if ((s->
+ cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSSRC |
+ CIRRUS_BLTMODE_MEMSYSDEST))
+ == (CIRRUS_BLTMODE_MEMSYSSRC | CIRRUS_BLTMODE_MEMSYSDEST)) {
+#ifdef DEBUG_BITBLT
+ printf("cirrus: bitblt - memory-to-memory copy is requested\n");
+#endif
+ goto bitblt_ignore;
+ }
+
+ if ((s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_SOLIDFILL) &&
+ (s->cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSDEST |
+ CIRRUS_BLTMODE_TRANSPARENTCOMP |
+ CIRRUS_BLTMODE_PATTERNCOPY |
+ CIRRUS_BLTMODE_COLOREXPAND)) ==
+ (CIRRUS_BLTMODE_PATTERNCOPY | CIRRUS_BLTMODE_COLOREXPAND)) {
+ cirrus_bitblt_fgcol(s);
+ cirrus_bitblt_solidfill(s, blt_rop);
+ } else {
+ if ((s->cirrus_blt_mode & (CIRRUS_BLTMODE_COLOREXPAND |
+ CIRRUS_BLTMODE_PATTERNCOPY)) ==
+ CIRRUS_BLTMODE_COLOREXPAND) {
+
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV)
+ cirrus_bitblt_bgcol(s);
+ else
+ cirrus_bitblt_fgcol(s);
+ s->cirrus_rop = cirrus_colorexpand_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ } else {
+ cirrus_bitblt_fgcol(s);
+ cirrus_bitblt_bgcol(s);
+ s->cirrus_rop = cirrus_colorexpand[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ }
+ } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV)
+ cirrus_bitblt_bgcol(s);
+ else
+ cirrus_bitblt_fgcol(s);
+ s->cirrus_rop = cirrus_colorexpand_pattern_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ } else {
+ cirrus_bitblt_fgcol(s);
+ cirrus_bitblt_bgcol(s);
+ s->cirrus_rop = cirrus_colorexpand_pattern[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ }
+ } else {
+ s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ }
+ } else {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+ if (s->cirrus_blt_pixelwidth > 2) {
+ printf("src transparent without colorexpand must be 8bpp or 16bpp\n");
+ goto bitblt_ignore;
+ }
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) {
+ s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch;
+ s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch;
+ s->cirrus_rop = cirrus_bkwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ } else {
+ s->cirrus_rop = cirrus_fwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+ }
+ } else {
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) {
+ s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch;
+ s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch;
+ s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]];
+ } else {
+ s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]];
+ }
+ }
+ }
+ // setup bitblt engine.
+ if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSSRC) {
+ if (!cirrus_bitblt_cputovideo(s))
+ goto bitblt_ignore;
+ } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSDEST) {
+ if (!cirrus_bitblt_videotocpu(s))
+ goto bitblt_ignore;
+ } else {
+ if (!cirrus_bitblt_videotovideo(s))
+ goto bitblt_ignore;
+ }
+ }
+ return;
+ bitblt_ignore:;
+ cirrus_bitblt_reset(s);
+}
+
+static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value)
+{
+ unsigned old_value;
+
+ old_value = s->vga.gr[0x31];
+ s->vga.gr[0x31] = reg_value;
+
+ if (((old_value & CIRRUS_BLT_RESET) != 0) &&
+ ((reg_value & CIRRUS_BLT_RESET) == 0)) {
+ cirrus_bitblt_reset(s);
+ } else if (((old_value & CIRRUS_BLT_START) == 0) &&
+ ((reg_value & CIRRUS_BLT_START) != 0)) {
+ cirrus_bitblt_start(s);
+ }
+}
+
+
+/***************************************
+ *
+ * basic parameters
+ *
+ ***************************************/
+
+static void cirrus_get_offsets(VGACommonState *s1,
+ uint32_t *pline_offset,
+ uint32_t *pstart_addr,
+ uint32_t *pline_compare)
+{
+ CirrusVGAState * s = container_of(s1, CirrusVGAState, vga);
+ uint32_t start_addr, line_offset, line_compare;
+
+ line_offset = s->vga.cr[0x13]
+ | ((s->vga.cr[0x1b] & 0x10) << 4);
+ line_offset <<= 3;
+ *pline_offset = line_offset;
+
+ start_addr = (s->vga.cr[0x0c] << 8)
+ | s->vga.cr[0x0d]
+ | ((s->vga.cr[0x1b] & 0x01) << 16)
+ | ((s->vga.cr[0x1b] & 0x0c) << 15)
+ | ((s->vga.cr[0x1d] & 0x80) << 12);
+ *pstart_addr = start_addr;
+
+ line_compare = s->vga.cr[0x18] |
+ ((s->vga.cr[0x07] & 0x10) << 4) |
+ ((s->vga.cr[0x09] & 0x40) << 3);
+ *pline_compare = line_compare;
+}
+
+static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s)
+{
+ uint32_t ret = 16;
+
+ switch (s->cirrus_hidden_dac_data & 0xf) {
+ case 0:
+ ret = 15;
+ break; /* Sierra HiColor */
+ case 1:
+ ret = 16;
+ break; /* XGA HiColor */
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: invalid DAC value %x in 16bpp\n",
+ (s->cirrus_hidden_dac_data & 0xf));
+#endif
+ ret = 15; /* XXX */
+ break;
+ }
+ return ret;
+}
+
+static int cirrus_get_bpp(VGACommonState *s1)
+{
+ CirrusVGAState * s = container_of(s1, CirrusVGAState, vga);
+ uint32_t ret = 8;
+
+ if ((s->vga.sr[0x07] & 0x01) != 0) {
+ /* Cirrus SVGA */
+ switch (s->vga.sr[0x07] & CIRRUS_SR7_BPP_MASK) {
+ case CIRRUS_SR7_BPP_8:
+ ret = 8;
+ break;
+ case CIRRUS_SR7_BPP_16_DOUBLEVCLK:
+ ret = cirrus_get_bpp16_depth(s);
+ break;
+ case CIRRUS_SR7_BPP_24:
+ ret = 24;
+ break;
+ case CIRRUS_SR7_BPP_16:
+ ret = cirrus_get_bpp16_depth(s);
+ break;
+ case CIRRUS_SR7_BPP_32:
+ ret = 32;
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: unknown bpp - sr7=%x\n", s->vga.sr[0x7]);
+#endif
+ ret = 8;
+ break;
+ }
+ } else {
+ /* VGA */
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void cirrus_get_resolution(VGACommonState *s, int *pwidth, int *pheight)
+{
+ int width, height;
+
+ width = (s->cr[0x01] + 1) * 8;
+ height = s->cr[0x12] |
+ ((s->cr[0x07] & 0x02) << 7) |
+ ((s->cr[0x07] & 0x40) << 3);
+ height = (height + 1);
+ /* interlace support */
+ if (s->cr[0x1a] & 0x01)
+ height = height * 2;
+ *pwidth = width;
+ *pheight = height;
+}
+
+/***************************************
+ *
+ * bank memory
+ *
+ ***************************************/
+
+static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index)
+{
+ unsigned offset;
+ unsigned limit;
+
+ if ((s->vga.gr[0x0b] & 0x01) != 0) /* dual bank */
+ offset = s->vga.gr[0x09 + bank_index];
+ else /* single bank */
+ offset = s->vga.gr[0x09];
+
+ if ((s->vga.gr[0x0b] & 0x20) != 0)
+ offset <<= 14;
+ else
+ offset <<= 12;
+
+ if (s->real_vram_size <= offset)
+ limit = 0;
+ else
+ limit = s->real_vram_size - offset;
+
+ if (((s->vga.gr[0x0b] & 0x01) == 0) && (bank_index != 0)) {
+ if (limit > 0x8000) {
+ offset += 0x8000;
+ limit -= 0x8000;
+ } else {
+ limit = 0;
+ }
+ }
+
+ if (limit > 0) {
+ s->cirrus_bank_base[bank_index] = offset;
+ s->cirrus_bank_limit[bank_index] = limit;
+ } else {
+ s->cirrus_bank_base[bank_index] = 0;
+ s->cirrus_bank_limit[bank_index] = 0;
+ }
+}
+
+/***************************************
+ *
+ * I/O access between 0x3c4-0x3c5
+ *
+ ***************************************/
+
+static int cirrus_vga_read_sr(CirrusVGAState * s)
+{
+ switch (s->vga.sr_index) {
+ case 0x00: // Standard VGA
+ case 0x01: // Standard VGA
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ return s->vga.sr[s->vga.sr_index];
+ case 0x06: // Unlock Cirrus extensions
+ return s->vga.sr[s->vga.sr_index];
+ case 0x10:
+ case 0x30:
+ case 0x50:
+ case 0x70: // Graphics Cursor X
+ case 0x90:
+ case 0xb0:
+ case 0xd0:
+ case 0xf0: // Graphics Cursor X
+ return s->vga.sr[0x10];
+ case 0x11:
+ case 0x31:
+ case 0x51:
+ case 0x71: // Graphics Cursor Y
+ case 0x91:
+ case 0xb1:
+ case 0xd1:
+ case 0xf1: // Graphics Cursor Y
+ return s->vga.sr[0x11];
+ case 0x05: // ???
+ case 0x07: // Extended Sequencer Mode
+ case 0x08: // EEPROM Control
+ case 0x09: // Scratch Register 0
+ case 0x0a: // Scratch Register 1
+ case 0x0b: // VCLK 0
+ case 0x0c: // VCLK 1
+ case 0x0d: // VCLK 2
+ case 0x0e: // VCLK 3
+ case 0x0f: // DRAM Control
+ case 0x12: // Graphics Cursor Attribute
+ case 0x13: // Graphics Cursor Pattern Address
+ case 0x14: // Scratch Register 2
+ case 0x15: // Scratch Register 3
+ case 0x16: // Performance Tuning Register
+ case 0x17: // Configuration Readback and Extended Control
+ case 0x18: // Signature Generator Control
+ case 0x19: // Signal Generator Result
+ case 0x1a: // Signal Generator Result
+ case 0x1b: // VCLK 0 Denominator & Post
+ case 0x1c: // VCLK 1 Denominator & Post
+ case 0x1d: // VCLK 2 Denominator & Post
+ case 0x1e: // VCLK 3 Denominator & Post
+ case 0x1f: // BIOS Write Enable and MCLK select
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: handled inport sr_index %02x\n", s->vga.sr_index);
+#endif
+ return s->vga.sr[s->vga.sr_index];
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: inport sr_index %02x\n", s->vga.sr_index);
+#endif
+ return 0xff;
+ break;
+ }
+}
+
+static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val)
+{
+ switch (s->vga.sr_index) {
+ case 0x00: // Standard VGA
+ case 0x01: // Standard VGA
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ s->vga.sr[s->vga.sr_index] = val & sr_mask[s->vga.sr_index];
+ if (s->vga.sr_index == 1)
+ s->vga.update_retrace_info(&s->vga);
+ break;
+ case 0x06: // Unlock Cirrus extensions
+ val &= 0x17;
+ if (val == 0x12) {
+ s->vga.sr[s->vga.sr_index] = 0x12;
+ } else {
+ s->vga.sr[s->vga.sr_index] = 0x0f;
+ }
+ break;
+ case 0x10:
+ case 0x30:
+ case 0x50:
+ case 0x70: // Graphics Cursor X
+ case 0x90:
+ case 0xb0:
+ case 0xd0:
+ case 0xf0: // Graphics Cursor X
+ s->vga.sr[0x10] = val;
+ s->hw_cursor_x = (val << 3) | (s->vga.sr_index >> 5);
+ break;
+ case 0x11:
+ case 0x31:
+ case 0x51:
+ case 0x71: // Graphics Cursor Y
+ case 0x91:
+ case 0xb1:
+ case 0xd1:
+ case 0xf1: // Graphics Cursor Y
+ s->vga.sr[0x11] = val;
+ s->hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5);
+ break;
+ case 0x07: // Extended Sequencer Mode
+ cirrus_update_memory_access(s);
+ case 0x08: // EEPROM Control
+ case 0x09: // Scratch Register 0
+ case 0x0a: // Scratch Register 1
+ case 0x0b: // VCLK 0
+ case 0x0c: // VCLK 1
+ case 0x0d: // VCLK 2
+ case 0x0e: // VCLK 3
+ case 0x0f: // DRAM Control
+ case 0x12: // Graphics Cursor Attribute
+ case 0x13: // Graphics Cursor Pattern Address
+ case 0x14: // Scratch Register 2
+ case 0x15: // Scratch Register 3
+ case 0x16: // Performance Tuning Register
+ case 0x18: // Signature Generator Control
+ case 0x19: // Signature Generator Result
+ case 0x1a: // Signature Generator Result
+ case 0x1b: // VCLK 0 Denominator & Post
+ case 0x1c: // VCLK 1 Denominator & Post
+ case 0x1d: // VCLK 2 Denominator & Post
+ case 0x1e: // VCLK 3 Denominator & Post
+ case 0x1f: // BIOS Write Enable and MCLK select
+ s->vga.sr[s->vga.sr_index] = val;
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: handled outport sr_index %02x, sr_value %02x\n",
+ s->vga.sr_index, val);
+#endif
+ break;
+ case 0x17: // Configuration Readback and Extended Control
+ s->vga.sr[s->vga.sr_index] = (s->vga.sr[s->vga.sr_index] & 0x38)
+ | (val & 0xc7);
+ cirrus_update_memory_access(s);
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: outport sr_index %02x, sr_value %02x\n",
+ s->vga.sr_index, val);
+#endif
+ break;
+ }
+}
+
+/***************************************
+ *
+ * I/O access at 0x3c6
+ *
+ ***************************************/
+
+static int cirrus_read_hidden_dac(CirrusVGAState * s)
+{
+ if (++s->cirrus_hidden_dac_lockindex == 5) {
+ s->cirrus_hidden_dac_lockindex = 0;
+ return s->cirrus_hidden_dac_data;
+ }
+ return 0xff;
+}
+
+static void cirrus_write_hidden_dac(CirrusVGAState * s, int reg_value)
+{
+ if (s->cirrus_hidden_dac_lockindex == 4) {
+ s->cirrus_hidden_dac_data = reg_value;
+#if defined(DEBUG_CIRRUS)
+ printf("cirrus: outport hidden DAC, value %02x\n", reg_value);
+#endif
+ }
+ s->cirrus_hidden_dac_lockindex = 0;
+}
+
+/***************************************
+ *
+ * I/O access at 0x3c9
+ *
+ ***************************************/
+
+static int cirrus_vga_read_palette(CirrusVGAState * s)
+{
+ int val;
+
+ if ((s->vga.sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) {
+ val = s->cirrus_hidden_palette[(s->vga.dac_read_index & 0x0f) * 3 +
+ s->vga.dac_sub_index];
+ } else {
+ val = s->vga.palette[s->vga.dac_read_index * 3 + s->vga.dac_sub_index];
+ }
+ if (++s->vga.dac_sub_index == 3) {
+ s->vga.dac_sub_index = 0;
+ s->vga.dac_read_index++;
+ }
+ return val;
+}
+
+static void cirrus_vga_write_palette(CirrusVGAState * s, int reg_value)
+{
+ s->vga.dac_cache[s->vga.dac_sub_index] = reg_value;
+ if (++s->vga.dac_sub_index == 3) {
+ if ((s->vga.sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) {
+ memcpy(&s->cirrus_hidden_palette[(s->vga.dac_write_index & 0x0f) * 3],
+ s->vga.dac_cache, 3);
+ } else {
+ memcpy(&s->vga.palette[s->vga.dac_write_index * 3], s->vga.dac_cache, 3);
+ }
+ /* XXX update cursor */
+ s->vga.dac_sub_index = 0;
+ s->vga.dac_write_index++;
+ }
+}
+
+/***************************************
+ *
+ * I/O access between 0x3ce-0x3cf
+ *
+ ***************************************/
+
+static int cirrus_vga_read_gr(CirrusVGAState * s, unsigned reg_index)
+{
+ switch (reg_index) {
+ case 0x00: // Standard VGA, BGCOLOR 0x000000ff
+ return s->cirrus_shadow_gr0;
+ case 0x01: // Standard VGA, FGCOLOR 0x000000ff
+ return s->cirrus_shadow_gr1;
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ case 0x06: // Standard VGA
+ case 0x07: // Standard VGA
+ case 0x08: // Standard VGA
+ return s->vga.gr[s->vga.gr_index];
+ case 0x05: // Standard VGA, Cirrus extended mode
+ default:
+ break;
+ }
+
+ if (reg_index < 0x3a) {
+ return s->vga.gr[reg_index];
+ } else {
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: inport gr_index %02x\n", reg_index);
+#endif
+ return 0xff;
+ }
+}
+
+static void
+cirrus_vga_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value)
+{
+#if defined(DEBUG_BITBLT) && 0
+ printf("gr%02x: %02x\n", reg_index, reg_value);
+#endif
+ switch (reg_index) {
+ case 0x00: // Standard VGA, BGCOLOR 0x000000ff
+ s->vga.gr[reg_index] = reg_value & gr_mask[reg_index];
+ s->cirrus_shadow_gr0 = reg_value;
+ break;
+ case 0x01: // Standard VGA, FGCOLOR 0x000000ff
+ s->vga.gr[reg_index] = reg_value & gr_mask[reg_index];
+ s->cirrus_shadow_gr1 = reg_value;
+ break;
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ case 0x06: // Standard VGA
+ case 0x07: // Standard VGA
+ case 0x08: // Standard VGA
+ s->vga.gr[reg_index] = reg_value & gr_mask[reg_index];
+ break;
+ case 0x05: // Standard VGA, Cirrus extended mode
+ s->vga.gr[reg_index] = reg_value & 0x7f;
+ cirrus_update_memory_access(s);
+ break;
+ case 0x09: // bank offset #0
+ case 0x0A: // bank offset #1
+ s->vga.gr[reg_index] = reg_value;
+ cirrus_update_bank_ptr(s, 0);
+ cirrus_update_bank_ptr(s, 1);
+ cirrus_update_memory_access(s);
+ break;
+ case 0x0B:
+ s->vga.gr[reg_index] = reg_value;
+ cirrus_update_bank_ptr(s, 0);
+ cirrus_update_bank_ptr(s, 1);
+ cirrus_update_memory_access(s);
+ break;
+ case 0x10: // BGCOLOR 0x0000ff00
+ case 0x11: // FGCOLOR 0x0000ff00
+ case 0x12: // BGCOLOR 0x00ff0000
+ case 0x13: // FGCOLOR 0x00ff0000
+ case 0x14: // BGCOLOR 0xff000000
+ case 0x15: // FGCOLOR 0xff000000
+ case 0x20: // BLT WIDTH 0x0000ff
+ case 0x22: // BLT HEIGHT 0x0000ff
+ case 0x24: // BLT DEST PITCH 0x0000ff
+ case 0x26: // BLT SRC PITCH 0x0000ff
+ case 0x28: // BLT DEST ADDR 0x0000ff
+ case 0x29: // BLT DEST ADDR 0x00ff00
+ case 0x2c: // BLT SRC ADDR 0x0000ff
+ case 0x2d: // BLT SRC ADDR 0x00ff00
+ case 0x2f: // BLT WRITEMASK
+ case 0x30: // BLT MODE
+ case 0x32: // RASTER OP
+ case 0x33: // BLT MODEEXT
+ case 0x34: // BLT TRANSPARENT COLOR 0x00ff
+ case 0x35: // BLT TRANSPARENT COLOR 0xff00
+ case 0x38: // BLT TRANSPARENT COLOR MASK 0x00ff
+ case 0x39: // BLT TRANSPARENT COLOR MASK 0xff00
+ s->vga.gr[reg_index] = reg_value;
+ break;
+ case 0x21: // BLT WIDTH 0x001f00
+ case 0x23: // BLT HEIGHT 0x001f00
+ case 0x25: // BLT DEST PITCH 0x001f00
+ case 0x27: // BLT SRC PITCH 0x001f00
+ s->vga.gr[reg_index] = reg_value & 0x1f;
+ break;
+ case 0x2a: // BLT DEST ADDR 0x3f0000
+ s->vga.gr[reg_index] = reg_value & 0x3f;
+ /* if auto start mode, starts bit blt now */
+ if (s->vga.gr[0x31] & CIRRUS_BLT_AUTOSTART) {
+ cirrus_bitblt_start(s);
+ }
+ break;
+ case 0x2e: // BLT SRC ADDR 0x3f0000
+ s->vga.gr[reg_index] = reg_value & 0x3f;
+ break;
+ case 0x31: // BLT STATUS/START
+ cirrus_write_bitblt(s, reg_value);
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: outport gr_index %02x, gr_value %02x\n", reg_index,
+ reg_value);
+#endif
+ break;
+ }
+}
+
+/***************************************
+ *
+ * I/O access between 0x3d4-0x3d5
+ *
+ ***************************************/
+
+static int cirrus_vga_read_cr(CirrusVGAState * s, unsigned reg_index)
+{
+ switch (reg_index) {
+ case 0x00: // Standard VGA
+ case 0x01: // Standard VGA
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ case 0x05: // Standard VGA
+ case 0x06: // Standard VGA
+ case 0x07: // Standard VGA
+ case 0x08: // Standard VGA
+ case 0x09: // Standard VGA
+ case 0x0a: // Standard VGA
+ case 0x0b: // Standard VGA
+ case 0x0c: // Standard VGA
+ case 0x0d: // Standard VGA
+ case 0x0e: // Standard VGA
+ case 0x0f: // Standard VGA
+ case 0x10: // Standard VGA
+ case 0x11: // Standard VGA
+ case 0x12: // Standard VGA
+ case 0x13: // Standard VGA
+ case 0x14: // Standard VGA
+ case 0x15: // Standard VGA
+ case 0x16: // Standard VGA
+ case 0x17: // Standard VGA
+ case 0x18: // Standard VGA
+ return s->vga.cr[s->vga.cr_index];
+ case 0x24: // Attribute Controller Toggle Readback (R)
+ return (s->vga.ar_flip_flop << 7);
+ case 0x19: // Interlace End
+ case 0x1a: // Miscellaneous Control
+ case 0x1b: // Extended Display Control
+ case 0x1c: // Sync Adjust and Genlock
+ case 0x1d: // Overlay Extended Control
+ case 0x22: // Graphics Data Latches Readback (R)
+ case 0x25: // Part Status
+ case 0x27: // Part ID (R)
+ return s->vga.cr[s->vga.cr_index];
+ case 0x26: // Attribute Controller Index Readback (R)
+ return s->vga.ar_index & 0x3f;
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: inport cr_index %02x\n", reg_index);
+#endif
+ return 0xff;
+ }
+}
+
+static void cirrus_vga_write_cr(CirrusVGAState * s, int reg_value)
+{
+ switch (s->vga.cr_index) {
+ case 0x00: // Standard VGA
+ case 0x01: // Standard VGA
+ case 0x02: // Standard VGA
+ case 0x03: // Standard VGA
+ case 0x04: // Standard VGA
+ case 0x05: // Standard VGA
+ case 0x06: // Standard VGA
+ case 0x07: // Standard VGA
+ case 0x08: // Standard VGA
+ case 0x09: // Standard VGA
+ case 0x0a: // Standard VGA
+ case 0x0b: // Standard VGA
+ case 0x0c: // Standard VGA
+ case 0x0d: // Standard VGA
+ case 0x0e: // Standard VGA
+ case 0x0f: // Standard VGA
+ case 0x10: // Standard VGA
+ case 0x11: // Standard VGA
+ case 0x12: // Standard VGA
+ case 0x13: // Standard VGA
+ case 0x14: // Standard VGA
+ case 0x15: // Standard VGA
+ case 0x16: // Standard VGA
+ case 0x17: // Standard VGA
+ case 0x18: // Standard VGA
+ /* handle CR0-7 protection */
+ if ((s->vga.cr[0x11] & 0x80) && s->vga.cr_index <= 7) {
+ /* can always write bit 4 of CR7 */
+ if (s->vga.cr_index == 7)
+ s->vga.cr[7] = (s->vga.cr[7] & ~0x10) | (reg_value & 0x10);
+ return;
+ }
+ s->vga.cr[s->vga.cr_index] = reg_value;
+ switch(s->vga.cr_index) {
+ case 0x00:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x11:
+ case 0x17:
+ s->vga.update_retrace_info(&s->vga);
+ break;
+ }
+ break;
+ case 0x19: // Interlace End
+ case 0x1a: // Miscellaneous Control
+ case 0x1b: // Extended Display Control
+ case 0x1c: // Sync Adjust and Genlock
+ case 0x1d: // Overlay Extended Control
+ s->vga.cr[s->vga.cr_index] = reg_value;
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: handled outport cr_index %02x, cr_value %02x\n",
+ s->vga.cr_index, reg_value);
+#endif
+ break;
+ case 0x22: // Graphics Data Latches Readback (R)
+ case 0x24: // Attribute Controller Toggle Readback (R)
+ case 0x26: // Attribute Controller Index Readback (R)
+ case 0x27: // Part ID (R)
+ break;
+ case 0x25: // Part Status
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: outport cr_index %02x, cr_value %02x\n",
+ s->vga.cr_index, reg_value);
+#endif
+ break;
+ }
+}
+
+/***************************************
+ *
+ * memory-mapped I/O (bitblt)
+ *
+ ***************************************/
+
+static uint8_t cirrus_mmio_blt_read(CirrusVGAState * s, unsigned address)
+{
+ int value = 0xff;
+
+ switch (address) {
+ case (CIRRUS_MMIO_BLTBGCOLOR + 0):
+ value = cirrus_vga_read_gr(s, 0x00);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 1):
+ value = cirrus_vga_read_gr(s, 0x10);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 2):
+ value = cirrus_vga_read_gr(s, 0x12);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 3):
+ value = cirrus_vga_read_gr(s, 0x14);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 0):
+ value = cirrus_vga_read_gr(s, 0x01);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 1):
+ value = cirrus_vga_read_gr(s, 0x11);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 2):
+ value = cirrus_vga_read_gr(s, 0x13);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 3):
+ value = cirrus_vga_read_gr(s, 0x15);
+ break;
+ case (CIRRUS_MMIO_BLTWIDTH + 0):
+ value = cirrus_vga_read_gr(s, 0x20);
+ break;
+ case (CIRRUS_MMIO_BLTWIDTH + 1):
+ value = cirrus_vga_read_gr(s, 0x21);
+ break;
+ case (CIRRUS_MMIO_BLTHEIGHT + 0):
+ value = cirrus_vga_read_gr(s, 0x22);
+ break;
+ case (CIRRUS_MMIO_BLTHEIGHT + 1):
+ value = cirrus_vga_read_gr(s, 0x23);
+ break;
+ case (CIRRUS_MMIO_BLTDESTPITCH + 0):
+ value = cirrus_vga_read_gr(s, 0x24);
+ break;
+ case (CIRRUS_MMIO_BLTDESTPITCH + 1):
+ value = cirrus_vga_read_gr(s, 0x25);
+ break;
+ case (CIRRUS_MMIO_BLTSRCPITCH + 0):
+ value = cirrus_vga_read_gr(s, 0x26);
+ break;
+ case (CIRRUS_MMIO_BLTSRCPITCH + 1):
+ value = cirrus_vga_read_gr(s, 0x27);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 0):
+ value = cirrus_vga_read_gr(s, 0x28);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 1):
+ value = cirrus_vga_read_gr(s, 0x29);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 2):
+ value = cirrus_vga_read_gr(s, 0x2a);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 0):
+ value = cirrus_vga_read_gr(s, 0x2c);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 1):
+ value = cirrus_vga_read_gr(s, 0x2d);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 2):
+ value = cirrus_vga_read_gr(s, 0x2e);
+ break;
+ case CIRRUS_MMIO_BLTWRITEMASK:
+ value = cirrus_vga_read_gr(s, 0x2f);
+ break;
+ case CIRRUS_MMIO_BLTMODE:
+ value = cirrus_vga_read_gr(s, 0x30);
+ break;
+ case CIRRUS_MMIO_BLTROP:
+ value = cirrus_vga_read_gr(s, 0x32);
+ break;
+ case CIRRUS_MMIO_BLTMODEEXT:
+ value = cirrus_vga_read_gr(s, 0x33);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0):
+ value = cirrus_vga_read_gr(s, 0x34);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1):
+ value = cirrus_vga_read_gr(s, 0x35);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0):
+ value = cirrus_vga_read_gr(s, 0x38);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1):
+ value = cirrus_vga_read_gr(s, 0x39);
+ break;
+ case CIRRUS_MMIO_BLTSTATUS:
+ value = cirrus_vga_read_gr(s, 0x31);
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: mmio read - address 0x%04x\n", address);
+#endif
+ break;
+ }
+
+ return (uint8_t) value;
+}
+
+static void cirrus_mmio_blt_write(CirrusVGAState * s, unsigned address,
+ uint8_t value)
+{
+ switch (address) {
+ case (CIRRUS_MMIO_BLTBGCOLOR + 0):
+ cirrus_vga_write_gr(s, 0x00, value);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 1):
+ cirrus_vga_write_gr(s, 0x10, value);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 2):
+ cirrus_vga_write_gr(s, 0x12, value);
+ break;
+ case (CIRRUS_MMIO_BLTBGCOLOR + 3):
+ cirrus_vga_write_gr(s, 0x14, value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 0):
+ cirrus_vga_write_gr(s, 0x01, value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 1):
+ cirrus_vga_write_gr(s, 0x11, value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 2):
+ cirrus_vga_write_gr(s, 0x13, value);
+ break;
+ case (CIRRUS_MMIO_BLTFGCOLOR + 3):
+ cirrus_vga_write_gr(s, 0x15, value);
+ break;
+ case (CIRRUS_MMIO_BLTWIDTH + 0):
+ cirrus_vga_write_gr(s, 0x20, value);
+ break;
+ case (CIRRUS_MMIO_BLTWIDTH + 1):
+ cirrus_vga_write_gr(s, 0x21, value);
+ break;
+ case (CIRRUS_MMIO_BLTHEIGHT + 0):
+ cirrus_vga_write_gr(s, 0x22, value);
+ break;
+ case (CIRRUS_MMIO_BLTHEIGHT + 1):
+ cirrus_vga_write_gr(s, 0x23, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTPITCH + 0):
+ cirrus_vga_write_gr(s, 0x24, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTPITCH + 1):
+ cirrus_vga_write_gr(s, 0x25, value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCPITCH + 0):
+ cirrus_vga_write_gr(s, 0x26, value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCPITCH + 1):
+ cirrus_vga_write_gr(s, 0x27, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 0):
+ cirrus_vga_write_gr(s, 0x28, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 1):
+ cirrus_vga_write_gr(s, 0x29, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 2):
+ cirrus_vga_write_gr(s, 0x2a, value);
+ break;
+ case (CIRRUS_MMIO_BLTDESTADDR + 3):
+ /* ignored */
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 0):
+ cirrus_vga_write_gr(s, 0x2c, value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 1):
+ cirrus_vga_write_gr(s, 0x2d, value);
+ break;
+ case (CIRRUS_MMIO_BLTSRCADDR + 2):
+ cirrus_vga_write_gr(s, 0x2e, value);
+ break;
+ case CIRRUS_MMIO_BLTWRITEMASK:
+ cirrus_vga_write_gr(s, 0x2f, value);
+ break;
+ case CIRRUS_MMIO_BLTMODE:
+ cirrus_vga_write_gr(s, 0x30, value);
+ break;
+ case CIRRUS_MMIO_BLTROP:
+ cirrus_vga_write_gr(s, 0x32, value);
+ break;
+ case CIRRUS_MMIO_BLTMODEEXT:
+ cirrus_vga_write_gr(s, 0x33, value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0):
+ cirrus_vga_write_gr(s, 0x34, value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1):
+ cirrus_vga_write_gr(s, 0x35, value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0):
+ cirrus_vga_write_gr(s, 0x38, value);
+ break;
+ case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1):
+ cirrus_vga_write_gr(s, 0x39, value);
+ break;
+ case CIRRUS_MMIO_BLTSTATUS:
+ cirrus_vga_write_gr(s, 0x31, value);
+ break;
+ default:
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: mmio write - addr 0x%04x val 0x%02x (ignored)\n",
+ address, value);
+#endif
+ break;
+ }
+}
+
+/***************************************
+ *
+ * write mode 4/5
+ *
+ ***************************************/
+
+static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s,
+ unsigned mode,
+ unsigned offset,
+ uint32_t mem_value)
+{
+ int x;
+ unsigned val = mem_value;
+ uint8_t *dst;
+
+ dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask);
+ for (x = 0; x < 8; x++) {
+ if (val & 0x80) {
+ *dst = s->cirrus_shadow_gr1;
+ } else if (mode == 5) {
+ *dst = s->cirrus_shadow_gr0;
+ }
+ val <<= 1;
+ dst++;
+ }
+ memory_region_set_dirty(&s->vga.vram, offset, 8);
+}
+
+static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s,
+ unsigned mode,
+ unsigned offset,
+ uint32_t mem_value)
+{
+ int x;
+ unsigned val = mem_value;
+ uint8_t *dst;
+
+ dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask);
+ for (x = 0; x < 8; x++) {
+ if (val & 0x80) {
+ *dst = s->cirrus_shadow_gr1;
+ *(dst + 1) = s->vga.gr[0x11];
+ } else if (mode == 5) {
+ *dst = s->cirrus_shadow_gr0;
+ *(dst + 1) = s->vga.gr[0x10];
+ }
+ val <<= 1;
+ dst += 2;
+ }
+ memory_region_set_dirty(&s->vga.vram, offset, 16);
+}
+
+/***************************************
+ *
+ * memory access between 0xa0000-0xbffff
+ *
+ ***************************************/
+
+static uint64_t cirrus_vga_mem_read(void *opaque,
+ hwaddr addr,
+ uint32_t size)
+{
+ CirrusVGAState *s = opaque;
+ unsigned bank_index;
+ unsigned bank_offset;
+ uint32_t val;
+
+ if ((s->vga.sr[0x07] & 0x01) == 0) {
+ return vga_mem_readb(&s->vga, addr);
+ }
+
+ if (addr < 0x10000) {
+ /* XXX handle bitblt */
+ /* video memory */
+ bank_index = addr >> 15;
+ bank_offset = addr & 0x7fff;
+ if (bank_offset < s->cirrus_bank_limit[bank_index]) {
+ bank_offset += s->cirrus_bank_base[bank_index];
+ if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+ bank_offset <<= 4;
+ } else if (s->vga.gr[0x0B] & 0x02) {
+ bank_offset <<= 3;
+ }
+ bank_offset &= s->cirrus_addr_mask;
+ val = *(s->vga.vram_ptr + bank_offset);
+ } else
+ val = 0xff;
+ } else if (addr >= 0x18000 && addr < 0x18100) {
+ /* memory-mapped I/O */
+ val = 0xff;
+ if ((s->vga.sr[0x17] & 0x44) == 0x04) {
+ val = cirrus_mmio_blt_read(s, addr & 0xff);
+ }
+ } else {
+ val = 0xff;
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: mem_readb " TARGET_FMT_plx "\n", addr);
+#endif
+ }
+ return val;
+}
+
+static void cirrus_vga_mem_write(void *opaque,
+ hwaddr addr,
+ uint64_t mem_value,
+ uint32_t size)
+{
+ CirrusVGAState *s = opaque;
+ unsigned bank_index;
+ unsigned bank_offset;
+ unsigned mode;
+
+ if ((s->vga.sr[0x07] & 0x01) == 0) {
+ vga_mem_writeb(&s->vga, addr, mem_value);
+ return;
+ }
+
+ if (addr < 0x10000) {
+ if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+ /* bitblt */
+ *s->cirrus_srcptr++ = (uint8_t) mem_value;
+ if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
+ cirrus_bitblt_cputovideo_next(s);
+ }
+ } else {
+ /* video memory */
+ bank_index = addr >> 15;
+ bank_offset = addr & 0x7fff;
+ if (bank_offset < s->cirrus_bank_limit[bank_index]) {
+ bank_offset += s->cirrus_bank_base[bank_index];
+ if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+ bank_offset <<= 4;
+ } else if (s->vga.gr[0x0B] & 0x02) {
+ bank_offset <<= 3;
+ }
+ bank_offset &= s->cirrus_addr_mask;
+ mode = s->vga.gr[0x05] & 0x7;
+ if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) {
+ *(s->vga.vram_ptr + bank_offset) = mem_value;
+ memory_region_set_dirty(&s->vga.vram, bank_offset,
+ sizeof(mem_value));
+ } else {
+ if ((s->vga.gr[0x0B] & 0x14) != 0x14) {
+ cirrus_mem_writeb_mode4and5_8bpp(s, mode,
+ bank_offset,
+ mem_value);
+ } else {
+ cirrus_mem_writeb_mode4and5_16bpp(s, mode,
+ bank_offset,
+ mem_value);
+ }
+ }
+ }
+ }
+ } else if (addr >= 0x18000 && addr < 0x18100) {
+ /* memory-mapped I/O */
+ if ((s->vga.sr[0x17] & 0x44) == 0x04) {
+ cirrus_mmio_blt_write(s, addr & 0xff, mem_value);
+ }
+ } else {
+#ifdef DEBUG_CIRRUS
+ printf("cirrus: mem_writeb " TARGET_FMT_plx " value %02x\n", addr,
+ mem_value);
+#endif
+ }
+}
+
+static const MemoryRegionOps cirrus_vga_mem_ops = {
+ .read = cirrus_vga_mem_read,
+ .write = cirrus_vga_mem_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+};
+
+/***************************************
+ *
+ * hardware cursor
+ *
+ ***************************************/
+
+static inline void invalidate_cursor1(CirrusVGAState *s)
+{
+ if (s->last_hw_cursor_size) {
+ vga_invalidate_scanlines(&s->vga,
+ s->last_hw_cursor_y + s->last_hw_cursor_y_start,
+ s->last_hw_cursor_y + s->last_hw_cursor_y_end);
+ }
+}
+
+static inline void cirrus_cursor_compute_yrange(CirrusVGAState *s)
+{
+ const uint8_t *src;
+ uint32_t content;
+ int y, y_min, y_max;
+
+ src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024;
+ if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
+ src += (s->vga.sr[0x13] & 0x3c) * 256;
+ y_min = 64;
+ y_max = -1;
+ for(y = 0; y < 64; y++) {
+ content = ((uint32_t *)src)[0] |
+ ((uint32_t *)src)[1] |
+ ((uint32_t *)src)[2] |
+ ((uint32_t *)src)[3];
+ if (content) {
+ if (y < y_min)
+ y_min = y;
+ if (y > y_max)
+ y_max = y;
+ }
+ src += 16;
+ }
+ } else {
+ src += (s->vga.sr[0x13] & 0x3f) * 256;
+ y_min = 32;
+ y_max = -1;
+ for(y = 0; y < 32; y++) {
+ content = ((uint32_t *)src)[0] |
+ ((uint32_t *)(src + 128))[0];
+ if (content) {
+ if (y < y_min)
+ y_min = y;
+ if (y > y_max)
+ y_max = y;
+ }
+ src += 4;
+ }
+ }
+ if (y_min > y_max) {
+ s->last_hw_cursor_y_start = 0;
+ s->last_hw_cursor_y_end = 0;
+ } else {
+ s->last_hw_cursor_y_start = y_min;
+ s->last_hw_cursor_y_end = y_max + 1;
+ }
+}
+
+/* NOTE: we do not currently handle the cursor bitmap change, so we
+ update the cursor only if it moves. */
+static void cirrus_cursor_invalidate(VGACommonState *s1)
+{
+ CirrusVGAState *s = container_of(s1, CirrusVGAState, vga);
+ int size;
+
+ if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW)) {
+ size = 0;
+ } else {
+ if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE)
+ size = 64;
+ else
+ size = 32;
+ }
+ /* invalidate last cursor and new cursor if any change */
+ if (s->last_hw_cursor_size != size ||
+ s->last_hw_cursor_x != s->hw_cursor_x ||
+ s->last_hw_cursor_y != s->hw_cursor_y) {
+
+ invalidate_cursor1(s);
+
+ s->last_hw_cursor_size = size;
+ s->last_hw_cursor_x = s->hw_cursor_x;
+ s->last_hw_cursor_y = s->hw_cursor_y;
+ /* compute the real cursor min and max y */
+ cirrus_cursor_compute_yrange(s);
+ invalidate_cursor1(s);
+ }
+}
+
+#define DEPTH 8
+#include "cirrus_vga_template.h"
+
+#define DEPTH 16
+#include "cirrus_vga_template.h"
+
+#define DEPTH 32
+#include "cirrus_vga_template.h"
+
+static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y)
+{
+ CirrusVGAState *s = container_of(s1, CirrusVGAState, vga);
+ DisplaySurface *surface = qemu_console_surface(s->vga.con);
+ int w, h, bpp, x1, x2, poffset;
+ unsigned int color0, color1;
+ const uint8_t *palette, *src;
+ uint32_t content;
+
+ if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW))
+ return;
+ /* fast test to see if the cursor intersects with the scan line */
+ if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
+ h = 64;
+ } else {
+ h = 32;
+ }
+ if (scr_y < s->hw_cursor_y ||
+ scr_y >= (s->hw_cursor_y + h))
+ return;
+
+ src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024;
+ if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
+ src += (s->vga.sr[0x13] & 0x3c) * 256;
+ src += (scr_y - s->hw_cursor_y) * 16;
+ poffset = 8;
+ content = ((uint32_t *)src)[0] |
+ ((uint32_t *)src)[1] |
+ ((uint32_t *)src)[2] |
+ ((uint32_t *)src)[3];
+ } else {
+ src += (s->vga.sr[0x13] & 0x3f) * 256;
+ src += (scr_y - s->hw_cursor_y) * 4;
+ poffset = 128;
+ content = ((uint32_t *)src)[0] |
+ ((uint32_t *)(src + 128))[0];
+ }
+ /* if nothing to draw, no need to continue */
+ if (!content)
+ return;
+ w = h;
+
+ x1 = s->hw_cursor_x;
+ if (x1 >= s->vga.last_scr_width)
+ return;
+ x2 = s->hw_cursor_x + w;
+ if (x2 > s->vga.last_scr_width)
+ x2 = s->vga.last_scr_width;
+ w = x2 - x1;
+ palette = s->cirrus_hidden_palette;
+ color0 = s->vga.rgb_to_pixel(c6_to_8(palette[0x0 * 3]),
+ c6_to_8(palette[0x0 * 3 + 1]),
+ c6_to_8(palette[0x0 * 3 + 2]));
+ color1 = s->vga.rgb_to_pixel(c6_to_8(palette[0xf * 3]),
+ c6_to_8(palette[0xf * 3 + 1]),
+ c6_to_8(palette[0xf * 3 + 2]));
+ bpp = surface_bytes_per_pixel(surface);
+ d1 += x1 * bpp;
+ switch (surface_bits_per_pixel(surface)) {
+ default:
+ break;
+ case 8:
+ vga_draw_cursor_line_8(d1, src, poffset, w, color0, color1, 0xff);
+ break;
+ case 15:
+ vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0x7fff);
+ break;
+ case 16:
+ vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0xffff);
+ break;
+ case 32:
+ vga_draw_cursor_line_32(d1, src, poffset, w, color0, color1, 0xffffff);
+ break;
+ }
+}
+
+/***************************************
+ *
+ * LFB memory access
+ *
+ ***************************************/
+
+static uint64_t cirrus_linear_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ CirrusVGAState *s = opaque;
+ uint32_t ret;
+
+ addr &= s->cirrus_addr_mask;
+
+ if (((s->vga.sr[0x17] & 0x44) == 0x44) &&
+ ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) {
+ /* memory-mapped I/O */
+ ret = cirrus_mmio_blt_read(s, addr & 0xff);
+ } else if (0) {
+ /* XXX handle bitblt */
+ ret = 0xff;
+ } else {
+ /* video memory */
+ if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+ addr <<= 4;
+ } else if (s->vga.gr[0x0B] & 0x02) {
+ addr <<= 3;
+ }
+ addr &= s->cirrus_addr_mask;
+ ret = *(s->vga.vram_ptr + addr);
+ }
+
+ return ret;
+}
+
+static void cirrus_linear_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ CirrusVGAState *s = opaque;
+ unsigned mode;
+
+ addr &= s->cirrus_addr_mask;
+
+ if (((s->vga.sr[0x17] & 0x44) == 0x44) &&
+ ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) {
+ /* memory-mapped I/O */
+ cirrus_mmio_blt_write(s, addr & 0xff, val);
+ } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+ /* bitblt */
+ *s->cirrus_srcptr++ = (uint8_t) val;
+ if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
+ cirrus_bitblt_cputovideo_next(s);
+ }
+ } else {
+ /* video memory */
+ if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+ addr <<= 4;
+ } else if (s->vga.gr[0x0B] & 0x02) {
+ addr <<= 3;
+ }
+ addr &= s->cirrus_addr_mask;
+
+ mode = s->vga.gr[0x05] & 0x7;
+ if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) {
+ *(s->vga.vram_ptr + addr) = (uint8_t) val;
+ memory_region_set_dirty(&s->vga.vram, addr, 1);
+ } else {
+ if ((s->vga.gr[0x0B] & 0x14) != 0x14) {
+ cirrus_mem_writeb_mode4and5_8bpp(s, mode, addr, val);
+ } else {
+ cirrus_mem_writeb_mode4and5_16bpp(s, mode, addr, val);
+ }
+ }
+ }
+}
+
+/***************************************
+ *
+ * system to screen memory access
+ *
+ ***************************************/
+
+
+static uint64_t cirrus_linear_bitblt_read(void *opaque,
+ hwaddr addr,
+ unsigned size)
+{
+ CirrusVGAState *s = opaque;
+ uint32_t ret;
+
+ /* XXX handle bitblt */
+ (void)s;
+ ret = 0xff;
+ return ret;
+}
+
+static void cirrus_linear_bitblt_write(void *opaque,
+ hwaddr addr,
+ uint64_t val,
+ unsigned size)
+{
+ CirrusVGAState *s = opaque;
+
+ if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+ /* bitblt */
+ *s->cirrus_srcptr++ = (uint8_t) val;
+ if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
+ cirrus_bitblt_cputovideo_next(s);
+ }
+ }
+}
+
+static const MemoryRegionOps cirrus_linear_bitblt_io_ops = {
+ .read = cirrus_linear_bitblt_read,
+ .write = cirrus_linear_bitblt_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+};
+
+static void map_linear_vram_bank(CirrusVGAState *s, unsigned bank)
+{
+ MemoryRegion *mr = &s->cirrus_bank[bank];
+ bool enabled = !(s->cirrus_srcptr != s->cirrus_srcptr_end)
+ && !((s->vga.sr[0x07] & 0x01) == 0)
+ && !((s->vga.gr[0x0B] & 0x14) == 0x14)
+ && !(s->vga.gr[0x0B] & 0x02);
+
+ memory_region_set_enabled(mr, enabled);
+ memory_region_set_alias_offset(mr, s->cirrus_bank_base[bank]);
+}
+
+static void map_linear_vram(CirrusVGAState *s)
+{
+ if (s->bustype == CIRRUS_BUSTYPE_PCI && !s->linear_vram) {
+ s->linear_vram = true;
+ memory_region_add_subregion_overlap(&s->pci_bar, 0, &s->vga.vram, 1);
+ }
+ map_linear_vram_bank(s, 0);
+ map_linear_vram_bank(s, 1);
+}
+
+static void unmap_linear_vram(CirrusVGAState *s)
+{
+ if (s->bustype == CIRRUS_BUSTYPE_PCI && s->linear_vram) {
+ s->linear_vram = false;
+ memory_region_del_subregion(&s->pci_bar, &s->vga.vram);
+ }
+ memory_region_set_enabled(&s->cirrus_bank[0], false);
+ memory_region_set_enabled(&s->cirrus_bank[1], false);
+}
+
+/* Compute the memory access functions */
+static void cirrus_update_memory_access(CirrusVGAState *s)
+{
+ unsigned mode;
+
+ memory_region_transaction_begin();
+ if ((s->vga.sr[0x17] & 0x44) == 0x44) {
+ goto generic_io;
+ } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+ goto generic_io;
+ } else {
+ if ((s->vga.gr[0x0B] & 0x14) == 0x14) {
+ goto generic_io;
+ } else if (s->vga.gr[0x0B] & 0x02) {
+ goto generic_io;
+ }
+
+ mode = s->vga.gr[0x05] & 0x7;
+ if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) {
+ map_linear_vram(s);
+ } else {
+ generic_io:
+ unmap_linear_vram(s);
+ }
+ }
+ memory_region_transaction_commit();
+}
+
+
+/* I/O ports */
+
+static uint64_t cirrus_vga_ioport_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ CirrusVGAState *c = opaque;
+ VGACommonState *s = &c->vga;
+ int val, index;
+
+ qemu_flush_coalesced_mmio_buffer();
+ addr += 0x3b0;
+
+ if (vga_ioport_invalid(s, addr)) {
+ val = 0xff;
+ } else {
+ switch (addr) {
+ case 0x3c0:
+ if (s->ar_flip_flop == 0) {
+ val = s->ar_index;
+ } else {
+ val = 0;
+ }
+ break;
+ case 0x3c1:
+ index = s->ar_index & 0x1f;
+ if (index < 21)
+ val = s->ar[index];
+ else
+ val = 0;
+ break;
+ case 0x3c2:
+ val = s->st00;
+ break;
+ case 0x3c4:
+ val = s->sr_index;
+ break;
+ case 0x3c5:
+ val = cirrus_vga_read_sr(c);
+ break;
+#ifdef DEBUG_VGA_REG
+ printf("vga: read SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+ break;
+ case 0x3c6:
+ val = cirrus_read_hidden_dac(c);
+ break;
+ case 0x3c7:
+ val = s->dac_state;
+ break;
+ case 0x3c8:
+ val = s->dac_write_index;
+ c->cirrus_hidden_dac_lockindex = 0;
+ break;
+ case 0x3c9:
+ val = cirrus_vga_read_palette(c);
+ break;
+ case 0x3ca:
+ val = s->fcr;
+ break;
+ case 0x3cc:
+ val = s->msr;
+ break;
+ case 0x3ce:
+ val = s->gr_index;
+ break;
+ case 0x3cf:
+ val = cirrus_vga_read_gr(c, s->gr_index);
+#ifdef DEBUG_VGA_REG
+ printf("vga: read GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+ break;
+ case 0x3b4:
+ case 0x3d4:
+ val = s->cr_index;
+ break;
+ case 0x3b5:
+ case 0x3d5:
+ val = cirrus_vga_read_cr(c, s->cr_index);
+#ifdef DEBUG_VGA_REG
+ printf("vga: read CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+ break;
+ case 0x3ba:
+ case 0x3da:
+ /* 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;
+}
+
+static void cirrus_vga_ioport_write(void *opaque, hwaddr addr, uint64_t val,
+ unsigned size)
+{
+ CirrusVGAState *c = opaque;
+ VGACommonState *s = &c->vga;
+ int index;
+
+ qemu_flush_coalesced_mmio_buffer();
+ addr += 0x3b0;
+
+ /* 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 0x3c0:
+ if (s->ar_flip_flop == 0) {
+ val &= 0x3f;
+ s->ar_index = val;
+ } else {
+ index = s->ar_index & 0x1f;
+ switch (index) {
+ case 0x00 ... 0x0f:
+ s->ar[index] = val & 0x3f;
+ break;
+ case 0x10:
+ s->ar[index] = val & ~0x10;
+ break;
+ case 0x11:
+ s->ar[index] = val;
+ break;
+ case 0x12:
+ s->ar[index] = val & ~0xc0;
+ break;
+ case 0x13:
+ s->ar[index] = val & ~0xf0;
+ break;
+ case 0x14:
+ s->ar[index] = val & ~0xf0;
+ break;
+ default:
+ break;
+ }
+ }
+ s->ar_flip_flop ^= 1;
+ break;
+ case 0x3c2:
+ s->msr = val & ~0x10;
+ s->update_retrace_info(s);
+ break;
+ case 0x3c4:
+ s->sr_index = val;
+ break;
+ case 0x3c5:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
+#endif
+ cirrus_vga_write_sr(c, val);
+ break;
+ break;
+ case 0x3c6:
+ cirrus_write_hidden_dac(c, val);
+ break;
+ case 0x3c7:
+ s->dac_read_index = val;
+ s->dac_sub_index = 0;
+ s->dac_state = 3;
+ break;
+ case 0x3c8:
+ s->dac_write_index = val;
+ s->dac_sub_index = 0;
+ s->dac_state = 0;
+ break;
+ case 0x3c9:
+ cirrus_vga_write_palette(c, val);
+ break;
+ case 0x3ce:
+ s->gr_index = val;
+ break;
+ case 0x3cf:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write GR%x = 0x%02x\n", s->gr_index, val);
+#endif
+ cirrus_vga_write_gr(c, s->gr_index, val);
+ break;
+ case 0x3b4:
+ case 0x3d4:
+ s->cr_index = val;
+ break;
+ case 0x3b5:
+ case 0x3d5:
+#ifdef DEBUG_VGA_REG
+ printf("vga: write CR%x = 0x%02x\n", s->cr_index, val);
+#endif
+ cirrus_vga_write_cr(c, val);
+ break;
+ case 0x3ba:
+ case 0x3da:
+ s->fcr = val & 0x10;
+ break;
+ }
+}
+
+/***************************************
+ *
+ * memory-mapped I/O access
+ *
+ ***************************************/
+
+static uint64_t cirrus_mmio_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ CirrusVGAState *s = opaque;
+
+ if (addr >= 0x100) {
+ return cirrus_mmio_blt_read(s, addr - 0x100);
+ } else {
+ return cirrus_vga_ioport_read(s, addr + 0x10, size);
+ }
+}
+
+static void cirrus_mmio_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ CirrusVGAState *s = opaque;
+
+ if (addr >= 0x100) {
+ cirrus_mmio_blt_write(s, addr - 0x100, val);
+ } else {
+ cirrus_vga_ioport_write(s, addr + 0x10, val, size);
+ }
+}
+
+static const MemoryRegionOps cirrus_mmio_io_ops = {
+ .read = cirrus_mmio_read,
+ .write = cirrus_mmio_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+};
+
+/* load/save state */
+
+static int cirrus_post_load(void *opaque, int version_id)
+{
+ CirrusVGAState *s = opaque;
+
+ s->vga.gr[0x00] = s->cirrus_shadow_gr0 & 0x0f;
+ s->vga.gr[0x01] = s->cirrus_shadow_gr1 & 0x0f;
+
+ cirrus_update_memory_access(s);
+ /* force refresh */
+ s->vga.graphic_mode = -1;
+ cirrus_update_bank_ptr(s, 0);
+ cirrus_update_bank_ptr(s, 1);
+ return 0;
+}
+
+static const VMStateDescription vmstate_cirrus_vga = {
+ .name = "cirrus_vga",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = cirrus_post_load,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT32(vga.latch, CirrusVGAState),
+ VMSTATE_UINT8(vga.sr_index, CirrusVGAState),
+ VMSTATE_BUFFER(vga.sr, CirrusVGAState),
+ VMSTATE_UINT8(vga.gr_index, CirrusVGAState),
+ VMSTATE_UINT8(cirrus_shadow_gr0, CirrusVGAState),
+ VMSTATE_UINT8(cirrus_shadow_gr1, CirrusVGAState),
+ VMSTATE_BUFFER_START_MIDDLE(vga.gr, CirrusVGAState, 2),
+ VMSTATE_UINT8(vga.ar_index, CirrusVGAState),
+ VMSTATE_BUFFER(vga.ar, CirrusVGAState),
+ VMSTATE_INT32(vga.ar_flip_flop, CirrusVGAState),
+ VMSTATE_UINT8(vga.cr_index, CirrusVGAState),
+ VMSTATE_BUFFER(vga.cr, CirrusVGAState),
+ VMSTATE_UINT8(vga.msr, CirrusVGAState),
+ VMSTATE_UINT8(vga.fcr, CirrusVGAState),
+ VMSTATE_UINT8(vga.st00, CirrusVGAState),
+ VMSTATE_UINT8(vga.st01, CirrusVGAState),
+ VMSTATE_UINT8(vga.dac_state, CirrusVGAState),
+ VMSTATE_UINT8(vga.dac_sub_index, CirrusVGAState),
+ VMSTATE_UINT8(vga.dac_read_index, CirrusVGAState),
+ VMSTATE_UINT8(vga.dac_write_index, CirrusVGAState),
+ VMSTATE_BUFFER(vga.dac_cache, CirrusVGAState),
+ VMSTATE_BUFFER(vga.palette, CirrusVGAState),
+ VMSTATE_INT32(vga.bank_offset, CirrusVGAState),
+ VMSTATE_UINT8(cirrus_hidden_dac_lockindex, CirrusVGAState),
+ VMSTATE_UINT8(cirrus_hidden_dac_data, CirrusVGAState),
+ VMSTATE_UINT32(hw_cursor_x, CirrusVGAState),
+ VMSTATE_UINT32(hw_cursor_y, CirrusVGAState),
+ /* XXX: we do not save the bitblt state - we assume we do not save
+ the state when the blitter is active */
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_pci_cirrus_vga = {
+ .name = "cirrus_vga",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, PCICirrusVGAState),
+ VMSTATE_STRUCT(cirrus_vga, PCICirrusVGAState, 0,
+ vmstate_cirrus_vga, CirrusVGAState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+/***************************************
+ *
+ * initialize
+ *
+ ***************************************/
+
+static void cirrus_reset(void *opaque)
+{
+ CirrusVGAState *s = opaque;
+
+ vga_common_reset(&s->vga);
+ unmap_linear_vram(s);
+ s->vga.sr[0x06] = 0x0f;
+ if (s->device_id == CIRRUS_ID_CLGD5446) {
+ /* 4MB 64 bit memory config, always PCI */
+ s->vga.sr[0x1F] = 0x2d; // MemClock
+ s->vga.gr[0x18] = 0x0f; // fastest memory configuration
+ s->vga.sr[0x0f] = 0x98;
+ s->vga.sr[0x17] = 0x20;
+ s->vga.sr[0x15] = 0x04; /* memory size, 3=2MB, 4=4MB */
+ } else {
+ s->vga.sr[0x1F] = 0x22; // MemClock
+ s->vga.sr[0x0F] = CIRRUS_MEMSIZE_2M;
+ s->vga.sr[0x17] = s->bustype;
+ s->vga.sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */
+ }
+ s->vga.cr[0x27] = s->device_id;
+
+ s->cirrus_hidden_dac_lockindex = 5;
+ s->cirrus_hidden_dac_data = 0;
+}
+
+static const MemoryRegionOps cirrus_linear_io_ops = {
+ .read = cirrus_linear_read,
+ .write = cirrus_linear_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+};
+
+static const MemoryRegionOps cirrus_vga_io_ops = {
+ .read = cirrus_vga_ioport_read,
+ .write = cirrus_vga_ioport_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+};
+
+static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci,
+ MemoryRegion *system_memory,
+ MemoryRegion *system_io)
+{
+ int i;
+ static int inited;
+
+ if (!inited) {
+ inited = 1;
+ for(i = 0;i < 256; i++)
+ rop_to_index[i] = CIRRUS_ROP_NOP_INDEX; /* nop rop */
+ rop_to_index[CIRRUS_ROP_0] = 0;
+ rop_to_index[CIRRUS_ROP_SRC_AND_DST] = 1;
+ rop_to_index[CIRRUS_ROP_NOP] = 2;
+ rop_to_index[CIRRUS_ROP_SRC_AND_NOTDST] = 3;
+ rop_to_index[CIRRUS_ROP_NOTDST] = 4;
+ rop_to_index[CIRRUS_ROP_SRC] = 5;
+ rop_to_index[CIRRUS_ROP_1] = 6;
+ rop_to_index[CIRRUS_ROP_NOTSRC_AND_DST] = 7;
+ rop_to_index[CIRRUS_ROP_SRC_XOR_DST] = 8;
+ rop_to_index[CIRRUS_ROP_SRC_OR_DST] = 9;
+ rop_to_index[CIRRUS_ROP_NOTSRC_OR_NOTDST] = 10;
+ rop_to_index[CIRRUS_ROP_SRC_NOTXOR_DST] = 11;
+ rop_to_index[CIRRUS_ROP_SRC_OR_NOTDST] = 12;
+ rop_to_index[CIRRUS_ROP_NOTSRC] = 13;
+ rop_to_index[CIRRUS_ROP_NOTSRC_OR_DST] = 14;
+ rop_to_index[CIRRUS_ROP_NOTSRC_AND_NOTDST] = 15;
+ s->device_id = device_id;
+ if (is_pci)
+ s->bustype = CIRRUS_BUSTYPE_PCI;
+ else
+ s->bustype = CIRRUS_BUSTYPE_ISA;
+ }
+
+ /* Register ioport 0x3b0 - 0x3df */
+ memory_region_init_io(&s->cirrus_vga_io, &cirrus_vga_io_ops, s,
+ "cirrus-io", 0x30);
+ memory_region_add_subregion(system_io, 0x3b0, &s->cirrus_vga_io);
+
+ memory_region_init(&s->low_mem_container,
+ "cirrus-lowmem-container",
+ 0x20000);
+
+ memory_region_init_io(&s->low_mem, &cirrus_vga_mem_ops, s,
+ "cirrus-low-memory", 0x20000);
+ memory_region_add_subregion(&s->low_mem_container, 0, &s->low_mem);
+ for (i = 0; i < 2; ++i) {
+ static const char *names[] = { "vga.bank0", "vga.bank1" };
+ MemoryRegion *bank = &s->cirrus_bank[i];
+ memory_region_init_alias(bank, names[i], &s->vga.vram, 0, 0x8000);
+ memory_region_set_enabled(bank, false);
+ memory_region_add_subregion_overlap(&s->low_mem_container, i * 0x8000,
+ bank, 1);
+ }
+ memory_region_add_subregion_overlap(system_memory,
+ isa_mem_base + 0x000a0000,
+ &s->low_mem_container,
+ 1);
+ memory_region_set_coalescing(&s->low_mem);
+
+ /* I/O handler for LFB */
+ memory_region_init_io(&s->cirrus_linear_io, &cirrus_linear_io_ops, s,
+ "cirrus-linear-io", s->vga.vram_size_mb
+ * 1024 * 1024);
+ memory_region_set_flush_coalesced(&s->cirrus_linear_io);
+
+ /* I/O handler for LFB */
+ memory_region_init_io(&s->cirrus_linear_bitblt_io,
+ &cirrus_linear_bitblt_io_ops,
+ s,
+ "cirrus-bitblt-mmio",
+ 0x400000);
+ memory_region_set_flush_coalesced(&s->cirrus_linear_bitblt_io);
+
+ /* I/O handler for memory-mapped I/O */
+ memory_region_init_io(&s->cirrus_mmio_io, &cirrus_mmio_io_ops, s,
+ "cirrus-mmio", CIRRUS_PNPMMIO_SIZE);
+ memory_region_set_flush_coalesced(&s->cirrus_mmio_io);
+
+ s->real_vram_size =
+ (s->device_id == CIRRUS_ID_CLGD5446) ? 4096 * 1024 : 2048 * 1024;
+
+ /* XXX: s->vga.vram_size must be a power of two */
+ s->cirrus_addr_mask = s->real_vram_size - 1;
+ s->linear_mmio_mask = s->real_vram_size - 256;
+
+ s->vga.get_bpp = cirrus_get_bpp;
+ s->vga.get_offsets = cirrus_get_offsets;
+ s->vga.get_resolution = cirrus_get_resolution;
+ s->vga.cursor_invalidate = cirrus_cursor_invalidate;
+ s->vga.cursor_draw_line = cirrus_cursor_draw_line;
+
+ qemu_register_reset(cirrus_reset, s);
+}
+
+/***************************************
+ *
+ * ISA bus support
+ *
+ ***************************************/
+
+static int vga_initfn(ISADevice *dev)
+{
+ ISACirrusVGAState *d = DO_UPCAST(ISACirrusVGAState, dev, dev);
+ VGACommonState *s = &d->cirrus_vga.vga;
+
+ vga_common_init(s);
+ cirrus_init_common(&d->cirrus_vga, CIRRUS_ID_CLGD5430, 0,
+ isa_address_space(dev), isa_address_space_io(dev));
+ s->con = graphic_console_init(s->update, s->invalidate,
+ s->screen_dump, s->text_update,
+ s);
+ rom_add_vga(VGABIOS_CIRRUS_FILENAME);
+ /* XXX ISA-LFB support */
+ /* FIXME not qdev yet */
+ return 0;
+}
+
+static Property isa_vga_cirrus_properties[] = {
+ DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState,
+ cirrus_vga.vga.vram_size_mb, 8),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data)
+{
+ ISADeviceClass *k = ISA_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_cirrus_vga;
+ k->init = vga_initfn;
+ dc->props = isa_vga_cirrus_properties;
+}
+
+static const TypeInfo isa_cirrus_vga_info = {
+ .name = "isa-cirrus-vga",
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(ISACirrusVGAState),
+ .class_init = isa_cirrus_vga_class_init,
+};
+
+/***************************************
+ *
+ * PCI bus support
+ *
+ ***************************************/
+
+static int pci_cirrus_vga_initfn(PCIDevice *dev)
+{
+ PCICirrusVGAState *d = DO_UPCAST(PCICirrusVGAState, dev, dev);
+ CirrusVGAState *s = &d->cirrus_vga;
+ PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
+ int16_t device_id = pc->device_id;
+
+ /* setup VGA */
+ vga_common_init(&s->vga);
+ cirrus_init_common(s, device_id, 1, pci_address_space(dev),
+ pci_address_space_io(dev));
+ s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
+ s->vga.screen_dump, s->vga.text_update,
+ &s->vga);
+
+ /* setup PCI */
+
+ memory_region_init(&s->pci_bar, "cirrus-pci-bar0", 0x2000000);
+
+ /* XXX: add byte swapping apertures */
+ memory_region_add_subregion(&s->pci_bar, 0, &s->cirrus_linear_io);
+ memory_region_add_subregion(&s->pci_bar, 0x1000000,
+ &s->cirrus_linear_bitblt_io);
+
+ /* setup memory space */
+ /* memory #0 LFB */
+ /* memory #1 memory-mapped I/O */
+ /* XXX: s->vga.vram_size must be a power of two */
+ pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->pci_bar);
+ if (device_id == CIRRUS_ID_CLGD5446) {
+ pci_register_bar(&d->dev, 1, 0, &s->cirrus_mmio_io);
+ }
+ return 0;
+}
+
+static Property pci_vga_cirrus_properties[] = {
+ DEFINE_PROP_UINT32("vgamem_mb", struct PCICirrusVGAState,
+ cirrus_vga.vga.vram_size_mb, 8),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void cirrus_vga_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->no_hotplug = 1;
+ k->init = pci_cirrus_vga_initfn;
+ k->romfile = VGABIOS_CIRRUS_FILENAME;
+ k->vendor_id = PCI_VENDOR_ID_CIRRUS;
+ k->device_id = CIRRUS_ID_CLGD5446;
+ k->class_id = PCI_CLASS_DISPLAY_VGA;
+ dc->desc = "Cirrus CLGD 54xx VGA";
+ dc->vmsd = &vmstate_pci_cirrus_vga;
+ dc->props = pci_vga_cirrus_properties;
+}
+
+static const TypeInfo cirrus_vga_info = {
+ .name = "cirrus-vga",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCICirrusVGAState),
+ .class_init = cirrus_vga_class_init,
+};
+
+static void cirrus_vga_register_types(void)
+{
+ type_register_static(&isa_cirrus_vga_info);
+ type_register_static(&cirrus_vga_info);
+}
+
+type_init(cirrus_vga_register_types)
diff --git a/hw/display/cirrus_vga_rop.h b/hw/display/cirrus_vga_rop.h
new file mode 100644
index 0000000000..9c7bb09286
--- /dev/null
+++ b/hw/display/cirrus_vga_rop.h
@@ -0,0 +1,208 @@
+/*
+ * QEMU Cirrus CLGD 54xx VGA Emulator.
+ *
+ * Copyright (c) 2004 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.
+ */
+
+static inline void glue(rop_8_,ROP_NAME)(uint8_t *dst, uint8_t src)
+{
+ *dst = ROP_FN(*dst, src);
+}
+
+static inline void glue(rop_16_,ROP_NAME)(uint16_t *dst, uint16_t src)
+{
+ *dst = ROP_FN(*dst, src);
+}
+
+static inline void glue(rop_32_,ROP_NAME)(uint32_t *dst, uint32_t src)
+{
+ *dst = ROP_FN(*dst, src);
+}
+
+#define ROP_OP(d, s) glue(rop_8_,ROP_NAME)(d, s)
+#define ROP_OP_16(d, s) glue(rop_16_,ROP_NAME)(d, s)
+#define ROP_OP_32(d, s) glue(rop_32_,ROP_NAME)(d, s)
+#undef ROP_FN
+
+static void
+glue(cirrus_bitblt_rop_fwd_, ROP_NAME)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ dstpitch -= bltwidth;
+ srcpitch -= bltwidth;
+
+ if (dstpitch < 0 || srcpitch < 0) {
+ /* is 0 valid? srcpitch == 0 could be useful */
+ return;
+ }
+
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x++) {
+ ROP_OP(dst, *src);
+ dst++;
+ src++;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+static void
+glue(cirrus_bitblt_rop_bkwd_, ROP_NAME)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ dstpitch += bltwidth;
+ srcpitch += bltwidth;
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x++) {
+ ROP_OP(dst, *src);
+ dst--;
+ src--;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+static void
+glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_8)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ uint8_t p;
+ dstpitch -= bltwidth;
+ srcpitch -= bltwidth;
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x++) {
+ p = *dst;
+ ROP_OP(&p, *src);
+ if (p != s->vga.gr[0x34]) *dst = p;
+ dst++;
+ src++;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+static void
+glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_8)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ uint8_t p;
+ dstpitch += bltwidth;
+ srcpitch += bltwidth;
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x++) {
+ p = *dst;
+ ROP_OP(&p, *src);
+ if (p != s->vga.gr[0x34]) *dst = p;
+ dst--;
+ src--;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+static void
+glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_16)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ uint8_t p1, p2;
+ dstpitch -= bltwidth;
+ srcpitch -= bltwidth;
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x+=2) {
+ p1 = *dst;
+ p2 = *(dst+1);
+ ROP_OP(&p1, *src);
+ ROP_OP(&p2, *(src + 1));
+ if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) {
+ *dst = p1;
+ *(dst+1) = p2;
+ }
+ dst+=2;
+ src+=2;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+static void
+glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_16)(CirrusVGAState *s,
+ uint8_t *dst,const uint8_t *src,
+ int dstpitch,int srcpitch,
+ int bltwidth,int bltheight)
+{
+ int x,y;
+ uint8_t p1, p2;
+ dstpitch += bltwidth;
+ srcpitch += bltwidth;
+ for (y = 0; y < bltheight; y++) {
+ for (x = 0; x < bltwidth; x+=2) {
+ p1 = *(dst-1);
+ p2 = *dst;
+ ROP_OP(&p1, *(src - 1));
+ ROP_OP(&p2, *src);
+ if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) {
+ *(dst-1) = p1;
+ *dst = p2;
+ }
+ dst-=2;
+ src-=2;
+ }
+ dst += dstpitch;
+ src += srcpitch;
+ }
+}
+
+#define DEPTH 8
+#include "cirrus_vga_rop2.h"
+
+#define DEPTH 16
+#include "cirrus_vga_rop2.h"
+
+#define DEPTH 24
+#include "cirrus_vga_rop2.h"
+
+#define DEPTH 32
+#include "cirrus_vga_rop2.h"
+
+#undef ROP_NAME
+#undef ROP_OP
+#undef ROP_OP_16
+#undef ROP_OP_32
diff --git a/hw/display/cirrus_vga_rop2.h b/hw/display/cirrus_vga_rop2.h
new file mode 100644
index 0000000000..d28bcc6f25
--- /dev/null
+++ b/hw/display/cirrus_vga_rop2.h
@@ -0,0 +1,281 @@
+/*
+ * QEMU Cirrus CLGD 54xx VGA Emulator.
+ *
+ * Copyright (c) 2004 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.
+ */
+
+#if DEPTH == 8
+#define PUTPIXEL() ROP_OP(&d[0], col)
+#elif DEPTH == 16
+#define PUTPIXEL() ROP_OP_16((uint16_t *)&d[0], col)
+#elif DEPTH == 24
+#define PUTPIXEL() ROP_OP(&d[0], col); \
+ ROP_OP(&d[1], (col >> 8)); \
+ ROP_OP(&d[2], (col >> 16))
+#elif DEPTH == 32
+#define PUTPIXEL() ROP_OP_32(((uint32_t *)&d[0]), col)
+#else
+#error unsupported DEPTH
+#endif
+
+static void
+glue(glue(glue(cirrus_patternfill_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint8_t *d;
+ int x, y, pattern_y, pattern_pitch, pattern_x;
+ unsigned int col;
+ const uint8_t *src1;
+#if DEPTH == 24
+ int skipleft = s->vga.gr[0x2f] & 0x1f;
+#else
+ int skipleft = (s->vga.gr[0x2f] & 0x07) * (DEPTH / 8);
+#endif
+
+#if DEPTH == 8
+ pattern_pitch = 8;
+#elif DEPTH == 16
+ pattern_pitch = 16;
+#else
+ pattern_pitch = 32;
+#endif
+ pattern_y = s->cirrus_blt_srcaddr & 7;
+ for(y = 0; y < bltheight; y++) {
+ pattern_x = skipleft;
+ d = dst + skipleft;
+ src1 = src + pattern_y * pattern_pitch;
+ for (x = skipleft; x < bltwidth; x += (DEPTH / 8)) {
+#if DEPTH == 8
+ col = src1[pattern_x];
+ pattern_x = (pattern_x + 1) & 7;
+#elif DEPTH == 16
+ col = ((uint16_t *)(src1 + pattern_x))[0];
+ pattern_x = (pattern_x + 2) & 15;
+#elif DEPTH == 24
+ {
+ const uint8_t *src2 = src1 + pattern_x * 3;
+ col = src2[0] | (src2[1] << 8) | (src2[2] << 16);
+ pattern_x = (pattern_x + 1) & 7;
+ }
+#else
+ col = ((uint32_t *)(src1 + pattern_x))[0];
+ pattern_x = (pattern_x + 4) & 31;
+#endif
+ PUTPIXEL();
+ d += (DEPTH / 8);
+ }
+ pattern_y = (pattern_y + 1) & 7;
+ dst += dstpitch;
+ }
+}
+
+/* NOTE: srcpitch is ignored */
+static void
+glue(glue(glue(cirrus_colorexpand_transp_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint8_t *d;
+ int x, y;
+ unsigned bits, bits_xor;
+ unsigned int col;
+ unsigned bitmask;
+ unsigned index;
+#if DEPTH == 24
+ int dstskipleft = s->vga.gr[0x2f] & 0x1f;
+ int srcskipleft = dstskipleft / 3;
+#else
+ int srcskipleft = s->vga.gr[0x2f] & 0x07;
+ int dstskipleft = srcskipleft * (DEPTH / 8);
+#endif
+
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) {
+ bits_xor = 0xff;
+ col = s->cirrus_blt_bgcol;
+ } else {
+ bits_xor = 0x00;
+ col = s->cirrus_blt_fgcol;
+ }
+
+ for(y = 0; y < bltheight; y++) {
+ bitmask = 0x80 >> srcskipleft;
+ bits = *src++ ^ bits_xor;
+ d = dst + dstskipleft;
+ for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
+ if ((bitmask & 0xff) == 0) {
+ bitmask = 0x80;
+ bits = *src++ ^ bits_xor;
+ }
+ index = (bits & bitmask);
+ if (index) {
+ PUTPIXEL();
+ }
+ d += (DEPTH / 8);
+ bitmask >>= 1;
+ }
+ dst += dstpitch;
+ }
+}
+
+static void
+glue(glue(glue(cirrus_colorexpand_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint32_t colors[2];
+ uint8_t *d;
+ int x, y;
+ unsigned bits;
+ unsigned int col;
+ unsigned bitmask;
+ int srcskipleft = s->vga.gr[0x2f] & 0x07;
+ int dstskipleft = srcskipleft * (DEPTH / 8);
+
+ colors[0] = s->cirrus_blt_bgcol;
+ colors[1] = s->cirrus_blt_fgcol;
+ for(y = 0; y < bltheight; y++) {
+ bitmask = 0x80 >> srcskipleft;
+ bits = *src++;
+ d = dst + dstskipleft;
+ for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
+ if ((bitmask & 0xff) == 0) {
+ bitmask = 0x80;
+ bits = *src++;
+ }
+ col = colors[!!(bits & bitmask)];
+ PUTPIXEL();
+ d += (DEPTH / 8);
+ bitmask >>= 1;
+ }
+ dst += dstpitch;
+ }
+}
+
+static void
+glue(glue(glue(cirrus_colorexpand_pattern_transp_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint8_t *d;
+ int x, y, bitpos, pattern_y;
+ unsigned int bits, bits_xor;
+ unsigned int col;
+#if DEPTH == 24
+ int dstskipleft = s->vga.gr[0x2f] & 0x1f;
+ int srcskipleft = dstskipleft / 3;
+#else
+ int srcskipleft = s->vga.gr[0x2f] & 0x07;
+ int dstskipleft = srcskipleft * (DEPTH / 8);
+#endif
+
+ if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) {
+ bits_xor = 0xff;
+ col = s->cirrus_blt_bgcol;
+ } else {
+ bits_xor = 0x00;
+ col = s->cirrus_blt_fgcol;
+ }
+ pattern_y = s->cirrus_blt_srcaddr & 7;
+
+ for(y = 0; y < bltheight; y++) {
+ bits = src[pattern_y] ^ bits_xor;
+ bitpos = 7 - srcskipleft;
+ d = dst + dstskipleft;
+ for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
+ if ((bits >> bitpos) & 1) {
+ PUTPIXEL();
+ }
+ d += (DEPTH / 8);
+ bitpos = (bitpos - 1) & 7;
+ }
+ pattern_y = (pattern_y + 1) & 7;
+ dst += dstpitch;
+ }
+}
+
+static void
+glue(glue(glue(cirrus_colorexpand_pattern_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState * s, uint8_t * dst,
+ const uint8_t * src,
+ int dstpitch, int srcpitch,
+ int bltwidth, int bltheight)
+{
+ uint32_t colors[2];
+ uint8_t *d;
+ int x, y, bitpos, pattern_y;
+ unsigned int bits;
+ unsigned int col;
+ int srcskipleft = s->vga.gr[0x2f] & 0x07;
+ int dstskipleft = srcskipleft * (DEPTH / 8);
+
+ colors[0] = s->cirrus_blt_bgcol;
+ colors[1] = s->cirrus_blt_fgcol;
+ pattern_y = s->cirrus_blt_srcaddr & 7;
+
+ for(y = 0; y < bltheight; y++) {
+ bits = src[pattern_y];
+ bitpos = 7 - srcskipleft;
+ d = dst + dstskipleft;
+ for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
+ col = colors[(bits >> bitpos) & 1];
+ PUTPIXEL();
+ d += (DEPTH / 8);
+ bitpos = (bitpos - 1) & 7;
+ }
+ pattern_y = (pattern_y + 1) & 7;
+ dst += dstpitch;
+ }
+}
+
+static void
+glue(glue(glue(cirrus_fill_, ROP_NAME), _),DEPTH)
+ (CirrusVGAState *s,
+ uint8_t *dst, int dst_pitch,
+ int width, int height)
+{
+ uint8_t *d, *d1;
+ uint32_t col;
+ int x, y;
+
+ col = s->cirrus_blt_fgcol;
+
+ d1 = dst;
+ for(y = 0; y < height; y++) {
+ d = d1;
+ for(x = 0; x < width; x += (DEPTH / 8)) {
+ PUTPIXEL();
+ d += (DEPTH / 8);
+ }
+ d1 += dst_pitch;
+ }
+}
+
+#undef DEPTH
+#undef PUTPIXEL
diff --git a/hw/display/cirrus_vga_template.h b/hw/display/cirrus_vga_template.h
new file mode 100644
index 0000000000..3b28280588
--- /dev/null
+++ b/hw/display/cirrus_vga_template.h
@@ -0,0 +1,102 @@
+/*
+ * QEMU Cirrus VGA Emulator templates
+ *
+ * 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.
+ */
+
+#if DEPTH == 8
+#define BPP 1
+#elif DEPTH == 15 || DEPTH == 16
+#define BPP 2
+#elif DEPTH == 32
+#define BPP 4
+#else
+#error unsupported depth
+#endif
+
+static void glue(vga_draw_cursor_line_, DEPTH)(uint8_t *d1,
+ const uint8_t *src1,
+ int poffset, int w,
+ unsigned int color0,
+ unsigned int color1,
+ unsigned int color_xor)
+{
+ const uint8_t *plane0, *plane1;
+ int x, b0, b1;
+ uint8_t *d;
+
+ d = d1;
+ plane0 = src1;
+ plane1 = src1 + poffset;
+ for (x = 0; x < w; x++) {
+ b0 = (plane0[x >> 3] >> (7 - (x & 7))) & 1;
+ b1 = (plane1[x >> 3] >> (7 - (x & 7))) & 1;
+#if DEPTH == 8
+ switch (b0 | (b1 << 1)) {
+ case 0:
+ break;
+ case 1:
+ d[0] ^= color_xor;
+ break;
+ case 2:
+ d[0] = color0;
+ break;
+ case 3:
+ d[0] = color1;
+ break;
+ }
+#elif DEPTH == 16
+ switch (b0 | (b1 << 1)) {
+ case 0:
+ break;
+ case 1:
+ ((uint16_t *)d)[0] ^= color_xor;
+ break;
+ case 2:
+ ((uint16_t *)d)[0] = color0;
+ break;
+ case 3:
+ ((uint16_t *)d)[0] = color1;
+ break;
+ }
+#elif DEPTH == 32
+ switch (b0 | (b1 << 1)) {
+ case 0:
+ break;
+ case 1:
+ ((uint32_t *)d)[0] ^= color_xor;
+ break;
+ case 2:
+ ((uint32_t *)d)[0] = color0;
+ break;
+ case 3:
+ ((uint32_t *)d)[0] = color1;
+ break;
+ }
+#else
+#error unsupported depth
+#endif
+ d += BPP;
+ }
+}
+
+#undef DEPTH
+#undef BPP
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 <batuzovk@ispras.ru>
+ *
+ * Contributed by Mitsyanko Igor <i.mitsyanko@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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..6be31db2b5
--- /dev/null
+++ b/hw/display/framebuffer.c
@@ -0,0 +1,110 @@
+/*
+ * Framebuffer device helper routines
+ *
+ * Copyright (c) 2009 CodeSourcery
+ * Written by Paul Brook <paul@codesourcery.com>
+ *
+ * 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 "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/framebuffer.h b/hw/display/framebuffer.h
new file mode 100644
index 0000000000..6eae035b7d
--- /dev/null
+++ b/hw/display/framebuffer.h
@@ -0,0 +1,25 @@
+#ifndef QEMU_FRAMEBUFFER_H
+#define QEMU_FRAMEBUFFER_H
+
+#include "exec/memory.h"
+
+/* Framebuffer device helper routines. */
+
+typedef void (*drawfn)(void *, uint8_t *, const uint8_t *, int, int);
+
+void framebuffer_update_display(
+ DisplaySurface *ds,
+ MemoryRegion *address_space,
+ hwaddr base,
+ int cols,
+ int rows,
+ int src_width,
+ int dest_row_pitch,
+ int dest_col_pitch,
+ int invalidate,
+ drawfn fn,
+ void *opaque,
+ int *first_row,
+ int *last_row);
+
+#endif
diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c
new file mode 100644
index 0000000000..f7014e9dd8
--- /dev/null
+++ b/hw/display/g364fb.c
@@ -0,0 +1,617 @@
+/*
+ * QEMU G364 framebuffer Emulator.
+ *
+ * Copyright (c) 2007-2011 Herve Poussineau
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "ui/pixel_ops.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+
+typedef struct G364State {
+ /* hardware */
+ uint8_t *vram;
+ uint32_t vram_size;
+ qemu_irq irq;
+ MemoryRegion mem_vram;
+ MemoryRegion mem_ctrl;
+ /* registers */
+ uint8_t color_palette[256][3];
+ uint8_t cursor_palette[3][3];
+ uint16_t cursor[512];
+ uint32_t cursor_position;
+ uint32_t ctla;
+ uint32_t top_of_screen;
+ uint32_t width, height; /* in pixels */
+ /* display refresh support */
+ QemuConsole *con;
+ int depth;
+ int blanked;
+} G364State;
+
+#define REG_BOOT 0x000000
+#define REG_DISPLAY 0x000118
+#define REG_VDISPLAY 0x000150
+#define REG_CTLA 0x000300
+#define REG_TOP 0x000400
+#define REG_CURS_PAL 0x000508
+#define REG_CURS_POS 0x000638
+#define REG_CLR_PAL 0x000800
+#define REG_CURS_PAT 0x001000
+#define REG_RESET 0x100000
+
+#define CTLA_FORCE_BLANK 0x00000400
+#define CTLA_NO_CURSOR 0x00800000
+
+#define G364_PAGE_SIZE 4096
+
+static inline int check_dirty(G364State *s, ram_addr_t page)
+{
+ return memory_region_get_dirty(&s->mem_vram, page, G364_PAGE_SIZE,
+ DIRTY_MEMORY_VGA);
+}
+
+static inline void reset_dirty(G364State *s,
+ ram_addr_t page_min, ram_addr_t page_max)
+{
+ memory_region_reset_dirty(&s->mem_vram,
+ page_min,
+ page_max + G364_PAGE_SIZE - page_min - 1,
+ DIRTY_MEMORY_VGA);
+}
+
+static void g364fb_draw_graphic8(G364State *s)
+{
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ int i, w;
+ uint8_t *vram;
+ uint8_t *data_display, *dd;
+ ram_addr_t page, page_min, page_max;
+ int x, y;
+ int xmin, xmax;
+ int ymin, ymax;
+ int xcursor, ycursor;
+ unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned int b);
+
+ switch (surface_bits_per_pixel(surface)) {
+ case 8:
+ rgb_to_pixel = rgb_to_pixel8;
+ w = 1;
+ break;
+ case 15:
+ rgb_to_pixel = rgb_to_pixel15;
+ w = 2;
+ break;
+ case 16:
+ rgb_to_pixel = rgb_to_pixel16;
+ w = 2;
+ break;
+ case 32:
+ rgb_to_pixel = rgb_to_pixel32;
+ w = 4;
+ break;
+ default:
+ hw_error("g364: unknown host depth %d",
+ surface_bits_per_pixel(surface));
+ return;
+ }
+
+ page = 0;
+ page_min = (ram_addr_t)-1;
+ page_max = 0;
+
+ x = y = 0;
+ xmin = s->width;
+ xmax = 0;
+ ymin = s->height;
+ ymax = 0;
+
+ if (!(s->ctla & CTLA_NO_CURSOR)) {
+ xcursor = s->cursor_position >> 12;
+ ycursor = s->cursor_position & 0xfff;
+ } else {
+ xcursor = ycursor = -65;
+ }
+
+ vram = s->vram + s->top_of_screen;
+ /* XXX: out of range in vram? */
+ data_display = dd = surface_data(surface);
+ while (y < s->height) {
+ if (check_dirty(s, page)) {
+ if (y < ymin)
+ ymin = ymax = y;
+ if (page_min == (ram_addr_t)-1)
+ page_min = page;
+ page_max = page;
+ if (x < xmin)
+ xmin = x;
+ for (i = 0; i < G364_PAGE_SIZE; i++) {
+ uint8_t index;
+ unsigned int color;
+ if (unlikely((y >= ycursor && y < ycursor + 64) &&
+ (x >= xcursor && x < xcursor + 64))) {
+ /* pointer area */
+ int xdiff = x - xcursor;
+ uint16_t curs = s->cursor[(y - ycursor) * 8 + xdiff / 8];
+ int op = (curs >> ((xdiff & 7) * 2)) & 3;
+ if (likely(op == 0)) {
+ /* transparent */
+ index = *vram;
+ color = (*rgb_to_pixel)(
+ s->color_palette[index][0],
+ s->color_palette[index][1],
+ s->color_palette[index][2]);
+ } else {
+ /* get cursor color */
+ index = op - 1;
+ color = (*rgb_to_pixel)(
+ s->cursor_palette[index][0],
+ s->cursor_palette[index][1],
+ s->cursor_palette[index][2]);
+ }
+ } else {
+ /* normal area */
+ index = *vram;
+ color = (*rgb_to_pixel)(
+ s->color_palette[index][0],
+ s->color_palette[index][1],
+ s->color_palette[index][2]);
+ }
+ memcpy(dd, &color, w);
+ dd += w;
+ x++;
+ vram++;
+ if (x == s->width) {
+ xmax = s->width - 1;
+ y++;
+ if (y == s->height) {
+ ymax = s->height - 1;
+ goto done;
+ }
+ data_display = dd = data_display + surface_stride(surface);
+ xmin = 0;
+ x = 0;
+ }
+ }
+ if (x > xmax)
+ xmax = x;
+ if (y > ymax)
+ ymax = y;
+ } else {
+ int dy;
+ if (page_min != (ram_addr_t)-1) {
+ reset_dirty(s, page_min, page_max);
+ page_min = (ram_addr_t)-1;
+ page_max = 0;
+ dpy_gfx_update(s->con, xmin, ymin,
+ xmax - xmin + 1, ymax - ymin + 1);
+ xmin = s->width;
+ xmax = 0;
+ ymin = s->height;
+ ymax = 0;
+ }
+ x += G364_PAGE_SIZE;
+ dy = x / s->width;
+ x = x % s->width;
+ y += dy;
+ vram += G364_PAGE_SIZE;
+ data_display += dy * surface_stride(surface);
+ dd = data_display + x * w;
+ }
+ page += G364_PAGE_SIZE;
+ }
+
+done:
+ if (page_min != (ram_addr_t)-1) {
+ dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
+ reset_dirty(s, page_min, page_max);
+ }
+}
+
+static void g364fb_draw_blank(G364State *s)
+{
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ int i, w;
+ uint8_t *d;
+
+ if (s->blanked) {
+ /* Screen is already blank. No need to redraw it */
+ return;
+ }
+
+ w = s->width * surface_bytes_per_pixel(surface);
+ d = surface_data(surface);
+ for (i = 0; i < s->height; i++) {
+ memset(d, 0, w);
+ d += surface_stride(surface);
+ }
+
+ dpy_gfx_update(s->con, 0, 0, s->width, s->height);
+ s->blanked = 1;
+}
+
+static void g364fb_update_display(void *opaque)
+{
+ G364State *s = opaque;
+ DisplaySurface *surface = qemu_console_surface(s->con);
+
+ qemu_flush_coalesced_mmio_buffer();
+
+ if (s->width == 0 || s->height == 0)
+ return;
+
+ if (s->width != surface_width(surface) ||
+ s->height != surface_height(surface)) {
+ qemu_console_resize(s->con, s->width, s->height);
+ }
+
+ if (s->ctla & CTLA_FORCE_BLANK) {
+ g364fb_draw_blank(s);
+ } else if (s->depth == 8) {
+ g364fb_draw_graphic8(s);
+ } else {
+ error_report("g364: unknown guest depth %d", s->depth);
+ }
+
+ qemu_irq_raise(s->irq);
+}
+
+static inline void g364fb_invalidate_display(void *opaque)
+{
+ G364State *s = opaque;
+
+ s->blanked = 0;
+ memory_region_set_dirty(&s->mem_vram, 0, s->vram_size);
+}
+
+static void g364fb_reset(G364State *s)
+{
+ qemu_irq_lower(s->irq);
+
+ memset(s->color_palette, 0, sizeof(s->color_palette));
+ memset(s->cursor_palette, 0, sizeof(s->cursor_palette));
+ memset(s->cursor, 0, sizeof(s->cursor));
+ s->cursor_position = 0;
+ s->ctla = 0;
+ s->top_of_screen = 0;
+ s->width = s->height = 0;
+ memset(s->vram, 0, s->vram_size);
+ g364fb_invalidate_display(s);
+}
+
+static void g364fb_screen_dump(void *opaque, const char *filename, bool cswitch,
+ Error **errp)
+{
+ G364State *s = opaque;
+ int ret, y, x;
+ uint8_t index;
+ uint8_t *data_buffer;
+ FILE *f;
+
+ qemu_flush_coalesced_mmio_buffer();
+
+ if (s->depth != 8) {
+ error_setg(errp, "g364: unknown guest depth %d", s->depth);
+ return;
+ }
+
+ f = fopen(filename, "wb");
+ if (!f) {
+ error_setg(errp, "failed to open file '%s': %s", filename,
+ strerror(errno));
+ return;
+ }
+
+ if (s->ctla & CTLA_FORCE_BLANK) {
+ /* blank screen */
+ ret = fprintf(f, "P4\n%d %d\n", s->width, s->height);
+ if (ret < 0) {
+ goto write_err;
+ }
+ for (y = 0; y < s->height; y++)
+ for (x = 0; x < s->width; x++) {
+ ret = fputc(0, f);
+ if (ret == EOF) {
+ goto write_err;
+ }
+ }
+ } else {
+ data_buffer = s->vram + s->top_of_screen;
+ ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
+ if (ret < 0) {
+ goto write_err;
+ }
+ for (y = 0; y < s->height; y++)
+ for (x = 0; x < s->width; x++, data_buffer++) {
+ index = *data_buffer;
+ ret = fputc(s->color_palette[index][0], f);
+ if (ret == EOF) {
+ goto write_err;
+ }
+ ret = fputc(s->color_palette[index][1], f);
+ if (ret == EOF) {
+ goto write_err;
+ }
+ ret = fputc(s->color_palette[index][2], f);
+ if (ret == EOF) {
+ goto write_err;
+ }
+ }
+ }
+
+out:
+ fclose(f);
+ return;
+
+write_err:
+ error_setg(errp, "failed to write to file '%s': %s", filename,
+ strerror(errno));
+ unlink(filename);
+ goto out;
+}
+
+/* called for accesses to io ports */
+static uint64_t g364fb_ctrl_read(void *opaque,
+ hwaddr addr,
+ unsigned int size)
+{
+ G364State *s = opaque;
+ uint32_t val;
+
+ if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) {
+ /* cursor pattern */
+ int idx = (addr - REG_CURS_PAT) >> 3;
+ val = s->cursor[idx];
+ } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) {
+ /* cursor palette */
+ int idx = (addr - REG_CURS_PAL) >> 3;
+ val = ((uint32_t)s->cursor_palette[idx][0] << 16);
+ val |= ((uint32_t)s->cursor_palette[idx][1] << 8);
+ val |= ((uint32_t)s->cursor_palette[idx][2] << 0);
+ } else {
+ switch (addr) {
+ case REG_DISPLAY:
+ val = s->width / 4;
+ break;
+ case REG_VDISPLAY:
+ val = s->height * 2;
+ break;
+ case REG_CTLA:
+ val = s->ctla;
+ break;
+ default:
+ {
+ error_report("g364: invalid read at [" TARGET_FMT_plx "]",
+ addr);
+ val = 0;
+ break;
+ }
+ }
+ }
+
+ trace_g364fb_read(addr, val);
+
+ return val;
+}
+
+static void g364fb_update_depth(G364State *s)
+{
+ static const int depths[8] = { 1, 2, 4, 8, 15, 16, 0 };
+ s->depth = depths[(s->ctla & 0x00700000) >> 20];
+}
+
+static void g364_invalidate_cursor_position(G364State *s)
+{
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ int ymin, ymax, start, end;
+
+ /* invalidate only near the cursor */
+ ymin = s->cursor_position & 0xfff;
+ ymax = MIN(s->height, ymin + 64);
+ start = ymin * surface_stride(surface);
+ end = (ymax + 1) * surface_stride(surface);
+
+ memory_region_set_dirty(&s->mem_vram, start, end - start);
+}
+
+static void g364fb_ctrl_write(void *opaque,
+ hwaddr addr,
+ uint64_t val,
+ unsigned int size)
+{
+ G364State *s = opaque;
+
+ trace_g364fb_write(addr, val);
+
+ if (addr >= REG_CLR_PAL && addr < REG_CLR_PAL + 0x800) {
+ /* color palette */
+ int idx = (addr - REG_CLR_PAL) >> 3;
+ s->color_palette[idx][0] = (val >> 16) & 0xff;
+ s->color_palette[idx][1] = (val >> 8) & 0xff;
+ s->color_palette[idx][2] = val & 0xff;
+ g364fb_invalidate_display(s);
+ } else if (addr >= REG_CURS_PAT && addr < REG_CURS_PAT + 0x1000) {
+ /* cursor pattern */
+ int idx = (addr - REG_CURS_PAT) >> 3;
+ s->cursor[idx] = val;
+ g364fb_invalidate_display(s);
+ } else if (addr >= REG_CURS_PAL && addr < REG_CURS_PAL + 0x18) {
+ /* cursor palette */
+ int idx = (addr - REG_CURS_PAL) >> 3;
+ s->cursor_palette[idx][0] = (val >> 16) & 0xff;
+ s->cursor_palette[idx][1] = (val >> 8) & 0xff;
+ s->cursor_palette[idx][2] = val & 0xff;
+ g364fb_invalidate_display(s);
+ } else {
+ switch (addr) {
+ case REG_BOOT: /* Boot timing */
+ case 0x00108: /* Line timing: half sync */
+ case 0x00110: /* Line timing: back porch */
+ case 0x00120: /* Line timing: short display */
+ case 0x00128: /* Frame timing: broad pulse */
+ case 0x00130: /* Frame timing: v sync */
+ case 0x00138: /* Frame timing: v preequalise */
+ case 0x00140: /* Frame timing: v postequalise */
+ case 0x00148: /* Frame timing: v blank */
+ case 0x00158: /* Line timing: line time */
+ case 0x00160: /* Frame store: line start */
+ case 0x00168: /* vram cycle: mem init */
+ case 0x00170: /* vram cycle: transfer delay */
+ case 0x00200: /* vram cycle: mask register */
+ /* ignore */
+ break;
+ case REG_TOP:
+ s->top_of_screen = val;
+ g364fb_invalidate_display(s);
+ break;
+ case REG_DISPLAY:
+ s->width = val * 4;
+ break;
+ case REG_VDISPLAY:
+ s->height = val / 2;
+ break;
+ case REG_CTLA:
+ s->ctla = val;
+ g364fb_update_depth(s);
+ g364fb_invalidate_display(s);
+ break;
+ case REG_CURS_POS:
+ g364_invalidate_cursor_position(s);
+ s->cursor_position = val;
+ g364_invalidate_cursor_position(s);
+ break;
+ case REG_RESET:
+ g364fb_reset(s);
+ break;
+ default:
+ error_report("g364: invalid write of 0x%" PRIx64
+ " at [" TARGET_FMT_plx "]", val, addr);
+ break;
+ }
+ }
+ qemu_irq_lower(s->irq);
+}
+
+static const MemoryRegionOps g364fb_ctrl_ops = {
+ .read = g364fb_ctrl_read,
+ .write = g364fb_ctrl_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+};
+
+static int g364fb_post_load(void *opaque, int version_id)
+{
+ G364State *s = opaque;
+
+ /* force refresh */
+ g364fb_update_depth(s);
+ g364fb_invalidate_display(s);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_g364fb = {
+ .name = "g364fb",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = g364fb_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_VBUFFER_UINT32(vram, G364State, 1, NULL, 0, vram_size),
+ VMSTATE_BUFFER_UNSAFE(color_palette, G364State, 0, 256 * 3),
+ VMSTATE_BUFFER_UNSAFE(cursor_palette, G364State, 0, 9),
+ VMSTATE_UINT16_ARRAY(cursor, G364State, 512),
+ VMSTATE_UINT32(cursor_position, G364State),
+ VMSTATE_UINT32(ctla, G364State),
+ VMSTATE_UINT32(top_of_screen, G364State),
+ VMSTATE_UINT32(width, G364State),
+ VMSTATE_UINT32(height, G364State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void g364fb_init(DeviceState *dev, G364State *s)
+{
+ s->vram = g_malloc0(s->vram_size);
+
+ s->con = graphic_console_init(g364fb_update_display,
+ g364fb_invalidate_display,
+ g364fb_screen_dump, NULL, s);
+
+ memory_region_init_io(&s->mem_ctrl, &g364fb_ctrl_ops, s, "ctrl", 0x180000);
+ memory_region_init_ram_ptr(&s->mem_vram, "vram",
+ s->vram_size, s->vram);
+ vmstate_register_ram(&s->mem_vram, dev);
+ memory_region_set_coalescing(&s->mem_vram);
+}
+
+typedef struct {
+ SysBusDevice busdev;
+ G364State g364;
+} G364SysBusState;
+
+static int g364fb_sysbus_init(SysBusDevice *dev)
+{
+ G364State *s = &FROM_SYSBUS(G364SysBusState, dev)->g364;
+
+ g364fb_init(&dev->qdev, s);
+ sysbus_init_irq(dev, &s->irq);
+ sysbus_init_mmio(dev, &s->mem_ctrl);
+ sysbus_init_mmio(dev, &s->mem_vram);
+
+ return 0;
+}
+
+static void g364fb_sysbus_reset(DeviceState *d)
+{
+ G364SysBusState *s = DO_UPCAST(G364SysBusState, busdev.qdev, d);
+ g364fb_reset(&s->g364);
+}
+
+static Property g364fb_sysbus_properties[] = {
+ DEFINE_PROP_HEX32("vram_size", G364SysBusState, g364.vram_size,
+ 8 * 1024 * 1024),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void g364fb_sysbus_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = g364fb_sysbus_init;
+ dc->desc = "G364 framebuffer";
+ dc->reset = g364fb_sysbus_reset;
+ dc->vmsd = &vmstate_g364fb;
+ dc->props = g364fb_sysbus_properties;
+}
+
+static const TypeInfo g364fb_sysbus_info = {
+ .name = "sysbus-g364",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(G364SysBusState),
+ .class_init = g364fb_sysbus_class_init,
+};
+
+static void g364fb_register_types(void)
+{
+ type_register_static(&g364fb_sysbus_info);
+}
+
+type_init(g364fb_register_types)
diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c
new file mode 100644
index 0000000000..05528c7c81
--- /dev/null
+++ b/hw/display/jazz_led.c
@@ -0,0 +1,304 @@
+/*
+ * QEMU JAZZ LED emulator.
+ *
+ * Copyright (c) 2007-2012 Herve Poussineau
+ *
+ * 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 "trace.h"
+#include "hw/sysbus.h"
+
+typedef enum {
+ REDRAW_NONE = 0, REDRAW_SEGMENTS = 1, REDRAW_BACKGROUND = 2,
+} screen_state_t;
+
+typedef struct LedState {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint8_t segments;
+ QemuConsole *con;
+ screen_state_t state;
+} LedState;
+
+static uint64_t jazz_led_read(void *opaque, hwaddr addr,
+ unsigned int size)
+{
+ LedState *s = opaque;
+ uint8_t val;
+
+ val = s->segments;
+ trace_jazz_led_read(addr, val);
+
+ return val;
+}
+
+static void jazz_led_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned int size)
+{
+ LedState *s = opaque;
+ uint8_t new_val = val & 0xff;
+
+ trace_jazz_led_write(addr, new_val);
+
+ s->segments = new_val;
+ s->state |= REDRAW_SEGMENTS;
+}
+
+static const MemoryRegionOps led_ops = {
+ .read = jazz_led_read,
+ .write = jazz_led_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 1,
+};
+
+/***********************************************************/
+/* jazz_led display */
+
+static void draw_horizontal_line(DisplaySurface *ds,
+ int posy, int posx1, int posx2,
+ uint32_t color)
+{
+ uint8_t *d;
+ int x, bpp;
+
+ bpp = (surface_bits_per_pixel(ds) + 7) >> 3;
+ d = surface_data(ds) + surface_stride(ds) * posy + bpp * posx1;
+ switch(bpp) {
+ case 1:
+ for (x = posx1; x <= posx2; x++) {
+ *((uint8_t *)d) = color;
+ d++;
+ }
+ break;
+ case 2:
+ for (x = posx1; x <= posx2; x++) {
+ *((uint16_t *)d) = color;
+ d += 2;
+ }
+ break;
+ case 4:
+ for (x = posx1; x <= posx2; x++) {
+ *((uint32_t *)d) = color;
+ d += 4;
+ }
+ break;
+ }
+}
+
+static void draw_vertical_line(DisplaySurface *ds,
+ int posx, int posy1, int posy2,
+ uint32_t color)
+{
+ uint8_t *d;
+ int y, bpp;
+
+ bpp = (surface_bits_per_pixel(ds) + 7) >> 3;
+ d = surface_data(ds) + surface_stride(ds) * posy1 + bpp * posx;
+ switch(bpp) {
+ case 1:
+ for (y = posy1; y <= posy2; y++) {
+ *((uint8_t *)d) = color;
+ d += surface_stride(ds);
+ }
+ break;
+ case 2:
+ for (y = posy1; y <= posy2; y++) {
+ *((uint16_t *)d) = color;
+ d += surface_stride(ds);
+ }
+ break;
+ case 4:
+ for (y = posy1; y <= posy2; y++) {
+ *((uint32_t *)d) = color;
+ d += surface_stride(ds);
+ }
+ break;
+ }
+}
+
+static void jazz_led_update_display(void *opaque)
+{
+ LedState *s = opaque;
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ uint8_t *d1;
+ uint32_t color_segment, color_led;
+ int y, bpp;
+
+ if (s->state & REDRAW_BACKGROUND) {
+ /* clear screen */
+ bpp = (surface_bits_per_pixel(surface) + 7) >> 3;
+ d1 = surface_data(surface);
+ for (y = 0; y < surface_height(surface); y++) {
+ memset(d1, 0x00, surface_width(surface) * bpp);
+ d1 += surface_stride(surface);
+ }
+ }
+
+ if (s->state & REDRAW_SEGMENTS) {
+ /* set colors according to bpp */
+ switch (surface_bits_per_pixel(surface)) {
+ case 8:
+ color_segment = rgb_to_pixel8(0xaa, 0xaa, 0xaa);
+ color_led = rgb_to_pixel8(0x00, 0xff, 0x00);
+ break;
+ case 15:
+ color_segment = rgb_to_pixel15(0xaa, 0xaa, 0xaa);
+ color_led = rgb_to_pixel15(0x00, 0xff, 0x00);
+ break;
+ case 16:
+ color_segment = rgb_to_pixel16(0xaa, 0xaa, 0xaa);
+ color_led = rgb_to_pixel16(0x00, 0xff, 0x00);
+ case 24:
+ color_segment = rgb_to_pixel24(0xaa, 0xaa, 0xaa);
+ color_led = rgb_to_pixel24(0x00, 0xff, 0x00);
+ break;
+ case 32:
+ color_segment = rgb_to_pixel32(0xaa, 0xaa, 0xaa);
+ color_led = rgb_to_pixel32(0x00, 0xff, 0x00);
+ break;
+ default:
+ return;
+ }
+
+ /* display segments */
+ draw_horizontal_line(surface, 40, 10, 40,
+ (s->segments & 0x02) ? color_segment : 0);
+ draw_vertical_line(surface, 10, 10, 40,
+ (s->segments & 0x04) ? color_segment : 0);
+ draw_vertical_line(surface, 10, 40, 70,
+ (s->segments & 0x08) ? color_segment : 0);
+ draw_horizontal_line(surface, 70, 10, 40,
+ (s->segments & 0x10) ? color_segment : 0);
+ draw_vertical_line(surface, 40, 40, 70,
+ (s->segments & 0x20) ? color_segment : 0);
+ draw_vertical_line(surface, 40, 10, 40,
+ (s->segments & 0x40) ? color_segment : 0);
+ draw_horizontal_line(surface, 10, 10, 40,
+ (s->segments & 0x80) ? color_segment : 0);
+
+ /* display led */
+ if (!(s->segments & 0x01))
+ color_led = 0; /* black */
+ draw_horizontal_line(surface, 68, 50, 50, color_led);
+ draw_horizontal_line(surface, 69, 49, 51, color_led);
+ draw_horizontal_line(surface, 70, 48, 52, color_led);
+ draw_horizontal_line(surface, 71, 49, 51, color_led);
+ draw_horizontal_line(surface, 72, 50, 50, color_led);
+ }
+
+ s->state = REDRAW_NONE;
+ dpy_gfx_update(s->con, 0, 0,
+ surface_width(surface), surface_height(surface));
+}
+
+static void jazz_led_invalidate_display(void *opaque)
+{
+ LedState *s = opaque;
+ s->state |= REDRAW_SEGMENTS | REDRAW_BACKGROUND;
+}
+
+static void jazz_led_text_update(void *opaque, console_ch_t *chardata)
+{
+ LedState *s = opaque;
+ char buf[2];
+
+ dpy_text_cursor(s->con, -1, -1);
+ qemu_console_resize(s->con, 2, 1);
+
+ /* TODO: draw the segments */
+ snprintf(buf, 2, "%02hhx\n", s->segments);
+ console_write_ch(chardata++, 0x00200100 | buf[0]);
+ console_write_ch(chardata++, 0x00200100 | buf[1]);
+
+ dpy_text_update(s->con, 0, 0, 2, 1);
+}
+
+static int jazz_led_post_load(void *opaque, int version_id)
+{
+ /* force refresh */
+ jazz_led_invalidate_display(opaque);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_jazz_led = {
+ .name = "jazz-led",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = jazz_led_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(segments, LedState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int jazz_led_init(SysBusDevice *dev)
+{
+ LedState *s = FROM_SYSBUS(LedState, dev);
+
+ memory_region_init_io(&s->iomem, &led_ops, s, "led", 1);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ s->con = graphic_console_init(jazz_led_update_display,
+ jazz_led_invalidate_display,
+ NULL,
+ jazz_led_text_update, s);
+
+ return 0;
+}
+
+static void jazz_led_reset(DeviceState *d)
+{
+ LedState *s = DO_UPCAST(LedState, busdev.qdev, d);
+
+ s->segments = 0;
+ s->state = REDRAW_SEGMENTS | REDRAW_BACKGROUND;
+ qemu_console_resize(s->con, 60, 80);
+}
+
+static void jazz_led_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = jazz_led_init;
+ dc->desc = "Jazz LED display",
+ dc->vmsd = &vmstate_jazz_led;
+ dc->reset = jazz_led_reset;
+}
+
+static const TypeInfo jazz_led_info = {
+ .name = "jazz-led",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(LedState),
+ .class_init = jazz_led_class_init,
+};
+
+static void jazz_led_register(void)
+{
+ type_register_static(&jazz_led_info);
+}
+
+type_init(jazz_led_register);
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 <michael@walle.cc>
+ * Copyright (c) 2010 Sebastien Bourdeauducq
+ * <sebastien.bourdeauducq@lekernel.net>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *
+ * 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 <X11/Xlib.h>
+#include <GL/gl.h>
+#include <GL/glx.h>
+
+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..3219041c81
--- /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 <michael@walle.cc>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *
+ * 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 "framebuffer.h"
+#include "ui/pixel_ops.h"
+#include "qemu/error-report.h"
+
+#define BITS 8
+#include "milkymist-vgafb_template.h"
+#define BITS 15
+#include "milkymist-vgafb_template.h"
+#define BITS 16
+#include "milkymist-vgafb_template.h"
+#define BITS 24
+#include "milkymist-vgafb_template.h"
+#define BITS 32
+#include "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/milkymist-vgafb_template.h b/hw/display/milkymist-vgafb_template.h
new file mode 100644
index 0000000000..e0036e16cf
--- /dev/null
+++ b/hw/display/milkymist-vgafb_template.h
@@ -0,0 +1,74 @@
+/*
+ * QEMU model of the Milkymist VGA framebuffer.
+ *
+ * Copyright (c) 2010 Michael Walle <michael@walle.cc>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if BITS == 8
+#define COPY_PIXEL(to, r, g, b) \
+ do { \
+ *to = rgb_to_pixel8(r, g, b); \
+ to += 1; \
+ } while (0)
+#elif BITS == 15
+#define COPY_PIXEL(to, r, g, b) \
+ do { \
+ *(uint16_t *)to = rgb_to_pixel15(r, g, b); \
+ to += 2; \
+ } while (0)
+#elif BITS == 16
+#define COPY_PIXEL(to, r, g, b) \
+ do { \
+ *(uint16_t *)to = rgb_to_pixel16(r, g, b); \
+ to += 2; \
+ } while (0)
+#elif BITS == 24
+#define COPY_PIXEL(to, r, g, b) \
+ do { \
+ uint32_t tmp = rgb_to_pixel24(r, g, b); \
+ *(to++) = tmp & 0xff; \
+ *(to++) = (tmp >> 8) & 0xff; \
+ *(to++) = (tmp >> 16) & 0xff; \
+ } while (0)
+#elif BITS == 32
+#define COPY_PIXEL(to, r, g, b) \
+ do { \
+ *(uint32_t *)to = rgb_to_pixel32(r, g, b); \
+ to += 4; \
+ } while (0)
+#else
+#error unknown bit depth
+#endif
+
+static void glue(draw_line_, BITS)(void *opaque, uint8_t *d, const uint8_t *s,
+ int width, int deststep)
+{
+ uint16_t rgb565;
+ uint8_t r, g, b;
+
+ while (width--) {
+ memcpy(&rgb565, s, sizeof(rgb565));
+ r = ((rgb565 >> 11) & 0x1f) << 3;
+ g = ((rgb565 >> 5) & 0x3f) << 2;
+ b = ((rgb565 >> 0) & 0x1f) << 3;
+ COPY_PIXEL(d, r, g, b);
+ s += 2;
+ }
+}
+
+#undef BITS
+#undef COPY_PIXEL
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 <andrew@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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 <http://www.gnu.org/licenses/>.
+ */
+#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_lcd_template.h b/hw/display/omap_lcd_template.h
new file mode 100644
index 0000000000..2fb96f83ae
--- /dev/null
+++ b/hw/display/omap_lcd_template.h
@@ -0,0 +1,175 @@
+/*
+ * QEMU OMAP LCD Emulator templates
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if DEPTH == 8
+# define BPP 1
+# define PIXEL_TYPE uint8_t
+#elif DEPTH == 15 || DEPTH == 16
+# define BPP 2
+# define PIXEL_TYPE uint16_t
+#elif DEPTH == 32
+# define BPP 4
+# define PIXEL_TYPE uint32_t
+#else
+# error unsupport depth
+#endif
+
+/*
+ * 2-bit colour
+ */
+static void glue(draw_line2_, DEPTH)(void *opaque,
+ uint8_t *d, const uint8_t *s, int width, int deststep)
+{
+ uint16_t *pal = opaque;
+ uint8_t v, r, g, b;
+
+ do {
+ v = ldub_raw((void *) s);
+ r = (pal[v & 3] >> 4) & 0xf0;
+ g = pal[v & 3] & 0xf0;
+ b = (pal[v & 3] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ d += BPP;
+ v >>= 2;
+ r = (pal[v & 3] >> 4) & 0xf0;
+ g = pal[v & 3] & 0xf0;
+ b = (pal[v & 3] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ d += BPP;
+ v >>= 2;
+ r = (pal[v & 3] >> 4) & 0xf0;
+ g = pal[v & 3] & 0xf0;
+ b = (pal[v & 3] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ d += BPP;
+ v >>= 2;
+ r = (pal[v & 3] >> 4) & 0xf0;
+ g = pal[v & 3] & 0xf0;
+ b = (pal[v & 3] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ d += BPP;
+ s ++;
+ width -= 4;
+ } while (width > 0);
+}
+
+/*
+ * 4-bit colour
+ */
+static void glue(draw_line4_, DEPTH)(void *opaque,
+ uint8_t *d, const uint8_t *s, int width, int deststep)
+{
+ uint16_t *pal = opaque;
+ uint8_t v, r, g, b;
+
+ do {
+ v = ldub_raw((void *) s);
+ r = (pal[v & 0xf] >> 4) & 0xf0;
+ g = pal[v & 0xf] & 0xf0;
+ b = (pal[v & 0xf] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ d += BPP;
+ v >>= 4;
+ r = (pal[v & 0xf] >> 4) & 0xf0;
+ g = pal[v & 0xf] & 0xf0;
+ b = (pal[v & 0xf] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ d += BPP;
+ s ++;
+ width -= 2;
+ } while (width > 0);
+}
+
+/*
+ * 8-bit colour
+ */
+static void glue(draw_line8_, DEPTH)(void *opaque,
+ uint8_t *d, const uint8_t *s, int width, int deststep)
+{
+ uint16_t *pal = opaque;
+ uint8_t v, r, g, b;
+
+ do {
+ v = ldub_raw((void *) s);
+ r = (pal[v] >> 4) & 0xf0;
+ g = pal[v] & 0xf0;
+ b = (pal[v] << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ s ++;
+ d += BPP;
+ } while (-- width != 0);
+}
+
+/*
+ * 12-bit colour
+ */
+static void glue(draw_line12_, DEPTH)(void *opaque,
+ uint8_t *d, const uint8_t *s, int width, int deststep)
+{
+ uint16_t v;
+ uint8_t r, g, b;
+
+ do {
+ v = lduw_raw((void *) s);
+ r = (v >> 4) & 0xf0;
+ g = v & 0xf0;
+ b = (v << 4) & 0xf0;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ s += 2;
+ d += BPP;
+ } while (-- width != 0);
+}
+
+/*
+ * 16-bit colour
+ */
+static void glue(draw_line16_, DEPTH)(void *opaque,
+ uint8_t *d, const uint8_t *s, int width, int deststep)
+{
+#if DEPTH == 16 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+ memcpy(d, s, width * 2);
+#else
+ uint16_t v;
+ uint8_t r, g, b;
+
+ do {
+ v = lduw_raw((void *) s);
+ r = (v >> 8) & 0xf8;
+ g = (v >> 3) & 0xfc;
+ b = (v << 3) & 0xf8;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, DEPTH)(r, g, b);
+ s += 2;
+ d += BPP;
+ } while (-- width != 0);
+#endif
+}
+
+#undef DEPTH
+#undef BPP
+#undef PIXEL_TYPE
diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c
new file mode 100644
index 0000000000..be7e9c0d74
--- /dev/null
+++ b/hw/display/omap_lcdc.c
@@ -0,0 +1,493 @@
+/*
+ * OMAP LCD controller.
+ *
+ * Copyright (C) 2006-2007 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "hw/arm/omap.h"
+#include "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 "omap_lcd_template.h"
+#define DEPTH 15
+#include "omap_lcd_template.h"
+#define DEPTH 16
+#include "omap_lcd_template.h"
+#define DEPTH 32
+#include "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/pl110.c b/hw/display/pl110.c
new file mode 100644
index 0000000000..295434eded
--- /dev/null
+++ b/hw/display/pl110.c
@@ -0,0 +1,533 @@
+/*
+ * Arm PrimeCell PL110 Color LCD Controller
+ *
+ * Copyright (c) 2005-2009 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GNU LGPL
+ */
+
+#include "hw/sysbus.h"
+#include "ui/console.h"
+#include "framebuffer.h"
+#include "ui/pixel_ops.h"
+
+#define PL110_CR_EN 0x001
+#define PL110_CR_BGR 0x100
+#define PL110_CR_BEBO 0x200
+#define PL110_CR_BEPO 0x400
+#define PL110_CR_PWR 0x800
+
+enum pl110_bppmode
+{
+ BPP_1,
+ BPP_2,
+ BPP_4,
+ BPP_8,
+ BPP_16,
+ BPP_32,
+ BPP_16_565, /* PL111 only */
+ BPP_12 /* PL111 only */
+};
+
+
+/* The Versatile/PB uses a slightly modified PL110 controller. */
+enum pl110_version
+{
+ PL110,
+ PL110_VERSATILE,
+ PL111
+};
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ QemuConsole *con;
+
+ int version;
+ uint32_t timing[4];
+ uint32_t cr;
+ uint32_t upbase;
+ uint32_t lpbase;
+ uint32_t int_status;
+ uint32_t int_mask;
+ int cols;
+ int rows;
+ enum pl110_bppmode bpp;
+ int invalidate;
+ uint32_t mux_ctrl;
+ uint32_t palette[256];
+ uint32_t raw_palette[128];
+ qemu_irq irq;
+} pl110_state;
+
+static int vmstate_pl110_post_load(void *opaque, int version_id);
+
+static const VMStateDescription vmstate_pl110 = {
+ .name = "pl110",
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .post_load = vmstate_pl110_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32(version, pl110_state),
+ VMSTATE_UINT32_ARRAY(timing, pl110_state, 4),
+ VMSTATE_UINT32(cr, pl110_state),
+ VMSTATE_UINT32(upbase, pl110_state),
+ VMSTATE_UINT32(lpbase, pl110_state),
+ VMSTATE_UINT32(int_status, pl110_state),
+ VMSTATE_UINT32(int_mask, pl110_state),
+ VMSTATE_INT32(cols, pl110_state),
+ VMSTATE_INT32(rows, pl110_state),
+ VMSTATE_UINT32(bpp, pl110_state),
+ VMSTATE_INT32(invalidate, pl110_state),
+ VMSTATE_UINT32_ARRAY(palette, pl110_state, 256),
+ VMSTATE_UINT32_ARRAY(raw_palette, pl110_state, 128),
+ VMSTATE_UINT32_V(mux_ctrl, pl110_state, 2),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const unsigned char pl110_id[] =
+{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+/* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
+ has a different ID. However Linux only looks for the normal ID. */
+#if 0
+static const unsigned char pl110_versatile_id[] =
+{ 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+#else
+#define pl110_versatile_id pl110_id
+#endif
+
+static const unsigned char pl111_id[] = {
+ 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
+};
+
+/* Indexed by pl110_version */
+static const unsigned char *idregs[] = {
+ pl110_id,
+ pl110_versatile_id,
+ pl111_id
+};
+
+#define BITS 8
+#include "pl110_template.h"
+#define BITS 15
+#include "pl110_template.h"
+#define BITS 16
+#include "pl110_template.h"
+#define BITS 24
+#include "pl110_template.h"
+#define BITS 32
+#include "pl110_template.h"
+
+static int pl110_enabled(pl110_state *s)
+{
+ return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
+}
+
+static void pl110_update_display(void *opaque)
+{
+ pl110_state *s = (pl110_state *)opaque;
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ drawfn* fntable;
+ drawfn fn;
+ int dest_width;
+ int src_width;
+ int bpp_offset;
+ int first;
+ int last;
+
+ if (!pl110_enabled(s))
+ return;
+
+ switch (surface_bits_per_pixel(surface)) {
+ case 0:
+ return;
+ case 8:
+ fntable = pl110_draw_fn_8;
+ dest_width = 1;
+ break;
+ case 15:
+ fntable = pl110_draw_fn_15;
+ dest_width = 2;
+ break;
+ case 16:
+ fntable = pl110_draw_fn_16;
+ dest_width = 2;
+ break;
+ case 24:
+ fntable = pl110_draw_fn_24;
+ dest_width = 3;
+ break;
+ case 32:
+ fntable = pl110_draw_fn_32;
+ dest_width = 4;
+ break;
+ default:
+ fprintf(stderr, "pl110: Bad color depth\n");
+ exit(1);
+ }
+ if (s->cr & PL110_CR_BGR)
+ bpp_offset = 0;
+ else
+ bpp_offset = 24;
+
+ if ((s->version != PL111) && (s->bpp == BPP_16)) {
+ /* The PL110's native 16 bit mode is 5551; however
+ * most boards with a PL110 implement an external
+ * mux which allows bits to be reshuffled to give
+ * 565 format. The mux is typically controlled by
+ * an external system register.
+ * This is controlled by a GPIO input pin
+ * so boards can wire it up to their register.
+ *
+ * The PL111 straightforwardly implements both
+ * 5551 and 565 under control of the bpp field
+ * in the LCDControl register.
+ */
+ switch (s->mux_ctrl) {
+ case 3: /* 565 BGR */
+ bpp_offset = (BPP_16_565 - BPP_16);
+ break;
+ case 1: /* 5551 */
+ break;
+ case 0: /* 888; also if we have loaded vmstate from an old version */
+ case 2: /* 565 RGB */
+ default:
+ /* treat as 565 but honour BGR bit */
+ bpp_offset += (BPP_16_565 - BPP_16);
+ break;
+ }
+ }
+
+ if (s->cr & PL110_CR_BEBO)
+ fn = fntable[s->bpp + 8 + bpp_offset];
+ else if (s->cr & PL110_CR_BEPO)
+ fn = fntable[s->bpp + 16 + bpp_offset];
+ else
+ fn = fntable[s->bpp + bpp_offset];
+
+ src_width = s->cols;
+ switch (s->bpp) {
+ case BPP_1:
+ src_width >>= 3;
+ break;
+ case BPP_2:
+ src_width >>= 2;
+ break;
+ case BPP_4:
+ src_width >>= 1;
+ break;
+ case BPP_8:
+ break;
+ case BPP_16:
+ case BPP_16_565:
+ case BPP_12:
+ src_width <<= 1;
+ break;
+ case BPP_32:
+ src_width <<= 2;
+ break;
+ }
+ dest_width *= s->cols;
+ first = 0;
+ framebuffer_update_display(surface, sysbus_address_space(&s->busdev),
+ s->upbase, s->cols, s->rows,
+ src_width, dest_width, 0,
+ s->invalidate,
+ fn, s->palette,
+ &first, &last);
+ if (first >= 0) {
+ dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
+ }
+ s->invalidate = 0;
+}
+
+static void pl110_invalidate_display(void * opaque)
+{
+ pl110_state *s = (pl110_state *)opaque;
+ s->invalidate = 1;
+ if (pl110_enabled(s)) {
+ qemu_console_resize(s->con, s->cols, s->rows);
+ }
+}
+
+static void pl110_update_palette(pl110_state *s, int n)
+{
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ int i;
+ uint32_t raw;
+ unsigned int r, g, b;
+
+ raw = s->raw_palette[n];
+ n <<= 1;
+ for (i = 0; i < 2; i++) {
+ r = (raw & 0x1f) << 3;
+ raw >>= 5;
+ g = (raw & 0x1f) << 3;
+ raw >>= 5;
+ b = (raw & 0x1f) << 3;
+ /* The I bit is ignored. */
+ raw >>= 6;
+ switch (surface_bits_per_pixel(surface)) {
+ case 8:
+ s->palette[n] = rgb_to_pixel8(r, g, b);
+ break;
+ case 15:
+ s->palette[n] = rgb_to_pixel15(r, g, b);
+ break;
+ case 16:
+ s->palette[n] = rgb_to_pixel16(r, g, b);
+ break;
+ case 24:
+ case 32:
+ s->palette[n] = rgb_to_pixel32(r, g, b);
+ break;
+ }
+ n++;
+ }
+}
+
+static void pl110_resize(pl110_state *s, int width, int height)
+{
+ if (width != s->cols || height != s->rows) {
+ if (pl110_enabled(s)) {
+ qemu_console_resize(s->con, width, height);
+ }
+ }
+ s->cols = width;
+ s->rows = height;
+}
+
+/* Update interrupts. */
+static void pl110_update(pl110_state *s)
+{
+ /* TODO: Implement interrupts. */
+}
+
+static uint64_t pl110_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ pl110_state *s = (pl110_state *)opaque;
+
+ if (offset >= 0xfe0 && offset < 0x1000) {
+ return idregs[s->version][(offset - 0xfe0) >> 2];
+ }
+ if (offset >= 0x200 && offset < 0x400) {
+ return s->raw_palette[(offset - 0x200) >> 2];
+ }
+ switch (offset >> 2) {
+ case 0: /* LCDTiming0 */
+ return s->timing[0];
+ case 1: /* LCDTiming1 */
+ return s->timing[1];
+ case 2: /* LCDTiming2 */
+ return s->timing[2];
+ case 3: /* LCDTiming3 */
+ return s->timing[3];
+ case 4: /* LCDUPBASE */
+ return s->upbase;
+ case 5: /* LCDLPBASE */
+ return s->lpbase;
+ case 6: /* LCDIMSC */
+ if (s->version != PL110) {
+ return s->cr;
+ }
+ return s->int_mask;
+ case 7: /* LCDControl */
+ if (s->version != PL110) {
+ return s->int_mask;
+ }
+ return s->cr;
+ case 8: /* LCDRIS */
+ return s->int_status;
+ case 9: /* LCDMIS */
+ return s->int_status & s->int_mask;
+ case 11: /* LCDUPCURR */
+ /* TODO: Implement vertical refresh. */
+ return s->upbase;
+ case 12: /* LCDLPCURR */
+ return s->lpbase;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "pl110_read: Bad offset %x\n", (int)offset);
+ return 0;
+ }
+}
+
+static void pl110_write(void *opaque, hwaddr offset,
+ uint64_t val, unsigned size)
+{
+ pl110_state *s = (pl110_state *)opaque;
+ int n;
+
+ /* For simplicity invalidate the display whenever a control register
+ is written to. */
+ s->invalidate = 1;
+ if (offset >= 0x200 && offset < 0x400) {
+ /* Palette. */
+ n = (offset - 0x200) >> 2;
+ s->raw_palette[(offset - 0x200) >> 2] = val;
+ pl110_update_palette(s, n);
+ return;
+ }
+ switch (offset >> 2) {
+ case 0: /* LCDTiming0 */
+ s->timing[0] = val;
+ n = ((val & 0xfc) + 4) * 4;
+ pl110_resize(s, n, s->rows);
+ break;
+ case 1: /* LCDTiming1 */
+ s->timing[1] = val;
+ n = (val & 0x3ff) + 1;
+ pl110_resize(s, s->cols, n);
+ break;
+ case 2: /* LCDTiming2 */
+ s->timing[2] = val;
+ break;
+ case 3: /* LCDTiming3 */
+ s->timing[3] = val;
+ break;
+ case 4: /* LCDUPBASE */
+ s->upbase = val;
+ break;
+ case 5: /* LCDLPBASE */
+ s->lpbase = val;
+ break;
+ case 6: /* LCDIMSC */
+ if (s->version != PL110) {
+ goto control;
+ }
+ imsc:
+ s->int_mask = val;
+ pl110_update(s);
+ break;
+ case 7: /* LCDControl */
+ if (s->version != PL110) {
+ goto imsc;
+ }
+ control:
+ s->cr = val;
+ s->bpp = (val >> 1) & 7;
+ if (pl110_enabled(s)) {
+ qemu_console_resize(s->con, s->cols, s->rows);
+ }
+ break;
+ case 10: /* LCDICR */
+ s->int_status &= ~val;
+ pl110_update(s);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "pl110_write: Bad offset %x\n", (int)offset);
+ }
+}
+
+static const MemoryRegionOps pl110_ops = {
+ .read = pl110_read,
+ .write = pl110_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void pl110_mux_ctrl_set(void *opaque, int line, int level)
+{
+ pl110_state *s = (pl110_state *)opaque;
+ s->mux_ctrl = level;
+}
+
+static int vmstate_pl110_post_load(void *opaque, int version_id)
+{
+ pl110_state *s = opaque;
+ /* Make sure we redraw, and at the right size */
+ pl110_invalidate_display(s);
+ return 0;
+}
+
+static int pl110_init(SysBusDevice *dev)
+{
+ pl110_state *s = FROM_SYSBUS(pl110_state, dev);
+
+ memory_region_init_io(&s->iomem, &pl110_ops, s, "pl110", 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq);
+ qdev_init_gpio_in(&s->busdev.qdev, pl110_mux_ctrl_set, 1);
+ s->con = graphic_console_init(pl110_update_display,
+ pl110_invalidate_display,
+ NULL, NULL, s);
+ return 0;
+}
+
+static int pl110_versatile_init(SysBusDevice *dev)
+{
+ pl110_state *s = FROM_SYSBUS(pl110_state, dev);
+ s->version = PL110_VERSATILE;
+ return pl110_init(dev);
+}
+
+static int pl111_init(SysBusDevice *dev)
+{
+ pl110_state *s = FROM_SYSBUS(pl110_state, dev);
+ s->version = PL111;
+ return pl110_init(dev);
+}
+
+static void pl110_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = pl110_init;
+ dc->no_user = 1;
+ dc->vmsd = &vmstate_pl110;
+}
+
+static const TypeInfo pl110_info = {
+ .name = "pl110",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(pl110_state),
+ .class_init = pl110_class_init,
+};
+
+static void pl110_versatile_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = pl110_versatile_init;
+ dc->no_user = 1;
+ dc->vmsd = &vmstate_pl110;
+}
+
+static const TypeInfo pl110_versatile_info = {
+ .name = "pl110_versatile",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(pl110_state),
+ .class_init = pl110_versatile_class_init,
+};
+
+static void pl111_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = pl111_init;
+ dc->no_user = 1;
+ dc->vmsd = &vmstate_pl110;
+}
+
+static const TypeInfo pl111_info = {
+ .name = "pl111",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(pl110_state),
+ .class_init = pl111_class_init,
+};
+
+static void pl110_register_types(void)
+{
+ type_register_static(&pl110_info);
+ type_register_static(&pl110_versatile_info);
+ type_register_static(&pl111_info);
+}
+
+type_init(pl110_register_types)
diff --git a/hw/display/pl110_template.h b/hw/display/pl110_template.h
new file mode 100644
index 0000000000..e738e4a241
--- /dev/null
+++ b/hw/display/pl110_template.h
@@ -0,0 +1,395 @@
+/*
+ * Arm PrimeCell PL110 Color LCD Controller
+ *
+ * Copyright (c) 2005 CodeSourcery, LLC.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GNU LGPL
+ *
+ * Framebuffer format conversion routines.
+ */
+
+#ifndef ORDER
+
+#if BITS == 8
+#define COPY_PIXEL(to, from) *(to++) = from
+#elif BITS == 15 || BITS == 16
+#define COPY_PIXEL(to, from) *(uint16_t *)to = from; to += 2;
+#elif BITS == 24
+#define COPY_PIXEL(to, from) \
+ *(to++) = from; *(to++) = (from) >> 8; *(to++) = (from) >> 16
+#elif BITS == 32
+#define COPY_PIXEL(to, from) *(uint32_t *)to = from; to += 4;
+#else
+#error unknown bit depth
+#endif
+
+#undef RGB
+#define BORDER bgr
+#define ORDER 0
+#include "pl110_template.h"
+#define ORDER 1
+#include "pl110_template.h"
+#define ORDER 2
+#include "pl110_template.h"
+#undef BORDER
+#define RGB
+#define BORDER rgb
+#define ORDER 0
+#include "pl110_template.h"
+#define ORDER 1
+#include "pl110_template.h"
+#define ORDER 2
+#include "pl110_template.h"
+#undef BORDER
+
+static drawfn glue(pl110_draw_fn_,BITS)[48] =
+{
+ glue(pl110_draw_line1_lblp_bgr,BITS),
+ glue(pl110_draw_line2_lblp_bgr,BITS),
+ glue(pl110_draw_line4_lblp_bgr,BITS),
+ glue(pl110_draw_line8_lblp_bgr,BITS),
+ glue(pl110_draw_line16_555_lblp_bgr,BITS),
+ glue(pl110_draw_line32_lblp_bgr,BITS),
+ glue(pl110_draw_line16_lblp_bgr,BITS),
+ glue(pl110_draw_line12_lblp_bgr,BITS),
+
+ glue(pl110_draw_line1_bbbp_bgr,BITS),
+ glue(pl110_draw_line2_bbbp_bgr,BITS),
+ glue(pl110_draw_line4_bbbp_bgr,BITS),
+ glue(pl110_draw_line8_bbbp_bgr,BITS),
+ glue(pl110_draw_line16_555_bbbp_bgr,BITS),
+ glue(pl110_draw_line32_bbbp_bgr,BITS),
+ glue(pl110_draw_line16_bbbp_bgr,BITS),
+ glue(pl110_draw_line12_bbbp_bgr,BITS),
+
+ glue(pl110_draw_line1_lbbp_bgr,BITS),
+ glue(pl110_draw_line2_lbbp_bgr,BITS),
+ glue(pl110_draw_line4_lbbp_bgr,BITS),
+ glue(pl110_draw_line8_lbbp_bgr,BITS),
+ glue(pl110_draw_line16_555_lbbp_bgr,BITS),
+ glue(pl110_draw_line32_lbbp_bgr,BITS),
+ glue(pl110_draw_line16_lbbp_bgr,BITS),
+ glue(pl110_draw_line12_lbbp_bgr,BITS),
+
+ glue(pl110_draw_line1_lblp_rgb,BITS),
+ glue(pl110_draw_line2_lblp_rgb,BITS),
+ glue(pl110_draw_line4_lblp_rgb,BITS),
+ glue(pl110_draw_line8_lblp_rgb,BITS),
+ glue(pl110_draw_line16_555_lblp_rgb,BITS),
+ glue(pl110_draw_line32_lblp_rgb,BITS),
+ glue(pl110_draw_line16_lblp_rgb,BITS),
+ glue(pl110_draw_line12_lblp_rgb,BITS),
+
+ glue(pl110_draw_line1_bbbp_rgb,BITS),
+ glue(pl110_draw_line2_bbbp_rgb,BITS),
+ glue(pl110_draw_line4_bbbp_rgb,BITS),
+ glue(pl110_draw_line8_bbbp_rgb,BITS),
+ glue(pl110_draw_line16_555_bbbp_rgb,BITS),
+ glue(pl110_draw_line32_bbbp_rgb,BITS),
+ glue(pl110_draw_line16_bbbp_rgb,BITS),
+ glue(pl110_draw_line12_bbbp_rgb,BITS),
+
+ glue(pl110_draw_line1_lbbp_rgb,BITS),
+ glue(pl110_draw_line2_lbbp_rgb,BITS),
+ glue(pl110_draw_line4_lbbp_rgb,BITS),
+ glue(pl110_draw_line8_lbbp_rgb,BITS),
+ glue(pl110_draw_line16_555_lbbp_rgb,BITS),
+ glue(pl110_draw_line32_lbbp_rgb,BITS),
+ glue(pl110_draw_line16_lbbp_rgb,BITS),
+ glue(pl110_draw_line12_lbbp_rgb,BITS),
+};
+
+#undef BITS
+#undef COPY_PIXEL
+
+#else
+
+#if ORDER == 0
+#define NAME glue(glue(lblp_, BORDER), BITS)
+#ifdef HOST_WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#endif
+#elif ORDER == 1
+#define NAME glue(glue(bbbp_, BORDER), BITS)
+#ifndef HOST_WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#endif
+#else
+#define SWAP_PIXELS 1
+#define NAME glue(glue(lbbp_, BORDER), BITS)
+#ifdef HOST_WORDS_BIGENDIAN
+#define SWAP_WORDS 1
+#endif
+#endif
+
+#define FN_2(x, y) FN(x, y) FN(x+1, y)
+#define FN_4(x, y) FN_2(x, y) FN_2(x+2, y)
+#define FN_8(y) FN_4(0, y) FN_4(4, y)
+
+static void glue(pl110_draw_line1_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *palette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_PIXELS
+#define FN(x, y) COPY_PIXEL(d, palette[(data >> (y + 7 - (x))) & 1]);
+#else
+#define FN(x, y) COPY_PIXEL(d, palette[(data >> ((x) + y)) & 1]);
+#endif
+#ifdef SWAP_WORDS
+ FN_8(24)
+ FN_8(16)
+ FN_8(8)
+ FN_8(0)
+#else
+ FN_8(0)
+ FN_8(8)
+ FN_8(16)
+ FN_8(24)
+#endif
+#undef FN
+ width -= 32;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line2_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *palette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_PIXELS
+#define FN(x, y) COPY_PIXEL(d, palette[(data >> (y + 6 - (x)*2)) & 3]);
+#else
+#define FN(x, y) COPY_PIXEL(d, palette[(data >> ((x)*2 + y)) & 3]);
+#endif
+#ifdef SWAP_WORDS
+ FN_4(0, 24)
+ FN_4(0, 16)
+ FN_4(0, 8)
+ FN_4(0, 0)
+#else
+ FN_4(0, 0)
+ FN_4(0, 8)
+ FN_4(0, 16)
+ FN_4(0, 24)
+#endif
+#undef FN
+ width -= 16;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line4_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *palette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_PIXELS
+#define FN(x, y) COPY_PIXEL(d, palette[(data >> (y + 4 - (x)*4)) & 0xf]);
+#else
+#define FN(x, y) COPY_PIXEL(d, palette[(data >> ((x)*4 + y)) & 0xf]);
+#endif
+#ifdef SWAP_WORDS
+ FN_2(0, 24)
+ FN_2(0, 16)
+ FN_2(0, 8)
+ FN_2(0, 0)
+#else
+ FN_2(0, 0)
+ FN_2(0, 8)
+ FN_2(0, 16)
+ FN_2(0, 24)
+#endif
+#undef FN
+ width -= 8;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line8_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *palette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#define FN(x) COPY_PIXEL(d, palette[(data >> (x)) & 0xff]);
+#ifdef SWAP_WORDS
+ FN(24)
+ FN(16)
+ FN(8)
+ FN(0)
+#else
+ FN(0)
+ FN(8)
+ FN(16)
+ FN(24)
+#endif
+#undef FN
+ width -= 4;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line16_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+#ifdef RGB
+#define LSB r
+#define MSB b
+#else
+#define LSB b
+#define MSB r
+#endif
+#if 0
+ LSB = data & 0x1f;
+ data >>= 5;
+ g = data & 0x3f;
+ data >>= 6;
+ MSB = data & 0x1f;
+ data >>= 5;
+#else
+ LSB = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ MSB = (data & 0x1f) << 3;
+ data >>= 5;
+#endif
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+ LSB = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ MSB = (data & 0x1f) << 3;
+ data >>= 5;
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+#undef MSB
+#undef LSB
+ width -= 2;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line32_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef RGB
+#define LSB r
+#define MSB b
+#else
+#define LSB b
+#define MSB r
+#endif
+#ifndef SWAP_WORDS
+ LSB = data & 0xff;
+ g = (data >> 8) & 0xff;
+ MSB = (data >> 16) & 0xff;
+#else
+ LSB = (data >> 24) & 0xff;
+ g = (data >> 16) & 0xff;
+ MSB = (data >> 8) & 0xff;
+#endif
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+#undef MSB
+#undef LSB
+ width--;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line16_555_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ /* RGB 555 plus an intensity bit (which we ignore) */
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+#ifdef RGB
+#define LSB r
+#define MSB b
+#else
+#define LSB b
+#define MSB r
+#endif
+ LSB = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x1f) << 3;
+ data >>= 5;
+ MSB = (data & 0x1f) << 3;
+ data >>= 5;
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+ LSB = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x1f) << 3;
+ data >>= 5;
+ MSB = (data & 0x1f) << 3;
+ data >>= 6;
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+#undef MSB
+#undef LSB
+ width -= 2;
+ src += 4;
+ }
+}
+
+static void glue(pl110_draw_line12_,NAME)(void *opaque, uint8_t *d, const uint8_t *src, int width, int deststep)
+{
+ /* RGB 444 with 4 bits of zeroes at the top of each halfword */
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *)src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+#ifdef RGB
+#define LSB r
+#define MSB b
+#else
+#define LSB b
+#define MSB r
+#endif
+ LSB = (data & 0xf) << 4;
+ data >>= 4;
+ g = (data & 0xf) << 4;
+ data >>= 4;
+ MSB = (data & 0xf) << 4;
+ data >>= 8;
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+ LSB = (data & 0xf) << 4;
+ data >>= 4;
+ g = (data & 0xf) << 4;
+ data >>= 4;
+ MSB = (data & 0xf) << 4;
+ data >>= 8;
+ COPY_PIXEL(d, glue(rgb_to_pixel,BITS)(r, g, b));
+#undef MSB
+#undef LSB
+ width -= 2;
+ src += 4;
+ }
+}
+
+#undef SWAP_PIXELS
+#undef NAME
+#undef SWAP_WORDS
+#undef ORDER
+
+#endif
diff --git a/hw/display/pxa2xx_lcd.c b/hw/display/pxa2xx_lcd.c
new file mode 100644
index 0000000000..c9bd42e023
--- /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 <balrog@zabor.org>
+ *
+ * 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 "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 "pxa2xx_template.h"
+#define BITS 15
+#include "pxa2xx_template.h"
+#define BITS 16
+#include "pxa2xx_template.h"
+#define BITS 24
+#include "pxa2xx_template.h"
+#define BITS 32
+#include "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/pxa2xx_template.h b/hw/display/pxa2xx_template.h
new file mode 100644
index 0000000000..1cbe36cb80
--- /dev/null
+++ b/hw/display/pxa2xx_template.h
@@ -0,0 +1,435 @@
+/*
+ * Intel XScale PXA255/270 LCDC emulation.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licensed under the GPLv2.
+ *
+ * Framebuffer format conversion routines.
+ */
+
+# define SKIP_PIXEL(to) to += deststep
+#if BITS == 8
+# define COPY_PIXEL(to, from) *to = from; SKIP_PIXEL(to)
+#elif BITS == 15 || BITS == 16
+# define COPY_PIXEL(to, from) *(uint16_t *) to = from; SKIP_PIXEL(to)
+#elif BITS == 24
+# define COPY_PIXEL(to, from) \
+ *(uint16_t *) to = from; *(to + 2) = (from) >> 16; SKIP_PIXEL(to)
+#elif BITS == 32
+# define COPY_PIXEL(to, from) *(uint32_t *) to = from; SKIP_PIXEL(to)
+#else
+# error unknown bit depth
+#endif
+
+#ifdef HOST_WORDS_BIGENDIAN
+# define SWAP_WORDS 1
+#endif
+
+#define FN_2(x) FN(x + 1) FN(x)
+#define FN_4(x) FN_2(x + 2) FN_2(x)
+
+static void glue(pxa2xx_draw_line2_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *palette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#define FN(x) COPY_PIXEL(dest, palette[(data >> ((x) * 2)) & 3]);
+#ifdef SWAP_WORDS
+ FN_4(12)
+ FN_4(8)
+ FN_4(4)
+ FN_4(0)
+#else
+ FN_4(0)
+ FN_4(4)
+ FN_4(8)
+ FN_4(12)
+#endif
+#undef FN
+ width -= 16;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line4_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *palette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#define FN(x) COPY_PIXEL(dest, palette[(data >> ((x) * 4)) & 0xf]);
+#ifdef SWAP_WORDS
+ FN_2(6)
+ FN_2(4)
+ FN_2(2)
+ FN_2(0)
+#else
+ FN_2(0)
+ FN_2(2)
+ FN_2(4)
+ FN_2(6)
+#endif
+#undef FN
+ width -= 8;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line8_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t *palette = opaque;
+ uint32_t data;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#define FN(x) COPY_PIXEL(dest, palette[(data >> (x)) & 0xff]);
+#ifdef SWAP_WORDS
+ FN(24)
+ FN(16)
+ FN(8)
+ FN(0)
+#else
+ FN(0)
+ FN(8)
+ FN(16)
+ FN(24)
+#endif
+#undef FN
+ width -= 4;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line16_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ r = (data & 0x1f) << 3;
+ data >>= 5;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ r = (data & 0x1f) << 3;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 2;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line16t_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x1f) << 3;
+ data >>= 5;
+ r = (data & 0x1f) << 3;
+ data >>= 5;
+ if (data & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ data >>= 1;
+ b = (data & 0x1f) << 3;
+ data >>= 5;
+ g = (data & 0x1f) << 3;
+ data >>= 5;
+ r = (data & 0x1f) << 3;
+ data >>= 5;
+ if (data & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 2;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line18_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = (data & 0x3f) << 2;
+ data >>= 6;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ r = (data & 0x3f) << 2;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 1;
+ src += 4;
+ }
+}
+
+/* The wicked packed format */
+static void glue(pxa2xx_draw_line18p_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data[3];
+ unsigned int r, g, b;
+ while (width > 0) {
+ data[0] = *(uint32_t *) src;
+ src += 4;
+ data[1] = *(uint32_t *) src;
+ src += 4;
+ data[2] = *(uint32_t *) src;
+ src += 4;
+#ifdef SWAP_WORDS
+ data[0] = bswap32(data[0]);
+ data[1] = bswap32(data[1]);
+ data[2] = bswap32(data[2]);
+#endif
+ b = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ g = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ r = (data[0] & 0x3f) << 2;
+ data[0] >>= 12;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ b = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ g = ((data[1] & 0xf) << 4) | (data[0] << 2);
+ data[1] >>= 4;
+ r = (data[1] & 0x3f) << 2;
+ data[1] >>= 12;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ b = (data[1] & 0x3f) << 2;
+ data[1] >>= 6;
+ g = (data[1] & 0x3f) << 2;
+ data[1] >>= 6;
+ r = ((data[2] & 0x3) << 6) | (data[1] << 2);
+ data[2] >>= 8;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ b = (data[2] & 0x3f) << 2;
+ data[2] >>= 6;
+ g = (data[2] & 0x3f) << 2;
+ data[2] >>= 6;
+ r = data[2] << 2;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line19_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = (data & 0x3f) << 2;
+ data >>= 6;
+ g = (data & 0x3f) << 2;
+ data >>= 6;
+ r = (data & 0x3f) << 2;
+ data >>= 6;
+ if (data & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 1;
+ src += 4;
+ }
+}
+
+/* The wicked packed format */
+static void glue(pxa2xx_draw_line19p_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data[3];
+ unsigned int r, g, b;
+ while (width > 0) {
+ data[0] = *(uint32_t *) src;
+ src += 4;
+ data[1] = *(uint32_t *) src;
+ src += 4;
+ data[2] = *(uint32_t *) src;
+ src += 4;
+# ifdef SWAP_WORDS
+ data[0] = bswap32(data[0]);
+ data[1] = bswap32(data[1]);
+ data[2] = bswap32(data[2]);
+# endif
+ b = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ g = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ r = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ if (data[0] & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ data[0] >>= 6;
+ b = (data[0] & 0x3f) << 2;
+ data[0] >>= 6;
+ g = ((data[1] & 0xf) << 4) | (data[0] << 2);
+ data[1] >>= 4;
+ r = (data[1] & 0x3f) << 2;
+ data[1] >>= 6;
+ if (data[1] & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ data[1] >>= 6;
+ b = (data[1] & 0x3f) << 2;
+ data[1] >>= 6;
+ g = (data[1] & 0x3f) << 2;
+ data[1] >>= 6;
+ r = ((data[2] & 0x3) << 6) | (data[1] << 2);
+ data[2] >>= 2;
+ if (data[2] & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ data[2] >>= 6;
+ b = (data[2] & 0x3f) << 2;
+ data[2] >>= 6;
+ g = (data[2] & 0x3f) << 2;
+ data[2] >>= 6;
+ r = data[2] << 2;
+ data[2] >>= 6;
+ if (data[2] & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line24_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = data & 0xff;
+ data >>= 8;
+ g = data & 0xff;
+ data >>= 8;
+ r = data & 0xff;
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 1;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line24t_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = (data & 0x7f) << 1;
+ data >>= 7;
+ g = data & 0xff;
+ data >>= 8;
+ r = data & 0xff;
+ data >>= 8;
+ if (data & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 1;
+ src += 4;
+ }
+}
+
+static void glue(pxa2xx_draw_line25_, BITS)(void *opaque,
+ uint8_t *dest, const uint8_t *src, int width, int deststep)
+{
+ uint32_t data;
+ unsigned int r, g, b;
+ while (width > 0) {
+ data = *(uint32_t *) src;
+#ifdef SWAP_WORDS
+ data = bswap32(data);
+#endif
+ b = data & 0xff;
+ data >>= 8;
+ g = data & 0xff;
+ data >>= 8;
+ r = data & 0xff;
+ data >>= 8;
+ if (data & 1)
+ SKIP_PIXEL(dest);
+ else
+ COPY_PIXEL(dest, glue(rgb_to_pixel, BITS)(r, g, b));
+ width -= 1;
+ src += 4;
+ }
+}
+
+/* Overlay planes disabled, no transparency */
+static drawfn glue(pxa2xx_draw_fn_, BITS)[16] =
+{
+ [0 ... 0xf] = NULL,
+ [pxa_lcdc_2bpp] = glue(pxa2xx_draw_line2_, BITS),
+ [pxa_lcdc_4bpp] = glue(pxa2xx_draw_line4_, BITS),
+ [pxa_lcdc_8bpp] = glue(pxa2xx_draw_line8_, BITS),
+ [pxa_lcdc_16bpp] = glue(pxa2xx_draw_line16_, BITS),
+ [pxa_lcdc_18bpp] = glue(pxa2xx_draw_line18_, BITS),
+ [pxa_lcdc_18pbpp] = glue(pxa2xx_draw_line18p_, BITS),
+ [pxa_lcdc_24bpp] = glue(pxa2xx_draw_line24_, BITS),
+};
+
+/* Overlay planes enabled, transparency used */
+static drawfn glue(glue(pxa2xx_draw_fn_, BITS), t)[16] =
+{
+ [0 ... 0xf] = NULL,
+ [pxa_lcdc_4bpp] = glue(pxa2xx_draw_line4_, BITS),
+ [pxa_lcdc_8bpp] = glue(pxa2xx_draw_line8_, BITS),
+ [pxa_lcdc_16bpp] = glue(pxa2xx_draw_line16t_, BITS),
+ [pxa_lcdc_19bpp] = glue(pxa2xx_draw_line19_, BITS),
+ [pxa_lcdc_19pbpp] = glue(pxa2xx_draw_line19p_, BITS),
+ [pxa_lcdc_24bpp] = glue(pxa2xx_draw_line24t_, BITS),
+ [pxa_lcdc_25bpp] = glue(pxa2xx_draw_line25_, BITS),
+};
+
+#undef BITS
+#undef COPY_PIXEL
+#undef SKIP_PIXEL
+
+#ifdef SWAP_WORDS
+# undef SWAP_WORDS
+#endif
diff --git a/hw/display/qxl-logger.c b/hw/display/qxl-logger.c
new file mode 100644
index 0000000000..3cd85d9b97
--- /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 <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/timer.h"
+#include "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(&copy->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..f511a622c6
--- /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 <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "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..930b7cf2ad
--- /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 <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <zlib.h>
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "qemu/queue.h"
+#include "monitor/monitor.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+
+#include "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/qxl.h b/hw/display/qxl.h
new file mode 100644
index 0000000000..8e9b0c299e
--- /dev/null
+++ b/hw/display/qxl.h
@@ -0,0 +1,165 @@
+#ifndef HW_QXL_H
+#define HW_QXL_H 1
+
+#include "qemu-common.h"
+
+#include "ui/console.h"
+#include "hw/hw.h"
+#include "hw/pci/pci.h"
+#include "vga_int.h"
+#include "qemu/thread.h"
+
+#include "ui/qemu-spice.h"
+#include "ui/spice-display.h"
+
+enum qxl_mode {
+ QXL_MODE_UNDEFINED,
+ QXL_MODE_VGA,
+ QXL_MODE_COMPAT, /* spice 0.4.x */
+ QXL_MODE_NATIVE,
+};
+
+#ifndef QXL_VRAM64_RANGE_INDEX
+#define QXL_VRAM64_RANGE_INDEX 4
+#endif
+
+#define QXL_UNDEFINED_IO UINT32_MAX
+
+#define QXL_NUM_DIRTY_RECTS 64
+
+typedef struct PCIQXLDevice {
+ PCIDevice pci;
+ SimpleSpiceDisplay ssd;
+ int id;
+ uint32_t debug;
+ uint32_t guestdebug;
+ uint32_t cmdlog;
+
+ uint32_t guest_bug;
+
+ enum qxl_mode mode;
+ uint32_t cmdflags;
+ int generation;
+ uint32_t revision;
+
+ int32_t num_memslots;
+
+ uint32_t current_async;
+ QemuMutex async_lock;
+
+ struct guest_slots {
+ QXLMemSlot slot;
+ void *ptr;
+ uint64_t size;
+ uint64_t delta;
+ uint32_t active;
+ } guest_slots[NUM_MEMSLOTS];
+
+ struct guest_primary {
+ QXLSurfaceCreate surface;
+ uint32_t commands;
+ uint32_t resized;
+ int32_t qxl_stride;
+ uint32_t abs_stride;
+ uint32_t bits_pp;
+ uint32_t bytes_pp;
+ uint8_t *data;
+ } guest_primary;
+
+ struct surfaces {
+ QXLPHYSICAL *cmds;
+ uint32_t count;
+ uint32_t max;
+ } guest_surfaces;
+ QXLPHYSICAL guest_cursor;
+
+ QXLPHYSICAL guest_monitors_config;
+
+ QemuMutex track_lock;
+
+ /* thread signaling */
+ QemuThread main;
+ int pipe[2];
+
+ /* ram pci bar */
+ QXLRam *ram;
+ VGACommonState vga;
+ uint32_t num_free_res;
+ QXLReleaseInfo *last_release;
+ uint32_t last_release_offset;
+ uint32_t oom_running;
+ uint32_t vgamem_size;
+
+ /* rom pci bar */
+ QXLRom shadow_rom;
+ QXLRom *rom;
+ QXLModes *modes;
+ uint32_t rom_size;
+ MemoryRegion rom_bar;
+
+ /* vram pci bar */
+ uint32_t vram_size;
+ MemoryRegion vram_bar;
+ uint32_t vram32_size;
+ MemoryRegion vram32_bar;
+
+ /* io bar */
+ MemoryRegion io_bar;
+
+ /* user-friendly properties (in megabytes) */
+ uint32_t ram_size_mb;
+ uint32_t vram_size_mb;
+ uint32_t vram32_size_mb;
+ uint32_t vgamem_size_mb;
+
+ /* qxl_render_update state */
+ int render_update_cookie_num;
+ int num_dirty_rects;
+ QXLRect dirty[QXL_NUM_DIRTY_RECTS];
+ QEMUBH *update_area_bh;
+} PCIQXLDevice;
+
+#define PANIC_ON(x) if ((x)) { \
+ printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \
+ abort(); \
+}
+
+#define dprint(_qxl, _level, _fmt, ...) \
+ do { \
+ if (_qxl->debug >= _level) { \
+ fprintf(stderr, "qxl-%d: ", _qxl->id); \
+ fprintf(stderr, _fmt, ## __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V12
+
+/* qxl.c */
+void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
+void qxl_set_guest_bug(PCIQXLDevice *qxl, const char *msg, ...)
+ GCC_FMT_ATTR(2, 3);
+
+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, QXLCookie *cookie);
+void qxl_spice_loadvm_commands(PCIQXLDevice *qxl, struct QXLCommandExt *ext,
+ uint32_t count);
+void qxl_spice_oom(PCIQXLDevice *qxl);
+void qxl_spice_reset_memslots(PCIQXLDevice *qxl);
+void qxl_spice_reset_image_cache(PCIQXLDevice *qxl);
+void qxl_spice_reset_cursor(PCIQXLDevice *qxl);
+
+/* qxl-logger.c */
+int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id);
+int qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext);
+
+/* qxl-render.c */
+void qxl_render_resize(PCIQXLDevice *qxl);
+void qxl_render_update(PCIQXLDevice *qxl);
+int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext);
+void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie);
+void qxl_render_update_area_bh(void *opaque);
+
+#endif
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
new file mode 100644
index 0000000000..6b660ac1c0
--- /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 <stdio.h>
+#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 "sm501_template.h"
+
+#define DEPTH 15
+#include "sm501_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 15
+#include "sm501_template.h"
+
+#define DEPTH 16
+#include "sm501_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 16
+#include "sm501_template.h"
+
+#define DEPTH 32
+#include "sm501_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 32
+#include "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/sm501_template.h b/hw/display/sm501_template.h
new file mode 100644
index 0000000000..2d4a3d8b48
--- /dev/null
+++ b/hw/display/sm501_template.h
@@ -0,0 +1,145 @@
+/*
+ * Pixel drawing function templates for 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.
+ */
+
+#if DEPTH == 8
+#define BPP 1
+#define PIXEL_TYPE uint8_t
+#elif DEPTH == 15 || DEPTH == 16
+#define BPP 2
+#define PIXEL_TYPE uint16_t
+#elif DEPTH == 32
+#define BPP 4
+#define PIXEL_TYPE uint32_t
+#else
+#error unsupport depth
+#endif
+
+#ifdef BGR_FORMAT
+#define PIXEL_NAME glue(DEPTH, bgr)
+#else
+#define PIXEL_NAME DEPTH
+#endif /* BGR_FORMAT */
+
+
+static void glue(draw_line8_, PIXEL_NAME)(
+ uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
+{
+ uint8_t v, r, g, b;
+ do {
+ v = ldub_raw(s);
+ r = (pal[v] >> 16) & 0xff;
+ g = (pal[v] >> 8) & 0xff;
+ b = (pal[v] >> 0) & 0xff;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s ++;
+ d += BPP;
+ } while (-- width != 0);
+}
+
+static void glue(draw_line16_, PIXEL_NAME)(
+ uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
+{
+ uint16_t rgb565;
+ uint8_t r, g, b;
+
+ do {
+ rgb565 = lduw_raw(s);
+ r = ((rgb565 >> 11) & 0x1f) << 3;
+ g = ((rgb565 >> 5) & 0x3f) << 2;
+ b = ((rgb565 >> 0) & 0x1f) << 3;
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 2;
+ d += BPP;
+ } while (-- width != 0);
+}
+
+static void glue(draw_line32_, PIXEL_NAME)(
+ uint8_t *d, const uint8_t *s, int width, const uint32_t *pal)
+{
+ uint8_t r, g, b;
+
+ do {
+ ldub_raw(s);
+#if defined(TARGET_WORDS_BIGENDIAN)
+ r = s[1];
+ g = s[2];
+ b = s[3];
+#else
+ b = s[0];
+ g = s[1];
+ r = s[2];
+#endif
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 4;
+ d += BPP;
+ } while (-- width != 0);
+}
+
+/**
+ * Draw hardware cursor image on the given line.
+ */
+static void glue(draw_hwc_line_, PIXEL_NAME)(SM501State * s, int crt,
+ uint8_t * palette, int c_y, uint8_t *d, int width)
+{
+ int x, i;
+ uint8_t bitset = 0;
+
+ /* get hardware cursor pattern */
+ uint32_t cursor_addr = get_hwc_address(s, crt);
+ assert(0 <= c_y && c_y < SM501_HWC_HEIGHT);
+ cursor_addr += 64 * c_y / 4; /* 4 pixels per byte */
+ cursor_addr += s->base;
+
+ /* get cursor position */
+ x = get_hwc_x(s, crt);
+ d += x * BPP;
+
+ for (i = 0; i < SM501_HWC_WIDTH && x + i < width; i++) {
+ uint8_t v;
+
+ /* get pixel value */
+ if (i % 4 == 0) {
+ bitset = ldub_phys(cursor_addr);
+ cursor_addr++;
+ }
+ v = bitset & 3;
+ bitset >>= 2;
+
+ /* write pixel */
+ if (v) {
+ v--;
+ uint8_t r = palette[v * 3 + 0];
+ uint8_t g = palette[v * 3 + 1];
+ uint8_t b = palette[v * 3 + 2];
+ ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ }
+ d += BPP;
+ }
+}
+
+#undef DEPTH
+#undef BPP
+#undef PIXEL_TYPE
+#undef PIXEL_NAME
+#undef BGR_FORMAT
diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c
new file mode 100644
index 0000000000..183a87835c
--- /dev/null
+++ b/hw/display/ssd0303.c
@@ -0,0 +1,322 @@
+/*
+ * SSD0303 OLED controller with OSRAM Pictiva 96x16 display.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+/* The controller can support a variety of different displays, but we only
+ implement one. Most of the commends relating to brightness and geometry
+ setup are ignored. */
+#include "hw/i2c/i2c.h"
+#include "ui/console.h"
+
+//#define DEBUG_SSD0303 1
+
+#ifdef DEBUG_SSD0303
+#define DPRINTF(fmt, ...) \
+do { printf("ssd0303: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+/* Scaling factor for pixels. */
+#define MAGNIFY 4
+
+enum ssd0303_mode
+{
+ SSD0303_IDLE,
+ SSD0303_DATA,
+ SSD0303_CMD
+};
+
+enum ssd0303_cmd {
+ SSD0303_CMD_NONE,
+ SSD0303_CMD_SKIP1
+};
+
+typedef struct {
+ I2CSlave i2c;
+ QemuConsole *con;
+ int row;
+ int col;
+ int start_line;
+ int mirror;
+ int flash;
+ int enabled;
+ int inverse;
+ int redraw;
+ enum ssd0303_mode mode;
+ enum ssd0303_cmd cmd_state;
+ uint8_t framebuffer[132*8];
+} ssd0303_state;
+
+static int ssd0303_recv(I2CSlave *i2c)
+{
+ BADF("Reads not implemented\n");
+ return -1;
+}
+
+static int ssd0303_send(I2CSlave *i2c, uint8_t data)
+{
+ ssd0303_state *s = (ssd0303_state *)i2c;
+ enum ssd0303_cmd old_cmd_state;
+ switch (s->mode) {
+ case SSD0303_IDLE:
+ DPRINTF("byte 0x%02x\n", data);
+ if (data == 0x80)
+ s->mode = SSD0303_CMD;
+ else if (data == 0x40)
+ s->mode = SSD0303_DATA;
+ else
+ BADF("Unexpected byte 0x%x\n", data);
+ break;
+ case SSD0303_DATA:
+ DPRINTF("data 0x%02x\n", data);
+ if (s->col < 132) {
+ s->framebuffer[s->col + s->row * 132] = data;
+ s->col++;
+ s->redraw = 1;
+ }
+ break;
+ case SSD0303_CMD:
+ old_cmd_state = s->cmd_state;
+ s->cmd_state = SSD0303_CMD_NONE;
+ switch (old_cmd_state) {
+ case SSD0303_CMD_NONE:
+ DPRINTF("cmd 0x%02x\n", data);
+ s->mode = SSD0303_IDLE;
+ switch (data) {
+ case 0x00 ... 0x0f: /* Set lower column address. */
+ s->col = (s->col & 0xf0) | (data & 0xf);
+ break;
+ case 0x10 ... 0x20: /* Set higher column address. */
+ s->col = (s->col & 0x0f) | ((data & 0xf) << 4);
+ break;
+ case 0x40 ... 0x7f: /* Set start line. */
+ s->start_line = 0;
+ break;
+ case 0x81: /* Set contrast (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xa0: /* Mirror off. */
+ s->mirror = 0;
+ break;
+ case 0xa1: /* Mirror off. */
+ s->mirror = 1;
+ break;
+ case 0xa4: /* Entire display off. */
+ s->flash = 0;
+ break;
+ case 0xa5: /* Entire display on. */
+ s->flash = 1;
+ break;
+ case 0xa6: /* Inverse off. */
+ s->inverse = 0;
+ break;
+ case 0xa7: /* Inverse on. */
+ s->inverse = 1;
+ break;
+ case 0xa8: /* Set multiplied ratio (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xad: /* DC-DC power control. */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xae: /* Display off. */
+ s->enabled = 0;
+ break;
+ case 0xaf: /* Display on. */
+ s->enabled = 1;
+ break;
+ case 0xb0 ... 0xbf: /* Set Page address. */
+ s->row = data & 7;
+ break;
+ case 0xc0 ... 0xc8: /* Set COM output direction (Ignored). */
+ break;
+ case 0xd3: /* Set display offset (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xd5: /* Set display clock (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xd8: /* Set color and power mode (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xd9: /* Set pre-charge period (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xda: /* Set COM pin configuration (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xdb: /* Set VCOM dselect level (Ignored). */
+ s->cmd_state = SSD0303_CMD_SKIP1;
+ break;
+ case 0xe3: /* no-op. */
+ break;
+ default:
+ BADF("Unknown command: 0x%x\n", data);
+ }
+ break;
+ case SSD0303_CMD_SKIP1:
+ DPRINTF("skip 0x%02x\n", data);
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+static void ssd0303_event(I2CSlave *i2c, enum i2c_event event)
+{
+ ssd0303_state *s = (ssd0303_state *)i2c;
+ switch (event) {
+ case I2C_FINISH:
+ s->mode = SSD0303_IDLE;
+ break;
+ case I2C_START_RECV:
+ case I2C_START_SEND:
+ case I2C_NACK:
+ /* Nothing to do. */
+ break;
+ }
+}
+
+static void ssd0303_update_display(void *opaque)
+{
+ ssd0303_state *s = (ssd0303_state *)opaque;
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ uint8_t *dest;
+ uint8_t *src;
+ int x;
+ int y;
+ int line;
+ char *colors[2];
+ char colortab[MAGNIFY * 8];
+ int dest_width;
+ uint8_t mask;
+
+ if (!s->redraw)
+ return;
+
+ switch (surface_bits_per_pixel(surface)) {
+ case 0:
+ return;
+ case 15:
+ dest_width = 2;
+ break;
+ case 16:
+ dest_width = 2;
+ break;
+ case 24:
+ dest_width = 3;
+ break;
+ case 32:
+ dest_width = 4;
+ break;
+ default:
+ BADF("Bad color depth\n");
+ return;
+ }
+ dest_width *= MAGNIFY;
+ memset(colortab, 0xff, dest_width);
+ memset(colortab + dest_width, 0, dest_width);
+ if (s->flash) {
+ colors[0] = colortab;
+ colors[1] = colortab;
+ } else if (s->inverse) {
+ colors[0] = colortab;
+ colors[1] = colortab + dest_width;
+ } else {
+ colors[0] = colortab + dest_width;
+ colors[1] = colortab;
+ }
+ dest = surface_data(surface);
+ for (y = 0; y < 16; y++) {
+ line = (y + s->start_line) & 63;
+ src = s->framebuffer + 132 * (line >> 3) + 36;
+ mask = 1 << (line & 7);
+ for (x = 0; x < 96; x++) {
+ memcpy(dest, colors[(*src & mask) != 0], dest_width);
+ dest += dest_width;
+ src++;
+ }
+ for (x = 1; x < MAGNIFY; x++) {
+ memcpy(dest, dest - dest_width * 96, dest_width * 96);
+ dest += dest_width * 96;
+ }
+ }
+ s->redraw = 0;
+ dpy_gfx_update(s->con, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
+}
+
+static void ssd0303_invalidate_display(void * opaque)
+{
+ ssd0303_state *s = (ssd0303_state *)opaque;
+ s->redraw = 1;
+}
+
+static const VMStateDescription vmstate_ssd0303 = {
+ .name = "ssd0303_oled",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_INT32(row, ssd0303_state),
+ VMSTATE_INT32(col, ssd0303_state),
+ VMSTATE_INT32(start_line, ssd0303_state),
+ VMSTATE_INT32(mirror, ssd0303_state),
+ VMSTATE_INT32(flash, ssd0303_state),
+ VMSTATE_INT32(enabled, ssd0303_state),
+ VMSTATE_INT32(inverse, ssd0303_state),
+ VMSTATE_INT32(redraw, ssd0303_state),
+ VMSTATE_UINT32(mode, ssd0303_state),
+ VMSTATE_UINT32(cmd_state, ssd0303_state),
+ VMSTATE_BUFFER(framebuffer, ssd0303_state),
+ VMSTATE_I2C_SLAVE(i2c, ssd0303_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int ssd0303_init(I2CSlave *i2c)
+{
+ ssd0303_state *s = FROM_I2C_SLAVE(ssd0303_state, i2c);
+
+ s->con = graphic_console_init(ssd0303_update_display,
+ ssd0303_invalidate_display,
+ NULL, NULL, s);
+ qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY);
+ return 0;
+}
+
+static void ssd0303_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+ k->init = ssd0303_init;
+ k->event = ssd0303_event;
+ k->recv = ssd0303_recv;
+ k->send = ssd0303_send;
+ dc->vmsd = &vmstate_ssd0303;
+}
+
+static const TypeInfo ssd0303_info = {
+ .name = "ssd0303",
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(ssd0303_state),
+ .class_init = ssd0303_class_init,
+};
+
+static void ssd0303_register_types(void)
+{
+ type_register_static(&ssd0303_info);
+}
+
+type_init(ssd0303_register_types)
diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c
new file mode 100644
index 0000000000..5cf2f7058f
--- /dev/null
+++ b/hw/display/ssd0323.c
@@ -0,0 +1,373 @@
+/*
+ * SSD0323 OLED controller with OSRAM Pictiva 128x64 display.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licensed under the GPL.
+ */
+
+/* The controller can support a variety of different displays, but we only
+ implement one. Most of the commends relating to brightness and geometry
+ setup are ignored. */
+#include "hw/ssi.h"
+#include "ui/console.h"
+
+//#define DEBUG_SSD0323 1
+
+#ifdef DEBUG_SSD0323
+#define DPRINTF(fmt, ...) \
+do { printf("ssd0323: " fmt , ## __VA_ARGS__); } while (0)
+#define BADF(fmt, ...) \
+do { \
+ fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); abort(); \
+} while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__);} while (0)
+#endif
+
+/* Scaling factor for pixels. */
+#define MAGNIFY 4
+
+#define REMAP_SWAP_COLUMN 0x01
+#define REMAP_SWAP_NYBBLE 0x02
+#define REMAP_VERTICAL 0x04
+#define REMAP_SWAP_COM 0x10
+#define REMAP_SPLIT_COM 0x40
+
+enum ssd0323_mode
+{
+ SSD0323_CMD,
+ SSD0323_DATA
+};
+
+typedef struct {
+ SSISlave ssidev;
+ QemuConsole *con;
+
+ int cmd_len;
+ int cmd;
+ int cmd_data[8];
+ int row;
+ int row_start;
+ int row_end;
+ int col;
+ int col_start;
+ int col_end;
+ int redraw;
+ int remap;
+ enum ssd0323_mode mode;
+ uint8_t framebuffer[128 * 80 / 2];
+} ssd0323_state;
+
+static uint32_t ssd0323_transfer(SSISlave *dev, uint32_t data)
+{
+ ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev);
+
+ switch (s->mode) {
+ case SSD0323_DATA:
+ DPRINTF("data 0x%02x\n", data);
+ s->framebuffer[s->col + s->row * 64] = data;
+ if (s->remap & REMAP_VERTICAL) {
+ s->row++;
+ if (s->row > s->row_end) {
+ s->row = s->row_start;
+ s->col++;
+ }
+ if (s->col > s->col_end) {
+ s->col = s->col_start;
+ }
+ } else {
+ s->col++;
+ if (s->col > s->col_end) {
+ s->row++;
+ s->col = s->col_start;
+ }
+ if (s->row > s->row_end) {
+ s->row = s->row_start;
+ }
+ }
+ s->redraw = 1;
+ break;
+ case SSD0323_CMD:
+ DPRINTF("cmd 0x%02x\n", data);
+ if (s->cmd_len == 0) {
+ s->cmd = data;
+ } else {
+ s->cmd_data[s->cmd_len - 1] = data;
+ }
+ s->cmd_len++;
+ switch (s->cmd) {
+#define DATA(x) if (s->cmd_len <= (x)) return 0
+ case 0x15: /* Set column. */
+ DATA(2);
+ s->col = s->col_start = s->cmd_data[0] % 64;
+ s->col_end = s->cmd_data[1] % 64;
+ break;
+ case 0x75: /* Set row. */
+ DATA(2);
+ s->row = s->row_start = s->cmd_data[0] % 80;
+ s->row_end = s->cmd_data[1] % 80;
+ break;
+ case 0x81: /* Set contrast */
+ DATA(1);
+ break;
+ case 0x84: case 0x85: case 0x86: /* Max current. */
+ DATA(0);
+ break;
+ case 0xa0: /* Set remapping. */
+ /* FIXME: Implement this. */
+ DATA(1);
+ s->remap = s->cmd_data[0];
+ break;
+ case 0xa1: /* Set display start line. */
+ case 0xa2: /* Set display offset. */
+ /* FIXME: Implement these. */
+ DATA(1);
+ break;
+ case 0xa4: /* Normal mode. */
+ case 0xa5: /* All on. */
+ case 0xa6: /* All off. */
+ case 0xa7: /* Inverse. */
+ /* FIXME: Implement these. */
+ DATA(0);
+ break;
+ case 0xa8: /* Set multiplex ratio. */
+ case 0xad: /* Set DC-DC converter. */
+ DATA(1);
+ /* Ignored. Don't care. */
+ break;
+ case 0xae: /* Display off. */
+ case 0xaf: /* Display on. */
+ DATA(0);
+ /* TODO: Implement power control. */
+ break;
+ case 0xb1: /* Set phase length. */
+ case 0xb2: /* Set row period. */
+ case 0xb3: /* Set clock rate. */
+ case 0xbc: /* Set precharge. */
+ case 0xbe: /* Set VCOMH. */
+ case 0xbf: /* Set segment low. */
+ DATA(1);
+ /* Ignored. Don't care. */
+ break;
+ case 0xb8: /* Set grey scale table. */
+ /* FIXME: Implement this. */
+ DATA(8);
+ break;
+ case 0xe3: /* NOP. */
+ DATA(0);
+ break;
+ case 0xff: /* Nasty hack because we don't handle chip selects
+ properly. */
+ break;
+ default:
+ BADF("Unknown command: 0x%x\n", data);
+ }
+ s->cmd_len = 0;
+ return 0;
+ }
+ return 0;
+}
+
+static void ssd0323_update_display(void *opaque)
+{
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ uint8_t *dest;
+ uint8_t *src;
+ int x;
+ int y;
+ int i;
+ int line;
+ char *colors[16];
+ char colortab[MAGNIFY * 64];
+ char *p;
+ int dest_width;
+
+ if (!s->redraw)
+ return;
+
+ switch (surface_bits_per_pixel(surface)) {
+ case 0:
+ return;
+ case 15:
+ dest_width = 2;
+ break;
+ case 16:
+ dest_width = 2;
+ break;
+ case 24:
+ dest_width = 3;
+ break;
+ case 32:
+ dest_width = 4;
+ break;
+ default:
+ BADF("Bad color depth\n");
+ return;
+ }
+ p = colortab;
+ for (i = 0; i < 16; i++) {
+ int n;
+ colors[i] = p;
+ switch (surface_bits_per_pixel(surface)) {
+ case 15:
+ n = i * 2 + (i >> 3);
+ p[0] = n | (n << 5);
+ p[1] = (n << 2) | (n >> 3);
+ break;
+ case 16:
+ n = i * 2 + (i >> 3);
+ p[0] = n | (n << 6) | ((n << 1) & 0x20);
+ p[1] = (n << 3) | (n >> 2);
+ break;
+ case 24:
+ case 32:
+ n = (i << 4) | i;
+ p[0] = p[1] = p[2] = n;
+ break;
+ default:
+ BADF("Bad color depth\n");
+ return;
+ }
+ p += dest_width;
+ }
+ /* TODO: Implement row/column remapping. */
+ dest = surface_data(surface);
+ for (y = 0; y < 64; y++) {
+ line = y;
+ src = s->framebuffer + 64 * line;
+ for (x = 0; x < 64; x++) {
+ int val;
+ val = *src >> 4;
+ for (i = 0; i < MAGNIFY; i++) {
+ memcpy(dest, colors[val], dest_width);
+ dest += dest_width;
+ }
+ val = *src & 0xf;
+ for (i = 0; i < MAGNIFY; i++) {
+ memcpy(dest, colors[val], dest_width);
+ dest += dest_width;
+ }
+ src++;
+ }
+ for (i = 1; i < MAGNIFY; i++) {
+ memcpy(dest, dest - dest_width * MAGNIFY * 128,
+ dest_width * 128 * MAGNIFY);
+ dest += dest_width * 128 * MAGNIFY;
+ }
+ }
+ s->redraw = 0;
+ dpy_gfx_update(s->con, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
+}
+
+static void ssd0323_invalidate_display(void * opaque)
+{
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ s->redraw = 1;
+}
+
+/* Command/data input. */
+static void ssd0323_cd(void *opaque, int n, int level)
+{
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ DPRINTF("%s mode\n", level ? "Data" : "Command");
+ s->mode = level ? SSD0323_DATA : SSD0323_CMD;
+}
+
+static void ssd0323_save(QEMUFile *f, void *opaque)
+{
+ SSISlave *ss = SSI_SLAVE(opaque);
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ int i;
+
+ qemu_put_be32(f, s->cmd_len);
+ qemu_put_be32(f, s->cmd);
+ for (i = 0; i < 8; i++)
+ qemu_put_be32(f, s->cmd_data[i]);
+ qemu_put_be32(f, s->row);
+ qemu_put_be32(f, s->row_start);
+ qemu_put_be32(f, s->row_end);
+ qemu_put_be32(f, s->col);
+ qemu_put_be32(f, s->col_start);
+ qemu_put_be32(f, s->col_end);
+ qemu_put_be32(f, s->redraw);
+ qemu_put_be32(f, s->remap);
+ qemu_put_be32(f, s->mode);
+ qemu_put_buffer(f, s->framebuffer, sizeof(s->framebuffer));
+
+ qemu_put_be32(f, ss->cs);
+}
+
+static int ssd0323_load(QEMUFile *f, void *opaque, int version_id)
+{
+ SSISlave *ss = SSI_SLAVE(opaque);
+ ssd0323_state *s = (ssd0323_state *)opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->cmd_len = qemu_get_be32(f);
+ s->cmd = qemu_get_be32(f);
+ for (i = 0; i < 8; i++)
+ s->cmd_data[i] = qemu_get_be32(f);
+ s->row = qemu_get_be32(f);
+ s->row_start = qemu_get_be32(f);
+ s->row_end = qemu_get_be32(f);
+ s->col = qemu_get_be32(f);
+ s->col_start = qemu_get_be32(f);
+ s->col_end = qemu_get_be32(f);
+ s->redraw = qemu_get_be32(f);
+ s->remap = qemu_get_be32(f);
+ s->mode = qemu_get_be32(f);
+ qemu_get_buffer(f, s->framebuffer, sizeof(s->framebuffer));
+
+ ss->cs = qemu_get_be32(f);
+
+ return 0;
+}
+
+static int ssd0323_init(SSISlave *dev)
+{
+ ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev);
+
+ s->col_end = 63;
+ s->row_end = 79;
+ s->con = graphic_console_init(ssd0323_update_display,
+ ssd0323_invalidate_display,
+ NULL, NULL, s);
+ qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY);
+
+ qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1);
+
+ register_savevm(&dev->qdev, "ssd0323_oled", -1, 1,
+ ssd0323_save, ssd0323_load, s);
+ return 0;
+}
+
+static void ssd0323_class_init(ObjectClass *klass, void *data)
+{
+ SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
+
+ k->init = ssd0323_init;
+ k->transfer = ssd0323_transfer;
+ k->cs_polarity = SSI_CS_HIGH;
+}
+
+static const TypeInfo ssd0323_info = {
+ .name = "ssd0323",
+ .parent = TYPE_SSI_SLAVE,
+ .instance_size = sizeof(ssd0323_state),
+ .class_init = ssd0323_class_init,
+};
+
+static void ssd03232_register_types(void)
+{
+ type_register_static(&ssd0323_info);
+}
+
+type_init(ssd03232_register_types)
diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c
new file mode 100644
index 0000000000..e252ce945f
--- /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 "tc6393xb_template.h"
+#define BITS 15
+#include "tc6393xb_template.h"
+#define BITS 16
+#include "tc6393xb_template.h"
+#define BITS 24
+#include "tc6393xb_template.h"
+#define BITS 32
+#include "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/tc6393xb_template.h b/hw/display/tc6393xb_template.h
new file mode 100644
index 0000000000..154aafd400
--- /dev/null
+++ b/hw/display/tc6393xb_template.h
@@ -0,0 +1,68 @@
+/*
+ * Toshiba TC6393XB I/O Controller.
+ * Found in Sharp Zaurus SL-6000 (tosa) or some
+ * Toshiba e-Series PDAs.
+ *
+ * FB support code. Based on G364 fb emulator
+ *
+ * Copyright (c) 2007 Hervé Poussineau
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#if BITS == 8
+# define SET_PIXEL(addr, color) *(uint8_t*)addr = color;
+#elif BITS == 15 || BITS == 16
+# define SET_PIXEL(addr, color) *(uint16_t*)addr = color;
+#elif BITS == 24
+# define SET_PIXEL(addr, color) \
+ addr[0] = color; addr[1] = (color) >> 8; addr[2] = (color) >> 16;
+#elif BITS == 32
+# define SET_PIXEL(addr, color) *(uint32_t*)addr = color;
+#else
+# error unknown bit depth
+#endif
+
+
+static void glue(tc6393xb_draw_graphic, BITS)(TC6393xbState *s)
+{
+ DisplaySurface *surface = qemu_console_surface(s->con);
+ int i;
+ uint16_t *data_buffer;
+ uint8_t *data_display;
+
+ data_buffer = s->vram_ptr;
+ data_display = surface_data(surface);
+ for(i = 0; i < s->scr_height; i++) {
+#if (BITS == 16)
+ memcpy(data_display, data_buffer, s->scr_width * 2);
+ data_buffer += s->scr_width;
+ data_display += surface_stride(surface);
+#else
+ int j;
+ for (j = 0; j < s->scr_width; j++, data_display += BITS / 8, data_buffer++) {
+ uint16_t color = *data_buffer;
+ uint32_t dest_color = glue(rgb_to_pixel, BITS)(
+ ((color & 0xf800) * 0x108) >> 11,
+ ((color & 0x7e0) * 0x41) >> 9,
+ ((color & 0x1f) * 0x21) >> 2
+ );
+ SET_PIXEL(data_display, dest_color);
+ }
+#endif
+ }
+}
+
+#undef BITS
+#undef SET_PIXEL
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-isa-mm.c b/hw/display/vga-isa-mm.c
new file mode 100644
index 0000000000..1c50070216
--- /dev/null
+++ b/hw/display/vga-isa-mm.c
@@ -0,0 +1,144 @@
+/*
+ * QEMU ISA MM 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 "ui/console.h"
+#include "hw/i386/pc.h"
+#include "vga_int.h"
+#include "ui/pixel_ops.h"
+#include "qemu/timer.h"
+
+#define VGA_RAM_SIZE (8192 * 1024)
+
+typedef struct ISAVGAMMState {
+ VGACommonState vga;
+ int it_shift;
+} ISAVGAMMState;
+
+/* Memory mapped interface */
+static uint32_t vga_mm_readb (void *opaque, hwaddr addr)
+{
+ ISAVGAMMState *s = opaque;
+
+ return vga_ioport_read(&s->vga, addr >> s->it_shift) & 0xff;
+}
+
+static void vga_mm_writeb (void *opaque,
+ hwaddr addr, uint32_t value)
+{
+ ISAVGAMMState *s = opaque;
+
+ vga_ioport_write(&s->vga, addr >> s->it_shift, value & 0xff);
+}
+
+static uint32_t vga_mm_readw (void *opaque, hwaddr addr)
+{
+ ISAVGAMMState *s = opaque;
+
+ return vga_ioport_read(&s->vga, addr >> s->it_shift) & 0xffff;
+}
+
+static void vga_mm_writew (void *opaque,
+ hwaddr addr, uint32_t value)
+{
+ ISAVGAMMState *s = opaque;
+
+ vga_ioport_write(&s->vga, addr >> s->it_shift, value & 0xffff);
+}
+
+static uint32_t vga_mm_readl (void *opaque, hwaddr addr)
+{
+ ISAVGAMMState *s = opaque;
+
+ return vga_ioport_read(&s->vga, addr >> s->it_shift);
+}
+
+static void vga_mm_writel (void *opaque,
+ hwaddr addr, uint32_t value)
+{
+ ISAVGAMMState *s = opaque;
+
+ vga_ioport_write(&s->vga, addr >> s->it_shift, value);
+}
+
+static const MemoryRegionOps vga_mm_ctrl_ops = {
+ .old_mmio = {
+ .read = {
+ vga_mm_readb,
+ vga_mm_readw,
+ vga_mm_readl,
+ },
+ .write = {
+ vga_mm_writeb,
+ vga_mm_writew,
+ vga_mm_writel,
+ },
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void vga_mm_init(ISAVGAMMState *s, hwaddr vram_base,
+ hwaddr ctrl_base, int it_shift,
+ MemoryRegion *address_space)
+{
+ MemoryRegion *s_ioport_ctrl, *vga_io_memory;
+
+ s->it_shift = it_shift;
+ s_ioport_ctrl = g_malloc(sizeof(*s_ioport_ctrl));
+ memory_region_init_io(s_ioport_ctrl, &vga_mm_ctrl_ops, s,
+ "vga-mm-ctrl", 0x100000);
+ memory_region_set_flush_coalesced(s_ioport_ctrl);
+
+ vga_io_memory = g_malloc(sizeof(*vga_io_memory));
+ /* XXX: endianness? */
+ memory_region_init_io(vga_io_memory, &vga_mem_ops, &s->vga,
+ "vga-mem", 0x20000);
+
+ vmstate_register(NULL, 0, &vmstate_vga_common, s);
+
+ memory_region_add_subregion(address_space, ctrl_base, s_ioport_ctrl);
+ s->vga.bank_offset = 0;
+ memory_region_add_subregion(address_space,
+ vram_base + 0x000a0000, vga_io_memory);
+ memory_region_set_coalescing(vga_io_memory);
+}
+
+int isa_vga_mm_init(hwaddr vram_base,
+ hwaddr ctrl_base, int it_shift,
+ MemoryRegion *address_space)
+{
+ ISAVGAMMState *s;
+
+ s = g_malloc0(sizeof(*s));
+
+ s->vga.vram_size_mb = VGA_RAM_SIZE >> 20;
+ vga_common_init(&s->vga);
+ vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space);
+
+ s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
+ s->vga.screen_dump, s->vga.text_update,
+ s);
+
+ vga_init_vbe(&s->vga, address_space);
+ return 0;
+}
diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c
new file mode 100644
index 0000000000..90959ebc2c
--- /dev/null
+++ b/hw/display/vga-isa.c
@@ -0,0 +1,101 @@
+/*
+ * QEMU ISA VGA Emulator.
+ *
+ * see docs/specs/standard-vga.txt for virtual hardware specs.
+ *
+ * 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 "ui/console.h"
+#include "hw/i386/pc.h"
+#include "vga_int.h"
+#include "ui/pixel_ops.h"
+#include "qemu/timer.h"
+#include "hw/loader.h"
+
+typedef struct ISAVGAState {
+ ISADevice dev;
+ struct VGACommonState state;
+} ISAVGAState;
+
+static void vga_reset_isa(DeviceState *dev)
+{
+ ISAVGAState *d = container_of(dev, ISAVGAState, dev.qdev);
+ VGACommonState *s = &d->state;
+
+ vga_common_reset(s);
+}
+
+static int vga_initfn(ISADevice *dev)
+{
+ ISAVGAState *d = DO_UPCAST(ISAVGAState, dev, dev);
+ VGACommonState *s = &d->state;
+ MemoryRegion *vga_io_memory;
+ const MemoryRegionPortio *vga_ports, *vbe_ports;
+
+ vga_common_init(s);
+ s->legacy_address_space = isa_address_space(dev);
+ vga_io_memory = vga_init_io(s, &vga_ports, &vbe_ports);
+ isa_register_portio_list(dev, 0x3b0, vga_ports, s, "vga");
+ if (vbe_ports) {
+ isa_register_portio_list(dev, 0x1ce, vbe_ports, s, "vbe");
+ }
+ memory_region_add_subregion_overlap(isa_address_space(dev),
+ isa_mem_base + 0x000a0000,
+ vga_io_memory, 1);
+ memory_region_set_coalescing(vga_io_memory);
+ s->con = graphic_console_init(s->update, s->invalidate,
+ s->screen_dump, s->text_update, s);
+
+ vga_init_vbe(s, isa_address_space(dev));
+ /* ROM BIOS */
+ rom_add_vga(VGABIOS_FILENAME);
+ return 0;
+}
+
+static Property vga_isa_properties[] = {
+ DEFINE_PROP_UINT32("vgamem_mb", ISAVGAState, state.vram_size_mb, 8),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vga_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ISADeviceClass *ic = ISA_DEVICE_CLASS(klass);
+ ic->init = vga_initfn;
+ dc->reset = vga_reset_isa;
+ dc->vmsd = &vmstate_vga_common;
+ dc->props = vga_isa_properties;
+}
+
+static const TypeInfo vga_info = {
+ .name = "isa-vga",
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(ISAVGAState),
+ .class_init = vga_class_initfn,
+};
+
+static void vga_register_types(void)
+{
+ type_register_static(&vga_info);
+}
+
+type_init(vga_register_types)
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
new file mode 100644
index 0000000000..a9c69b6ac7
--- /dev/null
+++ b/hw/display/vga-pci.c
@@ -0,0 +1,215 @@
+/*
+ * QEMU PCI VGA Emulator.
+ *
+ * see docs/specs/standard-vga.txt for virtual hardware specs.
+ *
+ * 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 "ui/console.h"
+#include "hw/pci/pci.h"
+#include "vga_int.h"
+#include "ui/pixel_ops.h"
+#include "qemu/timer.h"
+#include "hw/loader.h"
+
+#define PCI_VGA_IOPORT_OFFSET 0x400
+#define PCI_VGA_IOPORT_SIZE (0x3e0 - 0x3c0)
+#define PCI_VGA_BOCHS_OFFSET 0x500
+#define PCI_VGA_BOCHS_SIZE (0x0b * 2)
+#define PCI_VGA_MMIO_SIZE 0x1000
+
+enum vga_pci_flags {
+ PCI_VGA_FLAG_ENABLE_MMIO = 1,
+};
+
+typedef struct PCIVGAState {
+ PCIDevice dev;
+ VGACommonState vga;
+ uint32_t flags;
+ MemoryRegion mmio;
+ MemoryRegion ioport;
+ MemoryRegion bochs;
+} PCIVGAState;
+
+static const VMStateDescription vmstate_vga_pci = {
+ .name = "vga",
+ .version_id = 2,
+ .minimum_version_id = 2,
+ .minimum_version_id_old = 2,
+ .fields = (VMStateField []) {
+ VMSTATE_PCI_DEVICE(dev, PCIVGAState),
+ VMSTATE_STRUCT(vga, PCIVGAState, 0, vmstate_vga_common, VGACommonState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static uint64_t pci_vga_ioport_read(void *ptr, hwaddr addr,
+ unsigned size)
+{
+ PCIVGAState *d = ptr;
+ uint64_t ret = 0;
+
+ switch (size) {
+ case 1:
+ ret = vga_ioport_read(&d->vga, addr);
+ break;
+ case 2:
+ ret = vga_ioport_read(&d->vga, addr);
+ ret |= vga_ioport_read(&d->vga, addr+1) << 8;
+ break;
+ }
+ return ret;
+}
+
+static void pci_vga_ioport_write(void *ptr, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ PCIVGAState *d = ptr;
+
+ switch (size) {
+ case 1:
+ vga_ioport_write(&d->vga, addr + 0x3c0, val);
+ break;
+ case 2:
+ /*
+ * Update bytes in little endian order. Allows to update
+ * indexed registers with a single word write because the
+ * index byte is updated first.
+ */
+ vga_ioport_write(&d->vga, addr + 0x3c0, val & 0xff);
+ vga_ioport_write(&d->vga, addr + 0x3c1, (val >> 8) & 0xff);
+ break;
+ }
+}
+
+static const MemoryRegionOps pci_vga_ioport_ops = {
+ .read = pci_vga_ioport_read,
+ .write = pci_vga_ioport_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 2,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t pci_vga_bochs_read(void *ptr, hwaddr addr,
+ unsigned size)
+{
+ PCIVGAState *d = ptr;
+ int index = addr >> 1;
+
+ vbe_ioport_write_index(&d->vga, 0, index);
+ return vbe_ioport_read_data(&d->vga, 0);
+}
+
+static void pci_vga_bochs_write(void *ptr, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ PCIVGAState *d = ptr;
+ int index = addr >> 1;
+
+ vbe_ioport_write_index(&d->vga, 0, index);
+ vbe_ioport_write_data(&d->vga, 0, val);
+}
+
+static const MemoryRegionOps pci_vga_bochs_ops = {
+ .read = pci_vga_bochs_read,
+ .write = pci_vga_bochs_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 2,
+ .impl.max_access_size = 2,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static int pci_std_vga_initfn(PCIDevice *dev)
+{
+ PCIVGAState *d = DO_UPCAST(PCIVGAState, dev, dev);
+ VGACommonState *s = &d->vga;
+
+ /* vga + console init */
+ vga_common_init(s);
+ vga_init(s, pci_address_space(dev), pci_address_space_io(dev), true);
+
+ s->con = graphic_console_init(s->update, s->invalidate,
+ s->screen_dump, s->text_update, s);
+
+ /* XXX: VGA_RAM_SIZE must be a power of two */
+ pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
+
+ /* mmio bar for vga register access */
+ if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_MMIO)) {
+ memory_region_init(&d->mmio, "vga.mmio", 4096);
+ memory_region_init_io(&d->ioport, &pci_vga_ioport_ops, d,
+ "vga ioports remapped", PCI_VGA_IOPORT_SIZE);
+ memory_region_init_io(&d->bochs, &pci_vga_bochs_ops, d,
+ "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
+
+ memory_region_add_subregion(&d->mmio, PCI_VGA_IOPORT_OFFSET,
+ &d->ioport);
+ memory_region_add_subregion(&d->mmio, PCI_VGA_BOCHS_OFFSET,
+ &d->bochs);
+ pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
+ }
+
+ if (!dev->rom_bar) {
+ /* compatibility with pc-0.13 and older */
+ vga_init_vbe(s, pci_address_space(dev));
+ }
+
+ return 0;
+}
+
+static Property vga_pci_properties[] = {
+ DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16),
+ DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vga_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->no_hotplug = 1;
+ k->init = pci_std_vga_initfn;
+ k->romfile = "vgabios-stdvga.bin";
+ k->vendor_id = PCI_VENDOR_ID_QEMU;
+ k->device_id = PCI_DEVICE_ID_QEMU_VGA;
+ k->class_id = PCI_CLASS_DISPLAY_VGA;
+ dc->vmsd = &vmstate_vga_pci;
+ dc->props = vga_pci_properties;
+}
+
+static const TypeInfo vga_info = {
+ .name = "VGA",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PCIVGAState),
+ .class_init = vga_class_init,
+};
+
+static void vga_register_types(void)
+{
+ type_register_static(&vga_info);
+}
+
+type_init(vga_register_types)
diff --git a/hw/display/vga.c b/hw/display/vga.c
new file mode 100644
index 0000000000..c1b67bbbf8
--- /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 "vga.h"
+#include "ui/console.h"
+#include "hw/i386/pc.h"
+#include "hw/pci/pci.h"
+#include "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 "vga_template.h"
+
+#define DEPTH 15
+#include "vga_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 15
+#include "vga_template.h"
+
+#define DEPTH 16
+#include "vga_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 16
+#include "vga_template.h"
+
+#define DEPTH 32
+#include "vga_template.h"
+
+#define BGR_FORMAT
+#define DEPTH 32
+#include "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/display/vga.h b/hw/display/vga.h
new file mode 100644
index 0000000000..d917046da6
--- /dev/null
+++ b/hw/display/vga.h
@@ -0,0 +1,159 @@
+/*
+ * linux/include/video/vga.h -- standard VGA chipset interaction
+ *
+ * Copyright 1999 Jeff Garzik <jgarzik@pobox.com>
+ *
+ * Copyright history from vga16fb.c:
+ * Copyright 1999 Ben Pfaff and Petr Vandrovec
+ * Based on VGA info at http://www.osdever.net/FreeVGA/home.htm
+ * Based on VESA framebuffer (c) 1998 Gerd Knorr
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details.
+ *
+ */
+
+#ifndef __linux_video_vga_h__
+#define __linux_video_vga_h__
+
+/* Some of the code below is taken from SVGAlib. The original,
+ unmodified copyright notice for that code is below. */
+/* VGAlib version 1.2 - (c) 1993 Tommy Frandsen */
+/* */
+/* This library is free software; you can redistribute it and/or */
+/* modify it without any restrictions. This library is distributed */
+/* in the hope that it will be useful, but without any warranty. */
+
+/* Multi-chipset support Copyright 1993 Harm Hanemaayer */
+/* partially copyrighted (C) 1993 by Hartmut Schirmer */
+
+/* VGA data register ports */
+#define VGA_CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */
+#define VGA_CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */
+#define VGA_ATT_R 0x3C1 /* Attribute Controller Data Read Register */
+#define VGA_ATT_W 0x3C0 /* Attribute Controller Data Write Register */
+#define VGA_GFX_D 0x3CF /* Graphics Controller Data Register */
+#define VGA_SEQ_D 0x3C5 /* Sequencer Data Register */
+#define VGA_MIS_R 0x3CC /* Misc Output Read Register */
+#define VGA_MIS_W 0x3C2 /* Misc Output Write Register */
+#define VGA_FTC_R 0x3CA /* Feature Control Read Register */
+#define VGA_IS1_RC 0x3DA /* Input Status Register 1 - color emulation */
+#define VGA_IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */
+#define VGA_PEL_D 0x3C9 /* PEL Data Register */
+#define VGA_PEL_MSK 0x3C6 /* PEL mask register */
+
+/* EGA-specific registers */
+#define EGA_GFX_E0 0x3CC /* Graphics enable processor 0 */
+#define EGA_GFX_E1 0x3CA /* Graphics enable processor 1 */
+
+/* VGA index register ports */
+#define VGA_CRT_IC 0x3D4 /* CRT Controller Index - color emulation */
+#define VGA_CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */
+#define VGA_ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */
+#define VGA_GFX_I 0x3CE /* Graphics Controller Index */
+#define VGA_SEQ_I 0x3C4 /* Sequencer Index */
+#define VGA_PEL_IW 0x3C8 /* PEL Write Index */
+#define VGA_PEL_IR 0x3C7 /* PEL Read Index */
+
+/* standard VGA indexes max counts */
+#define VGA_CRT_C 0x19 /* Number of CRT Controller Registers */
+#define VGA_ATT_C 0x15 /* Number of Attribute Controller Registers */
+#define VGA_GFX_C 0x09 /* Number of Graphics Controller Registers */
+#define VGA_SEQ_C 0x05 /* Number of Sequencer Registers */
+#define VGA_MIS_C 0x01 /* Number of Misc Output Register */
+
+/* VGA misc register bit masks */
+#define VGA_MIS_COLOR 0x01
+#define VGA_MIS_ENB_MEM_ACCESS 0x02
+#define VGA_MIS_DCLK_28322_720 0x04
+#define VGA_MIS_ENB_PLL_LOAD (0x04 | 0x08)
+#define VGA_MIS_SEL_HIGH_PAGE 0x20
+
+/* VGA CRT controller register indices */
+#define VGA_CRTC_H_TOTAL 0
+#define VGA_CRTC_H_DISP 1
+#define VGA_CRTC_H_BLANK_START 2
+#define VGA_CRTC_H_BLANK_END 3
+#define VGA_CRTC_H_SYNC_START 4
+#define VGA_CRTC_H_SYNC_END 5
+#define VGA_CRTC_V_TOTAL 6
+#define VGA_CRTC_OVERFLOW 7
+#define VGA_CRTC_PRESET_ROW 8
+#define VGA_CRTC_MAX_SCAN 9
+#define VGA_CRTC_CURSOR_START 0x0A
+#define VGA_CRTC_CURSOR_END 0x0B
+#define VGA_CRTC_START_HI 0x0C
+#define VGA_CRTC_START_LO 0x0D
+#define VGA_CRTC_CURSOR_HI 0x0E
+#define VGA_CRTC_CURSOR_LO 0x0F
+#define VGA_CRTC_V_SYNC_START 0x10
+#define VGA_CRTC_V_SYNC_END 0x11
+#define VGA_CRTC_V_DISP_END 0x12
+#define VGA_CRTC_OFFSET 0x13
+#define VGA_CRTC_UNDERLINE 0x14
+#define VGA_CRTC_V_BLANK_START 0x15
+#define VGA_CRTC_V_BLANK_END 0x16
+#define VGA_CRTC_MODE 0x17
+#define VGA_CRTC_LINE_COMPARE 0x18
+#define VGA_CRTC_REGS VGA_CRT_C
+
+/* VGA CRT controller bit masks */
+#define VGA_CR11_LOCK_CR0_CR7 0x80 /* lock writes to CR0 - CR7 */
+#define VGA_CR17_H_V_SIGNALS_ENABLED 0x80
+
+/* VGA attribute controller register indices */
+#define VGA_ATC_PALETTE0 0x00
+#define VGA_ATC_PALETTE1 0x01
+#define VGA_ATC_PALETTE2 0x02
+#define VGA_ATC_PALETTE3 0x03
+#define VGA_ATC_PALETTE4 0x04
+#define VGA_ATC_PALETTE5 0x05
+#define VGA_ATC_PALETTE6 0x06
+#define VGA_ATC_PALETTE7 0x07
+#define VGA_ATC_PALETTE8 0x08
+#define VGA_ATC_PALETTE9 0x09
+#define VGA_ATC_PALETTEA 0x0A
+#define VGA_ATC_PALETTEB 0x0B
+#define VGA_ATC_PALETTEC 0x0C
+#define VGA_ATC_PALETTED 0x0D
+#define VGA_ATC_PALETTEE 0x0E
+#define VGA_ATC_PALETTEF 0x0F
+#define VGA_ATC_MODE 0x10
+#define VGA_ATC_OVERSCAN 0x11
+#define VGA_ATC_PLANE_ENABLE 0x12
+#define VGA_ATC_PEL 0x13
+#define VGA_ATC_COLOR_PAGE 0x14
+
+#define VGA_AR_ENABLE_DISPLAY 0x20
+
+/* VGA sequencer register indices */
+#define VGA_SEQ_RESET 0x00
+#define VGA_SEQ_CLOCK_MODE 0x01
+#define VGA_SEQ_PLANE_WRITE 0x02
+#define VGA_SEQ_CHARACTER_MAP 0x03
+#define VGA_SEQ_MEMORY_MODE 0x04
+
+/* VGA sequencer register bit masks */
+#define VGA_SR01_CHAR_CLK_8DOTS 0x01 /* bit 0: character clocks 8 dots wide are generated */
+#define VGA_SR01_SCREEN_OFF 0x20 /* bit 5: Screen is off */
+#define VGA_SR02_ALL_PLANES 0x0F /* bits 3-0: enable access to all planes */
+#define VGA_SR04_EXT_MEM 0x02 /* bit 1: allows complete mem access to 256K */
+#define VGA_SR04_SEQ_MODE 0x04 /* bit 2: directs system to use a sequential addressing mode */
+#define VGA_SR04_CHN_4M 0x08 /* bit 3: selects modulo 4 addressing for CPU access to display memory */
+
+/* VGA graphics controller register indices */
+#define VGA_GFX_SR_VALUE 0x00
+#define VGA_GFX_SR_ENABLE 0x01
+#define VGA_GFX_COMPARE_VALUE 0x02
+#define VGA_GFX_DATA_ROTATE 0x03
+#define VGA_GFX_PLANE_READ 0x04
+#define VGA_GFX_MODE 0x05
+#define VGA_GFX_MISC 0x06
+#define VGA_GFX_COMPARE_MASK 0x07
+#define VGA_GFX_BIT_MASK 0x08
+
+/* VGA graphics controller bit masks */
+#define VGA_GR06_GRAPHICS_MODE 0x01
+
+#endif /* __linux_video_vga_h__ */
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
new file mode 100644
index 0000000000..260f7d6948
--- /dev/null
+++ b/hw/display/vga_int.h
@@ -0,0 +1,218 @@
+/*
+ * QEMU internal VGA defines.
+ *
+ * Copyright (c) 2003-2004 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.
+ */
+#ifndef HW_VGA_INT_H
+#define HW_VGA_INT_H 1
+
+#include <hw/hw.h>
+#include "qapi/error.h"
+#include "exec/memory.h"
+
+#define ST01_V_RETRACE 0x08
+#define ST01_DISP_ENABLE 0x01
+
+#define VBE_DISPI_MAX_XRES 16000
+#define VBE_DISPI_MAX_YRES 12000
+#define VBE_DISPI_MAX_BPP 32
+
+#define VBE_DISPI_INDEX_ID 0x0
+#define VBE_DISPI_INDEX_XRES 0x1
+#define VBE_DISPI_INDEX_YRES 0x2
+#define VBE_DISPI_INDEX_BPP 0x3
+#define VBE_DISPI_INDEX_ENABLE 0x4
+#define VBE_DISPI_INDEX_BANK 0x5
+#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
+#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
+#define VBE_DISPI_INDEX_X_OFFSET 0x8
+#define VBE_DISPI_INDEX_Y_OFFSET 0x9
+#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */
+#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */
+
+#define VBE_DISPI_ID0 0xB0C0
+#define VBE_DISPI_ID1 0xB0C1
+#define VBE_DISPI_ID2 0xB0C2
+#define VBE_DISPI_ID3 0xB0C3
+#define VBE_DISPI_ID4 0xB0C4
+#define VBE_DISPI_ID5 0xB0C5
+
+#define VBE_DISPI_DISABLED 0x00
+#define VBE_DISPI_ENABLED 0x01
+#define VBE_DISPI_GETCAPS 0x02
+#define VBE_DISPI_8BIT_DAC 0x20
+#define VBE_DISPI_LFB_ENABLED 0x40
+#define VBE_DISPI_NOCLEARMEM 0x80
+
+#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000
+
+#define CH_ATTR_SIZE (160 * 100)
+#define VGA_MAX_HEIGHT 2048
+
+struct vga_precise_retrace {
+ int64_t ticks_per_char;
+ int64_t total_chars;
+ int htotal;
+ int hstart;
+ int hend;
+ int vstart;
+ int vend;
+ int freq;
+};
+
+union vga_retrace {
+ struct vga_precise_retrace precise;
+};
+
+struct VGACommonState;
+typedef uint8_t (* vga_retrace_fn)(struct VGACommonState *s);
+typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s);
+
+typedef struct VGACommonState {
+ MemoryRegion *legacy_address_space;
+ uint8_t *vram_ptr;
+ MemoryRegion vram;
+ MemoryRegion vram_vbe;
+ uint32_t vram_size;
+ uint32_t vram_size_mb; /* property */
+ uint32_t latch;
+ MemoryRegion *chain4_alias;
+ uint8_t sr_index;
+ uint8_t sr[256];
+ uint8_t gr_index;
+ uint8_t gr[256];
+ uint8_t ar_index;
+ uint8_t ar[21];
+ int ar_flip_flop;
+ uint8_t cr_index;
+ uint8_t cr[256]; /* CRT registers */
+ uint8_t msr; /* Misc Output Register */
+ uint8_t fcr; /* Feature Control Register */
+ uint8_t st00; /* status 0 */
+ uint8_t st01; /* status 1 */
+ uint8_t dac_state;
+ uint8_t dac_sub_index;
+ uint8_t dac_read_index;
+ uint8_t dac_write_index;
+ uint8_t dac_cache[3]; /* used when writing */
+ int dac_8bit;
+ uint8_t palette[768];
+ int32_t bank_offset;
+ int (*get_bpp)(struct VGACommonState *s);
+ void (*get_offsets)(struct VGACommonState *s,
+ uint32_t *pline_offset,
+ uint32_t *pstart_addr,
+ uint32_t *pline_compare);
+ void (*get_resolution)(struct VGACommonState *s,
+ int *pwidth,
+ int *pheight);
+ /* bochs vbe state */
+ uint16_t vbe_index;
+ uint16_t vbe_regs[VBE_DISPI_INDEX_NB];
+ uint32_t vbe_start_addr;
+ uint32_t vbe_line_offset;
+ uint32_t vbe_bank_mask;
+ int vbe_mapped;
+ /* display refresh support */
+ QemuConsole *con;
+ uint32_t font_offsets[2];
+ int graphic_mode;
+ uint8_t shift_control;
+ uint8_t double_scan;
+ uint32_t line_offset;
+ uint32_t line_compare;
+ uint32_t start_addr;
+ uint32_t plane_updated;
+ uint32_t last_line_offset;
+ uint8_t last_cw, last_ch;
+ uint32_t last_width, last_height; /* in chars or pixels */
+ uint32_t last_scr_width, last_scr_height; /* in pixels */
+ uint32_t last_depth; /* in bits */
+ uint8_t cursor_start, cursor_end;
+ bool cursor_visible_phase;
+ int64_t cursor_blink_time;
+ uint32_t cursor_offset;
+ unsigned int (*rgb_to_pixel)(unsigned int r,
+ unsigned int g, unsigned b);
+ vga_hw_update_ptr update;
+ vga_hw_invalidate_ptr invalidate;
+ vga_hw_screen_dump_ptr screen_dump;
+ vga_hw_text_update_ptr text_update;
+ bool full_update_text;
+ bool full_update_gfx;
+ /* hardware mouse cursor support */
+ uint32_t invalidated_y_table[VGA_MAX_HEIGHT / 32];
+ void (*cursor_invalidate)(struct VGACommonState *s);
+ void (*cursor_draw_line)(struct VGACommonState *s, uint8_t *d, int y);
+ /* tell for each page if it has been updated since the last time */
+ uint32_t last_palette[256];
+ uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */
+ /* retrace */
+ vga_retrace_fn retrace;
+ vga_update_retrace_info_fn update_retrace_info;
+ union vga_retrace retrace_info;
+ uint8_t is_vbe_vmstate;
+} VGACommonState;
+
+static inline int c6_to_8(int v)
+{
+ int b;
+ v &= 0x3f;
+ b = v & 1;
+ return (v << 2) | (b << 1) | b;
+}
+
+void vga_common_init(VGACommonState *s);
+void vga_init(VGACommonState *s, MemoryRegion *address_space,
+ MemoryRegion *address_space_io, bool init_vga_ports);
+MemoryRegion *vga_init_io(VGACommonState *s,
+ const MemoryRegionPortio **vga_ports,
+ const MemoryRegionPortio **vbe_ports);
+void vga_common_reset(VGACommonState *s);
+
+void vga_sync_dirty_bitmap(VGACommonState *s);
+void vga_dirty_log_start(VGACommonState *s);
+void vga_dirty_log_stop(VGACommonState *s);
+
+extern const VMStateDescription vmstate_vga_common;
+uint32_t vga_ioport_read(void *opaque, uint32_t addr);
+void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val);
+uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr);
+void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val);
+void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2);
+void ppm_save(const char *filename, struct DisplaySurface *ds, Error **errp);
+
+int vga_ioport_invalid(VGACommonState *s, uint32_t addr);
+
+void vga_init_vbe(VGACommonState *s, MemoryRegion *address_space);
+uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr);
+void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val);
+void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val);
+
+extern const uint8_t sr_mask[8];
+extern const uint8_t gr_mask[16];
+
+#define VGABIOS_FILENAME "vgabios.bin"
+#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
+
+extern const MemoryRegionOps vga_mem_ops;
+
+#endif
diff --git a/hw/display/vga_template.h b/hw/display/vga_template.h
new file mode 100644
index 0000000000..f6f6a01d84
--- /dev/null
+++ b/hw/display/vga_template.h
@@ -0,0 +1,459 @@
+/*
+ * QEMU VGA Emulator templates
+ *
+ * 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.
+ */
+
+#if DEPTH == 8
+#define BPP 1
+#define PIXEL_TYPE uint8_t
+#elif DEPTH == 15 || DEPTH == 16
+#define BPP 2
+#define PIXEL_TYPE uint16_t
+#elif DEPTH == 32
+#define BPP 4
+#define PIXEL_TYPE uint32_t
+#else
+#error unsupport depth
+#endif
+
+#ifdef BGR_FORMAT
+#define PIXEL_NAME glue(DEPTH, bgr)
+#else
+#define PIXEL_NAME DEPTH
+#endif /* BGR_FORMAT */
+
+#if DEPTH != 15 && !defined(BGR_FORMAT)
+
+static inline void glue(vga_draw_glyph_line_, DEPTH)(uint8_t *d,
+ uint32_t font_data,
+ uint32_t xorcol,
+ uint32_t bgcol)
+{
+#if BPP == 1
+ ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
+#elif BPP == 2
+ ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
+#else
+ ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+#endif
+}
+
+static void glue(vga_draw_glyph8_, DEPTH)(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol)
+{
+ uint32_t font_data, xorcol;
+
+ xorcol = bgcol ^ fgcol;
+ do {
+ font_data = font_ptr[0];
+ glue(vga_draw_glyph_line_, DEPTH)(d, font_data, xorcol, bgcol);
+ font_ptr += 4;
+ d += linesize;
+ } while (--h);
+}
+
+static void glue(vga_draw_glyph16_, DEPTH)(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol)
+{
+ uint32_t font_data, xorcol;
+
+ xorcol = bgcol ^ fgcol;
+ do {
+ font_data = font_ptr[0];
+ glue(vga_draw_glyph_line_, DEPTH)(d,
+ expand4to8[font_data >> 4],
+ xorcol, bgcol);
+ glue(vga_draw_glyph_line_, DEPTH)(d + 8 * BPP,
+ expand4to8[font_data & 0x0f],
+ xorcol, bgcol);
+ font_ptr += 4;
+ d += linesize;
+ } while (--h);
+}
+
+static void glue(vga_draw_glyph9_, DEPTH)(uint8_t *d, int linesize,
+ const uint8_t *font_ptr, int h,
+ uint32_t fgcol, uint32_t bgcol, int dup9)
+{
+ uint32_t font_data, xorcol, v;
+
+ xorcol = bgcol ^ fgcol;
+ do {
+ font_data = font_ptr[0];
+#if BPP == 1
+ cpu_to_32wu((uint32_t *)d, (dmask16[(font_data >> 4)] & xorcol) ^ bgcol);
+ v = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
+ cpu_to_32wu(((uint32_t *)d)+1, v);
+ if (dup9)
+ ((uint8_t *)d)[8] = v >> (24 * (1 - BIG));
+ else
+ ((uint8_t *)d)[8] = bgcol;
+
+#elif BPP == 2
+ cpu_to_32wu(((uint32_t *)d)+0, (dmask4[(font_data >> 6)] & xorcol) ^ bgcol);
+ cpu_to_32wu(((uint32_t *)d)+1, (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol);
+ cpu_to_32wu(((uint32_t *)d)+2, (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol);
+ v = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
+ cpu_to_32wu(((uint32_t *)d)+3, v);
+ if (dup9)
+ ((uint16_t *)d)[8] = v >> (16 * (1 - BIG));
+ else
+ ((uint16_t *)d)[8] = bgcol;
+#else
+ ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+ v = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+ ((uint32_t *)d)[7] = v;
+ if (dup9)
+ ((uint32_t *)d)[8] = v;
+ else
+ ((uint32_t *)d)[8] = bgcol;
+#endif
+ font_ptr += 4;
+ d += linesize;
+ } while (--h);
+}
+
+/*
+ * 4 color mode
+ */
+static void glue(vga_draw_line2_, DEPTH)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t plane_mask, *palette, data, v;
+ int x;
+
+ palette = s1->last_palette;
+ plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ data = ((uint32_t *)s)[0];
+ data &= plane_mask;
+ v = expand2[GET_PLANE(data, 0)];
+ v |= expand2[GET_PLANE(data, 2)] << 2;
+ ((PIXEL_TYPE *)d)[0] = palette[v >> 12];
+ ((PIXEL_TYPE *)d)[1] = palette[(v >> 8) & 0xf];
+ ((PIXEL_TYPE *)d)[2] = palette[(v >> 4) & 0xf];
+ ((PIXEL_TYPE *)d)[3] = palette[(v >> 0) & 0xf];
+
+ v = expand2[GET_PLANE(data, 1)];
+ v |= expand2[GET_PLANE(data, 3)] << 2;
+ ((PIXEL_TYPE *)d)[4] = palette[v >> 12];
+ ((PIXEL_TYPE *)d)[5] = palette[(v >> 8) & 0xf];
+ ((PIXEL_TYPE *)d)[6] = palette[(v >> 4) & 0xf];
+ ((PIXEL_TYPE *)d)[7] = palette[(v >> 0) & 0xf];
+ d += BPP * 8;
+ s += 4;
+ }
+}
+
+#if BPP == 1
+#define PUT_PIXEL2(d, n, v) ((uint16_t *)d)[(n)] = (v)
+#elif BPP == 2
+#define PUT_PIXEL2(d, n, v) ((uint32_t *)d)[(n)] = (v)
+#else
+#define PUT_PIXEL2(d, n, v) \
+((uint32_t *)d)[2*(n)] = ((uint32_t *)d)[2*(n)+1] = (v)
+#endif
+
+/*
+ * 4 color mode, dup2 horizontal
+ */
+static void glue(vga_draw_line2d2_, DEPTH)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t plane_mask, *palette, data, v;
+ int x;
+
+ palette = s1->last_palette;
+ plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ data = ((uint32_t *)s)[0];
+ data &= plane_mask;
+ v = expand2[GET_PLANE(data, 0)];
+ v |= expand2[GET_PLANE(data, 2)] << 2;
+ PUT_PIXEL2(d, 0, palette[v >> 12]);
+ PUT_PIXEL2(d, 1, palette[(v >> 8) & 0xf]);
+ PUT_PIXEL2(d, 2, palette[(v >> 4) & 0xf]);
+ PUT_PIXEL2(d, 3, palette[(v >> 0) & 0xf]);
+
+ v = expand2[GET_PLANE(data, 1)];
+ v |= expand2[GET_PLANE(data, 3)] << 2;
+ PUT_PIXEL2(d, 4, palette[v >> 12]);
+ PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]);
+ PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
+ PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
+ d += BPP * 16;
+ s += 4;
+ }
+}
+
+/*
+ * 16 color mode
+ */
+static void glue(vga_draw_line4_, DEPTH)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t plane_mask, data, v, *palette;
+ int x;
+
+ palette = s1->last_palette;
+ plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ data = ((uint32_t *)s)[0];
+ data &= plane_mask;
+ v = expand4[GET_PLANE(data, 0)];
+ v |= expand4[GET_PLANE(data, 1)] << 1;
+ v |= expand4[GET_PLANE(data, 2)] << 2;
+ v |= expand4[GET_PLANE(data, 3)] << 3;
+ ((PIXEL_TYPE *)d)[0] = palette[v >> 28];
+ ((PIXEL_TYPE *)d)[1] = palette[(v >> 24) & 0xf];
+ ((PIXEL_TYPE *)d)[2] = palette[(v >> 20) & 0xf];
+ ((PIXEL_TYPE *)d)[3] = palette[(v >> 16) & 0xf];
+ ((PIXEL_TYPE *)d)[4] = palette[(v >> 12) & 0xf];
+ ((PIXEL_TYPE *)d)[5] = palette[(v >> 8) & 0xf];
+ ((PIXEL_TYPE *)d)[6] = palette[(v >> 4) & 0xf];
+ ((PIXEL_TYPE *)d)[7] = palette[(v >> 0) & 0xf];
+ d += BPP * 8;
+ s += 4;
+ }
+}
+
+/*
+ * 16 color mode, dup2 horizontal
+ */
+static void glue(vga_draw_line4d2_, DEPTH)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t plane_mask, data, v, *palette;
+ int x;
+
+ palette = s1->last_palette;
+ plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ data = ((uint32_t *)s)[0];
+ data &= plane_mask;
+ v = expand4[GET_PLANE(data, 0)];
+ v |= expand4[GET_PLANE(data, 1)] << 1;
+ v |= expand4[GET_PLANE(data, 2)] << 2;
+ v |= expand4[GET_PLANE(data, 3)] << 3;
+ PUT_PIXEL2(d, 0, palette[v >> 28]);
+ PUT_PIXEL2(d, 1, palette[(v >> 24) & 0xf]);
+ PUT_PIXEL2(d, 2, palette[(v >> 20) & 0xf]);
+ PUT_PIXEL2(d, 3, palette[(v >> 16) & 0xf]);
+ PUT_PIXEL2(d, 4, palette[(v >> 12) & 0xf]);
+ PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]);
+ PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
+ PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
+ d += BPP * 16;
+ s += 4;
+ }
+}
+
+/*
+ * 256 color mode, double pixels
+ *
+ * XXX: add plane_mask support (never used in standard VGA modes)
+ */
+static void glue(vga_draw_line8d2_, DEPTH)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t *palette;
+ int x;
+
+ palette = s1->last_palette;
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ PUT_PIXEL2(d, 0, palette[s[0]]);
+ PUT_PIXEL2(d, 1, palette[s[1]]);
+ PUT_PIXEL2(d, 2, palette[s[2]]);
+ PUT_PIXEL2(d, 3, palette[s[3]]);
+ d += BPP * 8;
+ s += 4;
+ }
+}
+
+/*
+ * standard 256 color mode
+ *
+ * XXX: add plane_mask support (never used in standard VGA modes)
+ */
+static void glue(vga_draw_line8_, DEPTH)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ uint32_t *palette;
+ int x;
+
+ palette = s1->last_palette;
+ width >>= 3;
+ for(x = 0; x < width; x++) {
+ ((PIXEL_TYPE *)d)[0] = palette[s[0]];
+ ((PIXEL_TYPE *)d)[1] = palette[s[1]];
+ ((PIXEL_TYPE *)d)[2] = palette[s[2]];
+ ((PIXEL_TYPE *)d)[3] = palette[s[3]];
+ ((PIXEL_TYPE *)d)[4] = palette[s[4]];
+ ((PIXEL_TYPE *)d)[5] = palette[s[5]];
+ ((PIXEL_TYPE *)d)[6] = palette[s[6]];
+ ((PIXEL_TYPE *)d)[7] = palette[s[7]];
+ d += BPP * 8;
+ s += 8;
+ }
+}
+
+#endif /* DEPTH != 15 */
+
+
+/* XXX: optimize */
+
+/*
+ * 15 bit color
+ */
+static void glue(vga_draw_line15_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+#if DEPTH == 15 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+ memcpy(d, s, width * 2);
+#else
+ int w;
+ uint32_t v, r, g, b;
+
+ w = width;
+ do {
+ v = lduw_raw((void *)s);
+ r = (v >> 7) & 0xf8;
+ g = (v >> 2) & 0xf8;
+ b = (v << 3) & 0xf8;
+ ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 2;
+ d += BPP;
+ } while (--w != 0);
+#endif
+}
+
+/*
+ * 16 bit color
+ */
+static void glue(vga_draw_line16_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+#if DEPTH == 16 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
+ memcpy(d, s, width * 2);
+#else
+ int w;
+ uint32_t v, r, g, b;
+
+ w = width;
+ do {
+ v = lduw_raw((void *)s);
+ r = (v >> 8) & 0xf8;
+ g = (v >> 3) & 0xfc;
+ b = (v << 3) & 0xf8;
+ ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 2;
+ d += BPP;
+ } while (--w != 0);
+#endif
+}
+
+/*
+ * 24 bit color
+ */
+static void glue(vga_draw_line24_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+ int w;
+ uint32_t r, g, b;
+
+ w = width;
+ do {
+#if defined(TARGET_WORDS_BIGENDIAN)
+ r = s[0];
+ g = s[1];
+ b = s[2];
+#else
+ b = s[0];
+ g = s[1];
+ r = s[2];
+#endif
+ ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 3;
+ d += BPP;
+ } while (--w != 0);
+}
+
+/*
+ * 32 bit color
+ */
+static void glue(vga_draw_line32_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d,
+ const uint8_t *s, int width)
+{
+#if DEPTH == 32 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) && !defined(BGR_FORMAT)
+ memcpy(d, s, width * 4);
+#else
+ int w;
+ uint32_t r, g, b;
+
+ w = width;
+ do {
+#if defined(TARGET_WORDS_BIGENDIAN)
+ r = s[1];
+ g = s[2];
+ b = s[3];
+#else
+ b = s[0];
+ g = s[1];
+ r = s[2];
+#endif
+ ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
+ s += 4;
+ d += BPP;
+ } while (--w != 0);
+#endif
+}
+
+#undef PUT_PIXEL2
+#undef DEPTH
+#undef BPP
+#undef PIXEL_TYPE
+#undef PIXEL_NAME
+#undef BGR_FORMAT
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
new file mode 100644
index 0000000000..bcad47a68d
--- /dev/null
+++ b/hw/display/vmware_vga.c
@@ -0,0 +1,1282 @@
+/*
+ * QEMU VMware-SVGA "chipset".
+ *
+ * Copyright (c) 2007 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * 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/loader.h"
+#include "ui/console.h"
+#include "hw/pci/pci.h"
+
+#undef VERBOSE
+#define HW_RECT_ACCEL
+#define HW_FILL_ACCEL
+#define HW_MOUSE_ACCEL
+
+#include "vga_int.h"
+
+/* See http://vmware-svga.sf.net/ for some documentation on VMWare SVGA */
+
+struct vmsvga_state_s {
+ VGACommonState vga;
+
+ int invalidated;
+ int depth;
+ int bypp;
+ int enable;
+ int config;
+ struct {
+ int id;
+ int x;
+ int y;
+ int on;
+ } cursor;
+
+ int index;
+ int scratch_size;
+ uint32_t *scratch;
+ int new_width;
+ int new_height;
+ uint32_t guest;
+ uint32_t svgaid;
+ int syncing;
+
+ MemoryRegion fifo_ram;
+ uint8_t *fifo_ptr;
+ unsigned int fifo_size;
+
+ union {
+ uint32_t *fifo;
+ struct QEMU_PACKED {
+ uint32_t min;
+ uint32_t max;
+ uint32_t next_cmd;
+ uint32_t stop;
+ /* Add registers here when adding capabilities. */
+ uint32_t fifo[0];
+ } *cmd;
+ };
+
+#define REDRAW_FIFO_LEN 512
+ struct vmsvga_rect_s {
+ int x, y, w, h;
+ } redraw_fifo[REDRAW_FIFO_LEN];
+ int redraw_fifo_first, redraw_fifo_last;
+};
+
+struct pci_vmsvga_state_s {
+ PCIDevice card;
+ struct vmsvga_state_s chip;
+ MemoryRegion io_bar;
+};
+
+#define SVGA_MAGIC 0x900000UL
+#define SVGA_MAKE_ID(ver) (SVGA_MAGIC << 8 | (ver))
+#define SVGA_ID_0 SVGA_MAKE_ID(0)
+#define SVGA_ID_1 SVGA_MAKE_ID(1)
+#define SVGA_ID_2 SVGA_MAKE_ID(2)
+
+#define SVGA_LEGACY_BASE_PORT 0x4560
+#define SVGA_INDEX_PORT 0x0
+#define SVGA_VALUE_PORT 0x1
+#define SVGA_BIOS_PORT 0x2
+
+#define SVGA_VERSION_2
+
+#ifdef SVGA_VERSION_2
+# define SVGA_ID SVGA_ID_2
+# define SVGA_IO_BASE SVGA_LEGACY_BASE_PORT
+# define SVGA_IO_MUL 1
+# define SVGA_FIFO_SIZE 0x10000
+# define SVGA_PCI_DEVICE_ID PCI_DEVICE_ID_VMWARE_SVGA2
+#else
+# define SVGA_ID SVGA_ID_1
+# define SVGA_IO_BASE SVGA_LEGACY_BASE_PORT
+# define SVGA_IO_MUL 4
+# define SVGA_FIFO_SIZE 0x10000
+# define SVGA_PCI_DEVICE_ID PCI_DEVICE_ID_VMWARE_SVGA
+#endif
+
+enum {
+ /* ID 0, 1 and 2 registers */
+ SVGA_REG_ID = 0,
+ SVGA_REG_ENABLE = 1,
+ SVGA_REG_WIDTH = 2,
+ SVGA_REG_HEIGHT = 3,
+ SVGA_REG_MAX_WIDTH = 4,
+ SVGA_REG_MAX_HEIGHT = 5,
+ SVGA_REG_DEPTH = 6,
+ SVGA_REG_BITS_PER_PIXEL = 7, /* Current bpp in the guest */
+ SVGA_REG_PSEUDOCOLOR = 8,
+ SVGA_REG_RED_MASK = 9,
+ SVGA_REG_GREEN_MASK = 10,
+ SVGA_REG_BLUE_MASK = 11,
+ SVGA_REG_BYTES_PER_LINE = 12,
+ SVGA_REG_FB_START = 13,
+ SVGA_REG_FB_OFFSET = 14,
+ SVGA_REG_VRAM_SIZE = 15,
+ SVGA_REG_FB_SIZE = 16,
+
+ /* ID 1 and 2 registers */
+ SVGA_REG_CAPABILITIES = 17,
+ SVGA_REG_MEM_START = 18, /* Memory for command FIFO */
+ SVGA_REG_MEM_SIZE = 19,
+ SVGA_REG_CONFIG_DONE = 20, /* Set when memory area configured */
+ SVGA_REG_SYNC = 21, /* Write to force synchronization */
+ SVGA_REG_BUSY = 22, /* Read to check if sync is done */
+ SVGA_REG_GUEST_ID = 23, /* Set guest OS identifier */
+ SVGA_REG_CURSOR_ID = 24, /* ID of cursor */
+ SVGA_REG_CURSOR_X = 25, /* Set cursor X position */
+ SVGA_REG_CURSOR_Y = 26, /* Set cursor Y position */
+ SVGA_REG_CURSOR_ON = 27, /* Turn cursor on/off */
+ SVGA_REG_HOST_BITS_PER_PIXEL = 28, /* Current bpp in the host */
+ SVGA_REG_SCRATCH_SIZE = 29, /* Number of scratch registers */
+ SVGA_REG_MEM_REGS = 30, /* Number of FIFO registers */
+ SVGA_REG_NUM_DISPLAYS = 31, /* Number of guest displays */
+ SVGA_REG_PITCHLOCK = 32, /* Fixed pitch for all modes */
+
+ SVGA_PALETTE_BASE = 1024, /* Base of SVGA color map */
+ SVGA_PALETTE_END = SVGA_PALETTE_BASE + 767,
+ SVGA_SCRATCH_BASE = SVGA_PALETTE_BASE + 768,
+};
+
+#define SVGA_CAP_NONE 0
+#define SVGA_CAP_RECT_FILL (1 << 0)
+#define SVGA_CAP_RECT_COPY (1 << 1)
+#define SVGA_CAP_RECT_PAT_FILL (1 << 2)
+#define SVGA_CAP_LEGACY_OFFSCREEN (1 << 3)
+#define SVGA_CAP_RASTER_OP (1 << 4)
+#define SVGA_CAP_CURSOR (1 << 5)
+#define SVGA_CAP_CURSOR_BYPASS (1 << 6)
+#define SVGA_CAP_CURSOR_BYPASS_2 (1 << 7)
+#define SVGA_CAP_8BIT_EMULATION (1 << 8)
+#define SVGA_CAP_ALPHA_CURSOR (1 << 9)
+#define SVGA_CAP_GLYPH (1 << 10)
+#define SVGA_CAP_GLYPH_CLIPPING (1 << 11)
+#define SVGA_CAP_OFFSCREEN_1 (1 << 12)
+#define SVGA_CAP_ALPHA_BLEND (1 << 13)
+#define SVGA_CAP_3D (1 << 14)
+#define SVGA_CAP_EXTENDED_FIFO (1 << 15)
+#define SVGA_CAP_MULTIMON (1 << 16)
+#define SVGA_CAP_PITCHLOCK (1 << 17)
+
+/*
+ * FIFO offsets (seen as an array of 32-bit words)
+ */
+enum {
+ /*
+ * The original defined FIFO offsets
+ */
+ SVGA_FIFO_MIN = 0,
+ SVGA_FIFO_MAX, /* The distance from MIN to MAX must be at least 10K */
+ SVGA_FIFO_NEXT_CMD,
+ SVGA_FIFO_STOP,
+
+ /*
+ * Additional offsets added as of SVGA_CAP_EXTENDED_FIFO
+ */
+ SVGA_FIFO_CAPABILITIES = 4,
+ SVGA_FIFO_FLAGS,
+ SVGA_FIFO_FENCE,
+ SVGA_FIFO_3D_HWVERSION,
+ SVGA_FIFO_PITCHLOCK,
+};
+
+#define SVGA_FIFO_CAP_NONE 0
+#define SVGA_FIFO_CAP_FENCE (1 << 0)
+#define SVGA_FIFO_CAP_ACCELFRONT (1 << 1)
+#define SVGA_FIFO_CAP_PITCHLOCK (1 << 2)
+
+#define SVGA_FIFO_FLAG_NONE 0
+#define SVGA_FIFO_FLAG_ACCELFRONT (1 << 0)
+
+/* These values can probably be changed arbitrarily. */
+#define SVGA_SCRATCH_SIZE 0x8000
+#define SVGA_MAX_WIDTH 2360
+#define SVGA_MAX_HEIGHT 1770
+
+#ifdef VERBOSE
+# define GUEST_OS_BASE 0x5001
+static const char *vmsvga_guest_id[] = {
+ [0x00] = "Dos",
+ [0x01] = "Windows 3.1",
+ [0x02] = "Windows 95",
+ [0x03] = "Windows 98",
+ [0x04] = "Windows ME",
+ [0x05] = "Windows NT",
+ [0x06] = "Windows 2000",
+ [0x07] = "Linux",
+ [0x08] = "OS/2",
+ [0x09] = "an unknown OS",
+ [0x0a] = "BSD",
+ [0x0b] = "Whistler",
+ [0x0c] = "an unknown OS",
+ [0x0d] = "an unknown OS",
+ [0x0e] = "an unknown OS",
+ [0x0f] = "an unknown OS",
+ [0x10] = "an unknown OS",
+ [0x11] = "an unknown OS",
+ [0x12] = "an unknown OS",
+ [0x13] = "an unknown OS",
+ [0x14] = "an unknown OS",
+ [0x15] = "Windows 2003",
+};
+#endif
+
+enum {
+ SVGA_CMD_INVALID_CMD = 0,
+ SVGA_CMD_UPDATE = 1,
+ SVGA_CMD_RECT_FILL = 2,
+ SVGA_CMD_RECT_COPY = 3,
+ SVGA_CMD_DEFINE_BITMAP = 4,
+ SVGA_CMD_DEFINE_BITMAP_SCANLINE = 5,
+ SVGA_CMD_DEFINE_PIXMAP = 6,
+ SVGA_CMD_DEFINE_PIXMAP_SCANLINE = 7,
+ SVGA_CMD_RECT_BITMAP_FILL = 8,
+ SVGA_CMD_RECT_PIXMAP_FILL = 9,
+ SVGA_CMD_RECT_BITMAP_COPY = 10,
+ SVGA_CMD_RECT_PIXMAP_COPY = 11,
+ SVGA_CMD_FREE_OBJECT = 12,
+ SVGA_CMD_RECT_ROP_FILL = 13,
+ SVGA_CMD_RECT_ROP_COPY = 14,
+ SVGA_CMD_RECT_ROP_BITMAP_FILL = 15,
+ SVGA_CMD_RECT_ROP_PIXMAP_FILL = 16,
+ SVGA_CMD_RECT_ROP_BITMAP_COPY = 17,
+ SVGA_CMD_RECT_ROP_PIXMAP_COPY = 18,
+ SVGA_CMD_DEFINE_CURSOR = 19,
+ SVGA_CMD_DISPLAY_CURSOR = 20,
+ SVGA_CMD_MOVE_CURSOR = 21,
+ SVGA_CMD_DEFINE_ALPHA_CURSOR = 22,
+ SVGA_CMD_DRAW_GLYPH = 23,
+ SVGA_CMD_DRAW_GLYPH_CLIPPED = 24,
+ SVGA_CMD_UPDATE_VERBOSE = 25,
+ SVGA_CMD_SURFACE_FILL = 26,
+ SVGA_CMD_SURFACE_COPY = 27,
+ SVGA_CMD_SURFACE_ALPHA_BLEND = 28,
+ SVGA_CMD_FRONT_ROP_FILL = 29,
+ SVGA_CMD_FENCE = 30,
+};
+
+/* Legal values for the SVGA_REG_CURSOR_ON register in cursor bypass mode */
+enum {
+ SVGA_CURSOR_ON_HIDE = 0,
+ SVGA_CURSOR_ON_SHOW = 1,
+ SVGA_CURSOR_ON_REMOVE_FROM_FB = 2,
+ SVGA_CURSOR_ON_RESTORE_TO_FB = 3,
+};
+
+static inline void vmsvga_update_rect(struct vmsvga_state_s *s,
+ int x, int y, int w, int h)
+{
+ DisplaySurface *surface = qemu_console_surface(s->vga.con);
+ int line;
+ int bypl;
+ int width;
+ int start;
+ uint8_t *src;
+ uint8_t *dst;
+
+ if (x < 0) {
+ fprintf(stderr, "%s: update x was < 0 (%d)\n", __func__, x);
+ w += x;
+ x = 0;
+ }
+ if (w < 0) {
+ fprintf(stderr, "%s: update w was < 0 (%d)\n", __func__, w);
+ w = 0;
+ }
+ if (x + w > surface_width(surface)) {
+ fprintf(stderr, "%s: update width too large x: %d, w: %d\n",
+ __func__, x, w);
+ x = MIN(x, surface_width(surface));
+ w = surface_width(surface) - x;
+ }
+
+ if (y < 0) {
+ fprintf(stderr, "%s: update y was < 0 (%d)\n", __func__, y);
+ h += y;
+ y = 0;
+ }
+ if (h < 0) {
+ fprintf(stderr, "%s: update h was < 0 (%d)\n", __func__, h);
+ h = 0;
+ }
+ if (y + h > surface_height(surface)) {
+ fprintf(stderr, "%s: update height too large y: %d, h: %d\n",
+ __func__, y, h);
+ y = MIN(y, surface_height(surface));
+ h = surface_height(surface) - y;
+ }
+
+ bypl = surface_stride(surface);
+ width = surface_bytes_per_pixel(surface) * w;
+ start = surface_bytes_per_pixel(surface) * x + bypl * y;
+ src = s->vga.vram_ptr + start;
+ dst = surface_data(surface) + start;
+
+ for (line = h; line > 0; line--, src += bypl, dst += bypl) {
+ memcpy(dst, src, width);
+ }
+ dpy_gfx_update(s->vga.con, x, y, w, h);
+}
+
+static inline void vmsvga_update_rect_delayed(struct vmsvga_state_s *s,
+ int x, int y, int w, int h)
+{
+ struct vmsvga_rect_s *rect = &s->redraw_fifo[s->redraw_fifo_last++];
+
+ s->redraw_fifo_last &= REDRAW_FIFO_LEN - 1;
+ rect->x = x;
+ rect->y = y;
+ rect->w = w;
+ rect->h = h;
+}
+
+static inline void vmsvga_update_rect_flush(struct vmsvga_state_s *s)
+{
+ struct vmsvga_rect_s *rect;
+
+ if (s->invalidated) {
+ s->redraw_fifo_first = s->redraw_fifo_last;
+ return;
+ }
+ /* Overlapping region updates can be optimised out here - if someone
+ * knows a smart algorithm to do that, please share. */
+ while (s->redraw_fifo_first != s->redraw_fifo_last) {
+ rect = &s->redraw_fifo[s->redraw_fifo_first++];
+ s->redraw_fifo_first &= REDRAW_FIFO_LEN - 1;
+ vmsvga_update_rect(s, rect->x, rect->y, rect->w, rect->h);
+ }
+}
+
+#ifdef HW_RECT_ACCEL
+static inline void vmsvga_copy_rect(struct vmsvga_state_s *s,
+ int x0, int y0, int x1, int y1, int w, int h)
+{
+ DisplaySurface *surface = qemu_console_surface(s->vga.con);
+ uint8_t *vram = s->vga.vram_ptr;
+ int bypl = surface_stride(surface);
+ int bypp = surface_bytes_per_pixel(surface);
+ int width = bypp * w;
+ int line = h;
+ uint8_t *ptr[2];
+
+ if (y1 > y0) {
+ ptr[0] = vram + bypp * x0 + bypl * (y0 + h - 1);
+ ptr[1] = vram + bypp * x1 + bypl * (y1 + h - 1);
+ for (; line > 0; line --, ptr[0] -= bypl, ptr[1] -= bypl) {
+ memmove(ptr[1], ptr[0], width);
+ }
+ } else {
+ ptr[0] = vram + bypp * x0 + bypl * y0;
+ ptr[1] = vram + bypp * x1 + bypl * y1;
+ for (; line > 0; line --, ptr[0] += bypl, ptr[1] += bypl) {
+ memmove(ptr[1], ptr[0], width);
+ }
+ }
+
+ vmsvga_update_rect_delayed(s, x1, y1, w, h);
+}
+#endif
+
+#ifdef HW_FILL_ACCEL
+static inline void vmsvga_fill_rect(struct vmsvga_state_s *s,
+ uint32_t c, int x, int y, int w, int h)
+{
+ DisplaySurface *surface = qemu_console_surface(s->vga.con);
+ int bypl = surface_stride(surface);
+ int width = surface_bytes_per_pixel(surface) * w;
+ int line = h;
+ int column;
+ uint8_t *fst;
+ uint8_t *dst;
+ uint8_t *src;
+ uint8_t col[4];
+
+ col[0] = c;
+ col[1] = c >> 8;
+ col[2] = c >> 16;
+ col[3] = c >> 24;
+
+ fst = s->vga.vram_ptr + surface_bytes_per_pixel(surface) * x + bypl * y;
+
+ if (line--) {
+ dst = fst;
+ src = col;
+ for (column = width; column > 0; column--) {
+ *(dst++) = *(src++);
+ if (src - col == surface_bytes_per_pixel(surface)) {
+ src = col;
+ }
+ }
+ dst = fst;
+ for (; line > 0; line--) {
+ dst += bypl;
+ memcpy(dst, fst, width);
+ }
+ }
+
+ vmsvga_update_rect_delayed(s, x, y, w, h);
+}
+#endif
+
+struct vmsvga_cursor_definition_s {
+ int width;
+ int height;
+ int id;
+ int bpp;
+ int hot_x;
+ int hot_y;
+ uint32_t mask[1024];
+ uint32_t image[4096];
+};
+
+#define SVGA_BITMAP_SIZE(w, h) ((((w) + 31) >> 5) * (h))
+#define SVGA_PIXMAP_SIZE(w, h, bpp) (((((w) * (bpp)) + 31) >> 5) * (h))
+
+#ifdef HW_MOUSE_ACCEL
+static inline void vmsvga_cursor_define(struct vmsvga_state_s *s,
+ struct vmsvga_cursor_definition_s *c)
+{
+ QEMUCursor *qc;
+ int i, pixels;
+
+ qc = cursor_alloc(c->width, c->height);
+ qc->hot_x = c->hot_x;
+ qc->hot_y = c->hot_y;
+ switch (c->bpp) {
+ case 1:
+ cursor_set_mono(qc, 0xffffff, 0x000000, (void *)c->image,
+ 1, (void *)c->mask);
+#ifdef DEBUG
+ cursor_print_ascii_art(qc, "vmware/mono");
+#endif
+ break;
+ case 32:
+ /* fill alpha channel from mask, set color to zero */
+ cursor_set_mono(qc, 0x000000, 0x000000, (void *)c->mask,
+ 1, (void *)c->mask);
+ /* add in rgb values */
+ pixels = c->width * c->height;
+ for (i = 0; i < pixels; i++) {
+ qc->data[i] |= c->image[i] & 0xffffff;
+ }
+#ifdef DEBUG
+ cursor_print_ascii_art(qc, "vmware/32bit");
+#endif
+ break;
+ default:
+ fprintf(stderr, "%s: unhandled bpp %d, using fallback cursor\n",
+ __func__, c->bpp);
+ cursor_put(qc);
+ qc = cursor_builtin_left_ptr();
+ }
+
+ dpy_cursor_define(s->vga.con, qc);
+ cursor_put(qc);
+}
+#endif
+
+#define CMD(f) le32_to_cpu(s->cmd->f)
+
+static inline int vmsvga_fifo_length(struct vmsvga_state_s *s)
+{
+ int num;
+
+ if (!s->config || !s->enable) {
+ return 0;
+ }
+ num = CMD(next_cmd) - CMD(stop);
+ if (num < 0) {
+ num += CMD(max) - CMD(min);
+ }
+ return num >> 2;
+}
+
+static inline uint32_t vmsvga_fifo_read_raw(struct vmsvga_state_s *s)
+{
+ uint32_t cmd = s->fifo[CMD(stop) >> 2];
+
+ s->cmd->stop = cpu_to_le32(CMD(stop) + 4);
+ if (CMD(stop) >= CMD(max)) {
+ s->cmd->stop = s->cmd->min;
+ }
+ return cmd;
+}
+
+static inline uint32_t vmsvga_fifo_read(struct vmsvga_state_s *s)
+{
+ return le32_to_cpu(vmsvga_fifo_read_raw(s));
+}
+
+static void vmsvga_fifo_run(struct vmsvga_state_s *s)
+{
+ uint32_t cmd, colour;
+ int args, len;
+ int x, y, dx, dy, width, height;
+ struct vmsvga_cursor_definition_s cursor;
+ uint32_t cmd_start;
+
+ len = vmsvga_fifo_length(s);
+ while (len > 0) {
+ /* May need to go back to the start of the command if incomplete */
+ cmd_start = s->cmd->stop;
+
+ switch (cmd = vmsvga_fifo_read(s)) {
+ case SVGA_CMD_UPDATE:
+ case SVGA_CMD_UPDATE_VERBOSE:
+ len -= 5;
+ if (len < 0) {
+ goto rewind;
+ }
+
+ x = vmsvga_fifo_read(s);
+ y = vmsvga_fifo_read(s);
+ width = vmsvga_fifo_read(s);
+ height = vmsvga_fifo_read(s);
+ vmsvga_update_rect_delayed(s, x, y, width, height);
+ break;
+
+ case SVGA_CMD_RECT_FILL:
+ len -= 6;
+ if (len < 0) {
+ goto rewind;
+ }
+
+ colour = vmsvga_fifo_read(s);
+ x = vmsvga_fifo_read(s);
+ y = vmsvga_fifo_read(s);
+ width = vmsvga_fifo_read(s);
+ height = vmsvga_fifo_read(s);
+#ifdef HW_FILL_ACCEL
+ vmsvga_fill_rect(s, colour, x, y, width, height);
+ break;
+#else
+ args = 0;
+ goto badcmd;
+#endif
+
+ case SVGA_CMD_RECT_COPY:
+ len -= 7;
+ if (len < 0) {
+ goto rewind;
+ }
+
+ x = vmsvga_fifo_read(s);
+ y = vmsvga_fifo_read(s);
+ dx = vmsvga_fifo_read(s);
+ dy = vmsvga_fifo_read(s);
+ width = vmsvga_fifo_read(s);
+ height = vmsvga_fifo_read(s);
+#ifdef HW_RECT_ACCEL
+ vmsvga_copy_rect(s, x, y, dx, dy, width, height);
+ break;
+#else
+ args = 0;
+ goto badcmd;
+#endif
+
+ case SVGA_CMD_DEFINE_CURSOR:
+ len -= 8;
+ if (len < 0) {
+ goto rewind;
+ }
+
+ cursor.id = vmsvga_fifo_read(s);
+ cursor.hot_x = vmsvga_fifo_read(s);
+ cursor.hot_y = vmsvga_fifo_read(s);
+ cursor.width = x = vmsvga_fifo_read(s);
+ cursor.height = y = vmsvga_fifo_read(s);
+ vmsvga_fifo_read(s);
+ cursor.bpp = vmsvga_fifo_read(s);
+
+ args = SVGA_BITMAP_SIZE(x, y) + SVGA_PIXMAP_SIZE(x, y, cursor.bpp);
+ if (SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask ||
+ SVGA_PIXMAP_SIZE(x, y, cursor.bpp) > sizeof cursor.image) {
+ goto badcmd;
+ }
+
+ len -= args;
+ if (len < 0) {
+ goto rewind;
+ }
+
+ for (args = 0; args < SVGA_BITMAP_SIZE(x, y); args++) {
+ cursor.mask[args] = vmsvga_fifo_read_raw(s);
+ }
+ for (args = 0; args < SVGA_PIXMAP_SIZE(x, y, cursor.bpp); args++) {
+ cursor.image[args] = vmsvga_fifo_read_raw(s);
+ }
+#ifdef HW_MOUSE_ACCEL
+ vmsvga_cursor_define(s, &cursor);
+ break;
+#else
+ args = 0;
+ goto badcmd;
+#endif
+
+ /*
+ * Other commands that we at least know the number of arguments
+ * for so we can avoid FIFO desync if driver uses them illegally.
+ */
+ case SVGA_CMD_DEFINE_ALPHA_CURSOR:
+ len -= 6;
+ if (len < 0) {
+ goto rewind;
+ }
+ vmsvga_fifo_read(s);
+ vmsvga_fifo_read(s);
+ vmsvga_fifo_read(s);
+ x = vmsvga_fifo_read(s);
+ y = vmsvga_fifo_read(s);
+ args = x * y;
+ goto badcmd;
+ case SVGA_CMD_RECT_ROP_FILL:
+ args = 6;
+ goto badcmd;
+ case SVGA_CMD_RECT_ROP_COPY:
+ args = 7;
+ goto badcmd;
+ case SVGA_CMD_DRAW_GLYPH_CLIPPED:
+ len -= 4;
+ if (len < 0) {
+ goto rewind;
+ }
+ vmsvga_fifo_read(s);
+ vmsvga_fifo_read(s);
+ args = 7 + (vmsvga_fifo_read(s) >> 2);
+ goto badcmd;
+ case SVGA_CMD_SURFACE_ALPHA_BLEND:
+ args = 12;
+ goto badcmd;
+
+ /*
+ * Other commands that are not listed as depending on any
+ * CAPABILITIES bits, but are not described in the README either.
+ */
+ case SVGA_CMD_SURFACE_FILL:
+ case SVGA_CMD_SURFACE_COPY:
+ case SVGA_CMD_FRONT_ROP_FILL:
+ case SVGA_CMD_FENCE:
+ case SVGA_CMD_INVALID_CMD:
+ break; /* Nop */
+
+ default:
+ args = 0;
+ badcmd:
+ len -= args;
+ if (len < 0) {
+ goto rewind;
+ }
+ while (args--) {
+ vmsvga_fifo_read(s);
+ }
+ printf("%s: Unknown command 0x%02x in SVGA command FIFO\n",
+ __func__, cmd);
+ break;
+
+ rewind:
+ s->cmd->stop = cmd_start;
+ break;
+ }
+ }
+
+ s->syncing = 0;
+}
+
+static uint32_t vmsvga_index_read(void *opaque, uint32_t address)
+{
+ struct vmsvga_state_s *s = opaque;
+
+ return s->index;
+}
+
+static void vmsvga_index_write(void *opaque, uint32_t address, uint32_t index)
+{
+ struct vmsvga_state_s *s = opaque;
+
+ s->index = index;
+}
+
+static uint32_t vmsvga_value_read(void *opaque, uint32_t address)
+{
+ uint32_t caps;
+ struct vmsvga_state_s *s = opaque;
+ DisplaySurface *surface = qemu_console_surface(s->vga.con);
+
+ switch (s->index) {
+ case SVGA_REG_ID:
+ return s->svgaid;
+
+ case SVGA_REG_ENABLE:
+ return s->enable;
+
+ case SVGA_REG_WIDTH:
+ return surface_width(surface);
+
+ case SVGA_REG_HEIGHT:
+ return surface_height(surface);
+
+ case SVGA_REG_MAX_WIDTH:
+ return SVGA_MAX_WIDTH;
+
+ case SVGA_REG_MAX_HEIGHT:
+ return SVGA_MAX_HEIGHT;
+
+ case SVGA_REG_DEPTH:
+ return s->depth;
+
+ case SVGA_REG_BITS_PER_PIXEL:
+ return (s->depth + 7) & ~7;
+
+ case SVGA_REG_PSEUDOCOLOR:
+ return 0x0;
+
+ case SVGA_REG_RED_MASK:
+ return surface->pf.rmask;
+
+ case SVGA_REG_GREEN_MASK:
+ return surface->pf.gmask;
+
+ case SVGA_REG_BLUE_MASK:
+ return surface->pf.bmask;
+
+ case SVGA_REG_BYTES_PER_LINE:
+ return s->bypp * s->new_width;
+
+ case SVGA_REG_FB_START: {
+ struct pci_vmsvga_state_s *pci_vmsvga
+ = container_of(s, struct pci_vmsvga_state_s, chip);
+ return pci_get_bar_addr(&pci_vmsvga->card, 1);
+ }
+
+ case SVGA_REG_FB_OFFSET:
+ return 0x0;
+
+ case SVGA_REG_VRAM_SIZE:
+ return s->vga.vram_size; /* No physical VRAM besides the framebuffer */
+
+ case SVGA_REG_FB_SIZE:
+ return s->vga.vram_size;
+
+ case SVGA_REG_CAPABILITIES:
+ caps = SVGA_CAP_NONE;
+#ifdef HW_RECT_ACCEL
+ caps |= SVGA_CAP_RECT_COPY;
+#endif
+#ifdef HW_FILL_ACCEL
+ caps |= SVGA_CAP_RECT_FILL;
+#endif
+#ifdef HW_MOUSE_ACCEL
+ if (dpy_cursor_define_supported(s->vga.con)) {
+ caps |= SVGA_CAP_CURSOR | SVGA_CAP_CURSOR_BYPASS_2 |
+ SVGA_CAP_CURSOR_BYPASS;
+ }
+#endif
+ return caps;
+
+ case SVGA_REG_MEM_START: {
+ struct pci_vmsvga_state_s *pci_vmsvga
+ = container_of(s, struct pci_vmsvga_state_s, chip);
+ return pci_get_bar_addr(&pci_vmsvga->card, 2);
+ }
+
+ case SVGA_REG_MEM_SIZE:
+ return s->fifo_size;
+
+ case SVGA_REG_CONFIG_DONE:
+ return s->config;
+
+ case SVGA_REG_SYNC:
+ case SVGA_REG_BUSY:
+ return s->syncing;
+
+ case SVGA_REG_GUEST_ID:
+ return s->guest;
+
+ case SVGA_REG_CURSOR_ID:
+ return s->cursor.id;
+
+ case SVGA_REG_CURSOR_X:
+ return s->cursor.x;
+
+ case SVGA_REG_CURSOR_Y:
+ return s->cursor.x;
+
+ case SVGA_REG_CURSOR_ON:
+ return s->cursor.on;
+
+ case SVGA_REG_HOST_BITS_PER_PIXEL:
+ return (s->depth + 7) & ~7;
+
+ case SVGA_REG_SCRATCH_SIZE:
+ return s->scratch_size;
+
+ case SVGA_REG_MEM_REGS:
+ case SVGA_REG_NUM_DISPLAYS:
+ case SVGA_REG_PITCHLOCK:
+ case SVGA_PALETTE_BASE ... SVGA_PALETTE_END:
+ return 0;
+
+ default:
+ if (s->index >= SVGA_SCRATCH_BASE &&
+ s->index < SVGA_SCRATCH_BASE + s->scratch_size) {
+ return s->scratch[s->index - SVGA_SCRATCH_BASE];
+ }
+ printf("%s: Bad register %02x\n", __func__, s->index);
+ }
+
+ return 0;
+}
+
+static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value)
+{
+ struct vmsvga_state_s *s = opaque;
+
+ switch (s->index) {
+ case SVGA_REG_ID:
+ if (value == SVGA_ID_2 || value == SVGA_ID_1 || value == SVGA_ID_0) {
+ s->svgaid = value;
+ }
+ break;
+
+ case SVGA_REG_ENABLE:
+ s->enable = !!value;
+ s->invalidated = 1;
+ s->vga.invalidate(&s->vga);
+ if (s->enable && s->config) {
+ vga_dirty_log_stop(&s->vga);
+ } else {
+ vga_dirty_log_start(&s->vga);
+ }
+ break;
+
+ case SVGA_REG_WIDTH:
+ if (value <= SVGA_MAX_WIDTH) {
+ s->new_width = value;
+ s->invalidated = 1;
+ } else {
+ printf("%s: Bad width: %i\n", __func__, value);
+ }
+ break;
+
+ case SVGA_REG_HEIGHT:
+ if (value <= SVGA_MAX_HEIGHT) {
+ s->new_height = value;
+ s->invalidated = 1;
+ } else {
+ printf("%s: Bad height: %i\n", __func__, value);
+ }
+ break;
+
+ case SVGA_REG_BITS_PER_PIXEL:
+ if (value != s->depth) {
+ printf("%s: Bad bits per pixel: %i bits\n", __func__, value);
+ s->config = 0;
+ }
+ break;
+
+ case SVGA_REG_CONFIG_DONE:
+ if (value) {
+ s->fifo = (uint32_t *) s->fifo_ptr;
+ /* Check range and alignment. */
+ if ((CMD(min) | CMD(max) | CMD(next_cmd) | CMD(stop)) & 3) {
+ break;
+ }
+ if (CMD(min) < (uint8_t *) s->cmd->fifo - (uint8_t *) s->fifo) {
+ break;
+ }
+ if (CMD(max) > SVGA_FIFO_SIZE) {
+ break;
+ }
+ if (CMD(max) < CMD(min) + 10 * 1024) {
+ break;
+ }
+ vga_dirty_log_stop(&s->vga);
+ }
+ s->config = !!value;
+ break;
+
+ case SVGA_REG_SYNC:
+ s->syncing = 1;
+ vmsvga_fifo_run(s); /* Or should we just wait for update_display? */
+ break;
+
+ case SVGA_REG_GUEST_ID:
+ s->guest = value;
+#ifdef VERBOSE
+ if (value >= GUEST_OS_BASE && value < GUEST_OS_BASE +
+ ARRAY_SIZE(vmsvga_guest_id)) {
+ printf("%s: guest runs %s.\n", __func__,
+ vmsvga_guest_id[value - GUEST_OS_BASE]);
+ }
+#endif
+ break;
+
+ case SVGA_REG_CURSOR_ID:
+ s->cursor.id = value;
+ break;
+
+ case SVGA_REG_CURSOR_X:
+ s->cursor.x = value;
+ break;
+
+ case SVGA_REG_CURSOR_Y:
+ s->cursor.y = value;
+ break;
+
+ case SVGA_REG_CURSOR_ON:
+ s->cursor.on |= (value == SVGA_CURSOR_ON_SHOW);
+ s->cursor.on &= (value != SVGA_CURSOR_ON_HIDE);
+#ifdef HW_MOUSE_ACCEL
+ if (value <= SVGA_CURSOR_ON_SHOW) {
+ dpy_mouse_set(s->vga.con, s->cursor.x, s->cursor.y, s->cursor.on);
+ }
+#endif
+ break;
+
+ case SVGA_REG_DEPTH:
+ case SVGA_REG_MEM_REGS:
+ case SVGA_REG_NUM_DISPLAYS:
+ case SVGA_REG_PITCHLOCK:
+ case SVGA_PALETTE_BASE ... SVGA_PALETTE_END:
+ break;
+
+ default:
+ if (s->index >= SVGA_SCRATCH_BASE &&
+ s->index < SVGA_SCRATCH_BASE + s->scratch_size) {
+ s->scratch[s->index - SVGA_SCRATCH_BASE] = value;
+ break;
+ }
+ printf("%s: Bad register %02x\n", __func__, s->index);
+ }
+}
+
+static uint32_t vmsvga_bios_read(void *opaque, uint32_t address)
+{
+ printf("%s: what are we supposed to return?\n", __func__);
+ return 0xcafe;
+}
+
+static void vmsvga_bios_write(void *opaque, uint32_t address, uint32_t data)
+{
+ printf("%s: what are we supposed to do with (%08x)?\n", __func__, data);
+}
+
+static inline void vmsvga_check_size(struct vmsvga_state_s *s)
+{
+ DisplaySurface *surface = qemu_console_surface(s->vga.con);
+
+ if (s->new_width != surface_width(surface) ||
+ s->new_height != surface_height(surface)) {
+ qemu_console_resize(s->vga.con, s->new_width, s->new_height);
+ s->invalidated = 1;
+ }
+}
+
+static void vmsvga_update_display(void *opaque)
+{
+ struct vmsvga_state_s *s = opaque;
+ DisplaySurface *surface = qemu_console_surface(s->vga.con);
+ bool dirty = false;
+
+ if (!s->enable) {
+ s->vga.update(&s->vga);
+ return;
+ }
+
+ vmsvga_check_size(s);
+
+ vmsvga_fifo_run(s);
+ vmsvga_update_rect_flush(s);
+
+ /*
+ * Is it more efficient to look at vram VGA-dirty bits or wait
+ * for the driver to issue SVGA_CMD_UPDATE?
+ */
+ if (memory_region_is_logging(&s->vga.vram)) {
+ vga_sync_dirty_bitmap(&s->vga);
+ dirty = memory_region_get_dirty(&s->vga.vram, 0,
+ surface_stride(surface) * surface_height(surface),
+ DIRTY_MEMORY_VGA);
+ }
+ if (s->invalidated || dirty) {
+ s->invalidated = 0;
+ memcpy(surface_data(surface), s->vga.vram_ptr,
+ surface_stride(surface) * surface_height(surface));
+ dpy_gfx_update(s->vga.con, 0, 0,
+ surface_width(surface), surface_height(surface));
+ }
+ if (dirty) {
+ memory_region_reset_dirty(&s->vga.vram, 0,
+ surface_stride(surface) * surface_height(surface),
+ DIRTY_MEMORY_VGA);
+ }
+}
+
+static void vmsvga_reset(DeviceState *dev)
+{
+ struct pci_vmsvga_state_s *pci =
+ DO_UPCAST(struct pci_vmsvga_state_s, card.qdev, dev);
+ struct vmsvga_state_s *s = &pci->chip;
+
+ s->index = 0;
+ s->enable = 0;
+ s->config = 0;
+ s->svgaid = SVGA_ID;
+ s->cursor.on = 0;
+ s->redraw_fifo_first = 0;
+ s->redraw_fifo_last = 0;
+ s->syncing = 0;
+
+ vga_dirty_log_start(&s->vga);
+}
+
+static void vmsvga_invalidate_display(void *opaque)
+{
+ struct vmsvga_state_s *s = opaque;
+ if (!s->enable) {
+ s->vga.invalidate(&s->vga);
+ return;
+ }
+
+ s->invalidated = 1;
+}
+
+/* save the vga display in a PPM image even if no display is
+ available */
+static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch,
+ Error **errp)
+{
+ struct vmsvga_state_s *s = opaque;
+ DisplaySurface *surface = qemu_console_surface(s->vga.con);
+
+ if (!s->enable) {
+ s->vga.screen_dump(&s->vga, filename, cswitch, errp);
+ return;
+ }
+
+ if (surface_bits_per_pixel(surface) == 32) {
+ DisplaySurface *ds = qemu_create_displaysurface_from(
+ surface_width(surface),
+ surface_height(surface),
+ 32,
+ surface_stride(surface),
+ s->vga.vram_ptr, false);
+ ppm_save(filename, ds, errp);
+ g_free(ds);
+ }
+}
+
+static void vmsvga_text_update(void *opaque, console_ch_t *chardata)
+{
+ struct vmsvga_state_s *s = opaque;
+
+ if (s->vga.text_update) {
+ s->vga.text_update(&s->vga, chardata);
+ }
+}
+
+static int vmsvga_post_load(void *opaque, int version_id)
+{
+ struct vmsvga_state_s *s = opaque;
+
+ s->invalidated = 1;
+ if (s->config) {
+ s->fifo = (uint32_t *) s->fifo_ptr;
+ }
+ return 0;
+}
+
+static const VMStateDescription vmstate_vmware_vga_internal = {
+ .name = "vmware_vga_internal",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .post_load = vmsvga_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32_EQUAL(depth, struct vmsvga_state_s),
+ VMSTATE_INT32(enable, struct vmsvga_state_s),
+ VMSTATE_INT32(config, struct vmsvga_state_s),
+ VMSTATE_INT32(cursor.id, struct vmsvga_state_s),
+ VMSTATE_INT32(cursor.x, struct vmsvga_state_s),
+ VMSTATE_INT32(cursor.y, struct vmsvga_state_s),
+ VMSTATE_INT32(cursor.on, struct vmsvga_state_s),
+ VMSTATE_INT32(index, struct vmsvga_state_s),
+ VMSTATE_VARRAY_INT32(scratch, struct vmsvga_state_s,
+ scratch_size, 0, vmstate_info_uint32, uint32_t),
+ VMSTATE_INT32(new_width, struct vmsvga_state_s),
+ VMSTATE_INT32(new_height, struct vmsvga_state_s),
+ VMSTATE_UINT32(guest, struct vmsvga_state_s),
+ VMSTATE_UINT32(svgaid, struct vmsvga_state_s),
+ VMSTATE_INT32(syncing, struct vmsvga_state_s),
+ VMSTATE_UNUSED(4), /* was fb_size */
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_vmware_vga = {
+ .name = "vmware_vga",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .minimum_version_id_old = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(card, struct pci_vmsvga_state_s),
+ VMSTATE_STRUCT(chip, struct pci_vmsvga_state_s, 0,
+ vmstate_vmware_vga_internal, struct vmsvga_state_s),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void vmsvga_init(struct vmsvga_state_s *s,
+ MemoryRegion *address_space, MemoryRegion *io)
+{
+ DisplaySurface *surface;
+
+ s->scratch_size = SVGA_SCRATCH_SIZE;
+ s->scratch = g_malloc(s->scratch_size * 4);
+
+ s->vga.con = graphic_console_init(vmsvga_update_display,
+ vmsvga_invalidate_display,
+ vmsvga_screen_dump,
+ vmsvga_text_update, s);
+ surface = qemu_console_surface(s->vga.con);
+
+ s->fifo_size = SVGA_FIFO_SIZE;
+ memory_region_init_ram(&s->fifo_ram, "vmsvga.fifo", s->fifo_size);
+ vmstate_register_ram_global(&s->fifo_ram);
+ s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram);
+
+ vga_common_init(&s->vga);
+ vga_init(&s->vga, address_space, io, true);
+ vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga);
+ /* Save some values here in case they are changed later.
+ * This is suspicious and needs more though why it is needed. */
+ s->depth = surface_bits_per_pixel(surface);
+ s->bypp = surface_bytes_per_pixel(surface);
+}
+
+static uint64_t vmsvga_io_read(void *opaque, hwaddr addr, unsigned size)
+{
+ struct vmsvga_state_s *s = opaque;
+
+ switch (addr) {
+ case SVGA_IO_MUL * SVGA_INDEX_PORT: return vmsvga_index_read(s, addr);
+ case SVGA_IO_MUL * SVGA_VALUE_PORT: return vmsvga_value_read(s, addr);
+ case SVGA_IO_MUL * SVGA_BIOS_PORT: return vmsvga_bios_read(s, addr);
+ default: return -1u;
+ }
+}
+
+static void vmsvga_io_write(void *opaque, hwaddr addr,
+ uint64_t data, unsigned size)
+{
+ struct vmsvga_state_s *s = opaque;
+
+ switch (addr) {
+ case SVGA_IO_MUL * SVGA_INDEX_PORT:
+ vmsvga_index_write(s, addr, data);
+ break;
+ case SVGA_IO_MUL * SVGA_VALUE_PORT:
+ vmsvga_value_write(s, addr, data);
+ break;
+ case SVGA_IO_MUL * SVGA_BIOS_PORT:
+ vmsvga_bios_write(s, addr, data);
+ break;
+ }
+}
+
+static const MemoryRegionOps vmsvga_io_ops = {
+ .read = vmsvga_io_read,
+ .write = vmsvga_io_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static int pci_vmsvga_initfn(PCIDevice *dev)
+{
+ struct pci_vmsvga_state_s *s =
+ DO_UPCAST(struct pci_vmsvga_state_s, card, dev);
+
+ s->card.config[PCI_CACHE_LINE_SIZE] = 0x08; /* Cache line size */
+ s->card.config[PCI_LATENCY_TIMER] = 0x40; /* Latency timer */
+ s->card.config[PCI_INTERRUPT_LINE] = 0xff; /* End */
+
+ memory_region_init_io(&s->io_bar, &vmsvga_io_ops, &s->chip,
+ "vmsvga-io", 0x10);
+ memory_region_set_flush_coalesced(&s->io_bar);
+ pci_register_bar(&s->card, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_bar);
+
+ vmsvga_init(&s->chip, pci_address_space(dev), pci_address_space_io(dev));
+
+ pci_register_bar(&s->card, 1, PCI_BASE_ADDRESS_MEM_PREFETCH,
+ &s->chip.vga.vram);
+ pci_register_bar(&s->card, 2, PCI_BASE_ADDRESS_MEM_PREFETCH,
+ &s->chip.fifo_ram);
+
+ if (!dev->rom_bar) {
+ /* compatibility with pc-0.13 and older */
+ vga_init_vbe(&s->chip.vga, pci_address_space(dev));
+ }
+
+ return 0;
+}
+
+static Property vga_vmware_properties[] = {
+ DEFINE_PROP_UINT32("vgamem_mb", struct pci_vmsvga_state_s,
+ chip.vga.vram_size_mb, 16),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vmsvga_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->no_hotplug = 1;
+ k->init = pci_vmsvga_initfn;
+ k->romfile = "vgabios-vmware.bin";
+ k->vendor_id = PCI_VENDOR_ID_VMWARE;
+ k->device_id = SVGA_PCI_DEVICE_ID;
+ k->class_id = PCI_CLASS_DISPLAY_VGA;
+ k->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE;
+ k->subsystem_id = SVGA_PCI_DEVICE_ID;
+ dc->reset = vmsvga_reset;
+ dc->vmsd = &vmstate_vmware_vga;
+ dc->props = vga_vmware_properties;
+}
+
+static const TypeInfo vmsvga_info = {
+ .name = "vmware-svga",
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(struct pci_vmsvga_state_s),
+ .class_init = vmsvga_class_init,
+};
+
+static void vmsvga_register_types(void)
+{
+ type_register_static(&vmsvga_info);
+}
+
+type_init(vmsvga_register_types)
diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
new file mode 100644
index 0000000000..8e4266142d
--- /dev/null
+++ b/hw/display/xenfb.c
@@ -0,0 +1,1021 @@
+/*
+ * xen paravirt framebuffer backend
+ *
+ * Copyright IBM, Corp. 2005-2006
+ * Copyright Red Hat, Inc. 2006-2008
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>,
+ * Markus Armbruster <armbru@redhat.com>,
+ * Daniel P. Berrange <berrange@redhat.com>,
+ * Pat Campbell <plc@novell.com>,
+ * Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "hw/hw.h"
+#include "ui/console.h"
+#include "char/char.h"
+#include "hw/xen/xen_backend.h"
+
+#include <xen/event_channel.h>
+#include <xen/io/fbif.h>
+#include <xen/io/kbdif.h>
+#include <xen/io/protocols.h>
+
+#ifndef BTN_LEFT
+#define BTN_LEFT 0x110 /* from <linux/input.h> */
+#endif
+
+/* -------------------------------------------------------------------- */
+
+struct common {
+ struct XenDevice xendev; /* must be first */
+ void *page;
+ QemuConsole *con;
+};
+
+struct XenInput {
+ struct common c;
+ int abs_pointer_wanted; /* Whether guest supports absolute pointer */
+ int button_state; /* Last seen pointer button state */
+ int extended;
+ QEMUPutMouseEntry *qmouse;
+};
+
+#define UP_QUEUE 8
+
+struct XenFB {
+ struct common c;
+ size_t fb_len;
+ int row_stride;
+ int depth;
+ int width;
+ int height;
+ int offset;
+ void *pixels;
+ int fbpages;
+ int feature_update;
+ int refresh_period;
+ int bug_trigger;
+ int have_console;
+ int do_resize;
+
+ struct {
+ int x,y,w,h;
+ } up_rects[UP_QUEUE];
+ int up_count;
+ int up_fullscreen;
+};
+
+/* -------------------------------------------------------------------- */
+
+static int common_bind(struct common *c)
+{
+ int mfn;
+
+ if (xenstore_read_fe_int(&c->xendev, "page-ref", &mfn) == -1)
+ return -1;
+ if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1)
+ return -1;
+
+ c->page = xc_map_foreign_range(xen_xc, c->xendev.dom,
+ XC_PAGE_SIZE,
+ PROT_READ | PROT_WRITE, mfn);
+ if (c->page == NULL)
+ return -1;
+
+ xen_be_bind_evtchn(&c->xendev);
+ xen_be_printf(&c->xendev, 1, "ring mfn %d, remote-port %d, local-port %d\n",
+ mfn, c->xendev.remote_port, c->xendev.local_port);
+
+ return 0;
+}
+
+static void common_unbind(struct common *c)
+{
+ xen_be_unbind_evtchn(&c->xendev);
+ if (c->page) {
+ munmap(c->page, XC_PAGE_SIZE);
+ c->page = NULL;
+ }
+}
+
+/* -------------------------------------------------------------------- */
+
+#if 0
+/*
+ * These two tables are not needed any more, but left in here
+ * intentionally as documentation, to show how scancode2linux[]
+ * was generated.
+ *
+ * Tables to map from scancode to Linux input layer keycode.
+ * Scancodes are hardware-specific. These maps assumes a
+ * standard AT or PS/2 keyboard which is what QEMU feeds us.
+ */
+const unsigned char atkbd_set2_keycode[512] = {
+
+ 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117,
+ 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0,
+ 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183,
+ 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185,
+ 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0,
+ 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85,
+ 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0,
+ 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125,
+ 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127,
+ 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142,
+ 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0,
+ 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112,
+ 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0,
+
+};
+
+const unsigned char atkbd_unxlate_table[128] = {
+
+ 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
+ 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
+ 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
+ 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
+ 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
+ 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
+ 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
+ 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
+
+};
+#endif
+
+/*
+ * for (i = 0; i < 128; i++) {
+ * scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]];
+ * scancode2linux[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80];
+ * }
+ */
+static const unsigned char scancode2linux[512] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 99, 0, 86, 87, 88,117, 0, 0, 95,183,184,185,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 93, 0, 0, 89, 0, 0, 85, 91, 90, 92, 0, 94, 0,124,121, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 165, 0, 0, 0, 0, 0, 0, 0, 0,163, 0, 0, 96, 97, 0, 0,
+ 113,140,164, 0,166, 0, 0, 0, 0, 0,255, 0, 0, 0,114, 0,
+ 115, 0,150, 0, 0, 98,255, 99,100, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,119,119,102,103,104, 0,105,112,106,118,107,
+ 108,109,110,111, 0, 0, 0, 0, 0, 0, 0,125,126,127,116,142,
+ 0, 0, 0,143, 0,217,156,173,128,159,158,157,155,226, 0,112,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+/* Send an event to the keyboard frontend driver */
+static int xenfb_kbd_event(struct XenInput *xenfb,
+ union xenkbd_in_event *event)
+{
+ struct xenkbd_page *page = xenfb->c.page;
+ uint32_t prod;
+
+ if (xenfb->c.xendev.be_state != XenbusStateConnected)
+ return 0;
+ if (!page)
+ return 0;
+
+ prod = page->in_prod;
+ if (prod - page->in_cons == XENKBD_IN_RING_LEN) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ xen_mb(); /* ensure ring space available */
+ XENKBD_IN_RING_REF(page, prod) = *event;
+ xen_wmb(); /* ensure ring contents visible */
+ page->in_prod = prod + 1;
+ return xen_be_send_notify(&xenfb->c.xendev);
+}
+
+/* Send a keyboard (or mouse button) event */
+static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode)
+{
+ union xenkbd_in_event event;
+
+ memset(&event, 0, XENKBD_IN_EVENT_SIZE);
+ event.type = XENKBD_TYPE_KEY;
+ event.key.pressed = down ? 1 : 0;
+ event.key.keycode = keycode;
+
+ return xenfb_kbd_event(xenfb, &event);
+}
+
+/* Send a relative mouse movement event */
+static int xenfb_send_motion(struct XenInput *xenfb,
+ int rel_x, int rel_y, int rel_z)
+{
+ union xenkbd_in_event event;
+
+ memset(&event, 0, XENKBD_IN_EVENT_SIZE);
+ event.type = XENKBD_TYPE_MOTION;
+ event.motion.rel_x = rel_x;
+ event.motion.rel_y = rel_y;
+#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207
+ event.motion.rel_z = rel_z;
+#endif
+
+ return xenfb_kbd_event(xenfb, &event);
+}
+
+/* Send an absolute mouse movement event */
+static int xenfb_send_position(struct XenInput *xenfb,
+ int abs_x, int abs_y, int z)
+{
+ union xenkbd_in_event event;
+
+ memset(&event, 0, XENKBD_IN_EVENT_SIZE);
+ event.type = XENKBD_TYPE_POS;
+ event.pos.abs_x = abs_x;
+ event.pos.abs_y = abs_y;
+#if __XEN_LATEST_INTERFACE_VERSION__ == 0x00030207
+ event.pos.abs_z = z;
+#endif
+#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030208
+ event.pos.rel_z = z;
+#endif
+
+ return xenfb_kbd_event(xenfb, &event);
+}
+
+/*
+ * Send a key event from the client to the guest OS
+ * QEMU gives us a raw scancode from an AT / PS/2 style keyboard.
+ * We have to turn this into a Linux Input layer keycode.
+ *
+ * Extra complexity from the fact that with extended scancodes
+ * (like those produced by arrow keys) this method gets called
+ * twice, but we only want to send a single event. So we have to
+ * track the '0xe0' scancode state & collapse the extended keys
+ * as needed.
+ *
+ * Wish we could just send scancodes straight to the guest which
+ * already has code for dealing with this...
+ */
+static void xenfb_key_event(void *opaque, int scancode)
+{
+ struct XenInput *xenfb = opaque;
+ int down = 1;
+
+ if (scancode == 0xe0) {
+ xenfb->extended = 1;
+ return;
+ } else if (scancode & 0x80) {
+ scancode &= 0x7f;
+ down = 0;
+ }
+ if (xenfb->extended) {
+ scancode |= 0x80;
+ xenfb->extended = 0;
+ }
+ xenfb_send_key(xenfb, down, scancode2linux[scancode]);
+}
+
+/*
+ * Send a mouse event from the client to the guest OS
+ *
+ * The QEMU mouse can be in either relative, or absolute mode.
+ * Movement is sent separately from button state, which has to
+ * be encoded as virtual key events. We also don't actually get
+ * given any button up/down events, so have to track changes in
+ * the button state.
+ */
+static void xenfb_mouse_event(void *opaque,
+ int dx, int dy, int dz, int button_state)
+{
+ struct XenInput *xenfb = opaque;
+ DisplaySurface *surface = qemu_console_surface(xenfb->c.con);
+ int dw = surface_width(surface);
+ int dh = surface_height(surface);
+ int i;
+
+ if (xenfb->abs_pointer_wanted)
+ xenfb_send_position(xenfb,
+ dx * (dw - 1) / 0x7fff,
+ dy * (dh - 1) / 0x7fff,
+ dz);
+ else
+ xenfb_send_motion(xenfb, dx, dy, dz);
+
+ for (i = 0 ; i < 8 ; i++) {
+ int lastDown = xenfb->button_state & (1 << i);
+ int down = button_state & (1 << i);
+ if (down == lastDown)
+ continue;
+
+ if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0)
+ return;
+ }
+ xenfb->button_state = button_state;
+}
+
+static int input_init(struct XenDevice *xendev)
+{
+ xenstore_write_be_int(xendev, "feature-abs-pointer", 1);
+ return 0;
+}
+
+static int input_initialise(struct XenDevice *xendev)
+{
+ struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
+ int rc;
+
+ if (!in->c.con) {
+ xen_be_printf(xendev, 1, "ds not set (yet)\n");
+ return -1;
+ }
+
+ rc = common_bind(&in->c);
+ if (rc != 0)
+ return rc;
+
+ qemu_add_kbd_event_handler(xenfb_key_event, in);
+ return 0;
+}
+
+static void input_connected(struct XenDevice *xendev)
+{
+ struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
+
+ if (xenstore_read_fe_int(xendev, "request-abs-pointer",
+ &in->abs_pointer_wanted) == -1) {
+ in->abs_pointer_wanted = 0;
+ }
+
+ if (in->qmouse) {
+ qemu_remove_mouse_event_handler(in->qmouse);
+ }
+ in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in,
+ in->abs_pointer_wanted,
+ "Xen PVFB Mouse");
+}
+
+static void input_disconnect(struct XenDevice *xendev)
+{
+ struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
+
+ if (in->qmouse) {
+ qemu_remove_mouse_event_handler(in->qmouse);
+ in->qmouse = NULL;
+ }
+ qemu_add_kbd_event_handler(NULL, NULL);
+ common_unbind(&in->c);
+}
+
+static void input_event(struct XenDevice *xendev)
+{
+ struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev);
+ struct xenkbd_page *page = xenfb->c.page;
+
+ /* We don't understand any keyboard events, so just ignore them. */
+ if (page->out_prod == page->out_cons)
+ return;
+ page->out_cons = page->out_prod;
+ xen_be_send_notify(&xenfb->c.xendev);
+}
+
+/* -------------------------------------------------------------------- */
+
+static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src)
+{
+ uint32_t *src32 = src;
+ uint64_t *src64 = src;
+ int i;
+
+ for (i = 0; i < count; i++)
+ dst[i] = (mode == 32) ? src32[i] : src64[i];
+}
+
+static int xenfb_map_fb(struct XenFB *xenfb)
+{
+ struct xenfb_page *page = xenfb->c.page;
+ char *protocol = xenfb->c.xendev.protocol;
+ int n_fbdirs;
+ unsigned long *pgmfns = NULL;
+ unsigned long *fbmfns = NULL;
+ void *map, *pd;
+ int mode, ret = -1;
+
+ /* default to native */
+ pd = page->pd;
+ mode = sizeof(unsigned long) * 8;
+
+ if (!protocol) {
+ /*
+ * Undefined protocol, some guesswork needed.
+ *
+ * Old frontends which don't set the protocol use
+ * one page directory only, thus pd[1] must be zero.
+ * pd[1] of the 32bit struct layout and the lower
+ * 32 bits of pd[0] of the 64bit struct layout have
+ * the same location, so we can check that ...
+ */
+ uint32_t *ptr32 = NULL;
+ uint32_t *ptr64 = NULL;
+#if defined(__i386__)
+ ptr32 = (void*)page->pd;
+ ptr64 = ((void*)page->pd) + 4;
+#elif defined(__x86_64__)
+ ptr32 = ((void*)page->pd) - 4;
+ ptr64 = (void*)page->pd;
+#endif
+ if (ptr32) {
+ if (ptr32[1] == 0) {
+ mode = 32;
+ pd = ptr32;
+ } else {
+ mode = 64;
+ pd = ptr64;
+ }
+ }
+#if defined(__x86_64__)
+ } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
+ /* 64bit dom0, 32bit domU */
+ mode = 32;
+ pd = ((void*)page->pd) - 4;
+#elif defined(__i386__)
+ } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) == 0) {
+ /* 32bit dom0, 64bit domU */
+ mode = 64;
+ pd = ((void*)page->pd) + 4;
+#endif
+ }
+
+ if (xenfb->pixels) {
+ munmap(xenfb->pixels, xenfb->fbpages * XC_PAGE_SIZE);
+ xenfb->pixels = NULL;
+ }
+
+ xenfb->fbpages = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
+ n_fbdirs = xenfb->fbpages * mode / 8;
+ n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
+
+ pgmfns = g_malloc0(sizeof(unsigned long) * n_fbdirs);
+ fbmfns = g_malloc0(sizeof(unsigned long) * xenfb->fbpages);
+
+ xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd);
+ map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom,
+ PROT_READ, pgmfns, n_fbdirs);
+ if (map == NULL)
+ goto out;
+ xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map);
+ munmap(map, n_fbdirs * XC_PAGE_SIZE);
+
+ xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom,
+ PROT_READ | PROT_WRITE, fbmfns, xenfb->fbpages);
+ if (xenfb->pixels == NULL)
+ goto out;
+
+ ret = 0; /* all is fine */
+
+out:
+ g_free(pgmfns);
+ g_free(fbmfns);
+ return ret;
+}
+
+static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim,
+ int width, int height, int depth,
+ size_t fb_len, int offset, int row_stride)
+{
+ size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd);
+ size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz;
+ size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz;
+ size_t fb_len_max = fb_pages * XC_PAGE_SIZE;
+ int max_width, max_height;
+
+ if (fb_len_lim > fb_len_max) {
+ xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n",
+ fb_len_lim, fb_len_max);
+ fb_len_lim = fb_len_max;
+ }
+ if (fb_len_lim && fb_len > fb_len_lim) {
+ xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n",
+ fb_len, fb_len_lim);
+ fb_len = fb_len_lim;
+ }
+ if (depth != 8 && depth != 16 && depth != 24 && depth != 32) {
+ xen_be_printf(&xenfb->c.xendev, 0, "can't handle frontend fb depth %d\n",
+ depth);
+ return -1;
+ }
+ if (row_stride <= 0 || row_stride > fb_len) {
+ xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride);
+ return -1;
+ }
+ max_width = row_stride / (depth / 8);
+ if (width < 0 || width > max_width) {
+ xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n",
+ width, max_width);
+ width = max_width;
+ }
+ if (offset < 0 || offset >= fb_len) {
+ xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n",
+ offset, fb_len - 1);
+ return -1;
+ }
+ max_height = (fb_len - offset) / row_stride;
+ if (height < 0 || height > max_height) {
+ xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n",
+ height, max_height);
+ height = max_height;
+ }
+ xenfb->fb_len = fb_len;
+ xenfb->row_stride = row_stride;
+ xenfb->depth = depth;
+ xenfb->width = width;
+ xenfb->height = height;
+ xenfb->offset = offset;
+ xenfb->up_fullscreen = 1;
+ xenfb->do_resize = 1;
+ xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n",
+ width, height, depth, offset, row_stride);
+ return 0;
+}
+
+/* A convenient function for munging pixels between different depths */
+#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \
+ for (line = y ; line < (y+h) ; line++) { \
+ SRC_T *src = (SRC_T *)(xenfb->pixels \
+ + xenfb->offset \
+ + (line * xenfb->row_stride) \
+ + (x * xenfb->depth / 8)); \
+ DST_T *dst = (DST_T *)(data \
+ + (line * linesize) \
+ + (x * bpp / 8)); \
+ int col; \
+ const int RSS = 32 - (RSB + GSB + BSB); \
+ const int GSS = 32 - (GSB + BSB); \
+ const int BSS = 32 - (BSB); \
+ const uint32_t RSM = (~0U) << (32 - RSB); \
+ const uint32_t GSM = (~0U) << (32 - GSB); \
+ const uint32_t BSM = (~0U) << (32 - BSB); \
+ const int RDS = 32 - (RDB + GDB + BDB); \
+ const int GDS = 32 - (GDB + BDB); \
+ const int BDS = 32 - (BDB); \
+ const uint32_t RDM = (~0U) << (32 - RDB); \
+ const uint32_t GDM = (~0U) << (32 - GDB); \
+ const uint32_t BDM = (~0U) << (32 - BDB); \
+ for (col = x ; col < (x+w) ; col++) { \
+ uint32_t spix = *src; \
+ *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \
+ (((spix << GSS) & GSM & GDM) >> GDS) | \
+ (((spix << BSS) & BSM & BDM) >> BDS); \
+ src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \
+ dst = (DST_T *) ((unsigned long) dst + bpp / 8); \
+ } \
+ }
+
+
+/*
+ * This copies data from the guest framebuffer region, into QEMU's
+ * displaysurface. qemu uses 16 or 32 bpp. In case the pv framebuffer
+ * uses something else we must convert and copy, otherwise we can
+ * supply the buffer directly and no thing here.
+ */
+static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h)
+{
+ DisplaySurface *surface = qemu_console_surface(xenfb->c.con);
+ int line, oops = 0;
+ int bpp = surface_bits_per_pixel(surface);
+ int linesize = surface_stride(surface);
+ uint8_t *data = surface_data(surface);
+
+ if (!is_buffer_shared(surface)) {
+ switch (xenfb->depth) {
+ case 8:
+ if (bpp == 16) {
+ BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5);
+ } else if (bpp == 32) {
+ BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8);
+ } else {
+ oops = 1;
+ }
+ break;
+ case 24:
+ if (bpp == 16) {
+ BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5);
+ } else if (bpp == 32) {
+ BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8);
+ } else {
+ oops = 1;
+ }
+ break;
+ default:
+ oops = 1;
+ }
+ }
+ if (oops) /* should not happen */
+ xen_be_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n",
+ __FUNCTION__, xenfb->depth, bpp);
+
+ dpy_gfx_update(xenfb->c.con, x, y, w, h);
+}
+
+#if 0 /* def XENFB_TYPE_REFRESH_PERIOD */
+static int xenfb_queue_full(struct XenFB *xenfb)
+{
+ struct xenfb_page *page = xenfb->c.page;
+ uint32_t cons, prod;
+
+ if (!page)
+ return 1;
+
+ prod = page->in_prod;
+ cons = page->in_cons;
+ return prod - cons == XENFB_IN_RING_LEN;
+}
+
+static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event)
+{
+ uint32_t prod;
+ struct xenfb_page *page = xenfb->c.page;
+
+ prod = page->in_prod;
+ /* caller ensures !xenfb_queue_full() */
+ xen_mb(); /* ensure ring space available */
+ XENFB_IN_RING_REF(page, prod) = *event;
+ xen_wmb(); /* ensure ring contents visible */
+ page->in_prod = prod + 1;
+
+ xen_be_send_notify(&xenfb->c.xendev);
+}
+
+static void xenfb_send_refresh_period(struct XenFB *xenfb, int period)
+{
+ union xenfb_in_event event;
+
+ memset(&event, 0, sizeof(event));
+ event.type = XENFB_TYPE_REFRESH_PERIOD;
+ event.refresh_period.period = period;
+ xenfb_send_event(xenfb, &event);
+}
+#endif
+
+/*
+ * Periodic update of display.
+ * Also transmit the refresh interval to the frontend.
+ *
+ * Never ever do any qemu display operations
+ * (resize, screen update) outside this function.
+ * Our screen might be inactive. When asked for
+ * an update we know it is active.
+ */
+static void xenfb_update(void *opaque)
+{
+ struct XenFB *xenfb = opaque;
+ DisplaySurface *surface;
+ int i;
+
+ if (xenfb->c.xendev.be_state != XenbusStateConnected)
+ return;
+
+ if (xenfb->feature_update) {
+#if 0 /* XENFB_TYPE_REFRESH_PERIOD */
+ struct DisplayChangeListener *l;
+ int period = 99999999;
+ int idle = 1;
+
+ if (xenfb_queue_full(xenfb))
+ return;
+
+ QLIST_FOREACH(l, &xenfb->c.ds->listeners, next) {
+ if (l->idle)
+ continue;
+ idle = 0;
+ if (!l->gui_timer_interval) {
+ if (period > GUI_REFRESH_INTERVAL)
+ period = GUI_REFRESH_INTERVAL;
+ } else {
+ if (period > l->gui_timer_interval)
+ period = l->gui_timer_interval;
+ }
+ }
+ if (idle)
+ period = XENFB_NO_REFRESH;
+
+ if (xenfb->refresh_period != period) {
+ xenfb_send_refresh_period(xenfb, period);
+ xenfb->refresh_period = period;
+ xen_be_printf(&xenfb->c.xendev, 1, "refresh period: %d\n", period);
+ }
+#else
+ ; /* nothing */
+#endif
+ } else {
+ /* we don't get update notifications, thus use the
+ * sledge hammer approach ... */
+ xenfb->up_fullscreen = 1;
+ }
+
+ /* resize if needed */
+ if (xenfb->do_resize) {
+ xenfb->do_resize = 0;
+ switch (xenfb->depth) {
+ case 16:
+ case 32:
+ /* console.c supported depth -> buffer can be used directly */
+ surface = qemu_create_displaysurface_from
+ (xenfb->width, xenfb->height, xenfb->depth,
+ xenfb->row_stride, xenfb->pixels + xenfb->offset,
+ false);
+ break;
+ default:
+ /* we must convert stuff */
+ surface = qemu_create_displaysurface(xenfb->width, xenfb->height);
+ break;
+ }
+ dpy_gfx_replace_surface(xenfb->c.con, surface);
+ xen_be_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n",
+ xenfb->width, xenfb->height, xenfb->depth,
+ is_buffer_shared(surface) ? " (shared)" : "");
+ xenfb->up_fullscreen = 1;
+ }
+
+ /* run queued updates */
+ if (xenfb->up_fullscreen) {
+ xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n");
+ xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height);
+ } else if (xenfb->up_count) {
+ xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count);
+ for (i = 0; i < xenfb->up_count; i++)
+ xenfb_guest_copy(xenfb,
+ xenfb->up_rects[i].x,
+ xenfb->up_rects[i].y,
+ xenfb->up_rects[i].w,
+ xenfb->up_rects[i].h);
+ } else {
+ xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n");
+ }
+ xenfb->up_count = 0;
+ xenfb->up_fullscreen = 0;
+}
+
+/* QEMU display state changed, so refresh the framebuffer copy */
+static void xenfb_invalidate(void *opaque)
+{
+ struct XenFB *xenfb = opaque;
+ xenfb->up_fullscreen = 1;
+}
+
+static void xenfb_handle_events(struct XenFB *xenfb)
+{
+ uint32_t prod, cons;
+ struct xenfb_page *page = xenfb->c.page;
+
+ prod = page->out_prod;
+ if (prod == page->out_cons)
+ return;
+ xen_rmb(); /* ensure we see ring contents up to prod */
+ for (cons = page->out_cons; cons != prod; cons++) {
+ union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
+ int x, y, w, h;
+
+ switch (event->type) {
+ case XENFB_TYPE_UPDATE:
+ if (xenfb->up_count == UP_QUEUE)
+ xenfb->up_fullscreen = 1;
+ if (xenfb->up_fullscreen)
+ break;
+ x = MAX(event->update.x, 0);
+ y = MAX(event->update.y, 0);
+ w = MIN(event->update.width, xenfb->width - x);
+ h = MIN(event->update.height, xenfb->height - y);
+ if (w < 0 || h < 0) {
+ xen_be_printf(&xenfb->c.xendev, 1, "bogus update ignored\n");
+ break;
+ }
+ if (x != event->update.x ||
+ y != event->update.y ||
+ w != event->update.width ||
+ h != event->update.height) {
+ xen_be_printf(&xenfb->c.xendev, 1, "bogus update clipped\n");
+ }
+ if (w == xenfb->width && h > xenfb->height / 2) {
+ /* scroll detector: updated more than 50% of the lines,
+ * don't bother keeping track of the rectangles then */
+ xenfb->up_fullscreen = 1;
+ } else {
+ xenfb->up_rects[xenfb->up_count].x = x;
+ xenfb->up_rects[xenfb->up_count].y = y;
+ xenfb->up_rects[xenfb->up_count].w = w;
+ xenfb->up_rects[xenfb->up_count].h = h;
+ xenfb->up_count++;
+ }
+ break;
+#ifdef XENFB_TYPE_RESIZE
+ case XENFB_TYPE_RESIZE:
+ if (xenfb_configure_fb(xenfb, xenfb->fb_len,
+ event->resize.width,
+ event->resize.height,
+ event->resize.depth,
+ xenfb->fb_len,
+ event->resize.offset,
+ event->resize.stride) < 0)
+ break;
+ xenfb_invalidate(xenfb);
+ break;
+#endif
+ }
+ }
+ xen_mb(); /* ensure we're done with ring contents */
+ page->out_cons = cons;
+}
+
+static int fb_init(struct XenDevice *xendev)
+{
+ struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
+
+ fb->refresh_period = -1;
+
+#ifdef XENFB_TYPE_RESIZE
+ xenstore_write_be_int(xendev, "feature-resize", 1);
+#endif
+ return 0;
+}
+
+static int fb_initialise(struct XenDevice *xendev)
+{
+ struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
+ struct xenfb_page *fb_page;
+ int videoram;
+ int rc;
+
+ if (xenstore_read_fe_int(xendev, "videoram", &videoram) == -1)
+ videoram = 0;
+
+ rc = common_bind(&fb->c);
+ if (rc != 0)
+ return rc;
+
+ fb_page = fb->c.page;
+ rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U,
+ fb_page->width, fb_page->height, fb_page->depth,
+ fb_page->mem_length, 0, fb_page->line_length);
+ if (rc != 0)
+ return rc;
+
+ rc = xenfb_map_fb(fb);
+ if (rc != 0)
+ return rc;
+
+#if 0 /* handled in xen_init_display() for now */
+ if (!fb->have_console) {
+ fb->c.ds = graphic_console_init(xenfb_update,
+ xenfb_invalidate,
+ NULL,
+ NULL,
+ fb);
+ fb->have_console = 1;
+ }
+#endif
+
+ if (xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update) == -1)
+ fb->feature_update = 0;
+ if (fb->feature_update)
+ xenstore_write_be_int(xendev, "request-update", 1);
+
+ xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n",
+ fb->feature_update, videoram);
+ return 0;
+}
+
+static void fb_disconnect(struct XenDevice *xendev)
+{
+ struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
+
+ /*
+ * FIXME: qemu can't un-init gfx display (yet?).
+ * Replacing the framebuffer with anonymous shared memory
+ * instead. This releases the guest pages and keeps qemu happy.
+ */
+ fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE,
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON,
+ -1, 0);
+ common_unbind(&fb->c);
+ fb->feature_update = 0;
+ fb->bug_trigger = 0;
+}
+
+static void fb_frontend_changed(struct XenDevice *xendev, const char *node)
+{
+ struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
+
+ /*
+ * Set state to Connected *again* once the frontend switched
+ * to connected. We must trigger the watch a second time to
+ * workaround a frontend bug.
+ */
+ if (fb->bug_trigger == 0 && strcmp(node, "state") == 0 &&
+ xendev->fe_state == XenbusStateConnected &&
+ xendev->be_state == XenbusStateConnected) {
+ xen_be_printf(xendev, 2, "re-trigger connected (frontend bug)\n");
+ xen_be_set_state(xendev, XenbusStateConnected);
+ fb->bug_trigger = 1; /* only once */
+ }
+}
+
+static void fb_event(struct XenDevice *xendev)
+{
+ struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev);
+
+ xenfb_handle_events(xenfb);
+ xen_be_send_notify(&xenfb->c.xendev);
+}
+
+/* -------------------------------------------------------------------- */
+
+struct XenDevOps xen_kbdmouse_ops = {
+ .size = sizeof(struct XenInput),
+ .init = input_init,
+ .initialise = input_initialise,
+ .connected = input_connected,
+ .disconnect = input_disconnect,
+ .event = input_event,
+};
+
+struct XenDevOps xen_framebuffer_ops = {
+ .size = sizeof(struct XenFB),
+ .init = fb_init,
+ .initialise = fb_initialise,
+ .disconnect = fb_disconnect,
+ .event = fb_event,
+ .frontend_changed = fb_frontend_changed,
+};
+
+/*
+ * FIXME/TODO: Kill this.
+ * Temporary needed while DisplayState reorganization is in flight.
+ */
+void xen_init_display(int domid)
+{
+ struct XenDevice *xfb, *xin;
+ struct XenFB *fb;
+ struct XenInput *in;
+ int i = 0;
+
+wait_more:
+ i++;
+ main_loop_wait(true);
+ xfb = xen_be_find_xendev("vfb", domid, 0);
+ xin = xen_be_find_xendev("vkbd", domid, 0);
+ if (!xfb || !xin) {
+ if (i < 256) {
+ usleep(10000);
+ goto wait_more;
+ }
+ xen_be_printf(NULL, 1, "displaystate setup failed\n");
+ return;
+ }
+
+ /* vfb */
+ fb = container_of(xfb, struct XenFB, c.xendev);
+ fb->c.con = graphic_console_init(xenfb_update,
+ xenfb_invalidate,
+ NULL,
+ NULL,
+ fb);
+ fb->have_console = 1;
+
+ /* vkbd */
+ in = container_of(xin, struct XenInput, c.xendev);
+ in->c.con = fb->c.con;
+
+ /* retry ->init() */
+ xen_be_check_state(xin);
+ xen_be_check_state(xfb);
+}