hw: move display devices to hw/display/, configure via default-configs/

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2013-02-05 12:59:04 +01:00
parent 34b8f63ea1
commit fc97bb5ba3
31 changed files with 45 additions and 23 deletions

View file

@ -11,3 +11,24 @@ common-obj-$(CONFIG_VGA_PCI) += vga-pci.o
common-obj-$(CONFIG_VGA_ISA) += vga-isa.o
common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o
common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o
common-obj-$(CONFIG_BLIZZARD) += blizzard.o
common-obj-$(CONFIG_EXYNOS4) += exynos4210_fimd.o
common-obj-$(CONFIG_FRAMEBUFFER) += framebuffer.o
common-obj-$(CONFIG_MILKYMIST) += milkymist-vgafb.o
common-obj-$(CONFIG_ZAURUS) += tc6393xb.o
ifeq ($(CONFIG_GLX),y)
common-obj-$(CONFIG_MILKYMIST) += milkymist-tmu2.o
endif
obj-$(CONFIG_OMAP) += omap_dss.o
obj-$(CONFIG_OMAP) += omap_lcdc.o
obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o
obj-$(CONFIG_SM501) += sm501.o
obj-$(CONFIG_TCX) += tcx.o
obj-$(CONFIG_VGA) += vga.o
common-obj-$(CONFIG_QXL) += qxl-logger.o qxl-render.o
obj-$(CONFIG_QXL) += qxl.o

1004
hw/display/blizzard.c Normal file

File diff suppressed because it is too large Load diff

1930
hw/display/exynos4210_fimd.c Normal file

File diff suppressed because it is too large Load diff

110
hw/display/framebuffer.c Normal file
View file

@ -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 "hw/framebuffer.h"
/* Render an image from a shared memory framebuffer. */
void framebuffer_update_display(
DisplaySurface *ds,
MemoryRegion *address_space,
hwaddr base,
int cols, /* Width in pixels. */
int rows, /* Height in pixels. */
int src_width, /* Length of source line, in bytes. */
int dest_row_pitch, /* Bytes between adjacent horizontal output pixels. */
int dest_col_pitch, /* Bytes between adjacent vertical output pixels. */
int invalidate, /* nonzero to redraw the whole image. */
drawfn fn,
void *opaque,
int *first_row, /* Input and output. */
int *last_row /* Output only */)
{
hwaddr src_len;
uint8_t *dest;
uint8_t *src;
uint8_t *src_base;
int first, last = 0;
int dirty;
int i;
ram_addr_t addr;
MemoryRegionSection mem_section;
MemoryRegion *mem;
i = *first_row;
*first_row = -1;
src_len = src_width * rows;
mem_section = memory_region_find(address_space, base, src_len);
if (mem_section.size != src_len || !memory_region_is_ram(mem_section.mr)) {
return;
}
mem = mem_section.mr;
assert(mem);
assert(mem_section.offset_within_address_space == base);
memory_region_sync_dirty_bitmap(mem);
src_base = cpu_physical_memory_map(base, &src_len, 0);
/* If we can't map the framebuffer then bail. We could try harder,
but it's not really worth it as dirty flag tracking will probably
already have failed above. */
if (!src_base)
return;
if (src_len != src_width * rows) {
cpu_physical_memory_unmap(src_base, src_len, 0, 0);
return;
}
src = src_base;
dest = surface_data(ds);
if (dest_col_pitch < 0)
dest -= dest_col_pitch * (cols - 1);
if (dest_row_pitch < 0) {
dest -= dest_row_pitch * (rows - 1);
}
first = -1;
addr = mem_section.offset_within_region;
addr += i * src_width;
src += i * src_width;
dest += i * dest_row_pitch;
for (; i < rows; i++) {
dirty = memory_region_get_dirty(mem, addr, src_width,
DIRTY_MEMORY_VGA);
if (dirty || invalidate) {
fn(opaque, dest, src, cols, dest_col_pitch);
if (first == -1)
first = i;
last = i;
}
addr += src_width;
src += src_width;
dest += dest_row_pitch;
}
cpu_physical_memory_unmap(src_base, src_len, 0, 0);
if (first < 0) {
return;
}
memory_region_reset_dirty(mem, mem_section.offset_within_region, src_len,
DIRTY_MEMORY_VGA);
*first_row = first;
*last_row = last;
}

490
hw/display/milkymist-tmu2.c Normal file
View file

@ -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)

View file

@ -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 "hw/framebuffer.h"
#include "ui/pixel_ops.h"
#include "qemu/error-report.h"
#define BITS 8
#include "hw/milkymist-vgafb_template.h"
#define BITS 15
#include "hw/milkymist-vgafb_template.h"
#define BITS 16
#include "hw/milkymist-vgafb_template.h"
#define BITS 24
#include "hw/milkymist-vgafb_template.h"
#define BITS 32
#include "hw/milkymist-vgafb_template.h"
enum {
R_CTRL = 0,
R_HRES,
R_HSYNC_START,
R_HSYNC_END,
R_HSCAN,
R_VRES,
R_VSYNC_START,
R_VSYNC_END,
R_VSCAN,
R_BASEADDRESS,
R_BASEADDRESS_ACT,
R_BURST_COUNT,
R_DDC,
R_SOURCE_CLOCK,
R_MAX
};
enum {
CTRL_RESET = (1<<0),
};
struct MilkymistVgafbState {
SysBusDevice busdev;
MemoryRegion regs_region;
QemuConsole *con;
int invalidate;
uint32_t fb_offset;
uint32_t fb_mask;
uint32_t regs[R_MAX];
};
typedef struct MilkymistVgafbState MilkymistVgafbState;
static int vgafb_enabled(MilkymistVgafbState *s)
{
return !(s->regs[R_CTRL] & CTRL_RESET);
}
static void vgafb_update_display(void *opaque)
{
MilkymistVgafbState *s = opaque;
DisplaySurface *surface = qemu_console_surface(s->con);
int first = 0;
int last = 0;
drawfn fn;
if (!vgafb_enabled(s)) {
return;
}
int dest_width = s->regs[R_HRES];
switch (surface_bits_per_pixel(surface)) {
case 0:
return;
case 8:
fn = draw_line_8;
break;
case 15:
fn = draw_line_15;
dest_width *= 2;
break;
case 16:
fn = draw_line_16;
dest_width *= 2;
break;
case 24:
fn = draw_line_24;
dest_width *= 3;
break;
case 32:
fn = draw_line_32;
dest_width *= 4;
break;
default:
hw_error("milkymist_vgafb: bad color depth\n");
break;
}
framebuffer_update_display(surface, sysbus_address_space(&s->busdev),
s->regs[R_BASEADDRESS] + s->fb_offset,
s->regs[R_HRES],
s->regs[R_VRES],
s->regs[R_HRES] * 2,
dest_width,
0,
s->invalidate,
fn,
NULL,
&first, &last);
if (first >= 0) {
dpy_gfx_update(s->con, 0, first, s->regs[R_HRES], last - first + 1);
}
s->invalidate = 0;
}
static void vgafb_invalidate_display(void *opaque)
{
MilkymistVgafbState *s = opaque;
s->invalidate = 1;
}
static void vgafb_resize(MilkymistVgafbState *s)
{
if (!vgafb_enabled(s)) {
return;
}
qemu_console_resize(s->con, s->regs[R_HRES], s->regs[R_VRES]);
s->invalidate = 1;
}
static uint64_t vgafb_read(void *opaque, hwaddr addr,
unsigned size)
{
MilkymistVgafbState *s = opaque;
uint32_t r = 0;
addr >>= 2;
switch (addr) {
case R_CTRL:
case R_HRES:
case R_HSYNC_START:
case R_HSYNC_END:
case R_HSCAN:
case R_VRES:
case R_VSYNC_START:
case R_VSYNC_END:
case R_VSCAN:
case R_BASEADDRESS:
case R_BURST_COUNT:
case R_DDC:
case R_SOURCE_CLOCK:
r = s->regs[addr];
break;
case R_BASEADDRESS_ACT:
r = s->regs[R_BASEADDRESS];
break;
default:
error_report("milkymist_vgafb: read access to unknown register 0x"
TARGET_FMT_plx, addr << 2);
break;
}
trace_milkymist_vgafb_memory_read(addr << 2, r);
return r;
}
static void vgafb_write(void *opaque, hwaddr addr, uint64_t value,
unsigned size)
{
MilkymistVgafbState *s = opaque;
trace_milkymist_vgafb_memory_write(addr, value);
addr >>= 2;
switch (addr) {
case R_CTRL:
s->regs[addr] = value;
vgafb_resize(s);
break;
case R_HSYNC_START:
case R_HSYNC_END:
case R_HSCAN:
case R_VSYNC_START:
case R_VSYNC_END:
case R_VSCAN:
case R_BURST_COUNT:
case R_DDC:
case R_SOURCE_CLOCK:
s->regs[addr] = value;
break;
case R_BASEADDRESS:
if (value & 0x1f) {
error_report("milkymist_vgafb: framebuffer base address have to "
"be 32 byte aligned");
break;
}
s->regs[addr] = value & s->fb_mask;
s->invalidate = 1;
break;
case R_HRES:
case R_VRES:
s->regs[addr] = value;
vgafb_resize(s);
break;
case R_BASEADDRESS_ACT:
error_report("milkymist_vgafb: write to read-only register 0x"
TARGET_FMT_plx, addr << 2);
break;
default:
error_report("milkymist_vgafb: write access to unknown register 0x"
TARGET_FMT_plx, addr << 2);
break;
}
}
static const MemoryRegionOps vgafb_mmio_ops = {
.read = vgafb_read,
.write = vgafb_write,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void milkymist_vgafb_reset(DeviceState *d)
{
MilkymistVgafbState *s = container_of(d, MilkymistVgafbState, busdev.qdev);
int i;
for (i = 0; i < R_MAX; i++) {
s->regs[i] = 0;
}
/* defaults */
s->regs[R_CTRL] = CTRL_RESET;
s->regs[R_HRES] = 640;
s->regs[R_VRES] = 480;
s->regs[R_BASEADDRESS] = 0;
}
static int milkymist_vgafb_init(SysBusDevice *dev)
{
MilkymistVgafbState *s = FROM_SYSBUS(typeof(*s), dev);
memory_region_init_io(&s->regs_region, &vgafb_mmio_ops, s,
"milkymist-vgafb", R_MAX * 4);
sysbus_init_mmio(dev, &s->regs_region);
s->con = graphic_console_init(vgafb_update_display,
vgafb_invalidate_display,
NULL, NULL, s);
return 0;
}
static int vgafb_post_load(void *opaque, int version_id)
{
vgafb_invalidate_display(opaque);
return 0;
}
static const VMStateDescription vmstate_milkymist_vgafb = {
.name = "milkymist-vgafb",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.post_load = vgafb_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, MilkymistVgafbState, R_MAX),
VMSTATE_END_OF_LIST()
}
};
static Property milkymist_vgafb_properties[] = {
DEFINE_PROP_UINT32("fb_offset", MilkymistVgafbState, fb_offset, 0x0),
DEFINE_PROP_UINT32("fb_mask", MilkymistVgafbState, fb_mask, 0xffffffff),
DEFINE_PROP_END_OF_LIST(),
};
static void milkymist_vgafb_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = milkymist_vgafb_init;
dc->reset = milkymist_vgafb_reset;
dc->vmsd = &vmstate_milkymist_vgafb;
dc->props = milkymist_vgafb_properties;
}
static const TypeInfo milkymist_vgafb_info = {
.name = "milkymist-vgafb",
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MilkymistVgafbState),
.class_init = milkymist_vgafb_class_init,
};
static void milkymist_vgafb_register_types(void)
{
type_register_static(&milkymist_vgafb_info);
}
type_init(milkymist_vgafb_register_types)

1086
hw/display/omap_dss.c Normal file

File diff suppressed because it is too large Load diff

493
hw/display/omap_lcdc.c Normal file
View file

