mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-07 17:53:56 -06:00
hw: move display devices to hw/display/, configure via default-configs/
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
34b8f63ea1
commit
fc97bb5ba3
31 changed files with 45 additions and 23 deletions
|
@ -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
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
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
110
hw/display/framebuffer.c
Normal 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
490
hw/display/milkymist-tmu2.c
Normal 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)
|
335
hw/display/milkymist-vgafb.c
Normal file
335
hw/display/milkymist-vgafb.c
Normal 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
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
493
hw/display/omap_lcdc.c
Normal 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
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
275
hw/display/qxl-logger.c
Normal 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(©->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
280
hw/display/qxl-render.c
Normal 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
2365
hw/display/qxl.c
Normal file
File diff suppressed because it is too large
Load diff
1450
hw/display/sm501.c
Normal file
1450
hw/display/sm501.c
Normal file
File diff suppressed because it is too large
Load diff
593
hw/display/tc6393xb.c
Normal file
593
hw/display/tc6393xb.c
Normal 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
739
hw/display/tcx.c
Normal 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
2457
hw/display/vga.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue