diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2014-03-04 13:09:06 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2014-03-04 13:09:06 +0000 |
commit | d47e95c0c86ea5d8d050bf201ee27bcec617c10e (patch) | |
tree | e6d50e9845e1778153c176903e7aadaa7399f9c8 /hw | |
parent | 57f45b6207a65d96ee4913f33f1ec46cd32a083a (diff) | |
parent | af87bf290f31d8ef0aaf8f20259fa34e51ed1e7a (diff) |
Merge remote-tracking branch 'remotes/mcayland/qemu-sparc' into staging
* remotes/mcayland/qemu-sparc:
sun4m: Add Sun CG3 framebuffer initialisation function
sun4m: Add Sun CG3 framebuffer and corresponding OpenBIOS FCode ROM
sun4m: fix slavio timer RUN/STOP bit
sun4m: Set HostID in NVRAM
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/display/Makefile.objs | 1 | ||||
-rw-r--r-- | hw/display/cg3.c | 385 | ||||
-rw-r--r-- | hw/sparc/sun4m.c | 62 | ||||
-rw-r--r-- | hw/timer/slavio_timer.c | 25 |
4 files changed, 455 insertions, 18 deletions
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 540df82600..7ed76a9c24 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -28,6 +28,7 @@ obj-$(CONFIG_OMAP) += omap_lcdc.o obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o obj-$(CONFIG_SM501) += sm501.o obj-$(CONFIG_TCX) += tcx.o +obj-$(CONFIG_CG3) += cg3.o obj-$(CONFIG_VGA) += vga.o diff --git a/hw/display/cg3.c b/hw/display/cg3.c new file mode 100644 index 0000000000..6db8ca362a --- /dev/null +++ b/hw/display/cg3.c @@ -0,0 +1,385 @@ +/* + * QEMU CG3 Frame buffer + * + * Copyright (c) 2012 Bob Breuer + * Copyright (c) 2013 Mark Cave-Ayland + * + * 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 "qemu/error-report.h" +#include "ui/console.h" +#include "hw/sysbus.h" +#include "hw/loader.h" + +/* Change to 1 to enable debugging */ +#define DEBUG_CG3 0 + +#define CG3_ROM_FILE "QEMU,cgthree.bin" +#define FCODE_MAX_ROM_SIZE 0x10000 + +#define CG3_REG_SIZE 0x20 + +#define CG3_REG_BT458_ADDR 0x0 +#define CG3_REG_BT458_COLMAP 0x4 +#define CG3_REG_FBC_CTRL 0x10 +#define CG3_REG_FBC_STATUS 0x11 +#define CG3_REG_FBC_CURSTART 0x12 +#define CG3_REG_FBC_CUREND 0x13 +#define CG3_REG_FBC_VCTRL 0x14 + +/* Control register flags */ +#define CG3_CR_ENABLE_INTS 0x80 + +/* Status register flags */ +#define CG3_SR_PENDING_INT 0x80 +#define CG3_SR_1152_900_76_B 0x60 +#define CG3_SR_ID_COLOR 0x01 + +#define CG3_VRAM_SIZE 0x100000 +#define CG3_VRAM_OFFSET 0x800000 + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_CG3) { \ + printf("CG3: " fmt , ## __VA_ARGS__); \ + } \ +} while (0); + +#define TYPE_CG3 "cgthree" +#define CG3(obj) OBJECT_CHECK(CG3State, (obj), TYPE_CG3) + +typedef struct CG3State { + SysBusDevice parent_obj; + + QemuConsole *con; + qemu_irq irq; + hwaddr prom_addr; + MemoryRegion vram_mem; + MemoryRegion rom; + MemoryRegion reg; + uint32_t vram_size; + int full_update; + uint8_t regs[16]; + uint8_t r[256], g[256], b[256]; + uint16_t width, height, depth; + uint8_t dac_index, dac_state; +} CG3State; + +static void cg3_update_display(void *opaque) +{ + CG3State *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + const uint8_t *pix; + uint32_t *data; + uint32_t dval; + int x, y, y_start; + unsigned int width, height; + ram_addr_t page, page_min, page_max; + + if (surface_bits_per_pixel(surface) != 32) { + return; + } + width = s->width; + height = s->height; + + y_start = -1; + page_min = -1; + page_max = 0; + page = 0; + pix = memory_region_get_ram_ptr(&s->vram_mem); + data = (uint32_t *)surface_data(surface); + + for (y = 0; y < height; y++) { + int update = s->full_update; + + page = (y * width) & TARGET_PAGE_MASK; + update |= memory_region_get_dirty(&s->vram_mem, page, page + width, + DIRTY_MEMORY_VGA); + if (update) { + if (y_start < 0) { + y_start = y; + } + if (page < page_min) { + page_min = page; + } + if (page > page_max) { + page_max = page; + } + + for (x = 0; x < width; x++) { + dval = *pix++; + dval = (s->r[dval] << 16) | (s->g[dval] << 8) | s->b[dval]; + *data++ = dval; + } + } else { + if (y_start >= 0) { + dpy_gfx_update(s->con, 0, y_start, s->width, y - y_start); + y_start = -1; + } + pix += width; + data += width; + } + } + s->full_update = 0; + if (y_start >= 0) { + dpy_gfx_update(s->con, 0, y_start, s->width, y - y_start); + } + if (page_max >= page_min) { + memory_region_reset_dirty(&s->vram_mem, + page_min, page_max - page_min + TARGET_PAGE_SIZE, + DIRTY_MEMORY_VGA); + } + /* vsync interrupt? */ + if (s->regs[0] & CG3_CR_ENABLE_INTS) { + s->regs[1] |= CG3_SR_PENDING_INT; + qemu_irq_raise(s->irq); + } +} + +static void cg3_invalidate_display(void *opaque) +{ + CG3State *s = opaque; + + memory_region_set_dirty(&s->vram_mem, 0, CG3_VRAM_SIZE); +} + +static uint64_t cg3_reg_read(void *opaque, hwaddr addr, unsigned size) +{ + CG3State *s = opaque; + int val; + + switch (addr) { + case CG3_REG_BT458_ADDR: + case CG3_REG_BT458_COLMAP: + val = 0; + break; + case CG3_REG_FBC_CTRL: + val = s->regs[0]; + break; + case CG3_REG_FBC_STATUS: + /* monitor ID 6, board type = 1 (color) */ + val = s->regs[1] | CG3_SR_1152_900_76_B | CG3_SR_ID_COLOR; + break; + case CG3_REG_FBC_CURSTART ... CG3_REG_SIZE: + val = s->regs[addr - 0x10]; + break; + default: + qemu_log_mask(LOG_UNIMP, + "cg3: Unimplemented register read " + "reg 0x%" HWADDR_PRIx " size 0x%x\n", + addr, size); + val = 0; + break; + } + DPRINTF("read %02x from reg %" HWADDR_PRIx "\n", val, addr); + return val; +} + +static void cg3_reg_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + CG3State *s = opaque; + uint8_t regval; + int i; + + DPRINTF("write %" PRIx64 " to reg %" HWADDR_PRIx " size %d\n", + val, addr, size); + + switch (addr) { + case CG3_REG_BT458_ADDR: + s->dac_index = val; + s->dac_state = 0; + break; + case CG3_REG_BT458_COLMAP: + /* This register can be written to as either a long word or a byte */ + if (size == 1) { + val <<= 24; + } + + for (i = 0; i < size; i++) { + regval = val >> 24; + + switch (s->dac_state) { + case 0: + s->r[s->dac_index] = regval; + s->dac_state++; + break; + case 1: + s->g[s->dac_index] = regval; + s->dac_state++; + break; + case 2: + s->b[s->dac_index] = regval; + /* Index autoincrement */ + s->dac_index = (s->dac_index + 1) & 0xff; + default: + s->dac_state = 0; + break; + } + val <<= 8; + } + s->full_update = 1; + break; + case CG3_REG_FBC_CTRL: + s->regs[0] = val; + break; + case CG3_REG_FBC_STATUS: + if (s->regs[1] & CG3_SR_PENDING_INT) { + /* clear interrupt */ + s->regs[1] &= ~CG3_SR_PENDING_INT; + qemu_irq_lower(s->irq); + } + break; + case CG3_REG_FBC_CURSTART ... CG3_REG_SIZE: + s->regs[addr - 0x10] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, + "cg3: Unimplemented register write " + "reg 0x%" HWADDR_PRIx " size 0x%x value 0x%" PRIx64 "\n", + addr, size, val); + break; + } +} + +static const MemoryRegionOps cg3_reg_ops = { + .read = cg3_reg_read, + .write = cg3_reg_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static const GraphicHwOps cg3_ops = { + .invalidate = cg3_invalidate_display, + .gfx_update = cg3_update_display, +}; + +static void cg3_realizefn(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + CG3State *s = CG3(dev); + int ret; + char *fcode_filename; + + /* FCode ROM */ + memory_region_init_ram(&s->rom, NULL, "cg3.prom", FCODE_MAX_ROM_SIZE); + vmstate_register_ram_global(&s->rom); + memory_region_set_readonly(&s->rom, true); + sysbus_init_mmio(sbd, &s->rom); + + fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, CG3_ROM_FILE); + if (fcode_filename) { + ret = load_image_targphys(fcode_filename, s->prom_addr, + FCODE_MAX_ROM_SIZE); + if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { + error_report("cg3: could not load prom '%s'", CG3_ROM_FILE); + } + } + + memory_region_init_io(&s->reg, NULL, &cg3_reg_ops, s, "cg3.reg", + CG3_REG_SIZE); + sysbus_init_mmio(sbd, &s->reg); + + memory_region_init_ram(&s->vram_mem, NULL, "cg3.vram", s->vram_size); + vmstate_register_ram_global(&s->vram_mem); + sysbus_init_mmio(sbd, &s->vram_mem); + + sysbus_init_irq(sbd, &s->irq); + + s->con = graphic_console_init(DEVICE(dev), &cg3_ops, s); + qemu_console_resize(s->con, s->width, s->height); +} + +static int vmstate_cg3_post_load(void *opaque, int version_id) +{ + CG3State *s = opaque; + + cg3_invalidate_display(s); + + return 0; +} + +static const VMStateDescription vmstate_cg3 = { + .name = "cg3", + .version_id = 1, + .minimum_version_id = 1, + .post_load = vmstate_cg3_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT16(height, CG3State), + VMSTATE_UINT16(width, CG3State), + VMSTATE_UINT16(depth, CG3State), + VMSTATE_BUFFER(r, CG3State), + VMSTATE_BUFFER(g, CG3State), + VMSTATE_BUFFER(b, CG3State), + VMSTATE_UINT8(dac_index, CG3State), + VMSTATE_UINT8(dac_state, CG3State), + VMSTATE_END_OF_LIST() + } +}; + +static void cg3_reset(DeviceState *d) +{ + CG3State *s = CG3(d); + + /* Initialize palette */ + memset(s->r, 0, 256); + memset(s->g, 0, 256); + memset(s->b, 0, 256); + + s->dac_state = 0; + s->full_update = 1; + qemu_irq_lower(s->irq); +} + +static Property cg3_properties[] = { + DEFINE_PROP_UINT32("vram-size", CG3State, vram_size, -1), + DEFINE_PROP_UINT16("width", CG3State, width, -1), + DEFINE_PROP_UINT16("height", CG3State, height, -1), + DEFINE_PROP_UINT16("depth", CG3State, depth, -1), + DEFINE_PROP_UINT64("prom-addr", CG3State, prom_addr, -1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cg3_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = cg3_realizefn; + dc->reset = cg3_reset; + dc->vmsd = &vmstate_cg3; + dc->props = cg3_properties; +} + +static const TypeInfo cg3_info = { + .name = TYPE_CG3, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CG3State), + .class_init = cg3_class_init, +}; + +static void cg3_register_types(void) +{ + type_register_static(&cg3_info); +} + +type_init(cg3_register_types) diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 2957d90177..75adb68abc 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "hw/sysbus.h" +#include "qemu/error-report.h" #include "qemu/timer.h" #include "hw/sparc/sun4m.h" #include "hw/timer/m48t59.h" @@ -561,6 +562,31 @@ static void tcx_init(hwaddr addr, int vram_size, int width, } } +static void cg3_init(hwaddr addr, qemu_irq irq, int vram_size, int width, + int height, int depth) +{ + DeviceState *dev; + SysBusDevice *s; + + dev = qdev_create(NULL, "cgthree"); + qdev_prop_set_uint32(dev, "vram-size", vram_size); + qdev_prop_set_uint16(dev, "width", width); + qdev_prop_set_uint16(dev, "height", height); + qdev_prop_set_uint16(dev, "depth", depth); + qdev_prop_set_uint64(dev, "prom-addr", addr); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + + /* FCode ROM */ + sysbus_mmio_map(s, 0, addr); + /* DAC */ + sysbus_mmio_map(s, 1, addr + 0x400000ULL); + /* 8-bit plane */ + sysbus_mmio_map(s, 2, addr + 0x800000ULL); + + sysbus_connect_irq(s, 0, irq); +} + /* NCR89C100/MACIO Internal ID register */ #define TYPE_MACIO_ID_REGISTER "macio_idreg" @@ -914,13 +940,43 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, slavio_irq[16], iommu, &ledma_irq, 1); if (graphic_depth != 8 && graphic_depth != 24) { - fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth); + error_report("Unsupported depth: %d", graphic_depth); exit (1); } num_vsimms = 0; if (num_vsimms == 0) { - tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height, - graphic_depth); + if (vga_interface_type == VGA_CG3) { + if (graphic_depth != 8) { + error_report("Unsupported depth: %d", graphic_depth); + exit(1); + } + + if (!(graphic_width == 1024 && graphic_height == 768) && + !(graphic_width == 1152 && graphic_height == 900)) { + error_report("Unsupported resolution: %d x %d", graphic_width, + graphic_height); + exit(1); + } + + /* sbus irq 5 */ + cg3_init(hwdef->tcx_base, slavio_irq[11], 0x00100000, + graphic_width, graphic_height, graphic_depth); + } else { + /* If no display specified, default to TCX */ + if (graphic_depth != 8 && graphic_depth != 24) { + error_report("Unsupported depth: %d", graphic_depth); + exit(1); + } + + if (!(graphic_width == 1024 && graphic_height == 768)) { + error_report("Unsupported resolution: %d x %d", + graphic_width, graphic_height); + exit(1); + } + + tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height, + graphic_depth); + } } for (i = num_vsimms; i < MAX_VSIMMS; i++) { diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index f75b914951..e4dcceaf23 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -51,7 +51,7 @@ typedef struct CPUTimerState { ptimer_state *timer; uint32_t count, counthigh, reached; /* processor only */ - uint32_t running; + uint32_t run; uint64_t limit; } CPUTimerState; @@ -177,7 +177,7 @@ static uint64_t slavio_timer_mem_readl(void *opaque, hwaddr addr, // only available in processor counter/timer // read start/stop status if (timer_index > 0) { - ret = t->running; + ret = t->run; } else { ret = 0; } @@ -260,16 +260,15 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr, case TIMER_STATUS: if (slavio_timer_is_user(tc)) { // start/stop user counter - if ((val & 1) && !t->running) { + if (val & 1) { trace_slavio_timer_mem_writel_status_start(timer_index); ptimer_run(t->timer, 0); - t->running = 1; - } else if (!(val & 1) && t->running) { + } else { trace_slavio_timer_mem_writel_status_stop(timer_index); ptimer_stop(t->timer); - t->running = 0; } } + t->run = val & 1; break; case TIMER_MODE: if (timer_index == 0) { @@ -284,8 +283,9 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr, if (val & processor) { // counter -> user timer qemu_irq_lower(curr_timer->irq); // counters are always running - ptimer_stop(curr_timer->timer); - curr_timer->running = 0; + if (!curr_timer->run) { + ptimer_stop(curr_timer->timer); + } // user timer limit is always the same curr_timer->limit = TIMER_MAX_COUNT64; ptimer_set_limit(curr_timer->timer, @@ -296,13 +296,8 @@ static void slavio_timer_mem_writel(void *opaque, hwaddr addr, s->cputimer_mode |= processor; trace_slavio_timer_mem_writel_mode_user(timer_index); } else { // user timer -> counter - // stop the user timer if it is running - if (curr_timer->running) { - ptimer_stop(curr_timer->timer); - } // start the counter ptimer_run(curr_timer->timer, 0); - curr_timer->running = 1; // clear this processors user timer bit in config // register s->cputimer_mode &= ~processor; @@ -340,7 +335,7 @@ static const VMStateDescription vmstate_timer = { VMSTATE_UINT32(count, CPUTimerState), VMSTATE_UINT32(counthigh, CPUTimerState), VMSTATE_UINT32(reached, CPUTimerState), - VMSTATE_UINT32(running, CPUTimerState), + VMSTATE_UINT32(run , CPUTimerState), VMSTATE_PTIMER(timer, CPUTimerState), VMSTATE_END_OF_LIST() } @@ -373,7 +368,7 @@ static void slavio_timer_reset(DeviceState *d) ptimer_set_limit(curr_timer->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1); ptimer_run(curr_timer->timer, 0); - curr_timer->running = 1; + curr_timer->run = 1; } } s->cputimer_mode = 0; |