@ -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 "hw/framebuffer.h"
#include "ui/pixel_ops.h"
struct omap_lcd_panel_s {
MemoryRegion *sysmem;
MemoryRegion iomem;
qemu_irq irq;
QemuConsole *con;
int plm;
int tft;
int mono;
int enable;
int width;
int height;
int interrupts;
uint32_t timing[3];
uint32_t subpanel;
uint32_t ctrl;
struct omap_dma_lcd_channel_s *dma;
uint16_t palette[256];
int palette_done;
int frame_done;
int invalidate;
int sync_error;
};
static void omap_lcd_interrupts(struct omap_lcd_panel_s *s)
{
if (s->frame_done && (s->interrupts & 1)) {
qemu_irq_raise(s->irq);
return;
}
if (s->palette_done && (s->interrupts & 2)) {
qemu_irq_raise(s->irq);
return;
}
if (s->sync_error) {
qemu_irq_raise(s->irq);
return;
}
qemu_irq_lower(s->irq);
}
#define draw_line_func drawfn
#define DEPTH 8
#include "hw/omap_lcd_template.h"
#define DEPTH 15
#include "hw/omap_lcd_template.h"
#define DEPTH 16
#include "hw/omap_lcd_template.h"
#define DEPTH 32
#include "hw/omap_lcd_template.h"
static draw_line_func draw_line_table2[33] = {
[0 ... 32] = NULL,
[8] = draw_line2_8,
[15] = draw_line2_15,
[16] = draw_line2_16,
[32] = draw_line2_32,
}, draw_line_table4[33] = {
[0 ... 32] = NULL,
[8] = draw_line4_8,
[15] = draw_line4_15,
[16] = draw_line4_16,
[32] = draw_line4_32,
}, draw_line_table8[33] = {
[0 ... 32] = NULL,
[8] = draw_line8_8,
[15] = draw_line8_15,
[16] = draw_line8_16,
[32] = draw_line8_32,
}, draw_line_table12[33] = {
[0 ... 32] = NULL,
[8] = draw_line12_8,
[15] = draw_line12_15,
[16] = draw_line12_16,
[32] = draw_line12_32,
}, draw_line_table16[33] = {
[0 ... 32] = NULL,
[8] = draw_line16_8,
[15] = draw_line16_15,
[16] = draw_line16_16,
[32] = draw_line16_32,
};
static void omap_update_display(void *opaque)
{
struct omap_lcd_panel_s *omap_lcd = (struct omap_lcd_panel_s *) opaque;
DisplaySurface *surface = qemu_console_surface(omap_lcd->con);
draw_line_func draw_line;
int size, height, first, last;
int width, linesize, step, bpp, frame_offset;
hwaddr frame_base;
if (!omap_lcd || omap_lcd->plm == 1 || !omap_lcd->enable ||
!surface_bits_per_pixel(surface)) {
return;
}
frame_offset = 0;
if (omap_lcd->plm != 2) {
cpu_physical_memory_read(omap_lcd->dma->phys_framebuffer[
omap_lcd->dma->current_frame],
(void *)omap_lcd->palette, 0x200);
switch (omap_lcd->palette[0] >> 12 & 7) {
case 3 ... 7:
frame_offset += 0x200;
break;
default:
frame_offset += 0x20;
}
}
/* Colour depth */
switch ((omap_lcd->palette[0] >> 12) & 7) {
case 1:
draw_line = draw_line_table2[surface_bits_per_pixel(surface)];
bpp = 2;
break;
case 2:
draw_line = draw_line_table4[surface_bits_per_pixel(surface)];
bpp = 4;
break;
case 3:
draw_line = draw_line_table8[surface_bits_per_pixel(surface)];
bpp = 8;
break;
case 4 ... 7:
if (!omap_lcd->tft)
draw_line = draw_line_table12[surface_bits_per_pixel(surface)];
else
draw_line = draw_line_table16[surface_bits_per_pixel(surface)];
bpp = 16;
break;
default:
/* Unsupported at the moment. */
return;
}
/* Resolution */
width = omap_lcd->width;
if (width != surface_width(surface) ||
omap_lcd->height != surface_height(surface)) {
qemu_console_resize(omap_lcd->con,
omap_lcd->width, omap_lcd->height);
surface = qemu_console_surface(omap_lcd->con);
omap_lcd->invalidate = 1;
}
if (omap_lcd->dma->current_frame == 0)
size = omap_lcd->dma->src_f1_bottom - omap_lcd->dma->src_f1_top;
else
size = omap_lcd->dma->src_f2_bottom - omap_lcd->dma->src_f2_top;
if (frame_offset + ((width * omap_lcd->height * bpp) >> 3) > size + 2) {
omap_lcd->sync_error = 1;
omap_lcd_interrupts(omap_lcd);
omap_lcd->enable = 0;
return;
}
/* Content */
frame_base = omap_lcd->dma->phys_framebuffer[
omap_lcd->dma->current_frame] + frame_offset;
omap_lcd->dma->condition |= 1 << omap_lcd->dma->current_frame;
if (omap_lcd->dma->interrupts & 1)
qemu_irq_raise(omap_lcd->dma->irq);
if (omap_lcd->dma->dual)
omap_lcd->dma->current_frame ^= 1;
if (!surface_bits_per_pixel(surface)) {
return;
}
first = 0;
height = omap_lcd->height;
if (omap_lcd->subpanel & (1 << 31)) {
if (omap_lcd->subpanel & (1 << 29))
first = (omap_lcd->subpanel >> 16) & 0x3ff;
else
height = (omap_lcd->subpanel >> 16) & 0x3ff;
/* TODO: fill the rest of the panel with DPD */
}
step = width * bpp >> 3;
linesize = surface_stride(surface);
framebuffer_update_display(surface, omap_lcd->sysmem,
frame_base, width, height,
step, linesize, 0,
omap_lcd->invalidate,
draw_line, omap_lcd->palette,
&first, &last);
if (first >= 0) {
dpy_gfx_update(omap_lcd->con, 0, first, width, last - first + 1);
}
omap_lcd->invalidate = 0;
}
static void omap_ppm_save(const char *filename, uint8_t *data,
int w, int h, int linesize, Error **errp)
{
FILE *f;
uint8_t *d, *d1;
unsigned int v;
int ret, y, x, bpp;
f = fopen(filename, "wb");
if (!f) {
error_setg(errp, "failed to open file '%s': %s", filename,
strerror(errno));
return;
}
ret = fprintf(f, "P6\n%d %d\n%d\n", w, h, 255);
if (ret < 0) {
goto write_err;
}
d1 = data;
bpp = linesize / w;
for (y = 0; y < h; y ++) {
d = d1;
for (x = 0; x < w; x ++) {
v = *(uint32_t *) d;
switch (bpp) {
case 2:
ret = fputc((v >> 8) & 0xf8, f);
if (ret == EOF) {
goto write_err;
}
ret = fputc((v >> 3) & 0xfc, f);
if (ret == EOF) {
goto write_err;
}
ret = fputc((v << 3) & 0xf8, f);
if (ret == EOF) {
goto write_err;
}
break;
case 3:
case 4:
default:
ret = fputc((v >> 16) & 0xff, f);
if (ret == EOF) {
goto write_err;
}
ret = fputc((v >> 8) & 0xff, f);
if (ret == EOF) {
goto write_err;
}
ret = fputc((v) & 0xff, f);
if (ret == EOF) {
goto write_err;
}
break;
}
d += bpp;
}
d1 += linesize;
}
out:
fclose(f);
return;
write_err:
error_setg(errp, "failed to write to file '%s': %s", filename,
strerror(errno));
unlink(filename);
goto out;
}
static void omap_screen_dump(void *opaque, const char *filename, bool cswitch,
Error **errp)
{
struct omap_lcd_panel_s *omap_lcd = opaque;
DisplaySurface *surface = qemu_console_surface(omap_lcd->con);
omap_update_display(opaque);
if (omap_lcd && surface_data(surface))
omap_ppm_save(filename, surface_data(surface),
omap_lcd->width, omap_lcd->height,
surface_stride(surface), errp);
}
static void omap_invalidate_display(void *opaque) {
struct omap_lcd_panel_s *omap_lcd = opaque;
omap_lcd->invalidate = 1;
}
static void omap_lcd_update(struct omap_lcd_panel_s *s) {
if (!s->enable) {
s->dma->current_frame = -1;
s->sync_error = 0;
if (s->plm != 1)
s->frame_done = 1;
omap_lcd_interrupts(s);
return;
}
if (s->dma->current_frame == -1) {
s->frame_done = 0;
s->palette_done = 0;
s->dma->current_frame = 0;
}
if (!s->dma->mpu->port[s->dma->src].addr_valid(s->dma->mpu,
s->dma->src_f1_top) ||
!s->dma->mpu->port[
s->dma->src].addr_valid(s->dma->mpu,
s->dma->src_f1_bottom) ||
(s->dma->dual &&
(!s->dma->mpu->port[
s->dma->src].addr_valid(s->dma->mpu,
s->dma->src_f2_top) ||
!s->dma->mpu->port[
s->dma->src].addr_valid(s->dma->mpu,
s->dma->src_f2_bottom)))) {
s->dma->condition |= 1 << 2;
if (s->dma->interrupts & (1 << 1))
qemu_irq_raise(s->dma->irq);
s->enable = 0;
return;
}
s->dma->phys_framebuffer[0] = s->dma->src_f1_top;
s->dma->phys_framebuffer[1] = s->dma->src_f2_top;
if (s->plm != 2 && !s->palette_done) {
cpu_physical_memory_read(
s->dma->phys_framebuffer[s->dma->current_frame],
(void *)s->palette, 0x200);
s->palette_done = 1;
omap_lcd_interrupts(s);
}
}
static uint64_t omap_lcdc_read(void *opaque, hwaddr addr,
unsigned size)
{
struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
switch (addr) {
case 0x00: /* LCD_CONTROL */
return (s->tft << 23) | (s->plm << 20) |
(s->tft << 7) | (s->interrupts << 3) |
(s->mono << 1) | s->enable | s->ctrl | 0xfe000c34;
case 0x04: /* LCD_TIMING0 */
return (s->timing[0] << 10) | (s->width - 1) | 0x0000000f;
case 0x08: /* LCD_TIMING1 */
return (s->timing[1] << 10) | (s->height - 1);
case 0x0c: /* LCD_TIMING2 */
return s->timing[2] | 0xfc000000;
case 0x10: /* LCD_STATUS */
return (s->palette_done << 6) | (s->sync_error << 2) | s->frame_done;
case 0x14: /* LCD_SUBPANEL */
return s->subpanel;
default:
break;
}
OMAP_BAD_REG(addr);
return 0;
}
static void omap_lcdc_write(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *) opaque;
switch (addr) {
case 0x00: /* LCD_CONTROL */
s->plm = (value >> 20) & 3;
s->tft = (value >> 7) & 1;
s->interrupts = (value >> 3) & 3;
s->mono = (value >> 1) & 1;
s->ctrl = value & 0x01cff300;
if (s->enable != (value & 1)) {
s->enable = value & 1;
omap_lcd_update(s);
}
break;
case 0x04: /* LCD_TIMING0 */
s->timing[0] = value >> 10;
s->width = (value & 0x3ff) + 1;
break;
case 0x08: /* LCD_TIMING1 */
s->timing[1] = value >> 10;
s->height = (value & 0x3ff) + 1;
break;
case 0x0c: /* LCD_TIMING2 */
s->timing[2] = value;
break;
case 0x10: /* LCD_STATUS */
break;
case 0x14: /* LCD_SUBPANEL */
s->subpanel = value & 0xa1ffffff;
break;
default:
OMAP_BAD_REG(addr);
}
}
static const MemoryRegionOps omap_lcdc_ops = {
.read = omap_lcdc_read,
.write = omap_lcdc_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
void omap_lcdc_reset(struct omap_lcd_panel_s *s)
{
s->dma->current_frame = -1;
s->plm = 0;
s->tft = 0;
s->mono = 0;
s->enable = 0;
s->width = 0;
s->height = 0;
s->interrupts = 0;
s->timing[0] = 0;
s->timing[1] = 0;
s->timing[2] = 0;
s->subpanel = 0;
s->palette_done = 0;
s->frame_done = 0;
s->sync_error = 0;
s->invalidate = 1;
s->subpanel = 0;
s->ctrl = 0;
}
struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
hwaddr base,
qemu_irq irq,
struct omap_dma_lcd_channel_s *dma,
omap_clk clk)
{
struct omap_lcd_panel_s *s = (struct omap_lcd_panel_s *)
g_malloc0(sizeof(struct omap_lcd_panel_s));
s->irq = irq;
s->dma = dma;
s->sysmem = sysmem;
omap_lcdc_reset(s);
memory_region_init_io(&s->iomem, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
memory_region_add_subregion(sysmem, base, &s->iomem);
s->con = graphic_console_init(omap_update_display,
omap_invalidate_display,
omap_screen_dump, NULL, s);
return s;
}

1058
hw/display/pxa2xx_lcd.c Normal file

File diff suppressed because it is too large Load diff

275
hw/display/qxl-logger.c Normal file
View file

@ -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 "hw/qxl.h"
static const char *qxl_type[] = {
[ QXL_CMD_NOP ] = "nop",
[ QXL_CMD_DRAW ] = "draw",
[ QXL_CMD_UPDATE ] = "update",
[ QXL_CMD_CURSOR ] = "cursor",
[ QXL_CMD_MESSAGE ] = "message",
[ QXL_CMD_SURFACE ] = "surface",
};
static const char *qxl_draw_type[] = {
[ QXL_DRAW_NOP ] = "nop",
[ QXL_DRAW_FILL ] = "fill",
[ QXL_DRAW_OPAQUE ] = "opaque",
[ QXL_DRAW_COPY ] = "copy",
[ QXL_COPY_BITS ] = "copy-bits",
[ QXL_DRAW_BLEND ] = "blend",
[ QXL_DRAW_BLACKNESS ] = "blackness",
[ QXL_DRAW_WHITENESS ] = "whitemess",
[ QXL_DRAW_INVERS ] = "invers",
[ QXL_DRAW_ROP3 ] = "rop3",
[ QXL_DRAW_STROKE ] = "stroke",
[ QXL_DRAW_TEXT ] = "text",
[ QXL_DRAW_TRANSPARENT ] = "transparent",
[ QXL_DRAW_ALPHA_BLEND ] = "alpha-blend",
};
static const char *qxl_draw_effect[] = {
[ QXL_EFFECT_BLEND ] = "blend",
[ QXL_EFFECT_OPAQUE ] = "opaque",
[ QXL_EFFECT_REVERT_ON_DUP ] = "revert-on-dup",
[ QXL_EFFECT_BLACKNESS_ON_DUP ] = "blackness-on-dup",
[ QXL_EFFECT_WHITENESS_ON_DUP ] = "whiteness-on-dup",
[ QXL_EFFECT_NOP_ON_DUP ] = "nop-on-dup",
[ QXL_EFFECT_NOP ] = "nop",
[ QXL_EFFECT_OPAQUE_BRUSH ] = "opaque-brush",
};
static const char *qxl_surface_cmd[] = {
[ QXL_SURFACE_CMD_CREATE ] = "create",
[ QXL_SURFACE_CMD_DESTROY ] = "destroy",
};
static const char *spice_surface_fmt[] = {
[ SPICE_SURFACE_FMT_INVALID ] = "invalid",
[ SPICE_SURFACE_FMT_1_A ] = "alpha/1",
[ SPICE_SURFACE_FMT_8_A ] = "alpha/8",
[ SPICE_SURFACE_FMT_16_555 ] = "555/16",
[ SPICE_SURFACE_FMT_16_565 ] = "565/16",
[ SPICE_SURFACE_FMT_32_xRGB ] = "xRGB/32",
[ SPICE_SURFACE_FMT_32_ARGB ] = "ARGB/32",
};
static const char *qxl_cursor_cmd[] = {
[ QXL_CURSOR_SET ] = "set",
[ QXL_CURSOR_MOVE ] = "move",
[ QXL_CURSOR_HIDE ] = "hide",
[ QXL_CURSOR_TRAIL ] = "trail",
};
static const char *spice_cursor_type[] = {
[ SPICE_CURSOR_TYPE_ALPHA ] = "alpha",
[ SPICE_CURSOR_TYPE_MONO ] = "mono",
[ SPICE_CURSOR_TYPE_COLOR4 ] = "color4",
[ SPICE_CURSOR_TYPE_COLOR8 ] = "color8",
[ SPICE_CURSOR_TYPE_COLOR16 ] = "color16",
[ SPICE_CURSOR_TYPE_COLOR24 ] = "color24",
[ SPICE_CURSOR_TYPE_COLOR32 ] = "color32",
};
static const char *qxl_v2n(const char *n[], size_t l, int v)
{
if (v >= l || !n[v]) {
return "???";
}
return n[v];
}
#define qxl_name(_list, _value) qxl_v2n(_list, ARRAY_SIZE(_list), _value)
static int qxl_log_image(PCIQXLDevice *qxl, QXLPHYSICAL addr, int group_id)
{
QXLImage *image;
QXLImageDescriptor *desc;
image = qxl_phys2virt(qxl, addr, group_id);
if (!image) {
return 1;
}
desc = &image->descriptor;
fprintf(stderr, " (id %" PRIx64 " type %d flags %d width %d height %d",
desc->id, desc->type, desc->flags, desc->width, desc->height);
switch (desc->type) {
case SPICE_IMAGE_TYPE_BITMAP:
fprintf(stderr, ", fmt %d flags %d x %d y %d stride %d"
" palette %" PRIx64 " data %" PRIx64,
image->bitmap.format, image->bitmap.flags,
image->bitmap.x, image->bitmap.y,
image->bitmap.stride,
image->bitmap.palette, image->bitmap.data);
break;
}
fprintf(stderr, ")");
return 0;
}
static void qxl_log_rect(QXLRect *rect)
{
fprintf(stderr, " %dx%d+%d+%d",
rect->right - rect->left,
rect->bottom - rect->top,
rect->left, rect->top);
}
static int qxl_log_cmd_draw_copy(PCIQXLDevice *qxl, QXLCopy *copy,
int group_id)
{
int ret;
fprintf(stderr, " src %" PRIx64,
copy->src_bitmap);
ret = qxl_log_image(qxl, copy->src_bitmap, group_id);
if (ret != 0) {
return ret;
}
fprintf(stderr, " area");
qxl_log_rect(&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;
}

280
hw/display/qxl-render.c Normal file
View file

@ -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 "hw/qxl.h"
static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect)
{
DisplaySurface *surface = qemu_console_surface(qxl->vga.con);
uint8_t *dst = surface_data(surface);
uint8_t *src;
int len, i;
if (is_buffer_shared(surface)) {
return;
}
if (!qxl->guest_primary.data) {
trace_qxl_render_blit_guest_primary_initialized();
qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram);
}
trace_qxl_render_blit(qxl->guest_primary.qxl_stride,
rect->left, rect->right, rect->top, rect->bottom);
src = qxl->guest_primary.data;
if (qxl->guest_primary.qxl_stride < 0) {
/* qxl surface is upside down, walk src scanlines
* in reverse order to flip it */
src += (qxl->guest_primary.surface.height - rect->top - 1) *
qxl->guest_primary.abs_stride;
} else {
src += rect->top * qxl->guest_primary.abs_stride;
}
dst += rect->top * qxl->guest_primary.abs_stride;
src += rect->left * qxl->guest_primary.bytes_pp;
dst += rect->left * qxl->guest_primary.bytes_pp;
len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp;
for (i = rect->top; i < rect->bottom; i++) {
memcpy(dst, src, len);
dst += qxl->guest_primary.abs_stride;
src += qxl->guest_primary.qxl_stride;
}
}
void qxl_render_resize(PCIQXLDevice *qxl)
{
QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
qxl->guest_primary.qxl_stride = sc->stride;
qxl->guest_primary.abs_stride = abs(sc->stride);
qxl->guest_primary.resized++;
switch (sc->format) {
case SPICE_SURFACE_FMT_16_555:
qxl->guest_primary.bytes_pp = 2;
qxl->guest_primary.bits_pp = 15;
break;
case SPICE_SURFACE_FMT_16_565:
qxl->guest_primary.bytes_pp = 2;
qxl->guest_primary.bits_pp = 16;
break;
case SPICE_SURFACE_FMT_32_xRGB:
case SPICE_SURFACE_FMT_32_ARGB:
qxl->guest_primary.bytes_pp = 4;
qxl->guest_primary.bits_pp = 32;
break;
default:
fprintf(stderr, "%s: unhandled format: %x\n", __FUNCTION__,
qxl->guest_primary.surface.format);
qxl->guest_primary.bytes_pp = 4;
qxl->guest_primary.bits_pp = 32;
break;
}
}
static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area)
{
area->left = 0;
area->right = qxl->guest_primary.surface.width;
area->top = 0;
area->bottom = qxl->guest_primary.surface.height;
}
static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl)
{
VGACommonState *vga = &qxl->vga;
DisplaySurface *surface;
int i;
if (qxl->guest_primary.resized) {
qxl->guest_primary.resized = 0;
qxl->guest_primary.data = memory_region_get_ram_ptr(&qxl->vga.vram);
qxl_set_rect_to_surface(qxl, &qxl->dirty[0]);
qxl->num_dirty_rects = 1;
trace_qxl_render_guest_primary_resized(
qxl->guest_primary.surface.width,
qxl->guest_primary.surface.height,
qxl->guest_primary.qxl_stride,
qxl->guest_primary.bytes_pp,
qxl->guest_primary.bits_pp);
if (qxl->guest_primary.qxl_stride > 0) {
surface = qemu_create_displaysurface_from
(qxl->guest_primary.surface.width,
qxl->guest_primary.surface.height,
qxl->guest_primary.bits_pp,
qxl->guest_primary.abs_stride,
qxl->guest_primary.data,
false);
} else {
surface = qemu_create_displaysurface
(qxl->guest_primary.surface.width,
qxl->guest_primary.surface.height);
}
dpy_gfx_replace_surface(vga->con, surface);
}
for (i = 0; i < qxl->num_dirty_rects; i++) {
if (qemu_spice_rect_is_empty(qxl->dirty+i)) {
break;
}
qxl_blit(qxl, qxl->dirty+i);
dpy_gfx_update(vga->con,
qxl->dirty[i].left, qxl->dirty[i].top,
qxl->dirty[i].right - qxl->dirty[i].left,
qxl->dirty[i].bottom - qxl->dirty[i].top);
}
qxl->num_dirty_rects = 0;
}
/*
* use ssd.lock to protect render_update_cookie_num.
* qxl_render_update is called by io thread or vcpu thread, and the completion
* callbacks are called by spice_server thread, defering to bh called from the
* io thread.
*/
void qxl_render_update(PCIQXLDevice *qxl)
{
QXLCookie *cookie;
qemu_mutex_lock(&qxl->ssd.lock);
if (!runstate_is_running() || !qxl->guest_primary.commands) {
qxl_render_update_area_unlocked(qxl);
qemu_mutex_unlock(&qxl->ssd.lock);
return;
}
qxl->guest_primary.commands = 0;
qxl->render_update_cookie_num++;
qemu_mutex_unlock(&qxl->ssd.lock);
cookie = qxl_cookie_new(QXL_COOKIE_TYPE_RENDER_UPDATE_AREA,
0);
qxl_set_rect_to_surface(qxl, &cookie->u.render.area);
qxl_spice_update_area(qxl, 0, &cookie->u.render.area, NULL,
0, 1 /* clear_dirty_region */, QXL_ASYNC, cookie);
}
void qxl_render_update_area_bh(void *opaque)
{
PCIQXLDevice *qxl = opaque;
qemu_mutex_lock(&qxl->ssd.lock);
qxl_render_update_area_unlocked(qxl);
qemu_mutex_unlock(&qxl->ssd.lock);
}
void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie)
{
qemu_mutex_lock(&qxl->ssd.lock);
trace_qxl_render_update_area_done(cookie);
qemu_bh_schedule(qxl->update_area_bh);
qxl->render_update_cookie_num--;
qemu_mutex_unlock(&qxl->ssd.lock);
g_free(cookie);
}
static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor)
{
QEMUCursor *c;
uint8_t *image, *mask;
size_t size;
c = cursor_alloc(cursor->header.width, cursor->header.height);
c->hot_x = cursor->header.hot_spot_x;
c->hot_y = cursor->header.hot_spot_y;
switch (cursor->header.type) {
case SPICE_CURSOR_TYPE_ALPHA:
size = cursor->header.width * cursor->header.height * sizeof(uint32_t);
memcpy(c->data, cursor->chunk.data, size);
if (qxl->debug > 2) {
cursor_print_ascii_art(c, "qxl/alpha");
}
break;
case SPICE_CURSOR_TYPE_MONO:
mask = cursor->chunk.data;
image = mask + cursor_get_mono_bpl(c) * c->width;
cursor_set_mono(c, 0xffffff, 0x000000, image, 1, mask);
if (qxl->debug > 2) {
cursor_print_ascii_art(c, "qxl/mono");
}
break;
default:
fprintf(stderr, "%s: not implemented: type %d\n",
__FUNCTION__, cursor->header.type);
goto fail;
}
return c;
fail:
cursor_put(c);
return NULL;
}
/* called from spice server thread context only */
int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
{
QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
QXLCursor *cursor;
QEMUCursor *c;
if (!cmd) {
return 1;
}
if (!dpy_cursor_define_supported(qxl->vga.con)) {
return 0;
}
if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) {
fprintf(stderr, "%s", __FUNCTION__);
qxl_log_cmd_cursor(qxl, cmd, ext->group_id);
fprintf(stderr, "\n");
}
switch (cmd->type) {
case QXL_CURSOR_SET:
cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
if (!cursor) {
return 1;
}
if (cursor->chunk.data_size != cursor->data_size) {
fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__);
return 1;
}
c = qxl_cursor(qxl, cursor);
if (c == NULL) {
c = cursor_builtin_left_ptr();
}
qemu_mutex_lock(&qxl->ssd.lock);
if (qxl->ssd.cursor) {
cursor_put(qxl->ssd.cursor);
}
qxl->ssd.cursor = c;
qxl->ssd.mouse_x = cmd->u.set.position.x;
qxl->ssd.mouse_y = cmd->u.set.position.y;
qemu_mutex_unlock(&qxl->ssd.lock);
break;
case QXL_CURSOR_MOVE:
qemu_mutex_lock(&qxl->ssd.lock);
qxl->ssd.mouse_x = cmd->u.position.x;
qxl->ssd.mouse_y = cmd->u.position.y;
qemu_mutex_unlock(&qxl->ssd.lock);
break;
}
return 0;
}

2365
hw/display/qxl.c Normal file

File diff suppressed because it is too large Load diff

1450
hw/display/sm501.c Normal file

File diff suppressed because it is too large Load diff

593
hw/display/tc6393xb.c Normal file
View file

@ -0,0 +1,593 @@
/*
* Toshiba TC6393XB I/O Controller.
* Found in Sharp Zaurus SL-6000 (tosa) or some
* Toshiba e-Series PDAs.
*
* Most features are currently unsupported!!!
*
* This code is licensed under the GNU GPL v2.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "hw/hw.h"
#include "hw/arm/devices.h"
#include "hw/block/flash.h"
#include "ui/console.h"
#include "ui/pixel_ops.h"
#include "sysemu/blockdev.h"
#define IRQ_TC6393_NAND 0
#define IRQ_TC6393_MMC 1
#define IRQ_TC6393_OHCI 2
#define IRQ_TC6393_SERIAL 3
#define IRQ_TC6393_FB 4
#define TC6393XB_NR_IRQS 8
#define TC6393XB_GPIOS 16
#define SCR_REVID 0x08 /* b Revision ID */
#define SCR_ISR 0x50 /* b Interrupt Status */
#define SCR_IMR 0x52 /* b Interrupt Mask */
#define SCR_IRR 0x54 /* b Interrupt Routing */
#define SCR_GPER 0x60 /* w GP Enable */
#define SCR_GPI_SR(i) (0x64 + (i)) /* b3 GPI Status */
#define SCR_GPI_IMR(i) (0x68 + (i)) /* b3 GPI INT Mask */
#define SCR_GPI_EDER(i) (0x6c + (i)) /* b3 GPI Edge Detect Enable */
#define SCR_GPI_LIR(i) (0x70 + (i)) /* b3 GPI Level Invert */
#define SCR_GPO_DSR(i) (0x78 + (i)) /* b3 GPO Data Set */
#define SCR_GPO_DOECR(i) (0x7c + (i)) /* b3 GPO Data OE Control */
#define SCR_GP_IARCR(i) (0x80 + (i)) /* b3 GP Internal Active Register Control */
#define SCR_GP_IARLCR(i) (0x84 + (i)) /* b3 GP INTERNAL Active Register Level Control */
#define SCR_GPI_BCR(i) (0x88 + (i)) /* b3 GPI Buffer Control */
#define SCR_GPA_IARCR 0x8c /* w GPa Internal Active Register Control */
#define SCR_GPA_IARLCR 0x90 /* w GPa Internal Active Register Level Control */
#define SCR_GPA_BCR 0x94 /* w GPa Buffer Control */
#define SCR_CCR 0x98 /* w Clock Control */
#define SCR_PLL2CR 0x9a /* w PLL2 Control */
#define SCR_PLL1CR 0x9c /* l PLL1 Control */
#define SCR_DIARCR 0xa0 /* b Device Internal Active Register Control */
#define SCR_DBOCR 0xa1 /* b Device Buffer Off Control */
#define SCR_FER 0xe0 /* b Function Enable */
#define SCR_MCR 0xe4 /* w Mode Control */
#define SCR_CONFIG 0xfc /* b Configuration Control */
#define SCR_DEBUG 0xff /* b Debug */
#define NAND_CFG_COMMAND 0x04 /* w Command */
#define NAND_CFG_BASE 0x10 /* l Control Base Address */
#define NAND_CFG_INTP 0x3d /* b Interrupt Pin */
#define NAND_CFG_INTE 0x48 /* b Int Enable */
#define NAND_CFG_EC 0x4a /* b Event Control */
#define NAND_CFG_ICC 0x4c /* b Internal Clock Control */
#define NAND_CFG_ECCC 0x5b /* b ECC Control */
#define NAND_CFG_NFTC 0x60 /* b NAND Flash Transaction Control */
#define NAND_CFG_NFM 0x61 /* b NAND Flash Monitor */
#define NAND_CFG_NFPSC 0x62 /* b NAND Flash Power Supply Control */
#define NAND_CFG_NFDC 0x63 /* b NAND Flash Detect Control */
#define NAND_DATA 0x00 /* l Data */
#define NAND_MODE 0x04 /* b Mode */
#define NAND_STATUS 0x05 /* b Status */
#define NAND_ISR 0x06 /* b Interrupt Status */
#define NAND_IMR 0x07 /* b Interrupt Mask */
#define NAND_MODE_WP 0x80
#define NAND_MODE_CE 0x10
#define NAND_MODE_ALE 0x02
#define NAND_MODE_CLE 0x01
#define NAND_MODE_ECC_MASK 0x60
#define NAND_MODE_ECC_EN 0x20
#define NAND_MODE_ECC_READ 0x40
#define NAND_MODE_ECC_RST 0x60
struct TC6393xbState {
MemoryRegion iomem;
qemu_irq irq;
qemu_irq *sub_irqs;
struct {
uint8_t ISR;
uint8_t IMR;
uint8_t IRR;
uint16_t GPER;
uint8_t GPI_SR[3];
uint8_t GPI_IMR[3];
uint8_t GPI_EDER[3];
uint8_t GPI_LIR[3];
uint8_t GP_IARCR[3];
uint8_t GP_IARLCR[3];
uint8_t GPI_BCR[3];
uint16_t GPA_IARCR;
uint16_t GPA_IARLCR;
uint16_t CCR;
uint16_t PLL2CR;
uint32_t PLL1CR;
uint8_t DIARCR;
uint8_t DBOCR;
uint8_t FER;
uint16_t MCR;
uint8_t CONFIG;
uint8_t DEBUG;
} scr;
uint32_t gpio_dir;
uint32_t gpio_level;
uint32_t prev_level;
qemu_irq handler[TC6393XB_GPIOS];
qemu_irq *gpio_in;
struct {
uint8_t mode;
uint8_t isr;
uint8_t imr;
} nand;
int nand_enable;
uint32_t nand_phys;
DeviceState *flash;
ECCState ecc;
QemuConsole *con;
MemoryRegion vram;
uint16_t *vram_ptr;
uint32_t scr_width, scr_height; /* in pixels */
qemu_irq l3v;
unsigned blank : 1,
blanked : 1;
};
qemu_irq *tc6393xb_gpio_in_get(TC6393xbState *s)
{
return s->gpio_in;
}
static void tc6393xb_gpio_set(void *opaque, int line, int level)
{
// TC6393xbState *s = opaque;
if (line > TC6393XB_GPIOS) {
printf("%s: No GPIO pin %i\n", __FUNCTION__, line);
return;
}
// FIXME: how does the chip reflect the GPIO input level change?
}
void tc6393xb_gpio_out_set(TC6393xbState *s, int line,
qemu_irq handler)
{
if (line >= TC6393XB_GPIOS) {
fprintf(stderr, "TC6393xb: no GPIO pin %d\n", line);
return;
}
s->handler[line] = handler;
}
static void tc6393xb_gpio_handler_update(TC6393xbState *s)
{
uint32_t level, diff;
int bit;
level = s->gpio_level & s->gpio_dir;
for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
bit = ffs(diff) - 1;
qemu_set_irq(s->handler[bit], (level >> bit) & 1);
}
s->prev_level = level;
}
qemu_irq tc6393xb_l3v_get(TC6393xbState *s)
{
return s->l3v;
}
static void tc6393xb_l3v(void *opaque, int line, int level)
{
TC6393xbState *s = opaque;
s->blank = !level;
fprintf(stderr, "L3V: %d\n", level);
}
static void tc6393xb_sub_irq(void *opaque, int line, int level) {
TC6393xbState *s = opaque;
uint8_t isr = s->scr.ISR;
if (level)
isr |= 1 << line;
else
isr &= ~(1 << line);
s->scr.ISR = isr;
qemu_set_irq(s->irq, isr & s->scr.IMR);
}
#define SCR_REG_B(N) \
case SCR_ ##N: return s->scr.N
#define SCR_REG_W(N) \
case SCR_ ##N: return s->scr.N; \
case SCR_ ##N + 1: return s->scr.N >> 8;
#define SCR_REG_L(N) \
case SCR_ ##N: return s->scr.N; \
case SCR_ ##N + 1: return s->scr.N >> 8; \
case SCR_ ##N + 2: return s->scr.N >> 16; \
case SCR_ ##N + 3: return s->scr.N >> 24;
#define SCR_REG_A(N) \
case SCR_ ##N(0): return s->scr.N[0]; \
case SCR_ ##N(1): return s->scr.N[1]; \
case SCR_ ##N(2): return s->scr.N[2]
static uint32_t tc6393xb_scr_readb(TC6393xbState *s, hwaddr addr)
{
switch (addr) {
case SCR_REVID:
return 3;
case SCR_REVID+1:
return 0;
SCR_REG_B(ISR);
SCR_REG_B(IMR);
SCR_REG_B(IRR);
SCR_REG_W(GPER);
SCR_REG_A(GPI_SR);
SCR_REG_A(GPI_IMR);
SCR_REG_A(GPI_EDER);
SCR_REG_A(GPI_LIR);
case SCR_GPO_DSR(0):
case SCR_GPO_DSR(1):
case SCR_GPO_DSR(2):
return (s->gpio_level >> ((addr - SCR_GPO_DSR(0)) * 8)) & 0xff;
case SCR_GPO_DOECR(0):
case SCR_GPO_DOECR(1):
case SCR_GPO_DOECR(2):
return (s->gpio_dir >> ((addr - SCR_GPO_DOECR(0)) * 8)) & 0xff;
SCR_REG_A(GP_IARCR);
SCR_REG_A(GP_IARLCR);
SCR_REG_A(GPI_BCR);
SCR_REG_W(GPA_IARCR);
SCR_REG_W(GPA_IARLCR);
SCR_REG_W(CCR);
SCR_REG_W(PLL2CR);
SCR_REG_L(PLL1CR);
SCR_REG_B(DIARCR);
SCR_REG_B(DBOCR);
SCR_REG_B(FER);
SCR_REG_W(MCR);
SCR_REG_B(CONFIG);
SCR_REG_B(DEBUG);
}
fprintf(stderr, "tc6393xb_scr: unhandled read at %08x\n", (uint32_t) addr);
return 0;
}
#undef SCR_REG_B
#undef SCR_REG_W
#undef SCR_REG_L
#undef SCR_REG_A
#define SCR_REG_B(N) \
case SCR_ ##N: s->scr.N = value; return;
#define SCR_REG_W(N) \
case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \
case SCR_ ##N + 1: s->scr.N = (s->scr.N & 0xff) | (value << 8); return
#define SCR_REG_L(N) \
case SCR_ ##N: s->scr.N = (s->scr.N & ~0xff) | (value & 0xff); return; \
case SCR_ ##N + 1: s->scr.N = (s->scr.N & ~(0xff << 8)) | (value & (0xff << 8)); return; \
case SCR_ ##N + 2: s->scr.N = (s->scr.N & ~(0xff << 16)) | (value & (0xff << 16)); return; \
case SCR_ ##N + 3: s->scr.N = (s->scr.N & ~(0xff << 24)) | (value & (0xff << 24)); return;
#define SCR_REG_A(N) \
case SCR_ ##N(0): s->scr.N[0] = value; return; \
case SCR_ ##N(1): s->scr.N[1] = value; return; \
case SCR_ ##N(2): s->scr.N[2] = value; return
static void tc6393xb_scr_writeb(TC6393xbState *s, hwaddr addr, uint32_t value)
{
switch (addr) {
SCR_REG_B(ISR);
SCR_REG_B(IMR);
SCR_REG_B(IRR);
SCR_REG_W(GPER);
SCR_REG_A(GPI_SR);
SCR_REG_A(GPI_IMR);
SCR_REG_A(GPI_EDER);
SCR_REG_A(GPI_LIR);
case SCR_GPO_DSR(0):
case SCR_GPO_DSR(1):
case SCR_GPO_DSR(2):
s->gpio_level = (s->gpio_level & ~(0xff << ((addr - SCR_GPO_DSR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DSR(0))*8));
tc6393xb_gpio_handler_update(s);
return;
case SCR_GPO_DOECR(0):
case SCR_GPO_DOECR(1):
case SCR_GPO_DOECR(2):
s->gpio_dir = (s->gpio_dir & ~(0xff << ((addr - SCR_GPO_DOECR(0))*8))) | ((value & 0xff) << ((addr - SCR_GPO_DOECR(0))*8));
tc6393xb_gpio_handler_update(s);
return;
SCR_REG_A(GP_IARCR);
SCR_REG_A(GP_IARLCR);
SCR_REG_A(GPI_BCR);
SCR_REG_W(GPA_IARCR);
SCR_REG_W(GPA_IARLCR);
SCR_REG_W(CCR);
SCR_REG_W(PLL2CR);
SCR_REG_L(PLL1CR);
SCR_REG_B(DIARCR);
SCR_REG_B(DBOCR);
SCR_REG_B(FER);
SCR_REG_W(MCR);
SCR_REG_B(CONFIG);
SCR_REG_B(DEBUG);
}
fprintf(stderr, "tc6393xb_scr: unhandled write at %08x: %02x\n",
(uint32_t) addr, value & 0xff);
}
#undef SCR_REG_B
#undef SCR_REG_W
#undef SCR_REG_L
#undef SCR_REG_A
static void tc6393xb_nand_irq(TC6393xbState *s) {
qemu_set_irq(s->sub_irqs[IRQ_TC6393_NAND],
(s->nand.imr & 0x80) && (s->nand.imr & s->nand.isr));
}
static uint32_t tc6393xb_nand_cfg_readb(TC6393xbState *s, hwaddr addr) {
switch (addr) {
case NAND_CFG_COMMAND:
return s->nand_enable ? 2 : 0;
case NAND_CFG_BASE:
case NAND_CFG_BASE + 1:
case NAND_CFG_BASE + 2:
case NAND_CFG_BASE + 3:
return s->nand_phys >> (addr - NAND_CFG_BASE);
}
fprintf(stderr, "tc6393xb_nand_cfg: unhandled read at %08x\n", (uint32_t) addr);
return 0;
}
static void tc6393xb_nand_cfg_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) {
switch (addr) {
case NAND_CFG_COMMAND:
s->nand_enable = (value & 0x2);
return;
case NAND_CFG_BASE:
case NAND_CFG_BASE + 1:
case NAND_CFG_BASE + 2:
case NAND_CFG_BASE + 3:
s->nand_phys &= ~(0xff << ((addr - NAND_CFG_BASE) * 8));
s->nand_phys |= (value & 0xff) << ((addr - NAND_CFG_BASE) * 8);
return;
}
fprintf(stderr, "tc6393xb_nand_cfg: unhandled write at %08x: %02x\n",
(uint32_t) addr, value & 0xff);
}
static uint32_t tc6393xb_nand_readb(TC6393xbState *s, hwaddr addr) {
switch (addr) {
case NAND_DATA + 0:
case NAND_DATA + 1:
case NAND_DATA + 2:
case NAND_DATA + 3:
return nand_getio(s->flash);
case NAND_MODE:
return s->nand.mode;
case NAND_STATUS:
return 0x14;
case NAND_ISR:
return s->nand.isr;
case NAND_IMR:
return s->nand.imr;
}
fprintf(stderr, "tc6393xb_nand: unhandled read at %08x\n", (uint32_t) addr);
return 0;
}
static void tc6393xb_nand_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) {
// fprintf(stderr, "tc6393xb_nand: write at %08x: %02x\n",
// (uint32_t) addr, value & 0xff);
switch (addr) {
case NAND_DATA + 0:
case NAND_DATA + 1:
case NAND_DATA + 2:
case NAND_DATA + 3:
nand_setio(s->flash, value);
s->nand.isr |= 1;
tc6393xb_nand_irq(s);
return;
case NAND_MODE:
s->nand.mode = value;
nand_setpins(s->flash,
value & NAND_MODE_CLE,
value & NAND_MODE_ALE,
!(value & NAND_MODE_CE),
value & NAND_MODE_WP,
0); // FIXME: gnd
switch (value & NAND_MODE_ECC_MASK) {
case NAND_MODE_ECC_RST:
ecc_reset(&s->ecc);
break;
case NAND_MODE_ECC_READ:
// FIXME
break;
case NAND_MODE_ECC_EN:
ecc_reset(&s->ecc);
}
return;
case NAND_ISR:
s->nand.isr = value;
tc6393xb_nand_irq(s);
return;
case NAND_IMR:
s->nand.imr = value;
tc6393xb_nand_irq(s);
return;
}
fprintf(stderr, "tc6393xb_nand: unhandled write at %08x: %02x\n",
(uint32_t) addr, value & 0xff);
}
#define BITS 8
#include "hw/tc6393xb_template.h"
#define BITS 15
#include "hw/tc6393xb_template.h"
#define BITS 16
#include "hw/tc6393xb_template.h"
#define BITS 24
#include "hw/tc6393xb_template.h"
#define BITS 32
#include "hw/tc6393xb_template.h"
static void tc6393xb_draw_graphic(TC6393xbState *s, int full_update)
{
DisplaySurface *surface = qemu_console_surface(s->con);
switch (surface_bits_per_pixel(surface)) {
case 8:
tc6393xb_draw_graphic8(s);
break;
case 15:
tc6393xb_draw_graphic15(s);
break;
case 16:
tc6393xb_draw_graphic16(s);
break;
case 24:
tc6393xb_draw_graphic24(s);
break;
case 32:
tc6393xb_draw_graphic32(s);
break;
default:
printf("tc6393xb: unknown depth %d\n",
surface_bits_per_pixel(surface));
return;
}
dpy_gfx_update(s->con, 0, 0, s->scr_width, s->scr_height);
}
static void tc6393xb_draw_blank(TC6393xbState *s, int full_update)
{
DisplaySurface *surface = qemu_console_surface(s->con);
int i, w;
uint8_t *d;
if (!full_update)
return;
w = s->scr_width * surface_bytes_per_pixel(surface);
d = surface_data(surface);
for(i = 0; i < s->scr_height; i++) {
memset(d, 0, w);
d += surface_stride(surface);
}
dpy_gfx_update(s->con, 0, 0, s->scr_width, s->scr_height);
}
static void tc6393xb_update_display(void *opaque)
{
TC6393xbState *s = opaque;
DisplaySurface *surface = qemu_console_surface(s->con);
int full_update;
if (s->scr_width == 0 || s->scr_height == 0)
return;
full_update = 0;
if (s->blanked != s->blank) {
s->blanked = s->blank;
full_update = 1;
}
if (s->scr_width != surface_width(surface) ||
s->scr_height != surface_height(surface)) {
qemu_console_resize(s->con, s->scr_width, s->scr_height);
full_update = 1;
}
if (s->blanked)
tc6393xb_draw_blank(s, full_update);
else
tc6393xb_draw_graphic(s, full_update);
}
static uint64_t tc6393xb_readb(void *opaque, hwaddr addr,
unsigned size)
{
TC6393xbState *s = opaque;
switch (addr >> 8) {
case 0:
return tc6393xb_scr_readb(s, addr & 0xff);
case 1:
return tc6393xb_nand_cfg_readb(s, addr & 0xff);
};
if ((addr &~0xff) == s->nand_phys && s->nand_enable) {
// return tc6393xb_nand_readb(s, addr & 0xff);
uint8_t d = tc6393xb_nand_readb(s, addr & 0xff);
// fprintf(stderr, "tc6393xb_nand: read at %08x: %02hhx\n", (uint32_t) addr, d);
return d;
}
// fprintf(stderr, "tc6393xb: unhandled read at %08x\n", (uint32_t) addr);
return 0;
}
static void tc6393xb_writeb(void *opaque, hwaddr addr,
uint64_t value, unsigned size) {
TC6393xbState *s = opaque;
switch (addr >> 8) {
case 0:
tc6393xb_scr_writeb(s, addr & 0xff, value);
return;
case 1:
tc6393xb_nand_cfg_writeb(s, addr & 0xff, value);
return;
};
if ((addr &~0xff) == s->nand_phys && s->nand_enable)
tc6393xb_nand_writeb(s, addr & 0xff, value);
else
fprintf(stderr, "tc6393xb: unhandled write at %08x: %02x\n",
(uint32_t) addr, (int)value & 0xff);
}
TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq)
{
TC6393xbState *s;
DriveInfo *nand;
static const MemoryRegionOps tc6393xb_ops = {
.read = tc6393xb_readb,
.write = tc6393xb_writeb,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
.min_access_size = 1,
.max_access_size = 1,
},
};
s = (TC6393xbState *) g_malloc0(sizeof(TC6393xbState));
s->irq = irq;
s->gpio_in = qemu_allocate_irqs(tc6393xb_gpio_set, s, TC6393XB_GPIOS);
s->l3v = *qemu_allocate_irqs(tc6393xb_l3v, s, 1);
s->blanked = 1;
s->sub_irqs = qemu_allocate_irqs(tc6393xb_sub_irq, s, TC6393XB_NR_IRQS);
nand = drive_get(IF_MTD, 0, 0);
s->flash = nand_init(nand ? nand->bdrv : NULL, NAND_MFR_TOSHIBA, 0x76);
memory_region_init_io(&s->iomem, &tc6393xb_ops, s, "tc6393xb", 0x10000);
memory_region_add_subregion(sysmem, base, &s->iomem);
memory_region_init_ram(&s->vram, "tc6393xb.vram", 0x100000);
vmstate_register_ram_global(&s->vram);
s->vram_ptr = memory_region_get_ram_ptr(&s->vram);
memory_region_add_subregion(sysmem, base + 0x100000, &s->vram);
s->scr_width = 480;
s->scr_height = 640;
s->con = graphic_console_init(tc6393xb_update_display,
NULL, /* invalidate */
NULL, /* screen_dump */
NULL, /* text_update */
s);
return s;
}

739
hw/display/tcx.c Normal file
View file

@ -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)

2457
hw/display/vga.c Normal file

File diff suppressed because it is too large Load diff