Message ID | 4C3C93B7.9020804@mc.net |
---|---|
State | New |
Headers | show |
2010/7/13 Bob Breuer <breuerr@mc.net>: > Another preview of the cg14 framebuffer. > > Activate by selecting SS-20 machine and setting width > 1024, i.e. "-M SS-20 -g 1152x900". > Note that NetBSD assumes 1152x900, while OBP also supports 1024x768, 1280x1024, and 1600x1280. > > New since last time: > - All video memory accesses implemented > X under linux now works (uses RGB instead of XRGB space) > - Hooked into qdev Great job! > Todo: > - fix NetBSD display > - add draw_line templates to handle other than 32-bit RGB host displays > - use VGA_DIRTY tracking > - What's the equivalent of stb_p that also sets the dirty bits? afaics stb_phys does set the dirty bits. The ones which don't are st?_phys_notdirty . > - inform OpenBIOS of cg14 framebuffer > - Can we pass "nvalias screen /obio/cgfourteen" to the firmware? Do we have to? Why not just probe for the VSIMM? > > Bob > > --- > Makefile.target | 1 + > hw/cg14.c | 850 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > hw/sun4m.c | 22 ++ > 3 files changed, 873 insertions(+), 0 deletions(-) > create mode 100644 hw/cg14.c > > diff --git a/Makefile.target b/Makefile.target > index 3ef4666..54a2ae4 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -255,6 +255,7 @@ else > obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o > obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o > obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o > +obj-sparc-y += cg14.o > endif > > obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o > diff --git a/hw/cg14.c b/hw/cg14.c > new file mode 100644 > index 0000000..533dc04 > --- /dev/null > +++ b/hw/cg14.c > @@ -0,0 +1,850 @@ > +/* > + * QEMU CG14 Frame buffer > + * > + * Copyright (c) 2010 Bob Breuer <breuerr@mc.net> > + * > + * 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 "console.h" > +#include "sysbus.h" > +#include "qdev-addr.h" > + > +//#define DEBUG_CG14 > +//#define DEBUG_DAC > +//#define DEBUG_CONFIG > + > +/* > + * Sun CG14 framebuffer (without SX) > + * CG14 = vsimm framebuffer (video ram and dac) > + * SX = pixel processor (acceleration) built into chipset > + * > + * Documentation: not publicly documented by Sun > + * linux driver: drivers/video/cg14.c > + * NetBSD/OpenBSD: src/sys/arch/sparc/dev/cgfourteen* > + * > + * Takes up one memory slot: > + * A[28:26] = slot number (4 to 7) > + * regs: size 0x10000 @ 0x09c000000 (0x80000000 + slot * 64M) > + * vmem: size upto 16MB @ 0x0fc000000 (0xE0000000 + slot * 64M) > + * > + * SS-20 OBP only supports slots 7 (onboard output) and 4 (AVB output) > + * > + * memory map: > + * reg+0x0000 = control registers > + * reg+0x1000 = cursor registers > + * reg+0x2000 = dac registers (ADV7152) > + * reg+0x3000 = xlut > + * reg+0x4000 = clut1 > + * reg+0x5000 = clut2 > + * reg+0x6000 = clut3 (if implemented) > + * reg+0xf000 = autoinc > + * > + * mem+0x0000000 = XBGR (01234567) > + * mem+0x1000000 = BGR (.123.567) writes to X are blocked, reads are ok > + * mem+0x2000000 = X16 (0246) > + * mem+0x2800000 = C16 (1357) > + * mem+0x3000000 = X32 (04) > + * mem+0x3400000 = B32 (15) > + * mem+0x3800000 = G32 (26) > + * mem+0x3c00000 = R32 (37) > + */ > + > +#define CG14_REG_SIZE 0x10000 > +#define CG14_VMEM_SLOTSIZE (64<<20) > + > +#define CG14_MONID_1024x768 0 > +#define CG14_MONID_1600x1280 1 > +#define CG14_MONID_1280x1024 2 > +#define CG14_MONID_1152x900 7 > + > +#define CG14_MONID_DEFAULT CG14_MONID_1024x768 > + > + > +#define CG14_MCR_INTENABLE 0x80 > +#define CG14_MCR_VIDENABLE 0x40 > +#define CG14_MCR_PIXMODE_MASK 0x30 > +#define CG14_MCR_PIXMODE_8 0x00 > +#define CG14_MCR_PIXMODE_16 0x20 /* 8+8 (X16,C16) */ > +#define CG14_MCR_PIXMODE_32 0x30 /* XBGR */ > + > + > +#ifdef DEBUG_CG14 > +#define DPRINTF(fmt, ...) \ > + printf("CG14: " fmt , ## __VA_ARGS__) > +#else > +#define DPRINTF(fmt, ...) > +#endif > + > +#ifdef DEBUG_DAC > +#define DPRINTF_DAC(fmt, ...) \ > + printf("CG14 dac: " fmt , ## __VA_ARGS__) > +#else > +#define DPRINTF_DAC(fmt, ...) > +#endif > + > +#ifdef DEBUG_CONFIG > +#define DPRINTF_CONFIG(fmt, ...) \ > + printf("CG14: " fmt , ## __VA_ARGS__) > +#else > +#define DPRINTF_CONFIG(fmt, ...) > +#endif > + > +#define CG14_INFO(fmt, ...) \ > + do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0) > +#define CG14_ERROR(fmt, ...) \ > + do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0) > + > + > +struct dac_state { > + uint8_t mode; > + uint8_t address; > + int rgb_seq; > +}; > + > +typedef struct CG14State { > + SysBusDevice busdev; > + DisplayState *ds; > + > + target_phys_addr_t vram_addr; > + uint8_t *vram; > + uint32_t vram_size; > + uint32_t vram_amask; > + uint16_t width, height; > + int dirty, size_changed; > + struct { > + uint8_t mcr; > + uint8_t ppr; > + uint8_t msr; > + } ctrl; > + struct dac_state dac; > + struct { > + uint16_t hblank_start; > + uint16_t hblank_clear; > + uint16_t vblank_start; > + uint16_t vblank_clear; > + } timing; > + uint8_t xlut[256]; > + uint32_t clut1[256]; > + uint32_t clut2[256]; > +} CG14State; > + > +static void cg14_screen_dump(void *opaque, const char *filename); > +static void cg14_invalidate_display(void *opaque); > + > +static inline uint32_t bgr_to_rgb(uint32_t bgr) > +{ > + uint32_t rgb; > + > + /* swap r & b */ > + rgb = (bgr & 0x00FF00) > + | (bgr & 0x0000FF) << 16 > + | (bgr & 0xFF0000) >> 16; > + return rgb; > +} > + > +typedef void draw_line_func(const CG14State *s, void *dst, const uint8_t *src); > + > +// TODO: draw_line templates > +static void cg14_draw_line32_rgb32(const CG14State *s, void *dst, const uint8_t *src) > +{ > + unsigned int i; > + unsigned int x, r, g, b; > + uint8_t xlut_val; > + uint32_t dval; > + > + for (i=0; i<s->width; i++) { > + x = src[0]; > + b = src[1]; > + g = src[2]; > + r = src[3]; > + xlut_val = s->xlut[x]; > + src += 4; > + > + if (xlut_val == 0x40) { > + dval = bgr_to_rgb(s->clut1[x]); > + } else { > + /* xlut = 0x00 for true-color */ > + /* possible blending between 2 colors */ > + /* fallback to true-color just to display something if unimplemented */ > + dval = r << 16 | g << 8 | b; > + } > + /* dac lookup ? */ > + > + /* to surface format */ > + //dval = is_bgr ? (abgr & 0xFFFFFF) : bgr_to_rgb(abgr); > + ((uint32_t*)dst)[i] = dval; > + } > +} > + > +static void cg14_draw_line16_rgb32(const CG14State *s, void *dst, const uint8_t *src) > +{ > + unsigned int i; > + unsigned int x, c; > + uint8_t xlut_val; > + uint32_t dval; > + > + for (i=0; i<s->width; i++) { > + x = src[0]; > + c = src[1]; > + xlut_val = s->xlut[x]; > + src += 2; > + > + if (xlut_val == 0x40) { > + dval = bgr_to_rgb(s->clut1[x]); > + } else { > + /* possible blending between 2 colors */ > + /* fallback to green/blue just to display something if unimplemented */ > + dval = x << 8 | c; > + } > + /* dac lookup ? */ > + > + ((uint32_t*)dst)[i] = dval; > + } > +} > + > +static void cg14_draw_line8_rgb32(const CG14State *s, void *dst, const uint8_t *src) > +{ > + int i; > + const uint32_t *clut; > + uint32_t *dst32 = dst; > + > + if (s->ctrl.ppr == 0x40) { > + clut = s->clut1; > + for (i=0; i<s->width; i++) { > + dst32[i] = bgr_to_rgb(clut[src[i]]); > + } > + } else { > + /* FIXME: other xlut modes, just use grayscale ramp for now */ > + for (i=0; i<s->width; i++) { > + dst32[i] = 0x010101 * src[i]; > + } > + } > +} > + > +/* TODO: use VGA_DIRTY_FLAG */ > +static void cg14_update_display(void *opaque) > +{ > + CG14State *s = opaque; > + int h, src_linesize; > + uint8_t *pix; > + uint8_t *data; > + int new_width, new_height; > + draw_line_func * draw_line; > + > + if (s->size_changed) { > + new_width = 4 * (s->timing.hblank_start - s->timing.hblank_clear); > + new_height = s->timing.vblank_start - s->timing.vblank_clear; > + s->size_changed = 0; > + if ((new_width != s->width || new_height != s->height) && new_width > 0 && new_height > 0) { > + s->width = new_width; > + s->height = new_height; > + CG14_INFO("new resolution = %d x %d\n", new_width, new_height); > + qemu_console_resize(s->ds, s->width, s->height); > + s->dirty = 1; > + } > + } > + > + if (!s->dirty || !s->width || !s->height) { > + return; > + } > + > + if (ds_get_bits_per_pixel(s->ds) != 32) { > + CG14_ERROR("cg14_update: FIXME: bpp (%d) != 32, linesize %d\n", > + ds_get_bits_per_pixel(s->ds), ds_get_linesize(s->ds)); > + return; > + } > + // if (is_surface_bgr(s->ds->surface)) > + > + draw_line = NULL; > + src_linesize = s->width; > + if (s->ctrl.mcr & CG14_MCR_VIDENABLE) { > + switch (s->ctrl.mcr & CG14_MCR_PIXMODE_MASK) { > + case CG14_MCR_PIXMODE_32: > + src_linesize *= 4; > + draw_line = cg14_draw_line32_rgb32; > + break; > + case CG14_MCR_PIXMODE_16: > + src_linesize *= 2; > + draw_line = cg14_draw_line16_rgb32; > + break; > + case CG14_MCR_PIXMODE_8: > + draw_line = cg14_draw_line8_rgb32; > + break; > + } > + } > + if (!draw_line) { > + /* blank */ > + memset(ds_get_data(s->ds), 0, ds_get_linesize(s->ds)*ds_get_height(s->ds)); > + s->dirty = 0; > + return; > + } > + > + pix = s->vram; > + data = ds_get_data(s->ds); > + > + for (h=0; h<s->height; h++) { > + draw_line(s, data, pix); > + pix += src_linesize; > + data += ds_get_linesize(s->ds); > + } > + dpy_update(s->ds, 0, 0, s->width, s->height); > + s->dirty = 0; > +} > + > +static void cg14_invalidate_display(void *opaque) > +{ > + CG14State *s = opaque; > + > + s->dirty = 1; > +} > + > +static uint32_t dac_read(struct dac_state *s, unsigned int reg) > +{ > + uint32_t val = 0; > + > + switch (reg) { > + case 0: > + val = s->address; > + break; > + case 1: /* lookup table */ > + s->rgb_seq++; > + break; > + case 2: /* control registers */ > + break; > + case 3: > + val = s->mode; > + break; > + } > + DPRINTF_DAC("read %02x from dac reg %d\n", val, reg); > + return val; > +} > + > +static void dac_write(struct dac_state *s, unsigned int reg, unsigned int val) > +{ > + switch (reg) { > + case 0: /* address register */ > + DPRINTF_DAC("write address %02x\n", val); > + s->address = val; > + s->rgb_seq = 0; > + break; > + case 1: /* lookup table */ > + DPRINTF_DAC("write %02x to lookup table\n", val); > + s->rgb_seq++; > + break; > + case 2: /* control registers */ > + DPRINTF_DAC("write %02x to control reg %d\n", val, s->address); > + switch (s->address) { > + default: > + break; > + } > + break; > + case 3: /* mode register */ > + DPRINTF_DAC("write mode %02x (%d bit DAC, %d bit bus)\n", > + val, (val & 2) ? 10 : 8, (val & 4) ? 10 : 8); > + if (!val & 0x01) { > + // reset the dac > + s->rgb_seq = 0; > + } > + s->mode = val; > + break; > + } > +} > + > +static uint32_t cg14_reg_readb(void *opaque, target_phys_addr_t addr) > +{ > + CG14State *s = opaque; > + uint32_t val; > + uint32_t i; > + > + if ((addr & 0xfc00) == 0x2000) { > + i = (addr & 0x300) >> 8; > + return dac_read(&s->dac, i); > + } > + > + switch (addr) { > + case 0x0000: > + val = s->ctrl.mcr; > + break; > + case 0x0001: > + val = s->ctrl.ppr; > + break; > + case 0x0004: /* status ? */ > + val = s->ctrl.msr; > + break; > + case 0x0006: /* hw version */ > + //val = 0x00; /* old version */ > + val = 0x30; > + break; > + default: > + val = 0; > + CG14_INFO("readb from reg %x\n", (int)addr); > + break; > + } > + > + return val; > +} > + > +static void cg14_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) > +{ > + CG14State *s = opaque; > + uint32_t i; > + > + if ((addr & 0xfc00) == 0x2000) { > + i = (addr & 0x300) >> 8; > + dac_write(&s->dac, i, val); > + return; > + } > + if ((addr & 0xff00) == 0x3000) { > + /* xlut */ > + i = addr & 0xff; > + if (s->xlut[i] != val) { > + s->dirty = 1; > + s->xlut[i] = val; > + if (val && val != 0x40) > + CG14_ERROR("writeb xlut[%d] = %02x\n", i, val); > + } > + return; > + } > + > + s->dirty = 1; > + > + switch (addr) { > + case 0x0000: > + s->ctrl.mcr = val; > + DPRINTF_CONFIG("write %02x to MCR\n", val); > + break; > + case 0x0001: > + s->ctrl.ppr = val & 0xF0; > + break; > + case 0x0007: > + /* clock control (ICS1562AM-001) */ > + DPRINTF("write %02x to clock control\n", val); > + break; > + default: > + CG14_ERROR("writeb %02x to reg %x\n", val, (int)addr); > + break; > + } > +} > + > +static uint32_t cg14_reg_readw(void *opaque, target_phys_addr_t addr) > +{ > + CG14State *s = opaque; > + uint32_t val; > + > + switch (addr) { > + case 0x0018: > + val = s->timing.hblank_start; > + break; > + case 0x001a: > + val = s->timing.hblank_clear; > + break; > + case 0x0022: > + val = s->timing.vblank_start; > + break; > + case 0x0024: > + val = s->timing.vblank_clear; > + break; > + default: > + val = 0; > + CG14_INFO("readw from reg %x\n", (int)addr); > + break; > + } > + > + return val; > +} > + > +static void cg14_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val) > +{ > + CG14State *s = opaque; > + > + DPRINTF_CONFIG("writew %04x to reg %x\n", val, (int)addr); > + > + /* timing registers are 16bit */ > + > + switch (addr) { > + case 0x0018: > + s->timing.hblank_start = val; > + break; > + case 0x001a: > + s->timing.hblank_clear = val; > + s->size_changed = 1; > + break; > + case 0x0022: > + s->timing.vblank_start = val; > + break; > + case 0x0024: > + s->timing.vblank_clear = val; > + s->size_changed = 1; > + break; > + case 0x001c: /* hsync_start */ > + case 0x001e: /* hsync_clear */ > + case 0x0020: /* csync_clear */ > + case 0x0026: /* vsync_start */ > + case 0x0028: /* vsync_clear */ > + default: > + break; > + } > +} > + > +static uint32_t cg14_reg_readl(void *opaque, target_phys_addr_t addr) > +{ > + CG14State *s = opaque; > + uint32_t val; > + uint32_t i; > + > + i = (addr & 0x3ff) >> 2; > + switch (addr & 0xfc00) { > + case 0x4000: > + val = s->clut1[i]; > + break; > + case 0x5000: > + val = s->clut2[i]; > + break; > + default: > + val = 0; > + CG14_ERROR("readl %08x from reg %x\n", val, (int)addr); > + break; > + } > + > + return val; > +} > + > +static void cg14_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val) > +{ > + CG14State *s = opaque; > + uint32_t i; > + > + s->dirty = 1; > + > + i = addr & 0x3ff; > + switch (addr & 0xfc00) { > + case 0x3000: > + if (i < 256) { > + s->xlut[i+0] = (uint8_t)(val >> 24); > + s->xlut[i+1] = (uint8_t)(val >> 16); > + s->xlut[i+2] = (uint8_t)(val >> 8); > + s->xlut[i+3] = (uint8_t)val; > + } > + break; > + case 0x4000: > + s->clut1[i >> 2] = val; > + break; > + case 0x5000: > + s->clut2[i >> 2] = val; > + break; > + default: > + CG14_ERROR("writel %08x to reg %x\n", val, (int)addr); > + break; > + } > +} > + > +static CPUReadMemoryFunc *cg14_reg_read[3] = { > + cg14_reg_readb, > + cg14_reg_readw, > + cg14_reg_readl, > +}; > + > +static CPUWriteMemoryFunc *cg14_reg_write[3] = { > + cg14_reg_writeb, > + cg14_reg_writew, > + cg14_reg_writel, > +}; > + > +static uint32_t cg14_vram_readb(void *opaque, target_phys_addr_t addr) > +{ > + CG14State *s = opaque; > + uint32_t offset; > + uint32_t val = 0; > + > + switch (addr & 0x3000000) { > + case 0x0000000: > + case 0x1000000: > + offset = addr & s->vram_amask; > + val = ldub_p(s->vram+offset); > + break; > + case 0x2000000: > + offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1); > + val = ldub_p(s->vram+offset); > + break; > + case 0x3000000: > + offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3); > + val = ldub_p(s->vram+offset); > + break; > + } > + > + return val; > +} > + > +static void cg14_vram_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) > +{ > + CG14State *s = opaque; > + uint32_t offset; > + > + s->dirty = 1; > + > + switch (addr & 0x3000000) { > + case 0x0000000: > + offset = addr & s->vram_amask; > + stb_p(s->vram+offset, val); > + break; > + case 0x1000000: > + offset = addr & s->vram_amask; > + /* block writes to X */ > + if (offset & 3) { > + stb_p(s->vram+offset, val); > + } > + break; > + case 0x2000000: > + offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1); > + stb_p(s->vram+offset, val); > + break; > + case 0x3000000: > + offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3); > + stb_p(s->vram+offset, val); > + break; > + } > +} > + > +static uint32_t cg14_vram_readw(void *opaque, target_phys_addr_t addr) > +{ > + uint32_t val; > + > + val = cg14_vram_readb(opaque, addr) << 8; > + val |= cg14_vram_readb(opaque, addr+1); > + > + return val; > +} > + > +static void cg14_vram_writew(void *opaque, target_phys_addr_t addr, uint32_t val) > +{ > + cg14_vram_writeb(opaque, addr, val >> 8); > + cg14_vram_writeb(opaque, addr+1, val & 0xff); > +} > + > +static uint32_t cg14_vram_readl(void *opaque, target_phys_addr_t addr) > +{ > + CG14State *s = opaque; > + uint32_t offset; > + uint32_t val = 0; > + > + switch (addr & 0x3000000) { > + case 0x0000000: > + case 0x1000000: > + offset = addr & s->vram_amask; > + val = ldl_be_p(s->vram+offset); > + break; > + case 0x2000000: > + offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1); > + val = ldub_p(s->vram+offset+0) << 24; > + val |= ldub_p(s->vram+offset+2) << 16; > + val |= ldub_p(s->vram+offset+4) << 8; > + val |= ldub_p(s->vram+offset+6); > + break; > + case 0x3000000: > + offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3); > + val = ldub_p(s->vram+offset+0) << 24; > + val |= ldub_p(s->vram+offset+4) << 16; > + val |= ldub_p(s->vram+offset+8) << 8; > + val |= ldub_p(s->vram+offset+12); > + break; > + } > + > + return val; > +} > + > +static void cg14_vram_writel(void *opaque, target_phys_addr_t addr, uint32_t val) > +{ > + CG14State *s = opaque; > + uint32_t offset; > + > + s->dirty = 1; > + > + switch (addr & 0x3000000) { > + case 0x0000000: > + offset = addr & s->vram_amask; > + stl_be_p(s->vram+offset, val); > + break; > + case 0x1000000: > + offset = addr & s->vram_amask; > + /* block writes to X */ > + stb_p(s->vram+offset+1, val >> 16); > + stb_p(s->vram+offset+2, val >> 8); > + stb_p(s->vram+offset+3, val); > + break; > + case 0x2000000: > + offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1); > + stb_p(s->vram+offset+0, val >> 24); > + stb_p(s->vram+offset+2, val >> 16); > + stb_p(s->vram+offset+4, val >> 8); > + stb_p(s->vram+offset+6, val); > + break; > + case 0x3000000: > + offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3); > + stb_p(s->vram+offset+0, val >> 24); > + stb_p(s->vram+offset+4, val >> 16); > + stb_p(s->vram+offset+8, val >> 8); > + stb_p(s->vram+offset+12, val); > + break; > + } > +} > + > +static CPUReadMemoryFunc *cg14_vram_read[3] = { > + cg14_vram_readb, > + cg14_vram_readw, > + cg14_vram_readl, > +}; > + > +static CPUWriteMemoryFunc *cg14_vram_write[3] = { > + cg14_vram_writeb, > + cg14_vram_writew, > + cg14_vram_writel, > +}; > + > + > +static void cg14_set_monitor_id(CG14State *s) > +{ > + uint8_t id; > + > + /* pick something close, used as a default by Sun's OBP */ > + if (s->width >= 1600) { > + id = CG14_MONID_1600x1280; > + } else if (s->width >= 1280) { > + id = CG14_MONID_1280x1024; > + } else if (s->width >= 1152) { > + id = CG14_MONID_1152x900; > + } else if (s->width >= 1024) { > + id = CG14_MONID_1024x768; > + } else { > + id = CG14_MONID_DEFAULT; > + } > + > + /* monitor code in bits 1..3 */ > + s->ctrl.msr = id << 1; > +} > + > +static int cg14_init1(SysBusDevice *dev) > +{ > + CG14State *s = FROM_SYSBUS(CG14State, dev); > + ram_addr_t vram_offset; > + uint8_t *vram; > + int ctrl_memory, vram_memory; > + > + vram_offset = qemu_ram_alloc(NULL, "cg14.vram", s->vram_size); > + vram = qemu_get_ram_ptr(vram_offset); > + > + s->vram = vram; > + s->vram_amask = s->vram_size - 1; > + > + ctrl_memory = cpu_register_io_memory(cg14_reg_read, cg14_reg_write, s); > + sysbus_init_mmio(dev, CG14_REG_SIZE, ctrl_memory); > + > + /* TODO: register first vram mapping as ram with dirty tracking */ > + vram_memory = cpu_register_io_memory(cg14_vram_read, cg14_vram_write, s); > + sysbus_init_mmio(dev, CG14_VMEM_SLOTSIZE, vram_memory); > + > + s->ds = graphic_console_init(cg14_update_display, > + cg14_invalidate_display, > + cg14_screen_dump, NULL, s); > + > + cg14_set_monitor_id(s); > + > + qemu_console_resize(s->ds, s->width, s->height); > + return 0; > +} > + > +/* save to file */ > +static void cg14_screen_dump(void *opaque, const char *filename) > +{ > + CG14State *s = opaque; > + FILE *f; > + int y, src_linesize, dst_linesize; > + void *buf; > + uint8_t *pix; > + draw_line_func * draw_line = NULL; > + > + switch (s->ctrl.mcr & CG14_MCR_PIXMODE_MASK) { > + case CG14_MCR_PIXMODE_32: > + src_linesize = s->width * 4; > + // draw_line = cg14_draw_line32_bgr24; > + break; > + case CG14_MCR_PIXMODE_16: > + src_linesize = s->width * 2; > + // draw_line = cg14_draw_line16_bgr24; > + break; > + case CG14_MCR_PIXMODE_8: > + src_linesize = s->width; > + // draw_line = cg14_draw_line8_bgr24; > + break; > + default: > + /* blank */ > + return; > + } > + > + f = fopen(filename, "wb"); > + if (!f) { > + return; > + } > + fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); > + > + dst_linesize = s->width * 3; > + buf = qemu_mallocz(dst_linesize); > + pix = s->vram; > + > + for (y=0; y<s->height; y++) { > + if (draw_line) > + draw_line(s, buf, pix); > + fwrite(buf, 1, dst_linesize, f); > + pix += src_linesize; > + } > + > + qemu_free(buf); > + fclose(f); > +} > + > +static void cg14_reset(DeviceState *d) > +{ > + CG14State *s = container_of(d, CG14State, busdev.qdev); > + > + /* set to 8bpp so last prom output might be visible */ > + s->ctrl.mcr = CG14_MCR_VIDENABLE | CG14_MCR_PIXMODE_8; > + s->dirty = 1; > +} > + > +static SysBusDeviceInfo cg14_info = { > + .init = cg14_init1, > + .qdev.name = "cg14", > + .qdev.desc = "Sun CG14 Framebuffer", > + .qdev.size = sizeof(CG14State), > + .qdev.reset = cg14_reset, > + .qdev.props = (Property[]) { > + DEFINE_PROP_TADDR("vram_addr", CG14State, vram_addr, -1), > + DEFINE_PROP_HEX32("vram_size", CG14State, vram_size, 0x800000), > + DEFINE_PROP_UINT16("width", CG14State, width, 0), > + DEFINE_PROP_UINT16("height", CG14State, height, 0), > + DEFINE_PROP_END_OF_LIST(), > + } > +}; > + > +static void cg14_register_devices(void) > +{ > + sysbus_register_withprop(&cg14_info); > +} > + > +device_init(cg14_register_devices) > diff --git a/hw/sun4m.c b/hw/sun4m.c > index e7a4cf6..0665e1f 100644 > --- a/hw/sun4m.c > +++ b/hw/sun4m.c > @@ -577,6 +577,23 @@ static void tcx_init(target_phys_addr_t addr, int vram_size, int width, > } > } > > +static void cg14_init(target_phys_addr_t ctrl_base, > + target_phys_addr_t vram_base, > + int width, int height) > +{ > + DeviceState *dev; > + SysBusDevice *s; > + > + dev = qdev_create(NULL, "cg14"); > + qdev_prop_set_taddr(dev, "vram_addr", vram_base); > + qdev_prop_set_uint16(dev, "width", width); > + qdev_prop_set_uint16(dev, "height", height); > + qdev_init_nofail(dev); > + s = sysbus_from_qdev(dev); > + sysbus_mmio_map(s, 0, ctrl_base); > + sysbus_mmio_map(s, 1, vram_base); > +} > + > /* NCR89C100/MACIO Internal ID register */ > static const uint8_t idreg_data[] = { 0xfe, 0x81, 0x01, 0x03 }; > > @@ -879,6 +896,11 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size, > exit (1); > } > num_vsimms = 0; > + if (hwdef->vsimm[0].vram_base && (graphic_width > 1024 || !hwdef->tcx_base)) { > + cg14_init(hwdef->vsimm[0].reg_base, hwdef->vsimm[0].vram_base, > + graphic_width, graphic_height); > + num_vsimms++; > + } > if (num_vsimms == 0) { > tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height, > graphic_depth); > -- > 1.6.2.2.1669.g7eaf8 > > > >
On Tue, Jul 13, 2010 at 4:26 PM, Bob Breuer <breuerr@mc.net> wrote: > Another preview of the cg14 framebuffer. > > Activate by selecting SS-20 machine and setting width > 1024, i.e. "-M SS-20 -g 1152x900". > Note that NetBSD assumes 1152x900, while OBP also supports 1024x768, 1280x1024, and 1600x1280. > > New since last time: > - All video memory accesses implemented > X under linux now works (uses RGB instead of XRGB space) > - Hooked into qdev > > Todo: > - fix NetBSD display > - add draw_line templates to handle other than 32-bit RGB host displays > - use VGA_DIRTY tracking > - What's the equivalent of stb_p that also sets the dirty bits? You could also write directly and then call cpu_physical_memory_set_dirty(). > - inform OpenBIOS of cg14 framebuffer > - Can we pass "nvalias screen /obio/cgfourteen" to the firmware? > > Bob > > --- > Makefile.target | 1 + > hw/cg14.c | 850 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > hw/sun4m.c | 22 ++ > 3 files changed, 873 insertions(+), 0 deletions(-) > create mode 100644 hw/cg14.c > > diff --git a/Makefile.target b/Makefile.target > index 3ef4666..54a2ae4 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -255,6 +255,7 @@ else > obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o > obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o > obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o > +obj-sparc-y += cg14.o > endif > > obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o > diff --git a/hw/cg14.c b/hw/cg14.c > new file mode 100644 > index 0000000..533dc04 > --- /dev/null > +++ b/hw/cg14.c > @@ -0,0 +1,850 @@ > +/* > + * QEMU CG14 Frame buffer > + * > + * Copyright (c) 2010 Bob Breuer <breuerr@mc.net> > + * > + * 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 "console.h" > +#include "sysbus.h" > +#include "qdev-addr.h" > + > +//#define DEBUG_CG14 > +//#define DEBUG_DAC > +//#define DEBUG_CONFIG > + > +/* > + * Sun CG14 framebuffer (without SX) > + * CG14 = vsimm framebuffer (video ram and dac) > + * SX = pixel processor (acceleration) built into chipset > + * > + * Documentation: not publicly documented by Sun > + * linux driver: drivers/video/cg14.c > + * NetBSD/OpenBSD: src/sys/arch/sparc/dev/cgfourteen* > + * > + * Takes up one memory slot: > + * A[28:26] = slot number (4 to 7) > + * regs: size 0x10000 @ 0x09c000000 (0x80000000 + slot * 64M) > + * vmem: size upto 16MB @ 0x0fc000000 (0xE0000000 + slot * 64M) > + * > + * SS-20 OBP only supports slots 7 (onboard output) and 4 (AVB output) > + * > + * memory map: > + * reg+0x0000 = control registers > + * reg+0x1000 = cursor registers > + * reg+0x2000 = dac registers (ADV7152) > + * reg+0x3000 = xlut > + * reg+0x4000 = clut1 > + * reg+0x5000 = clut2 > + * reg+0x6000 = clut3 (if implemented) > + * reg+0xf000 = autoinc > + * > + * mem+0x0000000 = XBGR (01234567) > + * mem+0x1000000 = BGR (.123.567) writes to X are blocked, reads are ok > + * mem+0x2000000 = X16 (0246) > + * mem+0x2800000 = C16 (1357) > + * mem+0x3000000 = X32 (04) > + * mem+0x3400000 = B32 (15) > + * mem+0x3800000 = G32 (26) > + * mem+0x3c00000 = R32 (37) > + */ > + > +#define CG14_REG_SIZE 0x10000 > +#define CG14_VMEM_SLOTSIZE (64<<20) > + > +#define CG14_MONID_1024x768 0 > +#define CG14_MONID_1600x1280 1 > +#define CG14_MONID_1280x1024 2 > +#define CG14_MONID_1152x900 7 > + > +#define CG14_MONID_DEFAULT CG14_MONID_1024x768 > + > + > +#define CG14_MCR_INTENABLE 0x80 > +#define CG14_MCR_VIDENABLE 0x40 > +#define CG14_MCR_PIXMODE_MASK 0x30 > +#define CG14_MCR_PIXMODE_8 0x00 > +#define CG14_MCR_PIXMODE_16 0x20 /* 8+8 (X16,C16) */ > +#define CG14_MCR_PIXMODE_32 0x30 /* XBGR */ > + > + > +#ifdef DEBUG_CG14 > +#define DPRINTF(fmt, ...) \ > + printf("CG14: " fmt , ## __VA_ARGS__) > +#else > +#define DPRINTF(fmt, ...) > +#endif > + > +#ifdef DEBUG_DAC > +#define DPRINTF_DAC(fmt, ...) \ > + printf("CG14 dac: " fmt , ## __VA_ARGS__) > +#else > +#define DPRINTF_DAC(fmt, ...) > +#endif > + > +#ifdef DEBUG_CONFIG > +#define DPRINTF_CONFIG(fmt, ...) \ > + printf("CG14: " fmt , ## __VA_ARGS__) > +#else > +#define DPRINTF_CONFIG(fmt, ...) > +#endif > + > +#define CG14_INFO(fmt, ...) \ > + do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0) > +#define CG14_ERROR(fmt, ...) \ > + do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0) > + > + > +struct dac_state { > + uint8_t mode; > + uint8_t address; > + int rgb_seq; > +}; > + > +typedef struct CG14State { > + SysBusDevice busdev; > + DisplayState *ds; > + > + target_phys_addr_t vram_addr; > + uint8_t *vram; > + uint32_t vram_size; > + uint32_t vram_amask; > + uint16_t width, height; > + int dirty, size_changed; > + struct { > + uint8_t mcr; > + uint8_t ppr; > + uint8_t msr; > + } ctrl; > + struct dac_state dac; > + struct { > + uint16_t hblank_start; > + uint16_t hblank_clear; > + uint16_t vblank_start; > + uint16_t vblank_clear; > + } timing; > + uint8_t xlut[256]; > + uint32_t clut1[256]; > + uint32_t clut2[256]; > +} CG14State; > + > +static void cg14_screen_dump(void *opaque, const char *filename); > +static void cg14_invalidate_display(void *opaque); > + > +static inline uint32_t bgr_to_rgb(uint32_t bgr) > +{ > + uint32_t rgb; > + > + /* swap r & b */ > + rgb = (bgr & 0x00FF00) > + | (bgr & 0x0000FF) << 16 > + | (bgr & 0xFF0000) >> 16; > + return rgb; > +} > + > +typedef void draw_line_func(const CG14State *s, void *dst, const uint8_t *src); > + > +// TODO: draw_line templates > +static void cg14_draw_line32_rgb32(const CG14State *s, void *dst, const uint8_t *src) > +{ > + unsigned int i; > + unsigned int x, r, g, b; > + uint8_t xlut_val; > + uint32_t dval; > + > + for (i=0; i<s->width; i++) { > + x = src[0]; > + b = src[1]; > + g = src[2]; > + r = src[3]; > + xlut_val = s->xlut[x]; > + src += 4; > + > + if (xlut_val == 0x40) { > + dval = bgr_to_rgb(s->clut1[x]); You could translate s->clut1[] already when the clut registers are written so that this routine could use only table lookup. > + } else { > + /* xlut = 0x00 for true-color */ > + /* possible blending between 2 colors */ > + /* fallback to true-color just to display something if unimplemented */ > + dval = r << 16 | g << 8 | b; > + } > + /* dac lookup ? */ > + > + /* to surface format */ > + //dval = is_bgr ? (abgr & 0xFFFFFF) : bgr_to_rgb(abgr); > + ((uint32_t*)dst)[i] = dval; I think it's better to add uint32_t *p variable and just do *p++ = dval, like TCX does. > + } > +} > + > +static void cg14_draw_line16_rgb32(const CG14State *s, void *dst, const uint8_t *src) > +{ > + unsigned int i; > + unsigned int x, c; > + uint8_t xlut_val; > + uint32_t dval; > + > + for (i=0; i<s->width; i++) { > + x = src[0]; > + c = src[1]; > + xlut_val = s->xlut[x]; > + src += 2; > + > + if (xlut_val == 0x40) { > + dval = bgr_to_rgb(s->clut1[x]); > + } else { > + /* possible blending between 2 colors */ > + /* fallback to green/blue just to display something if unimplemented */ > + dval = x << 8 | c; > + } > + /* dac lookup ? */ > + > + ((uint32_t*)dst)[i] = dval; > + } > +} > + > +static void cg14_draw_line8_rgb32(const CG14State *s, void *dst, const uint8_t *src) > +{ > + int i; > + const uint32_t *clut; > + uint32_t *dst32 = dst; > + > + if (s->ctrl.ppr == 0x40) { > + clut = s->clut1; > + for (i=0; i<s->width; i++) { > + dst32[i] = bgr_to_rgb(clut[src[i]]); > + } > + } else { > + /* FIXME: other xlut modes, just use grayscale ramp for now */ > + for (i=0; i<s->width; i++) { > + dst32[i] = 0x010101 * src[i]; > + } > + } > +} > + > +/* TODO: use VGA_DIRTY_FLAG */ > +static void cg14_update_display(void *opaque) > +{ > + CG14State *s = opaque; > + int h, src_linesize; > + uint8_t *pix; > + uint8_t *data; > + int new_width, new_height; > + draw_line_func * draw_line; > + > + if (s->size_changed) { > + new_width = 4 * (s->timing.hblank_start - s->timing.hblank_clear); > + new_height = s->timing.vblank_start - s->timing.vblank_clear; > + s->size_changed = 0; > + if ((new_width != s->width || new_height != s->height) && new_width > 0 && new_height > 0) { > + s->width = new_width; > + s->height = new_height; > + CG14_INFO("new resolution = %d x %d\n", new_width, new_height); > + qemu_console_resize(s->ds, s->width, s->height); > + s->dirty = 1; > + } > + } > + > + if (!s->dirty || !s->width || !s->height) { > + return; > + } > + > + if (ds_get_bits_per_pixel(s->ds) != 32) { > + CG14_ERROR("cg14_update: FIXME: bpp (%d) != 32, linesize %d\n", > + ds_get_bits_per_pixel(s->ds), ds_get_linesize(s->ds)); > + return; > + } > + // if (is_surface_bgr(s->ds->surface)) > + > + draw_line = NULL; > + src_linesize = s->width; > + if (s->ctrl.mcr & CG14_MCR_VIDENABLE) { > + switch (s->ctrl.mcr & CG14_MCR_PIXMODE_MASK) { > + case CG14_MCR_PIXMODE_32: > + src_linesize *= 4; > + draw_line = cg14_draw_line32_rgb32; > + break; > + case CG14_MCR_PIXMODE_16: > + src_linesize *= 2; > + draw_line = cg14_draw_line16_rgb32; > + break; > + case CG14_MCR_PIXMODE_8: > + draw_line = cg14_draw_line8_rgb32; > + break; > + } > + } > + if (!draw_line) { > + /* blank */ > + memset(ds_get_data(s->ds), 0, ds_get_linesize(s->ds)*ds_get_height(s->ds)); > + s->dirty = 0; > + return; > + } > + > + pix = s->vram; > + data = ds_get_data(s->ds); > + > + for (h=0; h<s->height; h++) { > + draw_line(s, data, pix); > + pix += src_linesize; > + data += ds_get_linesize(s->ds); > + } > + dpy_update(s->ds, 0, 0, s->width, s->height); > + s->dirty = 0; > +} > + > +static void cg14_invalidate_display(void *opaque) > +{ > + CG14State *s = opaque; > + > + s->dirty = 1; > +} > + > +static uint32_t dac_read(struct dac_state *s, unsigned int reg) > +{ > + uint32_t val = 0; > + > + switch (reg) { > + case 0: > + val = s->address; > + break; > + case 1: /* lookup table */ > + s->rgb_seq++; > + break; > + case 2: /* control registers */ > + break; > + case 3: > + val = s->mode; > + break; > + } > + DPRINTF_DAC("read %02x from dac reg %d\n", val, reg); > + return val; > +} > + > +static void dac_write(struct dac_state *s, unsigned int reg, unsigned int val) > +{ > + switch (reg) { > + case 0: /* address register */ > + DPRINTF_DAC("write address %02x\n", val); > + s->address = val; > + s->rgb_seq = 0; > + break; > + case 1: /* lookup table */ > + DPRINTF_DAC("write %02x to lookup table\n", val); > + s->rgb_seq++; > + break; > + case 2: /* control registers */ > + DPRINTF_DAC("write %02x to control reg %d\n", val, s->address); > + switch (s->address) { > + default: > + break; > + } > + break; > + case 3: /* mode register */ > + DPRINTF_DAC("write mode %02x (%d bit DAC, %d bit bus)\n", > + val, (val & 2) ? 10 : 8, (val & 4) ? 10 : 8); > + if (!val & 0x01) { > + // reset the dac > + s->rgb_seq = 0; > + } > + s->mode = val; > + break; > + } > +} > + > +static uint32_t cg14_reg_readb(void *opaque, target_phys_addr_t addr) > +{ > + CG14State *s = opaque; > + uint32_t val; > + uint32_t i; > + > + if ((addr & 0xfc00) == 0x2000) { > + i = (addr & 0x300) >> 8; > + return dac_read(&s->dac, i); > + } > + > + switch (addr) { > + case 0x0000: > + val = s->ctrl.mcr; > + break; > + case 0x0001: > + val = s->ctrl.ppr; > + break; > + case 0x0004: /* status ? */ > + val = s->ctrl.msr; > + break; > + case 0x0006: /* hw version */ > + //val = 0x00; /* old version */ > + val = 0x30; > + break; > + default: > + val = 0; > + CG14_INFO("readb from reg %x\n", (int)addr); > + break; > + } > + > + return val; > +} > + > +static void cg14_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) > +{ > + CG14State *s = opaque; > + uint32_t i; > + > + if ((addr & 0xfc00) == 0x2000) { > + i = (addr & 0x300) >> 8; > + dac_write(&s->dac, i, val); > + return; > + } > + if ((addr & 0xff00) == 0x3000) { > + /* xlut */ > + i = addr & 0xff; > + if (s->xlut[i] != val) { > + s->dirty = 1; > + s->xlut[i] = val; > + if (val && val != 0x40) > + CG14_ERROR("writeb xlut[%d] = %02x\n", i, val); > + } > + return; > + } > + > + s->dirty = 1; > + > + switch (addr) { > + case 0x0000: > + s->ctrl.mcr = val; > + DPRINTF_CONFIG("write %02x to MCR\n", val); > + break; > + case 0x0001: > + s->ctrl.ppr = val & 0xF0; > + break; > + case 0x0007: > + /* clock control (ICS1562AM-001) */ > + DPRINTF("write %02x to clock control\n", val); > + break; > + default: > + CG14_ERROR("writeb %02x to reg %x\n", val, (int)addr); > + break; > + } > +} > + > +static uint32_t cg14_reg_readw(void *opaque, target_phys_addr_t addr) > +{ > + CG14State *s = opaque; > + uint32_t val; > + > + switch (addr) { > + case 0x0018: > + val = s->timing.hblank_start; > + break; > + case 0x001a: > + val = s->timing.hblank_clear; > + break; > + case 0x0022: > + val = s->timing.vblank_start; > + break; > + case 0x0024: > + val = s->timing.vblank_clear; > + break; > + default: > + val = 0; > + CG14_INFO("readw from reg %x\n", (int)addr); > + break; > + } > + > + return val; > +} > + > +static void cg14_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val) > +{ > + CG14State *s = opaque; > + > + DPRINTF_CONFIG("writew %04x to reg %x\n", val, (int)addr); > + > + /* timing registers are 16bit */ > + > + switch (addr) { > + case 0x0018: > + s->timing.hblank_start = val; > + break; > + case 0x001a: > + s->timing.hblank_clear = val; > + s->size_changed = 1; If the size changes, the entire display is invalid. > + break; > + case 0x0022: > + s->timing.vblank_start = val; > + break; > + case 0x0024: > + s->timing.vblank_clear = val; > + s->size_changed = 1; > + break; > + case 0x001c: /* hsync_start */ > + case 0x001e: /* hsync_clear */ > + case 0x0020: /* csync_clear */ > + case 0x0026: /* vsync_start */ > + case 0x0028: /* vsync_clear */ > + default: > + break; > + } > +} > + > +static uint32_t cg14_reg_readl(void *opaque, target_phys_addr_t addr) > +{ > + CG14State *s = opaque; > + uint32_t val; > + uint32_t i; > + > + i = (addr & 0x3ff) >> 2; > + switch (addr & 0xfc00) { > + case 0x4000: > + val = s->clut1[i]; > + break; > + case 0x5000: > + val = s->clut2[i]; > + break; > + default: > + val = 0; > + CG14_ERROR("readl %08x from reg %x\n", val, (int)addr); > + break; > + } > + > + return val; > +} > + > +static void cg14_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val) > +{ > + CG14State *s = opaque; > + uint32_t i; > + > + s->dirty = 1; > + > + i = addr & 0x3ff; > + switch (addr & 0xfc00) { > + case 0x3000: > + if (i < 256) { > + s->xlut[i+0] = (uint8_t)(val >> 24); > + s->xlut[i+1] = (uint8_t)(val >> 16); > + s->xlut[i+2] = (uint8_t)(val >> 8); > + s->xlut[i+3] = (uint8_t)val; > + } > + break; > + case 0x4000: > + s->clut1[i >> 2] = val; > + break; > + case 0x5000: > + s->clut2[i >> 2] = val; > + break; > + default: > + CG14_ERROR("writel %08x to reg %x\n", val, (int)addr); > + break; > + } > +} > + > +static CPUReadMemoryFunc *cg14_reg_read[3] = { Please add 'const', also in other tables. > + cg14_reg_readb, > + cg14_reg_readw, > + cg14_reg_readl, > +}; > + > +static CPUWriteMemoryFunc *cg14_reg_write[3] = { > + cg14_reg_writeb, > + cg14_reg_writew, > + cg14_reg_writel, > +}; > + > +static uint32_t cg14_vram_readb(void *opaque, target_phys_addr_t addr) > +{ > + CG14State *s = opaque; > + uint32_t offset; > + uint32_t val = 0; > + > + switch (addr & 0x3000000) { > + case 0x0000000: > + case 0x1000000: > + offset = addr & s->vram_amask; > + val = ldub_p(s->vram+offset); > + break; > + case 0x2000000: > + offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1); > + val = ldub_p(s->vram+offset); > + break; > + case 0x3000000: > + offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3); > + val = ldub_p(s->vram+offset); > + break; > + } > + > + return val; > +} > + > +static void cg14_vram_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) > +{ > + CG14State *s = opaque; > + uint32_t offset; > + > + s->dirty = 1; > + > + switch (addr & 0x3000000) { > + case 0x0000000: > + offset = addr & s->vram_amask; > + stb_p(s->vram+offset, val); > + break; > + case 0x1000000: > + offset = addr & s->vram_amask; > + /* block writes to X */ > + if (offset & 3) { > + stb_p(s->vram+offset, val); > + } > + break; > + case 0x2000000: > + offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1); > + stb_p(s->vram+offset, val); > + break; > + case 0x3000000: > + offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3); > + stb_p(s->vram+offset, val); > + break; > + } > +} > + > +static uint32_t cg14_vram_readw(void *opaque, target_phys_addr_t addr) > +{ > + uint32_t val; > + > + val = cg14_vram_readb(opaque, addr) << 8; > + val |= cg14_vram_readb(opaque, addr+1); > + > + return val; > +} > + > +static void cg14_vram_writew(void *opaque, target_phys_addr_t addr, uint32_t val) > +{ > + cg14_vram_writeb(opaque, addr, val >> 8); > + cg14_vram_writeb(opaque, addr+1, val & 0xff); > +} > + > +static uint32_t cg14_vram_readl(void *opaque, target_phys_addr_t addr) > +{ > + CG14State *s = opaque; > + uint32_t offset; > + uint32_t val = 0; > + > + switch (addr & 0x3000000) { > + case 0x0000000: > + case 0x1000000: > + offset = addr & s->vram_amask; > + val = ldl_be_p(s->vram+offset); > + break; > + case 0x2000000: > + offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1); > + val = ldub_p(s->vram+offset+0) << 24; > + val |= ldub_p(s->vram+offset+2) << 16; > + val |= ldub_p(s->vram+offset+4) << 8; > + val |= ldub_p(s->vram+offset+6); > + break; > + case 0x3000000: > + offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3); > + val = ldub_p(s->vram+offset+0) << 24; > + val |= ldub_p(s->vram+offset+4) << 16; > + val |= ldub_p(s->vram+offset+8) << 8; > + val |= ldub_p(s->vram+offset+12); > + break; > + } > + > + return val; > +} > + > +static void cg14_vram_writel(void *opaque, target_phys_addr_t addr, uint32_t val) > +{ > + CG14State *s = opaque; > + uint32_t offset; > + > + s->dirty = 1; > + > + switch (addr & 0x3000000) { > + case 0x0000000: > + offset = addr & s->vram_amask; > + stl_be_p(s->vram+offset, val); > + break; > + case 0x1000000: > + offset = addr & s->vram_amask; > + /* block writes to X */ > + stb_p(s->vram+offset+1, val >> 16); > + stb_p(s->vram+offset+2, val >> 8); > + stb_p(s->vram+offset+3, val); > + break; > + case 0x2000000: > + offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1); > + stb_p(s->vram+offset+0, val >> 24); > + stb_p(s->vram+offset+2, val >> 16); > + stb_p(s->vram+offset+4, val >> 8); > + stb_p(s->vram+offset+6, val); > + break; > + case 0x3000000: > + offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3); > + stb_p(s->vram+offset+0, val >> 24); > + stb_p(s->vram+offset+4, val >> 16); > + stb_p(s->vram+offset+8, val >> 8); > + stb_p(s->vram+offset+12, val); > + break; > + } > +} > + > +static CPUReadMemoryFunc *cg14_vram_read[3] = { > + cg14_vram_readb, > + cg14_vram_readw, > + cg14_vram_readl, > +}; > + > +static CPUWriteMemoryFunc *cg14_vram_write[3] = { > + cg14_vram_writeb, > + cg14_vram_writew, > + cg14_vram_writel, > +}; > + > + > +static void cg14_set_monitor_id(CG14State *s) > +{ > + uint8_t id; > + > + /* pick something close, used as a default by Sun's OBP */ > + if (s->width >= 1600) { > + id = CG14_MONID_1600x1280; > + } else if (s->width >= 1280) { > + id = CG14_MONID_1280x1024; > + } else if (s->width >= 1152) { > + id = CG14_MONID_1152x900; > + } else if (s->width >= 1024) { > + id = CG14_MONID_1024x768; > + } else { > + id = CG14_MONID_DEFAULT; > + } > + > + /* monitor code in bits 1..3 */ > + s->ctrl.msr = id << 1; > +} > + > +static int cg14_init1(SysBusDevice *dev) > +{ > + CG14State *s = FROM_SYSBUS(CG14State, dev); > + ram_addr_t vram_offset; > + uint8_t *vram; > + int ctrl_memory, vram_memory; > + > + vram_offset = qemu_ram_alloc(NULL, "cg14.vram", s->vram_size); > + vram = qemu_get_ram_ptr(vram_offset); > + > + s->vram = vram; > + s->vram_amask = s->vram_size - 1; > + > + ctrl_memory = cpu_register_io_memory(cg14_reg_read, cg14_reg_write, s); > + sysbus_init_mmio(dev, CG14_REG_SIZE, ctrl_memory); > + > + /* TODO: register first vram mapping as ram with dirty tracking */ > + vram_memory = cpu_register_io_memory(cg14_vram_read, cg14_vram_write, s); > + sysbus_init_mmio(dev, CG14_VMEM_SLOTSIZE, vram_memory); > + > + s->ds = graphic_console_init(cg14_update_display, > + cg14_invalidate_display, > + cg14_screen_dump, NULL, s); > + > + cg14_set_monitor_id(s); > + > + qemu_console_resize(s->ds, s->width, s->height); > + return 0; > +} > + > +/* save to file */ > +static void cg14_screen_dump(void *opaque, const char *filename) > +{ > + CG14State *s = opaque; > + FILE *f; > + int y, src_linesize, dst_linesize; > + void *buf; > + uint8_t *pix; > + draw_line_func * draw_line = NULL; > + > + switch (s->ctrl.mcr & CG14_MCR_PIXMODE_MASK) { > + case CG14_MCR_PIXMODE_32: > + src_linesize = s->width * 4; > + // draw_line = cg14_draw_line32_bgr24; > + break; > + case CG14_MCR_PIXMODE_16: > + src_linesize = s->width * 2; > + // draw_line = cg14_draw_line16_bgr24; > + break; > + case CG14_MCR_PIXMODE_8: > + src_linesize = s->width; > + // draw_line = cg14_draw_line8_bgr24; > + break; > + default: > + /* blank */ > + return; > + } > + > + f = fopen(filename, "wb"); > + if (!f) { > + return; > + } > + fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); > + > + dst_linesize = s->width * 3; > + buf = qemu_mallocz(dst_linesize); > + pix = s->vram; > + > + for (y=0; y<s->height; y++) { > + if (draw_line) Missing braces. > + draw_line(s, buf, pix); > + fwrite(buf, 1, dst_linesize, f); > + pix += src_linesize; > + } > + > + qemu_free(buf); > + fclose(f); > +} > + > +static void cg14_reset(DeviceState *d) > +{ > + CG14State *s = container_of(d, CG14State, busdev.qdev); > + > + /* set to 8bpp so last prom output might be visible */ > + s->ctrl.mcr = CG14_MCR_VIDENABLE | CG14_MCR_PIXMODE_8; > + s->dirty = 1; > +} > + > +static SysBusDeviceInfo cg14_info = { > + .init = cg14_init1, > + .qdev.name = "cg14", > + .qdev.desc = "Sun CG14 Framebuffer", > + .qdev.size = sizeof(CG14State), > + .qdev.reset = cg14_reset, > + .qdev.props = (Property[]) { > + DEFINE_PROP_TADDR("vram_addr", CG14State, vram_addr, -1), > + DEFINE_PROP_HEX32("vram_size", CG14State, vram_size, 0x800000), > + DEFINE_PROP_UINT16("width", CG14State, width, 0), > + DEFINE_PROP_UINT16("height", CG14State, height, 0), > + DEFINE_PROP_END_OF_LIST(), > + } > +}; > + > +static void cg14_register_devices(void) > +{ > + sysbus_register_withprop(&cg14_info); > +} > + > +device_init(cg14_register_devices) > diff --git a/hw/sun4m.c b/hw/sun4m.c > index e7a4cf6..0665e1f 100644 > --- a/hw/sun4m.c > +++ b/hw/sun4m.c > @@ -577,6 +577,23 @@ static void tcx_init(target_phys_addr_t addr, int vram_size, int width, > } > } > > +static void cg14_init(target_phys_addr_t ctrl_base, > + target_phys_addr_t vram_base, > + int width, int height) > +{ > + DeviceState *dev; > + SysBusDevice *s; > + > + dev = qdev_create(NULL, "cg14"); > + qdev_prop_set_taddr(dev, "vram_addr", vram_base); > + qdev_prop_set_uint16(dev, "width", width); > + qdev_prop_set_uint16(dev, "height", height); > + qdev_init_nofail(dev); > + s = sysbus_from_qdev(dev); > + sysbus_mmio_map(s, 0, ctrl_base); > + sysbus_mmio_map(s, 1, vram_base); > +} > + > /* NCR89C100/MACIO Internal ID register */ > static const uint8_t idreg_data[] = { 0xfe, 0x81, 0x01, 0x03 }; > > @@ -879,6 +896,11 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size, > exit (1); > } > num_vsimms = 0; > + if (hwdef->vsimm[0].vram_base && (graphic_width > 1024 || !hwdef->tcx_base)) { > + cg14_init(hwdef->vsimm[0].reg_base, hwdef->vsimm[0].vram_base, > + graphic_width, graphic_height); > + num_vsimms++; > + } > if (num_vsimms == 0) { > tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height, > graphic_depth); > -- > 1.6.2.2.1669.g7eaf8 > > > >
diff --git a/Makefile.target b/Makefile.target index 3ef4666..54a2ae4 100644 --- a/Makefile.target +++ b/Makefile.target @@ -255,6 +255,7 @@ else obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o +obj-sparc-y += cg14.o endif obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o diff --git a/hw/cg14.c b/hw/cg14.c new file mode 100644 index 0000000..533dc04 --- /dev/null +++ b/hw/cg14.c @@ -0,0 +1,850 @@ +/* + * QEMU CG14 Frame buffer + * + * Copyright (c) 2010 Bob Breuer <breuerr@mc.net> + * + * 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 "console.h" +#include "sysbus.h" +#include "qdev-addr.h" + +//#define DEBUG_CG14 +//#define DEBUG_DAC +//#define DEBUG_CONFIG + +/* + * Sun CG14 framebuffer (without SX) + * CG14 = vsimm framebuffer (video ram and dac) + * SX = pixel processor (acceleration) built into chipset + * + * Documentation: not publicly documented by Sun + * linux driver: drivers/video/cg14.c + * NetBSD/OpenBSD: src/sys/arch/sparc/dev/cgfourteen* + * + * Takes up one memory slot: + * A[28:26] = slot number (4 to 7) + * regs: size 0x10000 @ 0x09c000000 (0x80000000 + slot * 64M) + * vmem: size upto 16MB @ 0x0fc000000 (0xE0000000 + slot * 64M) + * + * SS-20 OBP only supports slots 7 (onboard output) and 4 (AVB output) + * + * memory map: + * reg+0x0000 = control registers + * reg+0x1000 = cursor registers + * reg+0x2000 = dac registers (ADV7152) + * reg+0x3000 = xlut + * reg+0x4000 = clut1 + * reg+0x5000 = clut2 + * reg+0x6000 = clut3 (if implemented) + * reg+0xf000 = autoinc + * + * mem+0x0000000 = XBGR (01234567) + * mem+0x1000000 = BGR (.123.567) writes to X are blocked, reads are ok + * mem+0x2000000 = X16 (0246) + * mem+0x2800000 = C16 (1357) + * mem+0x3000000 = X32 (04) + * mem+0x3400000 = B32 (15) + * mem+0x3800000 = G32 (26) + * mem+0x3c00000 = R32 (37) + */ + +#define CG14_REG_SIZE 0x10000 +#define CG14_VMEM_SLOTSIZE (64<<20) + +#define CG14_MONID_1024x768 0 +#define CG14_MONID_1600x1280 1 +#define CG14_MONID_1280x1024 2 +#define CG14_MONID_1152x900 7 + +#define CG14_MONID_DEFAULT CG14_MONID_1024x768 + + +#define CG14_MCR_INTENABLE 0x80 +#define CG14_MCR_VIDENABLE 0x40 +#define CG14_MCR_PIXMODE_MASK 0x30 +#define CG14_MCR_PIXMODE_8 0x00 +#define CG14_MCR_PIXMODE_16 0x20 /* 8+8 (X16,C16) */ +#define CG14_MCR_PIXMODE_32 0x30 /* XBGR */ + + +#ifdef DEBUG_CG14 +#define DPRINTF(fmt, ...) \ + printf("CG14: " fmt , ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) +#endif + +#ifdef DEBUG_DAC +#define DPRINTF_DAC(fmt, ...) \ + printf("CG14 dac: " fmt , ## __VA_ARGS__) +#else +#define DPRINTF_DAC(fmt, ...) +#endif + +#ifdef DEBUG_CONFIG +#define DPRINTF_CONFIG(fmt, ...) \ + printf("CG14: " fmt , ## __VA_ARGS__) +#else +#define DPRINTF_CONFIG(fmt, ...) +#endif + +#define CG14_INFO(fmt, ...) \ + do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0) +#define CG14_ERROR(fmt, ...) \ + do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0) + + +struct dac_state { + uint8_t mode; + uint8_t address; + int rgb_seq; +}; + +typedef struct CG14State { + SysBusDevice busdev; + DisplayState *ds; + + target_phys_addr_t vram_addr; + uint8_t *vram; + uint32_t vram_size; + uint32_t vram_amask; + uint16_t width, height; + int dirty, size_changed; + struct { + uint8_t mcr; + uint8_t ppr; + uint8_t msr; + } ctrl; + struct dac_state dac; + struct { + uint16_t hblank_start; + uint16_t hblank_clear; + uint16_t vblank_start; + uint16_t vblank_clear; + } timing; + uint8_t xlut[256]; + uint32_t clut1[256]; + uint32_t clut2[256]; +} CG14State; + +static void cg14_screen_dump(void *opaque, const char *filename); +static void cg14_invalidate_display(void *opaque); + +static inline uint32_t bgr_to_rgb(uint32_t bgr) +{ + uint32_t rgb; + + /* swap r & b */ + rgb = (bgr & 0x00FF00) + | (bgr & 0x0000FF) << 16 + | (bgr & 0xFF0000) >> 16; + return rgb; +} + +typedef void draw_line_func(const CG14State *s, void *dst, const uint8_t *src); + +// TODO: draw_line templates +static void cg14_draw_line32_rgb32(const CG14State *s, void *dst, const uint8_t *src) +{ + unsigned int i; + unsigned int x, r, g, b; + uint8_t xlut_val; + uint32_t dval; + + for (i=0; i<s->width; i++) { + x = src[0]; + b = src[1]; + g = src[2]; + r = src[3]; + xlut_val = s->xlut[x]; + src += 4; + + if (xlut_val == 0x40) { + dval = bgr_to_rgb(s->clut1[x]); + } else { + /* xlut = 0x00 for true-color */ + /* possible blending between 2 colors */ + /* fallback to true-color just to display something if unimplemented */ + dval = r << 16 | g << 8 | b; + } + /* dac lookup ? */ + + /* to surface format */ + //dval = is_bgr ? (abgr & 0xFFFFFF) : bgr_to_rgb(abgr); + ((uint32_t*)dst)[i] = dval; + } +} + +static void cg14_draw_line16_rgb32(const CG14State *s, void *dst, const uint8_t *src) +{ + unsigned int i; + unsigned int x, c; + uint8_t xlut_val; + uint32_t dval; + + for (i=0; i<s->width; i++) { + x = src[0]; + c = src[1]; + xlut_val = s->xlut[x]; + src += 2; + + if (xlut_val == 0x40) { + dval = bgr_to_rgb(s->clut1[x]); + } else { + /* possible blending between 2 colors */ + /* fallback to green/blue just to display something if unimplemented */ + dval = x << 8 | c; + } + /* dac lookup ? */ + + ((uint32_t*)dst)[i] = dval; + } +} + +static void cg14_draw_line8_rgb32(const CG14State *s, void *dst, const uint8_t *src) +{ + int i; + const uint32_t *clut; + uint32_t *dst32 = dst; + + if (s->ctrl.ppr == 0x40) { + clut = s->clut1; + for (i=0; i<s->width; i++) { + dst32[i] = bgr_to_rgb(clut[src[i]]); + } + } else { + /* FIXME: other xlut modes, just use grayscale ramp for now */ + for (i=0; i<s->width; i++) { + dst32[i] = 0x010101 * src[i]; + } + } +} + +/* TODO: use VGA_DIRTY_FLAG */ +static void cg14_update_display(void *opaque) +{ + CG14State *s = opaque; + int h, src_linesize; + uint8_t *pix; + uint8_t *data; + int new_width, new_height; + draw_line_func * draw_line; + + if (s->size_changed) { + new_width = 4 * (s->timing.hblank_start - s->timing.hblank_clear); + new_height = s->timing.vblank_start - s->timing.vblank_clear; + s->size_changed = 0; + if ((new_width != s->width || new_height != s->height) && new_width > 0 && new_height > 0) { + s->width = new_width; + s->height = new_height; + CG14_INFO("new resolution = %d x %d\n", new_width, new_height); + qemu_console_resize(s->ds, s->width, s->height); + s->dirty = 1; + } + } + + if (!s->dirty || !s->width || !s->height) { + return; + } + + if (ds_get_bits_per_pixel(s->ds) != 32) { + CG14_ERROR("cg14_update: FIXME: bpp (%d) != 32, linesize %d\n", + ds_get_bits_per_pixel(s->ds), ds_get_linesize(s->ds)); + return; + } + // if (is_surface_bgr(s->ds->surface)) + + draw_line = NULL; + src_linesize = s->width; + if (s->ctrl.mcr & CG14_MCR_VIDENABLE) { + switch (s->ctrl.mcr & CG14_MCR_PIXMODE_MASK) { + case CG14_MCR_PIXMODE_32: + src_linesize *= 4; + draw_line = cg14_draw_line32_rgb32; + break; + case CG14_MCR_PIXMODE_16: + src_linesize *= 2; + draw_line = cg14_draw_line16_rgb32; + break; + case CG14_MCR_PIXMODE_8: + draw_line = cg14_draw_line8_rgb32; + break; + } + } + if (!draw_line) { + /* blank */ + memset(ds_get_data(s->ds), 0, ds_get_linesize(s->ds)*ds_get_height(s->ds)); + s->dirty = 0; + return; + } + + pix = s->vram; + data = ds_get_data(s->ds); + + for (h=0; h<s->height; h++) { + draw_line(s, data, pix); + pix += src_linesize; + data += ds_get_linesize(s->ds); + } + dpy_update(s->ds, 0, 0, s->width, s->height); + s->dirty = 0; +} + +static void cg14_invalidate_display(void *opaque) +{ + CG14State *s = opaque; + + s->dirty = 1; +} + +static uint32_t dac_read(struct dac_state *s, unsigned int reg) +{ + uint32_t val = 0; + + switch (reg) { + case 0: + val = s->address; + break; + case 1: /* lookup table */ + s->rgb_seq++; + break; + case 2: /* control registers */ + break; + case 3: + val = s->mode; + break; + } + DPRINTF_DAC("read %02x from dac reg %d\n", val, reg); + return val; +} + +static void dac_write(struct dac_state *s, unsigned int reg, unsigned int val) +{ + switch (reg) { + case 0: /* address register */ + DPRINTF_DAC("write address %02x\n", val); + s->address = val; + s->rgb_seq = 0; + break; + case 1: /* lookup table */ + DPRINTF_DAC("write %02x to lookup table\n", val); + s->rgb_seq++; + break; + case 2: /* control registers */ + DPRINTF_DAC("write %02x to control reg %d\n", val, s->address); + switch (s->address) { + default: + break; + } + break; + case 3: /* mode register */ + DPRINTF_DAC("write mode %02x (%d bit DAC, %d bit bus)\n", + val, (val & 2) ? 10 : 8, (val & 4) ? 10 : 8); + if (!val & 0x01) { + // reset the dac + s->rgb_seq = 0; + } + s->mode = val; + break; + } +} + +static uint32_t cg14_reg_readb(void *opaque, target_phys_addr_t addr) +{ + CG14State *s = opaque; + uint32_t val; + uint32_t i; + + if ((addr & 0xfc00) == 0x2000) { + i = (addr & 0x300) >> 8; + return dac_read(&s->dac, i); + } + + switch (addr) { + case 0x0000: + val = s->ctrl.mcr; + break; + case 0x0001: + val = s->ctrl.ppr; + break; + case 0x0004: /* status ? */ + val = s->ctrl.msr; + break; + case 0x0006: /* hw version */ + //val = 0x00; /* old version */ + val = 0x30; + break; + default: + val = 0; + CG14_INFO("readb from reg %x\n", (int)addr); + break; + } + + return val; +} + +static void cg14_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + CG14State *s = opaque; + uint32_t i; + + if ((addr & 0xfc00) == 0x2000) { + i = (addr & 0x300) >> 8; + dac_write(&s->dac, i, val); + return; + } + if ((addr & 0xff00) == 0x3000) { + /* xlut */ + i = addr & 0xff; + if (s->xlut[i] != val) { + s->dirty = 1; + s->xlut[i] = val; + if (val && val != 0x40) + CG14_ERROR("writeb xlut[%d] = %02x\n", i, val); + } + return; + } + + s->dirty = 1; + + switch (addr) { + case 0x0000: + s->ctrl.mcr = val; + DPRINTF_CONFIG("write %02x to MCR\n", val); + break; + case 0x0001: + s->ctrl.ppr = val & 0xF0; + break; + case 0x0007: + /* clock control (ICS1562AM-001) */ + DPRINTF("write %02x to clock control\n", val); + break; + default: + CG14_ERROR("writeb %02x to reg %x\n", val, (int)addr); + break; + } +} + +static uint32_t cg14_reg_readw(void *opaque, target_phys_addr_t addr) +{ + CG14State *s = opaque; + uint32_t val; + + switch (addr) { + case 0x0018: + val = s->timing.hblank_start; + break; + case 0x001a: + val = s->timing.hblank_clear; + break; + case 0x0022: + val = s->timing.vblank_start; + break; + case 0x0024: + val = s->timing.vblank_clear; + break; + default: + val = 0; + CG14_INFO("readw from reg %x\n", (int)addr); + break; + } + + return val; +} + +static void cg14_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + CG14State *s = opaque; + + DPRINTF_CONFIG("writew %04x to reg %x\n", val, (int)addr); + + /* timing registers are 16bit */ + + switch (addr) { + case 0x0018: + s->timing.hblank_start = val; + break; + case 0x001a: + s->timing.hblank_clear = val; + s->size_changed = 1; + break; + case 0x0022: + s->timing.vblank_start = val; + break; + case 0x0024: + s->timing.vblank_clear = val; + s->size_changed = 1; + break; + case 0x001c: /* hsync_start */ + case 0x001e: /* hsync_clear */ + case 0x0020: /* csync_clear */ + case 0x0026: /* vsync_start */ + case 0x0028: /* vsync_clear */ + default: + break; + } +} + +static uint32_t cg14_reg_readl(void *opaque, target_phys_addr_t addr) +{ + CG14State *s = opaque; + uint32_t val; + uint32_t i; + + i = (addr & 0x3ff) >> 2; + switch (addr & 0xfc00) { + case 0x4000: + val = s->clut1[i]; + break; + case 0x5000: + val = s->clut2[i]; + break; + default: + val = 0; + CG14_ERROR("readl %08x from reg %x\n", val, (int)addr); + break; + } + + return val; +} + +static void cg14_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + CG14State *s = opaque; + uint32_t i; + + s->dirty = 1; + + i = addr & 0x3ff; + switch (addr & 0xfc00) { + case 0x3000: + if (i < 256) { + s->xlut[i+0] = (uint8_t)(val >> 24); + s->xlut[i+1] = (uint8_t)(val >> 16); + s->xlut[i+2] = (uint8_t)(val >> 8); + s->xlut[i+3] = (uint8_t)val; + } + break; + case 0x4000: + s->clut1[i >> 2] = val; + break; + case 0x5000: + s->clut2[i >> 2] = val; + break; + default: + CG14_ERROR("writel %08x to reg %x\n", val, (int)addr); + break; + } +} + +static CPUReadMemoryFunc *cg14_reg_read[3] = { + cg14_reg_readb, + cg14_reg_readw, + cg14_reg_readl, +}; + +static CPUWriteMemoryFunc *cg14_reg_write[3] = { + cg14_reg_writeb, + cg14_reg_writew, + cg14_reg_writel, +}; + +static uint32_t cg14_vram_readb(void *opaque, target_phys_addr_t addr) +{ + CG14State *s = opaque; + uint32_t offset; + uint32_t val = 0; + + switch (addr & 0x3000000) { + case 0x0000000: + case 0x1000000: + offset = addr & s->vram_amask; + val = ldub_p(s->vram+offset); + break; + case 0x2000000: + offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1); + val = ldub_p(s->vram+offset); + break; + case 0x3000000: + offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3); + val = ldub_p(s->vram+offset); + break; + } + + return val; +} + +static void cg14_vram_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + CG14State *s = opaque; + uint32_t offset; + + s->dirty = 1; + + switch (addr & 0x3000000) { + case 0x0000000: + offset = addr & s->vram_amask; + stb_p(s->vram+offset, val); + break; + case 0x1000000: + offset = addr & s->vram_amask; + /* block writes to X */ + if (offset & 3) { + stb_p(s->vram+offset, val); + } + break; + case 0x2000000: + offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1); + stb_p(s->vram+offset, val); + break; + case 0x3000000: + offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3); + stb_p(s->vram+offset, val); + break; + } +} + +static uint32_t cg14_vram_readw(void *opaque, target_phys_addr_t addr) +{ + uint32_t val; + + val = cg14_vram_readb(opaque, addr) << 8; + val |= cg14_vram_readb(opaque, addr+1); + + return val; +} + +static void cg14_vram_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + cg14_vram_writeb(opaque, addr, val >> 8); + cg14_vram_writeb(opaque, addr+1, val & 0xff); +} + +static uint32_t cg14_vram_readl(void *opaque, target_phys_addr_t addr) +{ + CG14State *s = opaque; + uint32_t offset; + uint32_t val = 0; + + switch (addr & 0x3000000) { + case 0x0000000: + case 0x1000000: + offset = addr & s->vram_amask; + val = ldl_be_p(s->vram+offset); + break; + case 0x2000000: + offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1); + val = ldub_p(s->vram+offset+0) << 24; + val |= ldub_p(s->vram+offset+2) << 16; + val |= ldub_p(s->vram+offset+4) << 8; + val |= ldub_p(s->vram+offset+6); + break; + case 0x3000000: + offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3); + val = ldub_p(s->vram+offset+0) << 24; + val |= ldub_p(s->vram+offset+4) << 16; + val |= ldub_p(s->vram+offset+8) << 8; + val |= ldub_p(s->vram+offset+12); + break; + } + + return val; +} + +static void cg14_vram_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + CG14State *s = opaque; + uint32_t offset; + + s->dirty = 1; + + switch (addr & 0x3000000) { + case 0x0000000: + offset = addr & s->vram_amask; + stl_be_p(s->vram+offset, val); + break; + case 0x1000000: + offset = addr & s->vram_amask; + /* block writes to X */ + stb_p(s->vram+offset+1, val >> 16); + stb_p(s->vram+offset+2, val >> 8); + stb_p(s->vram+offset+3, val); + break; + case 0x2000000: + offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1); + stb_p(s->vram+offset+0, val >> 24); + stb_p(s->vram+offset+2, val >> 16); + stb_p(s->vram+offset+4, val >> 8); + stb_p(s->vram+offset+6, val); + break; + case 0x3000000: + offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3); + stb_p(s->vram+offset+0, val >> 24); + stb_p(s->vram+offset+4, val >> 16); + stb_p(s->vram+offset+8, val >> 8); + stb_p(s->vram+offset+12, val); + break; + } +} + +static CPUReadMemoryFunc *cg14_vram_read[3] = { + cg14_vram_readb, + cg14_vram_readw, + cg14_vram_readl, +}; + +static CPUWriteMemoryFunc *cg14_vram_write[3] = { + cg14_vram_writeb, + cg14_vram_writew, + cg14_vram_writel, +}; + + +static void cg14_set_monitor_id(CG14State *s) +{ + uint8_t id; + + /* pick something close, used as a default by Sun's OBP */ + if (s->width >= 1600) { + id = CG14_MONID_1600x1280; + } else if (s->width >= 1280) { + id = CG14_MONID_1280x1024; + } else if (s->width >= 1152) { + id = CG14_MONID_1152x900; + } else if (s->width >= 1024) { + id = CG14_MONID_1024x768; + } else { + id = CG14_MONID_DEFAULT; + } + + /* monitor code in bits 1..3 */ + s->ctrl.msr = id << 1; +} + +static int cg14_init1(SysBusDevice *dev) +{ + CG14State *s = FROM_SYSBUS(CG14State, dev); + ram_addr_t vram_offset; + uint8_t *vram; + int ctrl_memory, vram_memory; + + vram_offset = qemu_ram_alloc(NULL, "cg14.vram", s->vram_size); + vram = qemu_get_ram_ptr(vram_offset); + + s->vram = vram; + s->vram_amask = s->vram_size - 1; + + ctrl_memory = cpu_register_io_memory(cg14_reg_read, cg14_reg_write, s); + sysbus_init_mmio(dev, CG14_REG_SIZE, ctrl_memory); + + /* TODO: register first vram mapping as ram with dirty tracking */ + vram_memory = cpu_register_io_memory(cg14_vram_read, cg14_vram_write, s); + sysbus_init_mmio(dev, CG14_VMEM_SLOTSIZE, vram_memory); + + s->ds = graphic_console_init(cg14_update_display, + cg14_invalidate_display, + cg14_screen_dump, NULL, s); + + cg14_set_monitor_id(s); + + qemu_console_resize(s->ds, s->width, s->height); + return 0; +} + +/* save to file */ +static void cg14_screen_dump(void *opaque, const char *filename) +{ + CG14State *s = opaque; + FILE *f; + int y, src_linesize, dst_linesize; + void *buf; + uint8_t *pix; + draw_line_func * draw_line = NULL; + + switch (s->ctrl.mcr & CG14_MCR_PIXMODE_MASK) { + case CG14_MCR_PIXMODE_32: + src_linesize = s->width * 4; + // draw_line = cg14_draw_line32_bgr24; + break; + case CG14_MCR_PIXMODE_16: + src_linesize = s->width * 2; + // draw_line = cg14_draw_line16_bgr24; + break; + case CG14_MCR_PIXMODE_8: + src_linesize = s->width; + // draw_line = cg14_draw_line8_bgr24; + break; + default: + /* blank */ + return; + } + + f = fopen(filename, "wb"); + if (!f) { + return; + } + fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255); + + dst_linesize = s->width * 3; + buf = qemu_mallocz(dst_linesize); + pix = s->vram; + + for (y=0; y<s->height; y++) { + if (draw_line) + draw_line(s, buf, pix); + fwrite(buf, 1, dst_linesize, f); + pix += src_linesize; + } + + qemu_free(buf); + fclose(f); +} + +static void cg14_reset(DeviceState *d) +{ + CG14State *s = container_of(d, CG14State, busdev.qdev); + + /* set to 8bpp so last prom output might be visible */ + s->ctrl.mcr = CG14_MCR_VIDENABLE | CG14_MCR_PIXMODE_8; + s->dirty = 1; +} + +static SysBusDeviceInfo cg14_info = { + .init = cg14_init1, + .qdev.name = "cg14", + .qdev.desc = "Sun CG14 Framebuffer", + .qdev.size = sizeof(CG14State), + .qdev.reset = cg14_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_TADDR("vram_addr", CG14State, vram_addr, -1), + DEFINE_PROP_HEX32("vram_size", CG14State, vram_size, 0x800000), + DEFINE_PROP_UINT16("width", CG14State, width, 0), + DEFINE_PROP_UINT16("height", CG14State, height, 0), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void cg14_register_devices(void) +{ + sysbus_register_withprop(&cg14_info); +} + +device_init(cg14_register_devices) diff --git a/hw/sun4m.c b/hw/sun4m.c index e7a4cf6..0665e1f 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -577,6 +577,23 @@ static void tcx_init(target_phys_addr_t addr, int vram_size, int width, } } +static void cg14_init(target_phys_addr_t ctrl_base, + target_phys_addr_t vram_base, + int width, int height) +{ + DeviceState *dev; + SysBusDevice *s; + + dev = qdev_create(NULL, "cg14"); + qdev_prop_set_taddr(dev, "vram_addr", vram_base); + qdev_prop_set_uint16(dev, "width", width); + qdev_prop_set_uint16(dev, "height", height); + qdev_init_nofail(dev); + s = sysbus_from_qdev(dev); + sysbus_mmio_map(s, 0, ctrl_base); + sysbus_mmio_map(s, 1, vram_base); +} + /* NCR89C100/MACIO Internal ID register */ static const uint8_t idreg_data[] = { 0xfe, 0x81, 0x01, 0x03 }; @@ -879,6 +896,11 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size, exit (1); } num_vsimms = 0; + if (hwdef->vsimm[0].vram_base && (graphic_width > 1024 || !hwdef->tcx_base)) { + cg14_init(hwdef->vsimm[0].reg_base, hwdef->vsimm[0].vram_base, + graphic_width, graphic_height); + num_vsimms++; + } if (num_vsimms == 0) { tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height, graphic_depth);