Message ID | 20190211040434.1C98674569F@zero.eik.bme.hu |
---|---|
State | New |
Headers | show |
Series | hw/display: Add basic ATI VGA emulation | expand |
Hi Zoltan, On 2/11/19 4:19 AM, BALATON Zoltan wrote: > At least two machines, the PPC mac99 and MIPS fulong2e, have an ATI > gfx chip by default (Rage 128 Pro and M6/RV100 respectively) and > guests running on these and the PMON2000 firmware of the fulong2e > expect this to be available. Fortunately these are very similar chips > so they can be mostly emulated in the same device model. This patch > adds basic emulation of these ATI VGA chips. > > While this is incomplete and currently only enough to run the MIPS > firmware and get console output with Linux, it allows the fulong2e > board to work more like the real hardware and having it in QEMU in > this state provides a way to experiment with it and allows others to > contribute to improve it (e.g. implement more 2D features to get X > running). It is only enabled for mips64le and ppc now but only the > fulong2e (which currently has no display output at all) is set to use > it by default (in a separate patch). > > Tested with Debian 8.11.0 powerpc on mac99 and the pmon_2e.bin > fulong2e firmware from https://mirrors.cloud.tencent.com/loongson/pmon/ > > Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> > --- > default-configs/mips64el-softmmu.mak | 1 + > default-configs/ppc-softmmu.mak | 1 + > hw/display/Makefile.objs | 2 + > hw/display/ati.c | 582 +++++++++++++++++++++++++++++++++++ > hw/display/ati_2d.c | 44 +++ > hw/display/ati_int.h | 67 ++++ > hw/display/ati_regs.h | 452 +++++++++++++++++++++++++++ > hw/display/trace-events | 4 + > 8 files changed, 1153 insertions(+) > create mode 100644 hw/display/ati.c > create mode 100644 hw/display/ati_2d.c > create mode 100644 hw/display/ati_int.h > create mode 100644 hw/display/ati_regs.h > > diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak > index 9eb1208b58..231f81eca2 100644 > --- a/default-configs/mips64el-softmmu.mak > +++ b/default-configs/mips64el-softmmu.mak > @@ -13,3 +13,4 @@ CONFIG_VT82C686=y > CONFIG_MIPS_BOSTON=y > CONFIG_FITLOADER=y > CONFIG_PCI_EXPRESS_XILINX=y > +CONFIG_ATI_VGA=y > diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak > index 52acb7cf39..75024b298e 100644 > --- a/default-configs/ppc-softmmu.mak > +++ b/default-configs/ppc-softmmu.mak > @@ -56,6 +56,7 @@ CONFIG_DEC_PCI=y > CONFIG_IDE_MACIO=y > CONFIG_MAC_OLDWORLD=y > CONFIG_MAC_NEWWORLD=y > +CONFIG_ATI_VGA=y > > # For PReP > CONFIG_PREP=y > diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs > index 7c4ae9a0fd..e58947e6f3 100644 > --- a/hw/display/Makefile.objs > +++ b/hw/display/Makefile.objs > @@ -53,3 +53,5 @@ virtio-gpu-3d.o-cflags := $(VIRGL_CFLAGS) > virtio-gpu-3d.o-libs += $(VIRGL_LIBS) > obj-$(CONFIG_DPCD) += dpcd.o > obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dp.o > + > +obj-$(CONFIG_ATI_VGA) += ati.o ati_2d.o > diff --git a/hw/display/ati.c b/hw/display/ati.c > new file mode 100644 > index 0000000000..18458110b2 > --- /dev/null > +++ b/hw/display/ati.c > @@ -0,0 +1,582 @@ > +/* > + * QEMU ATI SVGA emulation > + * > + * Copyright (c) 2019 BALATON Zoltan > + * > + * This work is licensed under the GNU GPL license version 2 or later. > + */ > + > +/* > + * WARNING: > + * This is very incomplete and only enough to get Linux console output yet. > + * At the moment it's little more than a frame buffer with minimal functions, > + * other more advanced features of the hardware are yet to be implemented. > + * We only aim for Rage 128 Pro (and some RV100) and 2D only at first, > + * No 3D at all yet (maybe after 2D works, but feel free to improve it) > + */ > + > +#include "ati_int.h" > +#include "ati_regs.h" > +#include "vga_regs.h" > +#include "qemu/log.h" > +#include "qemu/error-report.h" > +#include "qapi/error.h" > +#include "hw/hw.h" > +#include "ui/console.h" > +#include "trace.h" > + > +#define PCI_VENDOR_ID_ATI 0x1002 > +/* Rage128 Pro GL */ > +#define PCI_DEVICE_ID_ATI_RAGE128_PF 0x5046 > +/* Radeon RV100 (VE) */ > +#define PCI_DEVICE_ID_ATI_RADEON_QY 0x5159 > + > +enum { VGA_MODE, EXT_MODE }; > + > +static void ati_vga_switch_mode(ATIVGAState *s) > +{ > + DPRINTF("%d -> %d\n", > + s->mode, !!(s->regs.crtc_gen_cntl & CRTC2_EXT_DISP_EN)); > + if (s->regs.crtc_gen_cntl & CRTC2_EXT_DISP_EN) { > + /* Extended mode enabled */ > + s->mode = EXT_MODE; > + if (s->regs.crtc_gen_cntl & CRTC2_EN) { > + /* CRT controller enabled, use CRTC values */ > + uint32_t offs = s->regs.crtc_offset & 0x07ffffff; > + int stride = (s->regs.crtc_pitch & 0x7ff) * 8; > + int bpp = 0; > + int h, v; > + > + if (s->regs.crtc_h_total_disp == 0) { > + s->regs.crtc_h_total_disp = ((640 / 8) - 1) << 16; > + } > + if (s->regs.crtc_v_total_disp == 0) { > + s->regs.crtc_v_total_disp = (480 - 1) << 16; > + } > + h = ((s->regs.crtc_h_total_disp >> 16) + 1) * 8; > + v = (s->regs.crtc_v_total_disp >> 16) + 1; > + switch (s->regs.crtc_gen_cntl & CRTC_PIX_WIDTH_MASK) { > + case CRTC_PIX_WIDTH_4BPP: > + bpp = 4; > + break; > + case CRTC_PIX_WIDTH_8BPP: > + bpp = 8; > + break; > + case CRTC_PIX_WIDTH_15BPP: > + bpp = 15; > + break; > + case CRTC_PIX_WIDTH_16BPP: > + bpp = 16; > + break; > + case CRTC_PIX_WIDTH_24BPP: > + bpp = 24; > + break; > + case CRTC_PIX_WIDTH_32BPP: > + bpp = 32; > + break; > + default: > + qemu_log_mask(LOG_UNIMP, "Unsupported bpp value"); > + } > + assert(bpp != 0); > + DPRINTF("Switching to %dx%d %d %d @ %x\n", h, v, stride, bpp, offs); > + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_ENABLE); > + vbe_ioport_write_data(&s->vga, 0, VBE_DISPI_DISABLED); > + /* reset VBE regs then set up mode */ > + s->vga.vbe_regs[VBE_DISPI_INDEX_XRES] = h; > + s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] = v; > + s->vga.vbe_regs[VBE_DISPI_INDEX_BPP] = bpp; > + /* enable mode via ioport so it updates vga regs */ > + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_ENABLE); > + vbe_ioport_write_data(&s->vga, 0, VBE_DISPI_ENABLED | > + VBE_DISPI_LFB_ENABLED | VBE_DISPI_NOCLEARMEM | > + (s->regs.dac_cntl & DAC_8BIT_EN ? VBE_DISPI_8BIT_DAC : 0)); > + /* now set offset and stride after enable as that resets these */ > + if (stride) { > + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_VIRT_WIDTH); > + vbe_ioport_write_data(&s->vga, 0, stride); > + if (offs % stride == 0) { > + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_Y_OFFSET); > + vbe_ioport_write_data(&s->vga, 0, offs / stride); > + } else { > + /* FIXME what to do with this? */ > + error_report("VGA offset is not multiple of pitch, " > + "expect bad picture"); > + } > + } > + } > + } else { > + /* VGA mode enabled */ > + s->mode = VGA_MODE; > + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_ENABLE); > + vbe_ioport_write_data(&s->vga, 0, VBE_DISPI_DISABLED); > + } > +} > + > +static uint64_t ati_reg_read_offs(uint64_t reg, int offs, unsigned int size) > +{ > + uint64_t val = 0; > + uint32_t mask; > + int i; > + > + for (i = 0; i < size; i++) { > + mask = 0xffUL << (offs + i) * 8; > + val |= reg & mask; > + } > + val >>= offs * 8; > + return val; > +} > + > +static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size) > +{ > + ATIVGAState *s = opaque; > + uint64_t val = 0; > + > + switch (addr) { > + case MM_INDEX: > + val = s->regs.mm_index; > + break; > + case MM_DATA ... MM_DATA + 3: > + /* indexed access to regs or memory */ > + if (s->regs.mm_index & 0x80000000) { > + if (s->regs.mm_index <= s->vga.vram_size - size) { > + int i = size - 1; > + while (i >= 0) { > + val <<= 8; > + val |= s->vga.vram_ptr[s->regs.mm_index + i--]; > + } > + } > + } else { > + val = ati_mm_read(s, s->regs.mm_index + addr - MM_DATA, size); > + } > + break; > + case BIOS_0_SCRATCH: > + val = s->regs.bios_0_scratch; > + break; > + case CRTC_GEN_CNTL ... CRTC_GEN_CNTL + 3: > + if (addr == CRTC_GEN_CNTL && size == 4) { > + val = s->regs.crtc_gen_cntl; > + } else { > + val = ati_reg_read_offs(s->regs.crtc_gen_cntl, > + addr - CRTC_GEN_CNTL, size); > + } > + break; > + case CRTC_EXT_CNTL ... CRTC_EXT_CNTL + 3: > + if (addr == CRTC_EXT_CNTL && size == 4) { > + val = s->regs.crtc_ext_cntl; > + } else { > + val = ati_reg_read_offs(s->regs.crtc_ext_cntl, > + addr - CRTC_EXT_CNTL, size); > + } > + break; > + case DAC_CNTL: > + val = s->regs.dac_cntl; > + break; > +/* case GPIO_MONID: FIXME hook up DDC I2C here */ > + case PALETTE_INDEX: > + /* FIXME unaligned access */ > + val = vga_ioport_read(&s->vga, VGA_PEL_IR) << 16; > + val |= vga_ioport_read(&s->vga, VGA_PEL_IW) & 0xff; > + break; > + case PALETTE_DATA: > + val = vga_ioport_read(&s->vga, VGA_PEL_D); > + break; > + case CNFG_MEMSIZE: > + val = s->vga.vram_size; > + break; > + case MC_STATUS: > + val = 5; > + break; > + case RBBM_STATUS: > + case GUI_STAT: > + val = 64; /* free CMDFIFO entries */ > + break; > + case CRTC_H_TOTAL_DISP: > + val = s->regs.crtc_h_total_disp; > + break; > + case CRTC_H_SYNC_STRT_WID: > + val = s->regs.crtc_h_sync_strt_wid; > + break; > + case CRTC_V_TOTAL_DISP: > + val = s->regs.crtc_v_total_disp; > + break; > + case CRTC_V_SYNC_STRT_WID: > + val = s->regs.crtc_v_sync_strt_wid; > + break; > + case CRTC_OFFSET: > + val = s->regs.crtc_offset; > + break; > + case CRTC_OFFSET_CNTL: > + val = s->regs.crtc_offset_cntl; > + break; > + case CRTC_PITCH: > + val = s->regs.crtc_pitch; > + break; > + case 0xf00 ... 0xfff: > + val = pci_default_read_config(&s->dev, addr - 0xf00, size); > + break; > + case DST_OFFSET: > + val = s->regs.dst_offset; > + break; > + case DST_PITCH: > + val = s->regs.dst_pitch; > + break; > + case DST_WIDTH: > + val = s->regs.dst_width; > + break; > + case DST_HEIGHT: > + val = s->regs.dst_height; > + break; > + case SRC_X: > + val = s->regs.src_x; > + break; > + case SRC_Y: > + val = s->regs.src_y; > + break; > + case DST_X: > + val = s->regs.dst_x; > + break; > + case DST_Y: > + val = s->regs.dst_y; > + break; > + case DP_GUI_MASTER_CNTL: > + val = s->regs.dp_gui_master_cntl; > + break; > + case DP_BRUSH_BKGD_CLR: > + val = s->regs.dp_brush_bkgd_clr; > + break; > + case DP_BRUSH_FRGD_CLR: > + val = s->regs.dp_brush_frgd_clr; > + break; > + case DP_SRC_FRGD_CLR: > + val = s->regs.dp_src_frgd_clr; > + break; > + case DP_SRC_BKGD_CLR: > + val = s->regs.dp_src_bkgd_clr; > + break; > + case DP_CNTL: > + val = s->regs.dp_cntl; > + break; > + case DP_WRITE_MASK: > + val = s->regs.dp_write_mask; > + break; > + case DEFAULT_OFFSET: > + val = s->regs.default_offset; > + break; > + case DEFAULT_PITCH: > + val = s->regs.default_pitch; > + break; > + case DEFAULT_SC_BOTTOM_RIGHT: > + val = s->regs.default_sc_bottom_right; > + break; > + default: > + break; > + } > + trace_ati_mm_read(size, addr, val); > + > + return val; > +} > + > +static void ati_reg_write_offs(uint32_t *reg, int offs, > + uint64_t data, unsigned int size) > +{ > + int shift, i; > + uint32_t mask; > + > + for (i = 0; i < size; i++) { > + shift = (offs + i) * 8; > + mask = 0xffUL << shift; > + *reg &= ~mask; > + *reg |= (data & 0xff) << shift; > + data >>= 8; I'd have use a pair of extract32/deposit32 but this is probably easier to singlestep. > + } > +} > + > +static void ati_mm_write(void *opaque, hwaddr addr, > + uint64_t data, unsigned int size) > +{ > + ATIVGAState *s = opaque; > + int i; > + > + trace_ati_mm_write(size, addr, data); > + switch (addr) { > + case MM_INDEX: > + s->regs.mm_index = data; > + break; > + case MM_DATA ... MM_DATA + 3: > + /* indexed access to regs or memory */ > + if (s->regs.mm_index & 0x80000000) { > + if (s->regs.mm_index <= s->vga.vram_size - size) { > + int i = 0; > + while (i < size) { > + s->vga.vram_ptr[s->regs.mm_index + i] = data & 0xff; > + data >>= 8; > + } > + } > + } else { > + ati_mm_write(s, s->regs.mm_index + addr - MM_DATA, data, size); > + } > + break; > + case BIOS_0_SCRATCH: > + s->regs.bios_0_scratch = data; > + break; > + case CRTC_GEN_CNTL ... CRTC_GEN_CNTL + 3: > + if (addr == CRTC_GEN_CNTL && size == 4) { > + s->regs.crtc_gen_cntl = data; > + } else { > + ati_reg_write_offs(&s->regs.crtc_gen_cntl, > + addr - CRTC_GEN_CNTL, data, size); > + } > + break; > + case CRTC_EXT_CNTL ... CRTC_EXT_CNTL + 3: > + if (addr == CRTC_EXT_CNTL && size == 4) { > + s->regs.crtc_ext_cntl = data; > + } else { > + ati_reg_write_offs(&s->regs.crtc_ext_cntl, > + addr - CRTC_EXT_CNTL, data, size); > + } > + if (s->regs.crtc_ext_cntl & CRT_CRTC_DISPLAY_DIS) { > + DPRINTF("Display disabled\n"); > + s->vga.ar_index &= ~0x20; > + } else { > + DPRINTF("Display enabled\n"); > + s->vga.ar_index |= 0x20; > + } > + if (!!(s->regs.crtc_gen_cntl & CRTC2_EXT_DISP_EN) != s->mode) { > + ati_vga_switch_mode(s); > + } > + break; > + case DAC_CNTL: > + s->regs.dac_cntl = data & 0xffffe3ff; > + s->vga.dac_8bit = !!(data & DAC_8BIT_EN); > + break; > +/* case GPIO_MONID: FIXME hook up DDC I2C here */ > + case PALETTE_INDEX ... PALETTE_INDEX + 3: > + if (size == 4) { > + vga_ioport_write(&s->vga, VGA_PEL_IR, (data >> 16) & 0xff); > + vga_ioport_write(&s->vga, VGA_PEL_IW, data & 0xff); > + } else { > + if (addr == PALETTE_INDEX) { > + vga_ioport_write(&s->vga, VGA_PEL_IW, data & 0xff); > + } else { > + vga_ioport_write(&s->vga, VGA_PEL_IR, data & 0xff); > + } > + } > + break; > + case PALETTE_DATA: > + for (i = 0; i < size && i < 3; i++) { > + vga_ioport_write(&s->vga, VGA_PEL_D, data & 0xff); > + data >>= 8; > + } > + break; > + case CRTC_H_TOTAL_DISP: > + s->regs.crtc_h_total_disp = data & 0x07ff07ff; > + break; > + case CRTC_H_SYNC_STRT_WID: > + s->regs.crtc_h_sync_strt_wid = data & 0x17bf1fff; > + break; > + case CRTC_V_TOTAL_DISP: > + s->regs.crtc_v_total_disp = data & 0x0fff0fff; > + break; > + case CRTC_V_SYNC_STRT_WID: > + s->regs.crtc_v_sync_strt_wid = data & 0x9f0fff; > + break; > + case CRTC_OFFSET: > + s->regs.crtc_offset = data & 0xc7ffffff; > + break; > + case CRTC_OFFSET_CNTL: > + s->regs.crtc_offset_cntl = data; /* FIXME */ > + break; > + case CRTC_PITCH: > + s->regs.crtc_pitch = data & 0x07ff07ff; > + break; > + case 0xf00 ... 0xfff: > + /* read-only copy of PCI config space so ignore writes */ > + break; > + case DST_OFFSET: > + s->regs.dst_offset = data & 0xfffffc00; > + break; > + case DST_PITCH: > + s->regs.dst_pitch = data & 0x3fff; > + break; > + case DST_WIDTH: > + s->regs.dst_width = data & 0x3fff; > + ati_2d_blit(s); > + break; > + case DST_HEIGHT: > + s->regs.dst_height = data & 0x3fff; > + break; > + case SRC_X: > + s->regs.src_x = data & 0x3fff; > + break; > + case SRC_Y: > + s->regs.src_y = data & 0x3fff; > + break; > + case DST_X: > + s->regs.dst_x = data & 0x3fff; > + break; > + case DST_Y: > + s->regs.dst_y = data & 0x3fff; > + break; > + case SRC_Y_X: > + s->regs.src_x = data & 0x3fff; > + s->regs.src_y = (data >> 16) & 0x3fff; > + break; > + case DST_Y_X: > + s->regs.dst_x = data & 0x3fff; > + s->regs.dst_y = (data >> 16) & 0x3fff; > + break; > + case DST_HEIGHT_WIDTH: > + s->regs.dst_width = data & 0x3fff; > + s->regs.dst_height = (data >> 16) & 0x3fff; > + ati_2d_blit(s); > + break; > + case DP_GUI_MASTER_CNTL: > + s->regs.dp_gui_master_cntl = data; > + break; > + case DST_WIDTH_X: > + s->regs.dst_x = data & 0x3fff; > + s->regs.dst_width = (data >> 16) & 0x3fff; > + ati_2d_blit(s); > + break; > + case SRC_X_Y: > + s->regs.src_y = data & 0x3fff; > + s->regs.src_x = (data >> 16) & 0x3fff; > + break; > + case DST_X_Y: > + s->regs.dst_y = data & 0x3fff; > + s->regs.dst_x = (data >> 16) & 0x3fff; > + break; > + case DST_HEIGHT_Y: > + s->regs.dst_y = data & 0x3fff; > + s->regs.dst_height = (data >> 16) & 0x3fff; > + break; > + case DP_BRUSH_BKGD_CLR: > + s->regs.dp_brush_bkgd_clr = data; > + break; > + case DP_BRUSH_FRGD_CLR: > + s->regs.dp_brush_frgd_clr = data; > + break; > + case DP_CNTL: > + s->regs.dp_cntl = data; > + break; > + case DP_WRITE_MASK: > + s->regs.dp_write_mask = data; > + break; > + case DEFAULT_OFFSET: > + data &= (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF ? > + 0x03fffc00 : 0xfffffc00); > + s->regs.default_offset = data; > + break; > + case DEFAULT_PITCH: > + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { > + s->regs.default_pitch = data & 0x103ff; > + } > + break; > + case DEFAULT_SC_BOTTOM_RIGHT: > + s->regs.default_sc_bottom_right = data & 0x3fff3fff; > + break; > + default: > + break; > + } > +} > + > +static const MemoryRegionOps ati_mm_ops = { > + .read = ati_mm_read, > + .write = ati_mm_write, > + .endianness = DEVICE_LITTLE_ENDIAN, > +}; > + > +static void ati_vga_realize(PCIDevice *dev, Error **errp) > +{ > + ATIVGAState *s = ATI_VGA(dev); > + VGACommonState *vga = &s->vga; > + > + if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF && > + s->dev_id != PCI_DEVICE_ID_ATI_RADEON_QY) { > + error_setg(errp, "Unknown ATI VGA device id, " > + "only 0x5046 and 0x5159 are supported"); > + return; > + } > + if (s->dev_id == PCI_DEVICE_ID_ATI_RADEON_QY && > + s->vga.vram_size_mb < 16) { > + warn_report("Too small video memory for device id"); > + s->vga.vram_size_mb = 16; > + } > + > + /* init vga compat bits */ > + vga_common_init(vga, OBJECT(s)); > + vga_init(vga, OBJECT(s), pci_address_space(dev), > + pci_address_space_io(dev), true); > + vga->con = graphic_console_init(DEVICE(s), 0, s->vga.hw_ops, &s->vga); > + > + /* mmio register space */ > + memory_region_init_io(&s->mm, OBJECT(s), &ati_mm_ops, s, > + "ati.mmregs", 0x4000); > + /* io space is alias to beginning of mmregs */ > + memory_region_init_alias(&s->io, OBJECT(s), "ati.io", &s->mm, 0, 0x100); > + > + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram); > + pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io); > + pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mm); > +} > + > +static void ati_vga_reset(DeviceState *dev) > +{ > + ATIVGAState *s = ATI_VGA(dev); > + PCIDevice *pd = PCI_DEVICE(dev); > + > + pci_set_word(pd->config + PCI_DEVICE_ID, s->dev_id); > + /* reset vga */ > + vga_common_reset(&s->vga); > + s->mode = VGA_MODE; > +} > + > +static void ati_vga_exit(PCIDevice *dev) > +{ > + ATIVGAState *s = ATI_VGA(dev); > + > + graphic_console_close(s->vga.con); > +} > + > +static Property ati_vga_properties[] = { > + DEFINE_PROP_UINT32("vgamem_mb", ATIVGAState, vga.vram_size_mb, 16), > + DEFINE_PROP_UINT16("device_id", ATIVGAState, dev_id, > + PCI_DEVICE_ID_ATI_RAGE128_PF), > + DEFINE_PROP_END_OF_LIST() > +}; > + > +static void ati_vga_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); > + > + dc->reset = ati_vga_reset; > + dc->props = ati_vga_properties; > + dc->hotpluggable = false; > + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); > + > + k->class_id = PCI_CLASS_DISPLAY_VGA; > + k->vendor_id = PCI_VENDOR_ID_ATI; > + k->device_id = PCI_DEVICE_ID_ATI_RAGE128_PF; > + k->romfile = "vgabios-stdvga.bin"; > + k->realize = ati_vga_realize; > + k->exit = ati_vga_exit; > +} > + > +static const TypeInfo ati_vga_info = { > + .name = TYPE_ATI_VGA, > + .parent = TYPE_PCI_DEVICE, > + .instance_size = sizeof(ATIVGAState), > + .class_init = ati_vga_class_init, > + .interfaces = (InterfaceInfo[]) { > + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, > + { }, > + }, > +}; > + > +static void ati_vga_register_types(void) > +{ > + type_register_static(&ati_vga_info); > +} > + > +type_init(ati_vga_register_types) > diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c > new file mode 100644 > index 0000000000..ca3d296b23 > --- /dev/null > +++ b/hw/display/ati_2d.c > @@ -0,0 +1,44 @@ > +/* > + * QEMU ATI SVGA emulation > + * 2D engine functions > + * > + * Copyright (c) 2019 BALATON Zoltan > + * > + * This work is licensed under the GNU GPL license version 2 or later. > + */ > + > +#include "ati_int.h" > +#include "ati_regs.h" > +#include "qemu/log.h" > + > +/* > + * NOTE: > + * This is 2D _acceleration_ and supposed to be fast. Therefore, don't try to > + * reinvent the wheel (unlikely to get better with a naive implementation than > + * existing libraries) and avoid (poorly) reimplementing gfx primitives. > + * That is unnecessary and would become a performance problem. Instead, try to > + * map to and reuse existing optimised facilities (e.g. pixman) wherever > + * possible. > + */ > + > +void ati_2d_blit(ATIVGAState *s) > +{ > + /* FIXME it is really much more complex than this and will need to be */ > + /* rewritten but for now as a start just to get some console output: */ > + if ((s->regs.dp_gui_master_cntl & GMC_ROP3_MASK) == ROP3_SRCCOPY) { > + DisplaySurface *ds = qemu_console_surface(s->vga.con); > + DPRINTF("%p %p, %d %d, (%d,%d) -> (%d,%d) %dx%d\n", ds->image, > + ds->image, surface_stride(ds), surface_bits_per_pixel(ds), > + s->regs.src_x, s->regs.src_y, s->regs.dst_x, s->regs.dst_y, > + s->regs.dst_width, s->regs.dst_height); > + pixman_image_composite(PIXMAN_OP_SRC, ds->image, NULL, ds->image, > + s->regs.src_x, s->regs.src_y, 0, 0, > + s->regs.dst_x, s->regs.dst_y, > + s->regs.dst_width, s->regs.dst_height); > + memory_region_set_dirty(&s->vga.vram, s->vga.vbe_start_addr + > + s->regs.dst_y * surface_stride(ds), > + s->regs.dst_height * surface_stride(ds)); > + } else { > + qemu_log_mask(LOG_UNIMP, "Unimplemented ati_2d blit op"); > + } > +} > diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h > new file mode 100644 > index 0000000000..85d045517c > --- /dev/null > +++ b/hw/display/ati_int.h > @@ -0,0 +1,67 @@ > +/* > + * QEMU ATI SVGA emulation > + * > + * Copyright (c) 2019 BALATON Zoltan > + * > + * This work is licensed under the GNU GPL license version 2 or later. > + */ > + > +#include "qemu/osdep.h" > +#include "hw/pci/pci.h" > +#include "vga_int.h" > + > +#undef DEBUG_ATI > + > +#ifdef DEBUG_ATI > +#define DPRINTF(fmt, ...) printf("%s: " fmt, __func__, ## __VA_ARGS__) > +#else > +#define DPRINTF(fmt, ...) do {} while (0) Please use tracepoints (you already add some!). > +#endif > + > +#define TYPE_ATI_VGA "ati-vga" > +#define ATI_VGA(obj) OBJECT_CHECK(ATIVGAState, (obj), TYPE_ATI_VGA) > + > +typedef struct ATIVGARegs { > + uint32_t mm_index; > + uint32_t bios_0_scratch; > + uint32_t crtc_gen_cntl; > + uint32_t crtc_ext_cntl; > + uint32_t dac_cntl; > + uint32_t crtc_h_total_disp; > + uint32_t crtc_h_sync_strt_wid; > + uint32_t crtc_v_total_disp; > + uint32_t crtc_v_sync_strt_wid; > + uint32_t crtc_offset; > + uint32_t crtc_offset_cntl; > + uint32_t crtc_pitch; > + uint32_t dst_offset; > + uint32_t dst_pitch; > + uint32_t dst_width; > + uint32_t dst_height; > + uint32_t src_x; > + uint32_t src_y; > + uint32_t dst_x; > + uint32_t dst_y; > + uint32_t dp_gui_master_cntl; > + uint32_t dp_brush_bkgd_clr; > + uint32_t dp_brush_frgd_clr; > + uint32_t dp_src_frgd_clr; > + uint32_t dp_src_bkgd_clr; > + uint32_t dp_cntl; > + uint32_t dp_write_mask; > + uint32_t default_offset; > + uint32_t default_pitch; > + uint32_t default_sc_bottom_right; > +} ATIVGARegs; > + > +typedef struct ATIVGAState { > + PCIDevice dev; > + VGACommonState vga; > + uint16_t dev_id; > + uint16_t mode; > + MemoryRegion io; > + MemoryRegion mm; > + ATIVGARegs regs; > +} ATIVGAState; > + > +void ati_2d_blit(ATIVGAState *s); > diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h > new file mode 100644 > index 0000000000..dc5ff53ceb > --- /dev/null > +++ b/hw/display/ati_regs.h > @@ -0,0 +1,452 @@ > +/* > + * ATI VGA register definitions > + * > + * based on: > + * linux/include/video/aty128.h > + * Register definitions for ATI Rage128 boards > + * Anthony Tong <atong@uiuc.edu>, 1999 > + * Brad Douglas <brad@neruo.com>, 2000 > + * > + * and linux/include/video/radeon.h > + * > + * This work is licensed under the GNU GPL license version 2. > + */ > + > +/* > + * Register mapping: > + * 0x0000-0x00ff Misc regs also accessible via io and mmio space > + * 0x0100-0x0eff Misc regs only accessible via mmio > + * 0x0f00-0x0fff Read-only copy of PCI config regs > + * 0x1000-0x13ff Concurrent Command Engine (CCE) regs > + * 0x1400-0x1fff GUI (drawing engine) regs > + */ > + > +#ifndef ATI_REGS_H > +#define ATI_REGS_H > + > +#define MM_INDEX 0x0000 > +#define MM_DATA 0x0004 > +#define CLOCK_CNTL_INDEX 0x0008 > +#define CLOCK_CNTL_DATA 0x000c > +#define BIOS_0_SCRATCH 0x0010 > +#define BUS_CNTL 0x0030 > +#define BUS_CNTL1 0x0034 > +#define GEN_INT_CNTL 0x0040 > +#define CRTC_GEN_CNTL 0x0050 > +#define CRTC_EXT_CNTL 0x0054 > +#define DAC_CNTL 0x0058 > +#define GPIO_MONID 0x0068 > +#define I2C_CNTL_1 0x0094 > +#define PALETTE_INDEX 0x00b0 > +#define PALETTE_DATA 0x00b4 > +#define CNFG_CNTL 0x00e0 > +#define GEN_RESET_CNTL 0x00f0 > +#define CNFG_MEMSIZE 0x00f8 > +#define MEM_CNTL 0x0140 > +#define MC_FB_LOCATION 0x0148 > +#define MC_AGP_LOCATION 0x014C > +#define MC_STATUS 0x0150 > +#define MEM_POWER_MISC 0x015c > +#define AGP_BASE 0x0170 > +#define AGP_CNTL 0x0174 > +#define AGP_APER_OFFSET 0x0178 > +#define PCI_GART_PAGE 0x017c > +#define PC_NGUI_MODE 0x0180 > +#define PC_NGUI_CTLSTAT 0x0184 > +#define MPP_TB_CONFIG 0x01C0 > +#define MPP_GP_CONFIG 0x01C8 > +#define VIPH_CONTROL 0x01D0 > +#define CRTC_H_TOTAL_DISP 0x0200 > +#define CRTC_H_SYNC_STRT_WID 0x0204 > +#define CRTC_V_TOTAL_DISP 0x0208 > +#define CRTC_V_SYNC_STRT_WID 0x020c > +#define CRTC_VLINE_CRNT_VLINE 0x0210 > +#define CRTC_CRNT_FRAME 0x0214 > +#define CRTC_GUI_TRIG_VLINE 0x0218 > +#define CRTC_OFFSET 0x0224 > +#define CRTC_OFFSET_CNTL 0x0228 > +#define CRTC_PITCH 0x022c > +#define OVR_CLR 0x0230 > +#define OVR_WID_LEFT_RIGHT 0x0234 > +#define OVR_WID_TOP_BOTTOM 0x0238 > +#define LVDS_GEN_CNTL 0x02d0 > +#define DDA_CONFIG 0x02e0 > +#define DDA_ON_OFF 0x02e4 > +#define VGA_DDA_CONFIG 0x02e8 > +#define VGA_DDA_ON_OFF 0x02ec > +#define CRTC2_H_TOTAL_DISP 0x0300 > +#define CRTC2_H_SYNC_STRT_WID 0x0304 > +#define CRTC2_V_TOTAL_DISP 0x0308 > +#define CRTC2_V_SYNC_STRT_WID 0x030c > +#define CRTC2_VLINE_CRNT_VLINE 0x0310 > +#define CRTC2_CRNT_FRAME 0x0314 > +#define CRTC2_GUI_TRIG_VLINE 0x0318 > +#define CRTC2_OFFSET 0x0324 > +#define CRTC2_OFFSET_CNTL 0x0328 > +#define CRTC2_PITCH 0x032c > +#define DDA2_CONFIG 0x03e0 > +#define DDA2_ON_OFF 0x03e4 > +#define CRTC2_GEN_CNTL 0x03f8 > +#define CRTC2_STATUS 0x03fc > +#define OV0_SCALE_CNTL 0x0420 > +#define SUBPIC_CNTL 0x0540 > +#define PM4_BUFFER_OFFSET 0x0700 > +#define PM4_BUFFER_CNTL 0x0704 > +#define PM4_BUFFER_WM_CNTL 0x0708 > +#define PM4_BUFFER_DL_RPTR_ADDR 0x070c > +#define PM4_BUFFER_DL_RPTR 0x0710 > +#define PM4_BUFFER_DL_WPTR 0x0714 > +#define PM4_VC_FPU_SETUP 0x071c > +#define PM4_FPU_CNTL 0x0720 > +#define PM4_VC_FORMAT 0x0724 > +#define PM4_VC_CNTL 0x0728 > +#define PM4_VC_I01 0x072c > +#define PM4_VC_VLOFF 0x0730 > +#define PM4_VC_VLSIZE 0x0734 > +#define PM4_IW_INDOFF 0x0738 > +#define PM4_IW_INDSIZE 0x073c > +#define PM4_FPU_FPX0 0x0740 > +#define PM4_FPU_FPY0 0x0744 > +#define PM4_FPU_FPX1 0x0748 > +#define PM4_FPU_FPY1 0x074c > +#define PM4_FPU_FPX2 0x0750 > +#define PM4_FPU_FPY2 0x0754 > +#define PM4_FPU_FPY3 0x0758 > +#define PM4_FPU_FPY4 0x075c > +#define PM4_FPU_FPY5 0x0760 > +#define PM4_FPU_FPY6 0x0764 > +#define PM4_FPU_FPR 0x0768 > +#define PM4_FPU_FPG 0x076c > +#define PM4_FPU_FPB 0x0770 > +#define PM4_FPU_FPA 0x0774 > +#define PM4_FPU_INTXY0 0x0780 > +#define PM4_FPU_INTXY1 0x0784 > +#define PM4_FPU_INTXY2 0x0788 > +#define PM4_FPU_INTARGB 0x078c > +#define PM4_FPU_FPTWICEAREA 0x0790 > +#define PM4_FPU_DMAJOR01 0x0794 > +#define PM4_FPU_DMAJOR12 0x0798 > +#define PM4_FPU_DMAJOR02 0x079c > +#define PM4_FPU_STAT 0x07a0 > +#define PM4_STAT 0x07b8 > +#define PM4_TEST_CNTL 0x07d0 > +#define PM4_MICROCODE_ADDR 0x07d4 > +#define PM4_MICROCODE_RADDR 0x07d8 > +#define PM4_MICROCODE_DATAH 0x07dc > +#define PM4_MICROCODE_DATAL 0x07e0 > +#define PM4_CMDFIFO_ADDR 0x07e4 > +#define PM4_CMDFIFO_DATAH 0x07e8 > +#define PM4_CMDFIFO_DATAL 0x07ec > +#define PM4_BUFFER_ADDR 0x07f0 > +#define PM4_BUFFER_DATAH 0x07f4 > +#define PM4_BUFFER_DATAL 0x07f8 > +#define PM4_MICRO_CNTL 0x07fc > +#define CAP0_TRIG_CNTL 0x0950 > +#define CAP1_TRIG_CNTL 0x09c0 > + > +#define RBBM_STATUS 0x0E40 > +#define RBBM_STATUS_alt_1 0x1740 > + > +/* > + * GUI Block Memory Mapped Registers > + * These registers are FIFOed. > + */ > +#define PM4_FIFO_DATA_EVEN 0x1000 > +#define PM4_FIFO_DATA_ODD 0x1004 > + > +#define DST_OFFSET 0x1404 > +#define DST_PITCH 0x1408 > +#define DST_WIDTH 0x140c > +#define DST_HEIGHT 0x1410 > +#define SRC_X 0x1414 > +#define SRC_Y 0x1418 > +#define DST_X 0x141c > +#define DST_Y 0x1420 > +#define SRC_PITCH_OFFSET 0x1428 > +#define DST_PITCH_OFFSET 0x142c > +#define SRC_Y_X 0x1434 > +#define DST_Y_X 0x1438 > +#define DST_HEIGHT_WIDTH 0x143c > +#define DP_GUI_MASTER_CNTL 0x146c > +#define BRUSH_SCALE 0x1470 > +#define BRUSH_Y_X 0x1474 > +#define DP_BRUSH_BKGD_CLR 0x1478 > +#define DP_BRUSH_FRGD_CLR 0x147c > +#define DST_WIDTH_X 0x1588 > +#define DST_HEIGHT_WIDTH_8 0x158c > +#define SRC_X_Y 0x1590 > +#define DST_X_Y 0x1594 > +#define DST_WIDTH_HEIGHT 0x1598 > +#define DST_WIDTH_X_INCY 0x159c > +#define DST_HEIGHT_Y 0x15a0 > +#define DST_X_SUB 0x15a4 > +#define DST_Y_SUB 0x15a8 > +#define SRC_OFFSET 0x15ac > +#define SRC_PITCH 0x15b0 > +#define DST_HEIGHT_WIDTH_BW 0x15b4 > +#define CLR_CMP_CNTL 0x15c0 > +#define CLR_CMP_CLR_SRC 0x15c4 > +#define CLR_CMP_CLR_DST 0x15c8 > +#define CLR_CMP_MASK 0x15cc > +#define DP_SRC_FRGD_CLR 0x15d8 > +#define DP_SRC_BKGD_CLR 0x15dc > +#define DST_BRES_ERR 0x1628 > +#define DST_BRES_INC 0x162c > +#define DST_BRES_DEC 0x1630 > +#define DST_BRES_LNTH 0x1634 > +#define DST_BRES_LNTH_SUB 0x1638 > +#define SC_LEFT 0x1640 > +#define SC_RIGHT 0x1644 > +#define SC_TOP 0x1648 > +#define SC_BOTTOM 0x164c > +#define SRC_SC_RIGHT 0x1654 > +#define SRC_SC_BOTTOM 0x165c > +#define GUI_DEBUG0 0x16a0 > +#define GUI_DEBUG1 0x16a4 > +#define GUI_TIMEOUT 0x16b0 > +#define GUI_TIMEOUT0 0x16b4 > +#define GUI_TIMEOUT1 0x16b8 > +#define GUI_PROBE 0x16bc > +#define DP_CNTL 0x16c0 > +#define DP_DATATYPE 0x16c4 > +#define DP_MIX 0x16c8 > +#define DP_WRITE_MASK 0x16cc > +#define DP_CNTL_XDIR_YDIR_YMAJOR 0x16d0 > +#define DEFAULT_OFFSET 0x16e0 > +#define DEFAULT_PITCH 0x16e4 > +#define DEFAULT_SC_BOTTOM_RIGHT 0x16e8 > +#define SC_TOP_LEFT 0x16ec > +#define SC_BOTTOM_RIGHT 0x16f0 > +#define SRC_SC_BOTTOM_RIGHT 0x16f4 > +#define WAIT_UNTIL 0x1720 > +#define CACHE_CNTL 0x1724 > +#define GUI_STAT 0x1740 > +#define PC_GUI_MODE 0x1744 > +#define PC_GUI_CTLSTAT 0x1748 > +#define PC_DEBUG_MODE 0x1760 > +#define BRES_DST_ERR_DEC 0x1780 > +#define TRAIL_BRES_T12_ERR_DEC 0x1784 > +#define TRAIL_BRES_T12_INC 0x1788 > +#define DP_T12_CNTL 0x178c > +#define DST_BRES_T1_LNTH 0x1790 > +#define DST_BRES_T2_LNTH 0x1794 > +#define SCALE_SRC_HEIGHT_WIDTH 0x1994 > +#define SCALE_OFFSET_0 0x1998 > +#define SCALE_PITCH 0x199c > +#define SCALE_X_INC 0x19a0 > +#define SCALE_Y_INC 0x19a4 > +#define SCALE_HACC 0x19a8 > +#define SCALE_VACC 0x19ac > +#define SCALE_DST_X_Y 0x19b0 > +#define SCALE_DST_HEIGHT_WIDTH 0x19b4 > +#define SCALE_3D_CNTL 0x1a00 > +#define SCALE_3D_DATATYPE 0x1a20 > +#define SETUP_CNTL 0x1bc4 > +#define SOLID_COLOR 0x1bc8 > +#define WINDOW_XY_OFFSET 0x1bcc > +#define DRAW_LINE_POINT 0x1bd0 > +#define SETUP_CNTL_PM4 0x1bd4 > +#define DST_PITCH_OFFSET_C 0x1c80 > +#define DP_GUI_MASTER_CNTL_C 0x1c84 > +#define SC_TOP_LEFT_C 0x1c88 > +#define SC_BOTTOM_RIGHT_C 0x1c8c > + > +#define CLR_CMP_MASK_3D 0x1A28 > +#define MISC_3D_STATE_CNTL_REG 0x1CA0 > +#define MC_SRC1_CNTL 0x19D8 > +#define TEX_CNTL 0x1800 > + > +/* CONSTANTS */ > +#define GUI_ACTIVE 0x80000000 > +#define ENGINE_IDLE 0x0 > + > +#define PLL_WR_EN 0x00000080 > + > +#define CLK_PIN_CNTL 0x0001 > +#define PPLL_CNTL 0x0002 > +#define PPLL_REF_DIV 0x0003 > +#define PPLL_DIV_0 0x0004 > +#define PPLL_DIV_1 0x0005 > +#define PPLL_DIV_2 0x0006 > +#define PPLL_DIV_3 0x0007 > +#define VCLK_ECP_CNTL 0x0008 > +#define HTOTAL_CNTL 0x0009 > +#define X_MPLL_REF_FB_DIV 0x000a > +#define XPLL_CNTL 0x000b > +#define XDLL_CNTL 0x000c > +#define XCLK_CNTL 0x000d > +#define MPLL_CNTL 0x000e > +#define MCLK_CNTL 0x000f > +#define AGP_PLL_CNTL 0x0010 > +#define FCP_CNTL 0x0012 > +#define PLL_TEST_CNTL 0x0013 > +#define P2PLL_CNTL 0x002a > +#define P2PLL_REF_DIV 0x002b > +#define P2PLL_DIV_0 0x002b > +#define POWER_MANAGEMENT 0x002f > + > +#define PPLL_RESET 0x01 > +#define PPLL_ATOMIC_UPDATE_EN 0x10000 > +#define PPLL_VGA_ATOMIC_UPDATE_EN 0x20000 > +#define PPLL_REF_DIV_MASK 0x3FF > +#define PPLL_FB3_DIV_MASK 0x7FF > +#define PPLL_POST3_DIV_MASK 0x70000 > +#define PPLL_ATOMIC_UPDATE_R 0x8000 > +#define PPLL_ATOMIC_UPDATE_W 0x8000 > +#define MEM_CFG_TYPE_MASK 0x3 > +#define XCLK_SRC_SEL_MASK 0x7 > +#define XPLL_FB_DIV_MASK 0xFF00 > +#define X_MPLL_REF_DIV_MASK 0xFF > + > +/* Config control values (CONFIG_CNTL) */ > +#define CFG_VGA_IO_DIS 0x00000400 > + > +/* CRTC control values (CRTC_GEN_CNTL) */ > +#define CRTC_CSYNC_EN 0x00000010 > + > +#define CRTC2_DBL_SCAN_EN 0x00000001 > +#define CRTC2_DISPLAY_DIS 0x00800000 > +#define CRTC2_FIFO_EXTSENSE 0x00200000 > +#define CRTC2_ICON_EN 0x00100000 > +#define CRTC2_CUR_EN 0x00010000 > +#define CRTC2_EXT_DISP_EN 0x01000000 > +#define CRTC2_EN 0x02000000 > +#define CRTC2_DISP_REQ_EN_B 0x04000000 > + > +#define CRTC_PIX_WIDTH_MASK 0x00000700 > +#define CRTC_PIX_WIDTH_4BPP 0x00000100 > +#define CRTC_PIX_WIDTH_8BPP 0x00000200 > +#define CRTC_PIX_WIDTH_15BPP 0x00000300 > +#define CRTC_PIX_WIDTH_16BPP 0x00000400 > +#define CRTC_PIX_WIDTH_24BPP 0x00000500 > +#define CRTC_PIX_WIDTH_32BPP 0x00000600 > + > +/* DAC_CNTL bit constants */ > +#define DAC_8BIT_EN 0x00000100 > +#define DAC_MASK 0xFF000000 > +#define DAC_BLANKING 0x00000004 > +#define DAC_RANGE_CNTL 0x00000003 > +#define DAC_CLK_SEL 0x00000010 > +#define DAC_PALETTE_ACCESS_CNTL 0x00000020 > +#define DAC_PALETTE2_SNOOP_EN 0x00000040 > +#define DAC_PDWN 0x00008000 > + > +/* CRTC_EXT_CNTL */ > +#define CRT_CRTC_DISPLAY_DIS 0x00000400 > +#define CRT_CRTC_ON 0x00008000 > + > +/* GEN_RESET_CNTL bit constants */ > +#define SOFT_RESET_GUI 0x00000001 > +#define SOFT_RESET_VCLK 0x00000100 > +#define SOFT_RESET_PCLK 0x00000200 > +#define SOFT_RESET_ECP 0x00000400 > +#define SOFT_RESET_DISPENG_XCLK 0x00000800 > + > +/* PC_GUI_CTLSTAT bit constants */ > +#define PC_BUSY_INIT 0x10000000 > +#define PC_BUSY_GUI 0x20000000 > +#define PC_BUSY_NGUI 0x40000000 > +#define PC_BUSY 0x80000000 > + > +#define BUS_MASTER_DIS 0x00000040 > +#define PM4_BUFFER_CNTL_NONPM4 0x00000000 > + > +/* DP_DATATYPE bit constants */ > +#define DST_8BPP 0x00000002 > +#define DST_15BPP 0x00000003 > +#define DST_16BPP 0x00000004 > +#define DST_24BPP 0x00000005 > +#define DST_32BPP 0x00000006 > + > +#define BRUSH_SOLIDCOLOR 0x00000d00 > + > +/* DP_GUI_MASTER_CNTL bit constants */ > +#define GMC_SRC_PITCH_OFFSET_DEFAULT 0x00000000 > +#define GMC_DST_PITCH_OFFSET_DEFAULT 0x00000000 > +#define GMC_SRC_CLIP_DEFAULT 0x00000000 > +#define GMC_DST_CLIP_DEFAULT 0x00000000 > +#define GMC_BRUSH_SOLIDCOLOR 0x000000d0 > +#define GMC_SRC_DSTCOLOR 0x00003000 > +#define GMC_BYTE_ORDER_MSB_TO_LSB 0x00000000 > +#define GMC_DP_SRC_RECT 0x02000000 > +#define GMC_3D_FCN_EN_CLR 0x00000000 > +#define GMC_AUX_CLIP_CLEAR 0x20000000 > +#define GMC_DST_CLR_CMP_FCN_CLEAR 0x10000000 > +#define GMC_WRITE_MASK_SET 0x40000000 > +#define GMC_DP_CONVERSION_TEMP_6500 0x00000000 > + > +/* DP_GUI_MASTER_CNTL ROP3 named constants */ > +#define GMC_ROP3_MASK 0x00ff0000 > +#define ROP3_PATCOPY 0x00f00000 > +#define ROP3_SRCCOPY 0x00cc0000 > + > +#define SRC_DSTCOLOR 0x00030000 > + > +/* DP_CNTL bit constants */ > +#define DST_X_RIGHT_TO_LEFT 0x00000000 > +#define DST_X_LEFT_TO_RIGHT 0x00000001 > +#define DST_Y_BOTTOM_TO_TOP 0x00000000 > +#define DST_Y_TOP_TO_BOTTOM 0x00000002 > +#define DST_X_MAJOR 0x00000000 > +#define DST_Y_MAJOR 0x00000004 > +#define DST_X_TILE 0x00000008 > +#define DST_Y_TILE 0x00000010 > +#define DST_LAST_PEL 0x00000020 > +#define DST_TRAIL_X_RIGHT_TO_LEFT 0x00000000 > +#define DST_TRAIL_X_LEFT_TO_RIGHT 0x00000040 > +#define DST_TRAP_FILL_RIGHT_TO_LEFT 0x00000000 > +#define DST_TRAP_FILL_LEFT_TO_RIGHT 0x00000080 > +#define DST_BRES_SIGN 0x00000100 > +#define DST_HOST_BIG_ENDIAN_EN 0x00000200 > +#define DST_POLYLINE_NONLAST 0x00008000 > +#define DST_RASTER_STALL 0x00010000 > +#define DST_POLY_EDGE 0x00040000 > + > +/* DP_MIX bit constants */ > +#define DP_SRC_RECT 0x00000200 > +#define DP_SRC_HOST 0x00000300 > +#define DP_SRC_HOST_BYTEALIGN 0x00000400 > + > +/* LVDS_GEN_CNTL constants */ > +#define LVDS_BL_MOD_LEVEL_MASK 0x0000ff00 > +#define LVDS_BL_MOD_LEVEL_SHIFT 8 > +#define LVDS_BL_MOD_EN 0x00010000 > +#define LVDS_DIGION 0x00040000 > +#define LVDS_BLON 0x00080000 > +#define LVDS_ON 0x00000001 > +#define LVDS_DISPLAY_DIS 0x00000002 > +#define LVDS_PANEL_TYPE_2PIX_PER_CLK 0x00000004 > +#define LVDS_PANEL_24BITS_TFT 0x00000008 > +#define LVDS_FRAME_MOD_NO 0x00000000 > +#define LVDS_FRAME_MOD_2_LEVELS 0x00000010 > +#define LVDS_FRAME_MOD_4_LEVELS 0x00000020 > +#define LVDS_RST_FM 0x00000040 > +#define LVDS_EN 0x00000080 > + > +/* CRTC2_GEN_CNTL constants */ > +#define CRTC2_EN 0x02000000 > + > +/* POWER_MANAGEMENT constants */ > +#define PWR_MGT_ON 0x00000001 > +#define PWR_MGT_MODE_MASK 0x00000006 > +#define PWR_MGT_MODE_PIN 0x00000000 > +#define PWR_MGT_MODE_REGISTER 0x00000002 > +#define PWR_MGT_MODE_TIMER 0x00000004 > +#define PWR_MGT_MODE_PCI 0x00000006 > +#define PWR_MGT_AUTO_PWR_UP_EN 0x00000008 > +#define PWR_MGT_ACTIVITY_PIN_ON 0x00000010 > +#define PWR_MGT_STANDBY_POL 0x00000020 > +#define PWR_MGT_SUSPEND_POL 0x00000040 > +#define PWR_MGT_SELF_REFRESH 0x00000080 > +#define PWR_MGT_ACTIVITY_PIN_EN 0x00000100 > +#define PWR_MGT_KEYBD_SNOOP 0x00000200 > +#define PWR_MGT_TRISTATE_MEM_EN 0x00000800 > +#define PWR_MGT_SELW4MS 0x00001000 > +#define PWR_MGT_SLOWDOWN_MCLK 0x00002000 > + > +#define PMI_PMSCR_REG 0x60 > + > +/* used by ATI bug fix for hardware ROM */ > +#define RAGE128_MPP_TB_CONFIG 0x01c0 > + > +#endif /* ATI_REGS_H */ > diff --git a/hw/display/trace-events b/hw/display/trace-events > index 387c6b8931..6b85cf2f64 100644 > --- a/hw/display/trace-events > +++ b/hw/display/trace-events > @@ -137,3 +137,7 @@ vga_cirrus_write_blt(uint32_t offset, uint32_t val) "offset 0x%x, val 0x%x" > sii9022_read_reg(uint8_t addr, uint8_t val) "addr 0x%02x, val 0x%02x" > sii9022_write_reg(uint8_t addr, uint8_t val) "addr 0x%02x, val 0x%02x" > sii9022_switch_mode(const char *mode) "mode: %s" > + > +# hw/display/ati*.c > +ati_mm_read(unsigned int size, uint64_t addr, uint64_t val) "%d 0x%"HWADDR_PRIx" -> 0x%"PRIx64 > +ati_mm_write(unsigned int size, uint64_t addr, uint64_t val) "%d 0x%"HWADDR_PRIx" <- 0x%"PRIx64 > I don't understand well the display code, but the result works very well, nice work :) Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Hello, On Tue, 12 Feb 2019, Philippe Mathieu-Daudé wrote: > Hi Zoltan, Thanks for the quick review and testing. I'll use your suggestions for the other (mips) patches in a v2. For this one I'm not convinced. > On 2/11/19 4:19 AM, BALATON Zoltan wrote: [...] >> + >> +static void ati_reg_write_offs(uint32_t *reg, int offs, >> + uint64_t data, unsigned int size) >> +{ >> + int shift, i; >> + uint32_t mask; >> + >> + for (i = 0; i < size; i++) { >> + shift = (offs + i) * 8; >> + mask = 0xffUL << shift; >> + *reg &= ~mask; >> + *reg |= (data & 0xff) << shift; >> + data >>= 8; > > I'd have use a pair of extract32/deposit32 but this is probably easier > to singlestep. You've told me that before but I have concerns about the asserts in those functions which to me seem like unnecessary overhead in such low level functions so unless these are removed or *_noassert versions introduced I'll stay away from them. But I'm also not too happy about these *_offs functions but some registers support 8/16/32 bit access and guest code seems to actually do this to update bits in the middle of the register at an odd address. Best would be if I could just set .impl.min = 4, .impl.max = 4 and .valid.min = 1 .valid.max = 4 for the mem region ops but I'm not sure that would work or would it? If that's working maybe I should just go with that instead. [...] >> diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h >> new file mode 100644 >> index 0000000000..85d045517c >> --- /dev/null >> +++ b/hw/display/ati_int.h >> @@ -0,0 +1,67 @@ >> +/* >> + * QEMU ATI SVGA emulation >> + * >> + * Copyright (c) 2019 BALATON Zoltan >> + * >> + * This work is licensed under the GNU GPL license version 2 or later. >> + */ >> + >> +#include "qemu/osdep.h" >> +#include "hw/pci/pci.h" >> +#include "vga_int.h" >> + >> +#undef DEBUG_ATI >> + >> +#ifdef DEBUG_ATI >> +#define DPRINTF(fmt, ...) printf("%s: " fmt, __func__, ## __VA_ARGS__) >> +#else >> +#define DPRINTF(fmt, ...) do {} while (0) > > Please use tracepoints (you already add some!). I won't and here's why: This is not a finished device model and I expect to need to add debug logs and change them frequently during further development and for such ad-hoc debugging DPRINF is still easier to use because I don't have to define the format string at one file and use them somewhere else. With DPRINTF I can just add a debug log at one place and change it easily without editing it at two unrelated places so it's easier to work with. Once development is finished those that we intend to leave in for later tracing can be converted to trace points (for which trace point is better) and at that point remove the DPRINTF macro. We still have enough DPRINTFs in QEMU so this should be OK. I've already added trace points to two such places but even for those I almost considered ditching them when checkpatch insisted I have to add 0x prefix to hex numbers (I don't like this because I know these are hex and printing e.g. 0x8 instead of 8 is just distracting from the actual important value which is what counts when I'm looking at a lot of these during debugging. Anything that distracts from actual values and makes it harder to read (such as timestamps and pids added by trace) is bad so I've considered going back to DPRINTF even for those trace points but will see if I can live with these for now.) But those that are still DPRINTFs won't be converted to trace but supposed to be removed when no longer needed. [...] > I don't understand well the display code, but the result works very > well, nice work :) > > Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com> Thanks, it's a start and currently only targeting Linux console with a lot more to do for it to be more useful. But I have limited time for this so since it's already useful to get mips_fulong2e working I thought that justifies including it now so others have a chance to look at it and maybe even help to improve it which can't happen if it's only sitting on my machine. Regards, BALATON Zoltan
On 12/02/2019 23:59, BALATON Zoltan wrote: > Hello, > > On Tue, 12 Feb 2019, Philippe Mathieu-Daudé wrote: >> Hi Zoltan, > > Thanks for the quick review and testing. I'll use your suggestions for the other > (mips) patches in a v2. For this one I'm not convinced. > >> On 2/11/19 4:19 AM, BALATON Zoltan wrote: > [...] >>> + >>> +static void ati_reg_write_offs(uint32_t *reg, int offs, >>> +                              uint64_t data, unsigned int size) >>> +{ >>> +   int shift, i; >>> +   uint32_t mask; >>> + >>> +   for (i = 0; i < size; i++) { >>> +       shift = (offs + i) * 8; >>> +       mask = 0xffUL << shift; >>> +       *reg &= ~mask; >>> +       *reg |= (data & 0xff) << shift; >>> +       data >>= 8; >> >> I'd have use a pair of extract32/deposit32 but this is probably easier >> to singlestep. > > You've told me that before but I have concerns about the asserts in those functions > which to me seem like unnecessary overhead in such low level functions so unless > these are removed or *_noassert versions introduced I'll stay away from them. > > But I'm also not too happy about these *_offs functions but some registers support > 8/16/32 bit access and guest code seems to actually do this to update bits in the > middle of the register at an odd address. Best would be if I could just set .impl.min > = 4, .impl.max = 4 and .valid.min = 1 .valid.max = 4 for the mem region ops but I'm > not sure that would work or would it? If that's working maybe I should just go with > that instead. > > [...] >>> diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h >>> new file mode 100644 >>> index 0000000000..85d045517c >>> --- /dev/null >>> +++ b/hw/display/ati_int.h >>> @@ -0,0 +1,67 @@ >>> +/* >>> + * QEMU ATI SVGA emulation >>> + * >>> + * Copyright (c) 2019 BALATON Zoltan >>> + * >>> + * This work is licensed under the GNU GPL license version 2 or later. >>> + */ >>> + >>> +#include "qemu/osdep.h" >>> +#include "hw/pci/pci.h" >>> +#include "vga_int.h" >>> + >>> +#undef DEBUG_ATI >>> + >>> +#ifdef DEBUG_ATI >>> +#define DPRINTF(fmt, ...) printf("%s: " fmt, __func__, ## __VA_ARGS__) >>> +#else >>> +#define DPRINTF(fmt, ...) do {} while (0) >> >> Please use tracepoints (you already add some!). > > I won't and here's why: This is not a finished device model and I expect to need to > add debug logs and change them frequently during further development and for such > ad-hoc debugging DPRINF is still easier to use because I don't have to define the > format string at one file and use them somewhere else. With DPRINTF I can just add a > debug log at one place and change it easily without editing it at two unrelated > places so it's easier to work with. Once development is finished those that we intend > to leave in for later tracing can be converted to trace points (for which trace point > is better) and at that point remove the DPRINTF macro. We still have enough DPRINTFs > in QEMU so this should be OK. I've already added trace points to two such places but > even for those I almost considered ditching them when checkpatch insisted I have to > add 0x prefix to hex numbers (I don't like this because I know these are hex and > printing e.g. 0x8 instead of 8 is just distracting from the actual important value > which is what counts when I'm looking at a lot of these during debugging. Anything > that distracts from actual values and makes it harder to read (such as timestamps and > pids added by trace) is bad so I've considered going back to DPRINTF even for those > trace points but will see if I can live with these for now.) But those that are still > DPRINTFs won't be converted to trace but supposed to be removed when no longer needed. > > [...] >> I don't understand well the display code, but the result works very >> well, nice work :) >> >> Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com> > > Thanks, it's a start and currently only targeting Linux console with a lot more to do > for it to be more useful. But I have limited time for this so since it's already > useful to get mips_fulong2e working I thought that justifies including it now so > others have a chance to look at it and maybe even help to improve it which can't > happen if it's only sitting on my machine. This looks interesting, however I never received the original via the mailing list. Did it get held somewhere because its size? Also it's probably worth pushing it to a suitable git repo since then it's easier for people to pull and update as required. ATB, Mark.
Hello, On Wed, 13 Feb 2019, Mark Cave-Ayland wrote: > On 12/02/2019 23:59, BALATON Zoltan wrote: > This looks interesting, however I never received the original via the mailing list. > Did it get held somewhere because its size? It appears in the list archives: http://lists.nongnu.org/archive/html/qemu-devel/2019-02/msg02471.html so I think it reached the list. If it's held for you it may be your systems. > Also it's probably worth pushing it to a suitable git repo since then it's easier for > people to pull and update as required. Yes, I though the qemu git repo would be suitable for that that's why I've submitted it. For testing it's just a single patch so git am should not be too difficult. For mac99 you can try with -vga none -device ati-vga and it gives you console under Linux but not much more at the moment. MacOS can't find it presumably because the info and driver it needs is missing from the device tree. I think these should be set up by the card ROM but even if I add a Mac card ROM with -device ati-vga,romfile=rom_from_an_ati_card.bin OpenBIOS does not seem to be able to run FCode roms so currently could not test with MacOS. Regards, BALATON Zoltan
On Mon, Feb 11, 2019 at 04:19:14AM +0100, BALATON Zoltan wrote: > At least two machines, the PPC mac99 and MIPS fulong2e, have an ATI > gfx chip by default (Rage 128 Pro and M6/RV100 respectively) and > guests running on these and the PMON2000 firmware of the fulong2e > expect this to be available. Fortunately these are very similar chips > so they can be mostly emulated in the same device model. This patch > adds basic emulation of these ATI VGA chips. > > While this is incomplete and currently only enough to run the MIPS > firmware and get console output with Linux, Which linux driver is this? If linux has a native driver it might make sense to also enable it on x86. Makes testing easier. You can add the driver to default_list[] in vl.c, then you don't need -vga none to remove the default vga when adding -device ati-vga. From a very brief look this looks sane overall, will have to find some time for a more detailed review. cheers, Gerd
On Tue, 12 Feb 2019 at 23:59, BALATON Zoltan <balaton@eik.bme.hu> wrote: > > Hello, > > On Tue, 12 Feb 2019, Philippe Mathieu-Daudé wrote: > > Hi Zoltan, > > Thanks for the quick review and testing. I'll use your suggestions for the > other (mips) patches in a v2. For this one I'm not convinced. > > > On 2/11/19 4:19 AM, BALATON Zoltan wrote: > [...] > >> + > >> +static void ati_reg_write_offs(uint32_t *reg, int offs, > >> + uint64_t data, unsigned int size) > >> +{ > >> + int shift, i; > >> + uint32_t mask; > >> + > >> + for (i = 0; i < size; i++) { > >> + shift = (offs + i) * 8; > >> + mask = 0xffUL << shift; > >> + *reg &= ~mask; > >> + *reg |= (data & 0xff) << shift; > >> + data >>= 8; > > > > I'd have use a pair of extract32/deposit32 but this is probably easier > > to singlestep. > > You've told me that before but I have concerns about the asserts in those > functions which to me seem like unnecessary overhead in such low level > functions so unless these are removed or *_noassert versions introduced > I'll stay away from them. The code above is IMHO pretty hard to read -- you have to think through all the shifts and masks to figure out exactly what is being done. I would definitely recommend extract32/deposit32, as they convey the intent much better. You're already inside a register accessor for a device model, there is much more overhead on this path than a few assert condition checks. (And they do catch bugs -- they found one in the arm code last month.) (Alternatively, if you believe the overhead of the asserts matters, then provide benchmarking demonstrating it, and we could look at restricting this assert to the case where start and length are compile-time constant, or to only the --enable-debug build.) > But I'm also not too happy about these *_offs functions but some registers > support 8/16/32 bit access and guest code seems to actually do this to > update bits in the middle of the register at an odd address. Best would be > if I could just set .impl.min = 4, .impl.max = 4 and .valid.min = 1 > .valid.max = 4 for the mem region ops but I'm not sure that would work or > would it? If that's working maybe I should just go with that instead. This will work, but only if all the registers in the memory region are happy with "read 32 bits, write back 32 bits", ie they have no "write-1-to-clear", "special behaviour on read", etc. (The memory system will implement byte reads as "read 32 bits, modify, write back".) If it does work then that's a nice way to do it. > I won't and here's why: This is not a finished device model and I expect > to need to add debug logs and change them frequently during further > development and for such ad-hoc debugging DPRINF is still easier to use > because I don't have to define the format string at one file and use them > somewhere else. If you want to use local debug printfs for your convenience that's fine. You don't have to submit them in the patches you send. (I use them quite a bit in development, and my stack of patches usually has a patch at the end called "debug junk" with that kind of thing in it.) But new code we take into upstream should be preferring trace events over ad-hoc DPRINTF. > Anything that > distracts from actual values and makes it harder to read (such as > timestamps and pids added by trace) -d trace:your_trace_event doesn't add timestamps or PIDs, FWIW. thanks -- PMM
On Tue, 19 Feb 2019, Gerd Hoffmann wrote: > On Mon, Feb 11, 2019 at 04:19:14AM +0100, BALATON Zoltan wrote: >> At least two machines, the PPC mac99 and MIPS fulong2e, have an ATI >> gfx chip by default (Rage 128 Pro and M6/RV100 respectively) and >> guests running on these and the PMON2000 firmware of the fulong2e >> expect this to be available. Fortunately these are very similar chips >> so they can be mostly emulated in the same device model. This patch >> adds basic emulation of these ATI VGA chips. >> >> While this is incomplete and currently only enough to run the MIPS >> firmware and get console output with Linux, > > Which linux driver is this? Those in linux/drivers/video/fbdev/aty/. This ati-vga model defaults to Rage 128 Pro (what PoweMac3,1 has, device id 0x5046) which is handled by the aty128fb driver under Linux. MIPS fulong2e has Mobility Radeon M6 (RV100, device id 0x5159) that you can also use as -device ati-vga,device_id=0x5159 which is driven by radeonfb under Linux. > If linux has a native driver it might make sense to also enable it on > x86. Makes testing easier. Yes it could work on all archs under Linux but I've only enabled it on the two I've tried. It could be added instead to the same place where vga is added in pci.mak if you like. > You can add the driver to default_list[] in vl.c, then you don't need > -vga none to remove the default vga when adding -device ati-vga. I could do that but since it's a bit unfinished (lacks advanced features such as video overlay, 3D accel, command FIFO, etc.) and only a basic implementation at the moment to work with Linux framebuffer that doesn't use these features I thought it might be a godd idea to make it a bit harder to access yet to avoid problem of someone trying it with a Windows guest with drivers from ATI which probably will result in bad picture. But it's up to you to decide if you want to add it to -vga option from the start or have it as optional device and only add to -vga when it's more fully implemented and tested. Regards, BALATON Zoltan
On Tue, 19 Feb 2019, Peter Maydell wrote: > On Tue, 12 Feb 2019 at 23:59, BALATON Zoltan <balaton@eik.bme.hu> wrote: >> On Tue, 12 Feb 2019, Philippe Mathieu-Daudé wrote: >>> On 2/11/19 4:19 AM, BALATON Zoltan wrote: >> [...] >>>> + >>>> +static void ati_reg_write_offs(uint32_t *reg, int offs, >>>> + uint64_t data, unsigned int size) >>>> +{ >>>> + int shift, i; >>>> + uint32_t mask; >>>> + >>>> + for (i = 0; i < size; i++) { >>>> + shift = (offs + i) * 8; >>>> + mask = 0xffUL << shift; >>>> + *reg &= ~mask; >>>> + *reg |= (data & 0xff) << shift; >>>> + data >>= 8; >>> >>> I'd have use a pair of extract32/deposit32 but this is probably easier >>> to singlestep. >> >> You've told me that before but I have concerns about the asserts in those >> functions which to me seem like unnecessary overhead in such low level >> functions so unless these are removed or *_noassert versions introduced >> I'll stay away from them. > > The code above is IMHO pretty hard to read -- you have to > think through all the shifts and masks to figure out exactly > what is being done. I would definitely recommend extract32/deposit32, > as they convey the intent much better. You're already inside a > register accessor for a device model, there is much more overhead > on this path than a few assert condition checks. (And they do > catch bugs -- they found one in the arm code last month.) > > (Alternatively, if you believe the overhead of the asserts matters, > then provide benchmarking demonstrating it, and we could look > at restricting this assert to the case where start and length are > compile-time constant, or to only the --enable-debug build.) > >> But I'm also not too happy about these *_offs functions but some registers >> support 8/16/32 bit access and guest code seems to actually do this to >> update bits in the middle of the register at an odd address. Best would be >> if I could just set .impl.min = 4, .impl.max = 4 and .valid.min = 1 >> .valid.max = 4 for the mem region ops but I'm not sure that would work or >> would it? If that's working maybe I should just go with that instead. > > This will work, but only if all the registers in the memory region > are happy with "read 32 bits, write back 32 bits", ie they have > no "write-1-to-clear", "special behaviour on read", etc. (The > memory system will implement byte reads as "read 32 bits, modify, > write back".) If it does work then that's a nice way to do it. OK, I'll try this and if it works that should be the best until a w1tc reg is found. >> I won't and here's why: This is not a finished device model and I expect >> to need to add debug logs and change them frequently during further >> development and for such ad-hoc debugging DPRINF is still easier to use >> because I don't have to define the format string at one file and use them >> somewhere else. > > If you want to use local debug printfs for your convenience that's fine. > You don't have to submit them in the patches you send. (I use them > quite a bit in development, and my stack of patches usually has a > patch at the end called "debug junk" with that kind of thing in it.) > But new code we take into upstream should be preferring trace events > over ad-hoc DPRINTF. I could do that but that wouldn't help anyone but me. Since the device model is far from finished at the moment I expect anyone who tries it with anything more complex than Linux framebuffer and maybe X driver will need to debug and fix it to add more features their drivers need and I'd like to make their job easier because I'd like to encourage people to help finishing this. (It would probably take me alone about 2 years otherwise with the current rate of effort I can put in this.) So not having any debug facilities to help this is counterproductive at this stage. Once the model is more finished these can be removed or turned into trace points but not yet I think. This wouldn't be the only device with DPRINTFs in QEMU so as a special case I think this makes sense to allow it to use DPRINTF as well for development. >> Anything that >> distracts from actual values and makes it harder to read (such as >> timestamps and pids added by trace) > > -d trace:your_trace_event doesn't add timestamps or PIDs, FWIW. Thanks, I was looking for that option but couldn't find yet. Regards, BALATON Zoltan
Hi, > > Which linux driver is this? > > Those in linux/drivers/video/fbdev/aty/. This ati-vga model defaults to Rage > 128 Pro (what PoweMac3,1 has, device id 0x5046) which is handled by the > aty128fb driver under Linux. MIPS fulong2e has Mobility Radeon M6 (RV100, > device id 0x5159) that you can also use as -device ati-vga,device_id=0x5159 > which is driven by radeonfb under Linux. So it could be tested with linux guests on x86 too I guess? Can the radeon drm driver handle the devices too? I'd also use model=<name> instead of device_id=... to switch between different devices. > > If linux has a native driver it might make sense to also enable it on > > x86. Makes testing easier. > > Yes it could work on all archs under Linux but I've only enabled it on the > two I've tried. It could be added instead to the same place where vga is > added in pci.mak if you like. Makes sense IMHO. > > You can add the driver to default_list[] in vl.c, then you don't need > > -vga none to remove the default vga when adding -device ati-vga. > > I could do that but since it's a bit unfinished (lacks advanced features > such as video overlay, 3D accel, command FIFO, etc.) and only a basic > implementation at the moment to work with Linux framebuffer that doesn't use > these features I thought it might be a godd idea to make it a bit harder to > access yet to avoid problem of someone trying it with a Windows guest with > drivers from ATI which probably will result in bad picture. But it's up to > you to decide if you want to add it to -vga option from the start or have it > as optional device and only add to -vga when it's more fully implemented and > tested. It's not about "-vga ati". This is about adding ati-vga to the list of display devices, so qemu will not try to add a vga automatically in case it finds "-device ati-vga" on the command line. That way "qemu -device ati-vga" will work fine, without "-vga none". Adding -vga ati indeed only makes sense once the implementation is more complete. cheers, Gerd
On Tue, 19 Feb 2019, Gerd Hoffmann wrote: >>> Which linux driver is this? >> >> Those in linux/drivers/video/fbdev/aty/. This ati-vga model defaults to Rage >> 128 Pro (what PoweMac3,1 has, device id 0x5046) which is handled by the >> aty128fb driver under Linux. MIPS fulong2e has Mobility Radeon M6 (RV100, >> device id 0x5159) that you can also use as -device ati-vga,device_id=0x5159 >> which is driven by radeonfb under Linux. > > So it could be tested with linux guests on x86 too I guess? > Can the radeon drm driver handle the devices too? Yes you can try with x86 guests, I haven't tested that yet. The radeon driver only supports RV100 and up I think so may only work with the 0x5159 variant not with Rage 128 Pro which had another driver r128 but not sure that still exists. Although these two chips are similar, Rage 128 Pro is a bit simpler that's why I'm targeting it first and also that's what the PowerMac3,1 (the ppc mac99 machine is converging to) has. The R128Pro is the last of the previous generation before Radeon, while RV100 is the stripped down simplest version of the R100 family which has some more 3D capability). But even if the DRM driver loads, probably only the mode setting part is useful at the moment as 3D is not implemented yet by this device model. So not sure how useful that is at the moment. (It was tested that MacOS X can load the low level driver probably similar to Linux DRM: https://www.emaculation.com/forum/viewtopic.php?f=34&t=7047&sid=7daa827c3e3d44ae7927a4ef900790bd&start=2025#p62215 but the high level rendering driver does not work yet. This was with v1 of this patch, I've made some fixes in v2 but likely that's not enough yet.) I haven't thought about 3D yet as it's too early for that but for planning I know that the Xenia XBox 360 emulator (https://xenia.jp/) has some emulation of the Xenos chip https://en.wikipedia.org/wiki/Xenos_(graphics_chip) which is like the last of the Radeon family started with R100 so probably has way more features than needed for RV100 so we could emulate that similarly or even get up to later Radeons based on that in the future. We may look there for inspiration and maybe even code (altough have to convert from C++ and it only seems to care about the 3D part of the chip as the legacy stuff is missing from or not used in the XBox). So I've worked mostly on the legacy part and 2D for now and left 3D for later but unlikely that I'll be able to do that anytime soon so if someone is interested to take that up it would be very welcome. This should go into the yet missing ati_3d.c. > I'd also use model=<name> instead of device_id=... to switch between > different devices. The only problem with that is that there are this many versions with confusing names (and maybe different device ids for different versions): https://www.x.org/wiki/RadeonFeature/#index5h2 so the only really good way to identify a chip is via device_id. This is not user friendly but at this stage probably will do and we can add alternative model property later which aliases some device ids (like it's done for CPU selection). Just to avoid confusion between e.g. R128, R128P, RV100, Radeon7000, MobilityM6, R100 some of which refer to the same and some are different models. (This table above is part of the X.org ATI page that has some useful info and also the Arch Linux https://wiki.archlinux.org/index.php/ATI page may have info about Linux drivers for these cards.) >>> You can add the driver to default_list[] in vl.c, then you don't need >>> -vga none to remove the default vga when adding -device ati-vga. >> >> I could do that but since it's a bit unfinished (lacks advanced features >> such as video overlay, 3D accel, command FIFO, etc.) and only a basic >> implementation at the moment to work with Linux framebuffer that doesn't use >> these features I thought it might be a godd idea to make it a bit harder to >> access yet to avoid problem of someone trying it with a Windows guest with >> drivers from ATI which probably will result in bad picture. But it's up to >> you to decide if you want to add it to -vga option from the start or have it >> as optional device and only add to -vga when it's more fully implemented and >> tested. > > It's not about "-vga ati". This is about adding ati-vga to the list of > display devices, so qemu will not try to add a vga automatically in case > it finds "-device ati-vga" on the command line. That way "qemu -device > ati-vga" will work fine, without "-vga none". > > Adding -vga ati indeed only makes sense once the implementation is more > complete. OK I thought those are related but if adding it to the default_list[] won't automatically add a -vga option then that's a good idea. I'll do that and also move the config lines to pci.mak in next version but wait for a few more days for more comments. Regards, BALATON Zoltan
Hi, > > So it could be tested with linux guests on x86 too I guess? > > Can the radeon drm driver handle the devices too? > > Yes you can try with x86 guests, I haven't tested that yet. The radeon > driver only supports RV100 and up I think so may only work with the 0x5159 > variant not with Rage 128 Pro which had another driver r128 but not sure > that still exists. Although these two chips are similar, Rage 128 Pro is a > bit simpler that's why I'm targeting it first and also that's what the > PowerMac3,1 (the ppc mac99 machine is converging to) has. The R128Pro is the > last of the previous generation before Radeon, while RV100 is the stripped > down simplest version of the R100 family which has some more 3D capability). > But even if the DRM driver loads, probably only the mode setting part is > useful at the moment as 3D is not implemented yet by this device model. Chances are not too bad that it'll be good enough to bring up a linux console. > > I'd also use model=<name> instead of device_id=... to switch between > > different devices. > > The only problem with that is that there are this many versions with > confusing names (and maybe different device ids for different versions): > > https://www.x.org/wiki/RadeonFeature/#index5h2 Do we want emulate them all? I'd guess picking a few models would be more useful ... > so the only really good way to identify a chip is via device_id. This is not > user friendly but at this stage probably will do and we can add alternative > model property later which aliases some device ids (like it's done for CPU Ok, I'd suggest to rename it to x-device-id (to indicate that it may go away later) if you want stick with device id for now. > OK I thought those are related but if adding it to the default_list[] won't > automatically add a -vga option then that's a good idea. I'll do that and > also move the config lines to pci.mak in next version but wait for a few > more days for more comments. One thing I've noticed is that you use the vbe registers internally. I'd suggest to not do that, I suspect it will only get into the way latter on. Better register your own GraphicsHwOps, then go call the vga ops in vga mode and your ati modesetting code in extended mode. virtio-vga does it that way if you want look at some sample code. Also looking at bochs-display.c is probably more helpful than looking at vga.c when figuring how to handle display updates. cheers, Gerd
Hello, On Wed, 20 Feb 2019, Gerd Hoffmann wrote: >>> So it could be tested with linux guests on x86 too I guess? >>> Can the radeon drm driver handle the devices too? >> >> Yes you can try with x86 guests, I haven't tested that yet. The radeon >> driver only supports RV100 and up I think so may only work with the 0x5159 >> variant not with Rage 128 Pro which had another driver r128 but not sure >> that still exists. Although these two chips are similar, Rage 128 Pro is a >> bit simpler that's why I'm targeting it first and also that's what the >> PowerMac3,1 (the ppc mac99 machine is converging to) has. The R128Pro is the >> last of the previous generation before Radeon, while RV100 is the stripped >> down simplest version of the R100 family which has some more 3D capability). >> But even if the DRM driver loads, probably only the mode setting part is >> useful at the moment as 3D is not implemented yet by this device model. > > Chances are not too bad that it'll be good enough to bring up a linux > console. Yes, that may work but I haven't tested it. I've quickly tried booting a Linux install CD with 0x5159 but got no picture while the same iso got me output with the default r128p. The latter uses aty128fb. I haven't try to debug the radeon case not sure if the boot CD I've tried does not support that or something is missing for the Linux driver. In general I'm only targeting r128p now and added 0x5159 for the mips_fulong2e board and for later direction to develop it further but haven't tested that much yet. >>> I'd also use model=<name> instead of device_id=... to switch between >>> different devices. >> >> The only problem with that is that there are this many versions with >> confusing names (and maybe different device ids for different versions): >> >> https://www.x.org/wiki/RadeonFeature/#index5h2 > > Do we want emulate them all? > I'd guess picking a few models would be more useful ... Sure we don't want them all I was just trying to show that names are less clear than device id. > Ok, I'd suggest to rename it to x-device-id (to indicate that it may go > away later) if you want stick with device id for now. That works for me as well, will do that in next version. > One thing I've noticed is that you use the vbe registers internally. > I'd suggest to not do that, I suspect it will only get into the way > latter on. Better register your own GraphicsHwOps, then go call the vga > ops in vga mode and your ati modesetting code in extended mode. > virtio-vga does it that way if you want look at some sample code. Also > looking at bochs-display.c is probably more helpful than looking at > vga.c when figuring how to handle display updates. Currently it's simpler for me the way I've done it so until this becomes a problem I don't want to add more code. Also I'm not sure how it would work because the hardware has both VGA engine and GUI engine (extended mode) working from the same memory and drivers sometimes poke VGA registers for example to set palette even in extended mode. So we can't avoid VGA altogether. Also we have no VESA BIOS and the one we have depends on VBE so that's also needed at the moment. This could be cleaned up later so I'd leave it for now and add more functionality instead. Regards, BALATON Zoltan
On Tue, 19 Feb 2019, Peter Maydell wrote: > On Tue, 12 Feb 2019 at 23:59, BALATON Zoltan <balaton@eik.bme.hu> wrote: >> On Tue, 12 Feb 2019, Philippe Mathieu-Daudé wrote: >>> On 2/11/19 4:19 AM, BALATON Zoltan wrote: This is where my question about valid/impl on mem ops started but I asked separately again after not getting an answer here. Then Peter answered here so I'm merging these threads again. I think for me this is solved with that I can't use mem ops for this even if it was working so I'm only recording here what I've found and will likely stay with implementing this in the device model. >> [...] >>>> + >>>> +static void ati_reg_write_offs(uint32_t *reg, int offs, >>>> + uint64_t data, unsigned int size) >>>> +{ >>>> + int shift, i; >>>> + uint32_t mask; >>>> + >>>> + for (i = 0; i < size; i++) { >>>> + shift = (offs + i) * 8; >>>> + mask = 0xffUL << shift; >>>> + *reg &= ~mask; >>>> + *reg |= (data & 0xff) << shift; >>>> + data >>= 8; >>> >>> I'd have use a pair of extract32/deposit32 but this is probably easier >>> to singlestep. >> >> You've told me that before but I have concerns about the asserts in those >> functions which to me seem like unnecessary overhead in such low level >> functions so unless these are removed or *_noassert versions introduced >> I'll stay away from them. > > The code above is IMHO pretty hard to read -- you have to > think through all the shifts and masks to figure out exactly > what is being done. I would definitely recommend extract32/deposit32, > as they convey the intent much better. You're already inside a > register accessor for a device model, there is much more overhead > on this path than a few assert condition checks. (And they do > catch bugs -- they found one in the arm code last month.) > > (Alternatively, if you believe the overhead of the asserts matters, > then provide benchmarking demonstrating it, and we could look > at restricting this assert to the case where start and length are > compile-time constant, or to only the --enable-debug build.) > >> But I'm also not too happy about these *_offs functions but some registers >> support 8/16/32 bit access and guest code seems to actually do this to >> update bits in the middle of the register at an odd address. Best would be >> if I could just set .impl.min = 4, .impl.max = 4 and .valid.min = 1 >> .valid.max = 4 for the mem region ops but I'm not sure that would work or >> would it? If that's working maybe I should just go with that instead. > > This will work, but only if all the registers in the memory region > are happy with "read 32 bits, write back 32 bits", ie they have > no "write-1-to-clear", "special behaviour on read", etc. (The > memory system will implement byte reads as "read 32 bits, modify, > write back".) If it does work then that's a nice way to do it. Based on a quick try This does not seem to work. With setting impl.{min,max}=4 and valid.min=1 valid.max=4 I get: ati_mm_write 4 0x9c0 <- 0x0 ati_mm_write 4 0x55 <- 0x4 ati_mm_write 4 0x50 <- 0x3000200 where this was before: ati_mm_write 4 0x9c0 <- 0x0 ati_mm_write 1 0x55 <- 0x4 ati_mm_write 4 0x50 <- 0x3000200 and should probably be something like: ati_mm_read 4 0x54 -> 0x0 ati_mm_write 4 0x54 <- 0x400 if access was adjusted as expected. So only the size was adjusted but not the address or value. Should I also add unaligned to either valid or impl? But probably that would not help either, I see a comment saying: /* FIXME: support unaligned access? */ in memory.c:access_with_adjusted_size(). But now I think that it would be a better idea to not use valid/impl for this but keep a local function (maybe rewritten to use deposit/extract) for now and use that explicitely. This is better for two reasons: no added read before write which might have side effects and also can model device better which only allows unaligned access to some registers but not all. It's a bit more code but I think this cannot be correctly handled by the memory subsystem anyway even if the above is fixed. >> Anything that >> distracts from actual values and makes it harder to read (such as >> timestamps and pids added by trace) > > -d trace:your_trace_event doesn't add timestamps or PIDs, FWIW. Neither this seems to work, I still get pid@timestamp: added to log lines with this (same as with -trace enable=pattern). Should I use something else than "log" trace backend? (But I can live with this, it's only slightly distracting as this is before the log line so I can just start reading from the colon, the interesting info is at the end of the line anyway.) Regards, BALATON Zoltan
On Thu, 21 Feb 2019, BALATON Zoltan wrote: > On Tue, 19 Feb 2019, Peter Maydell wrote: >> On Tue, 12 Feb 2019 at 23:59, BALATON Zoltan <balaton@eik.bme.hu> wrote: >>> On Tue, 12 Feb 2019, Philippe Mathieu-Daudé wrote: >>>> I'd have use a pair of extract32/deposit32 but this is probably easier By the way, should these lines in include/qemu/bitops.h have 1ULL instead of 1UL? 22 #define BIT(nr) (1UL << (nr)) 23 #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) Regards, BALATON Zoltan
On Thu, 21 Feb 2019 at 00:51, BALATON Zoltan <balaton@eik.bme.hu> wrote: > > On Thu, 21 Feb 2019, BALATON Zoltan wrote: > > On Tue, 19 Feb 2019, Peter Maydell wrote: > >> On Tue, 12 Feb 2019 at 23:59, BALATON Zoltan <balaton@eik.bme.hu> wrote: > >>> On Tue, 12 Feb 2019, Philippe Mathieu-Daudé wrote: > >>>> I'd have use a pair of extract32/deposit32 but this is probably easier > > By the way, should these lines in include/qemu/bitops.h have 1ULL instead of 1UL? > > 22 #define BIT(nr) (1UL << (nr)) > 23 #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) No, those are part of the bitmap API borrowed from the Linux kernel, which works on bitmaps defined as arrays of "unsigned long" values. (These are an annoying mismatch with how QEMU generally prefers to work in types of well-defined sizes, but they are what they are.) thanks -- PMM
On Wed, 20 Feb 2019, Gerd Hoffmann wrote: >>> So it could be tested with linux guests on x86 too I guess? >>> Can the radeon drm driver handle the devices too? >> >> Yes you can try with x86 guests, I haven't tested that yet. The radeon >> driver only supports RV100 and up I think so may only work with the 0x5159 >> variant not with Rage 128 Pro which had another driver r128 but not sure >> that still exists. Although these two chips are similar, Rage 128 Pro is a >> bit simpler that's why I'm targeting it first and also that's what the >> PowerMac3,1 (the ppc mac99 machine is converging to) has. The R128Pro is the >> last of the previous generation before Radeon, while RV100 is the stripped >> down simplest version of the R100 family which has some more 3D capability). >> But even if the DRM driver loads, probably only the mode setting part is >> useful at the moment as 3D is not implemented yet by this device model. > > Chances are not too bad that it'll be good enough to bring up a linux > console. I've tried a random Fedora live CD on qemu-system-x86_64 with ati-vga and with the default 0x5046 (Rage128Pro) that got me to X desktop. With 0x5159 (RV100) I don't get any picture. It looks like first problem is that the vgabios in QEMU doesn't like this card. I haven't checked further from there. Maybe eventually we'll need a vgabios-ati.bin for this but so far the existing one worked enough for at least the R128P. There's another problem I know about with the vgabios, that it's compiled with gcc that cannot compile real x86 16-bit code, only code that can be run in i386 real mode as discussed here https://stackoverflow.com/questions/19055647/how-to-tell-gcc-to-generate-16-bit-code-for-real-mode This is usually not a problem but some PPC machines e.g. the sam460ex has a BIOS emulator (usually a variant of x86emu) in their firmware (and maybe also X server does the same on these platforms) which runs the card's VESA bios to initialise the card and set mode but this fails with QEMU's vgabios due to unsupported 32bit prefixes in 16bit code. There's a VGA_FIXUP_ASM config option in vgabios which is supposed to help with this but it doesn't. Vgabios compiled with that option still has other opcodes not supported by x86emu. As a workaround I've found there are real 16-bit version of the Bochs vgabios compiled with bcc available here: http://www.nongnu.org/vgabios/ which work with x86emu and can be used via -device ati-vga,romfile=VGABIOS-lgpl-latest.bin but this has the same problem with 0x5159 as well. Regards, BALATON Zoltan
On Thu, Feb 21, 2019 at 12:44:22PM +0100, BALATON Zoltan wrote: > On Wed, 20 Feb 2019, Gerd Hoffmann wrote: > > > > So it could be tested with linux guests on x86 too I guess? > > > > Can the radeon drm driver handle the devices too? > > > > > > Yes you can try with x86 guests, I haven't tested that yet. The radeon > > > driver only supports RV100 and up I think so may only work with the 0x5159 > > > variant not with Rage 128 Pro which had another driver r128 but not sure > > > that still exists. Although these two chips are similar, Rage 128 Pro is a > > > bit simpler that's why I'm targeting it first and also that's what the > > > PowerMac3,1 (the ppc mac99 machine is converging to) has. The R128Pro is the > > > last of the previous generation before Radeon, while RV100 is the stripped > > > down simplest version of the R100 family which has some more 3D capability). > > > But even if the DRM driver loads, probably only the mode setting part is > > > useful at the moment as 3D is not implemented yet by this device model. > > > > Chances are not too bad that it'll be good enough to bring up a linux > > console. > > I've tried a random Fedora live CD on qemu-system-x86_64 with ati-vga and > with the default 0x5046 (Rage128Pro) that got me to X desktop. Wow. Guess X11 just runs unaccelerated on some framebuffer though. > With 0x5159 (RV100) I don't get any picture. So maybe the drm driver tries some more advanced stuff here. > It looks like first problem is that the > vgabios in QEMU doesn't like this card. The seabios banner should show up in any case. That is just using standard vga text mode without any vendor-specific extensions. The PCI IDs of device and bios must match, so you need one bios per card version. You can use roms/config.vga-qxl as template and replace the IDs there. Maybe it makes sense to have an abstract ati-vga-base type, then create two (or more) types using that base type which only differ in pci id and vgabios (and maybe more later on when the more advanced stuff is implemented). > There's another problem I know about with the vgabios, that it's compiled > with gcc that cannot compile real x86 16-bit code, only code that can be run > in i386 real mode as discussed here > https://stackoverflow.com/questions/19055647/how-to-tell-gcc-to-generate-16-bit-code-for-real-mode > > This is usually not a problem but some PPC machines e.g. the sam460ex has a > BIOS emulator (usually a variant of x86emu) in their firmware (and maybe > also X server does the same on these platforms) which runs the card's VESA > bios to initialise the card and set mode but this fails with QEMU's vgabios > due to unsupported 32bit prefixes in 16bit code. The X server does it on pretty much anything but i386. Specifically x86emu is also used on x86_64 (when using the vesa driver). > There's a VGA_FIXUP_ASM config option in vgabios which is supposed to > help with this but it doesn't. It surely helps at least with the x86emu variant used by the X server, even for RHEL-5 (which is more than 10 years old meanwhile). So the sam460ex firmware must have a ancient version/fork of x86emu. Is it open source, so there is a chance to update it? cheers, Gerd
On Thu, 21 Feb 2019, Gerd Hoffmann wrote: > On Thu, Feb 21, 2019 at 12:44:22PM +0100, BALATON Zoltan wrote: >> On Wed, 20 Feb 2019, Gerd Hoffmann wrote: >>>>> So it could be tested with linux guests on x86 too I guess? >>>>> Can the radeon drm driver handle the devices too? >>>> >>>> Yes you can try with x86 guests, I haven't tested that yet. The radeon >>>> driver only supports RV100 and up I think so may only work with the 0x5159 >>>> variant not with Rage 128 Pro which had another driver r128 but not sure >>>> that still exists. Although these two chips are similar, Rage 128 Pro is a >>>> bit simpler that's why I'm targeting it first and also that's what the >>>> PowerMac3,1 (the ppc mac99 machine is converging to) has. The R128Pro is the >>>> last of the previous generation before Radeon, while RV100 is the stripped >>>> down simplest version of the R100 family which has some more 3D capability). >>>> But even if the DRM driver loads, probably only the mode setting part is >>>> useful at the moment as 3D is not implemented yet by this device model. >>> >>> Chances are not too bad that it'll be good enough to bring up a linux >>> console. >> >> I've tried a random Fedora live CD on qemu-system-x86_64 with ati-vga and >> with the default 0x5046 (Rage128Pro) that got me to X desktop. > > Wow. Guess X11 just runs unaccelerated on some framebuffer though. Sure, no acceleration is implemented yet apart from some simple 2D the Linux console and the PMON2000 firmware of the fulong2e uses (and even those may have bugs) so anything that tries to use more than that will have gfx issues for now. The Fedora image I've tried was an LXDE one I think so it probably does not use many advanced features and did not work very well in that I could not get a terminal window but there was picture and could get the window manager preferences at least but trying to access the Applications or Terminals menu got me an error so looks like it's a bug in the image itself. I did not try any other Gnome or KDE ones as those are twice the size of the simple image I've downloaded. Maybe those would use more acceleration and fail. >> With 0x5159 (RV100) I don't get any picture. > > So maybe the drm driver tries some more advanced stuff here. I'm not sure, haven't seen it poking the card with -trace enable=ati* so maybe it's missing something very early. Since I got no output even in VGA mode I could not find out what it's trying to do. (It may also be possible it's trying to access the card via a command FIFO or some other advanced mode which is not yet implemented. These cards have some other ways to send reg values than just via io and mmio and anything more advanced than simple drivers would probably try to use those.) >> It looks like first problem is that the >> vgabios in QEMU doesn't like this card. > > The seabios banner should show up in any case. That is just using > standard vga text mode without any vendor-specific extensions. And that's not showing up with 0x5159 that's why I think vgabios has an issue. You can try: qemu-system-x86_64 -device ati-vga -d trace:vga\*,trace:ati\* and qemu-system-x86_64 -device ati-vga,x-device-id=0x5159 -d trace:vga\*,trace:ati\* to see what I mean. (Also just sent a v3 where device_id is now x-device-id and some more review changes but nothing new otherwise.) > The PCI IDs of device and bios must match, so you need one bios per card > version. You can use roms/config.vga-qxl as template and replace the > IDs there. OK but the vgabios-std.bin seems to work with the r128p emulation but not with the rv100 device id. I don't have time for it now so using the vgabios-std is good enough for me to test the r128p with OpenBIOS (which just uses VBE ports to configure the device ignoring the vgabios and eventually should be fixed to use a PPC compatible OpenFirmware FCode ROM instead for MacOS). It also seems to work with x86_64 so unless you want to target rv100 from the beginning it could wait until r128p works. There's enough stuff to implement for that as well before I can get to rv100 so I won't try to take up vgabios development as well now. > Maybe it makes sense to have an abstract ati-vga-base type, then create > two (or more) types using that base type which only differ in pci id and > vgabios (and maybe more later on when the more advanced stuff is > implemented). We could do that but until we only have two of these I think it's simper to handle them in one type then this could be split up later when it's clearer what're the common parts and what would go in subclasses. I'll think about this but for now it seems simpler to just have a single class with some ifs where there are differences. Later Radeon versions probably will have more differences so for those a subclass would make sense but maybe not yet. >> There's another problem I know about with the vgabios, that it's compiled >> with gcc that cannot compile real x86 16-bit code, only code that can be run >> in i386 real mode as discussed here >> https://stackoverflow.com/questions/19055647/how-to-tell-gcc-to-generate-16-bit-code-for-real-mode >> >> This is usually not a problem but some PPC machines e.g. the sam460ex has a >> BIOS emulator (usually a variant of x86emu) in their firmware (and maybe >> also X server does the same on these platforms) which runs the card's VESA >> bios to initialise the card and set mode but this fails with QEMU's vgabios >> due to unsupported 32bit prefixes in 16bit code. > > The X server does it on pretty much anything but i386. > Specifically x86emu is also used on x86_64 (when using the vesa driver). > >> There's a VGA_FIXUP_ASM config option in vgabios which is supposed to >> help with this but it doesn't. > > It surely helps at least with the x86emu variant used by the X server, > even for RHEL-5 (which is more than 10 years old meanwhile). Have you tested it recently? I've tried recompiling vgabios in QEMU with that but it still failed on the BIOS emulator in the firmware although with different error than before. > So the sam460ex firmware must have a ancient version/fork of x86emu. > Is it open source, so there is a chance to update it? Yes, see qemu/roms/u-boot-sam460ex/ (mirrored from my github.com/zbalaton/u-boot-sam460ex.git repo for lack of better upstream at the moment). There's also a newer version of this firmware based on U-Boot 2005.a (http://www.acube-systems.biz/index.php?page=hardware&pid=5) to which we may be able to update instead of fixing this old version but I haven't tried that and I had to fix some bugs in the 2010/2011 version we have now too so those sources might not be working too well without some testing and fixes. Also I've actually found this vgabios problem with a different machine I'm working on and that does not have source for the firmware so that could not be fixed other than using bcc compiled vgabios which seems to work at least until a better solution can be done. Regards, BALATON Zoltan
diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak index 9eb1208b58..231f81eca2 100644 --- a/default-configs/mips64el-softmmu.mak +++ b/default-configs/mips64el-softmmu.mak @@ -13,3 +13,4 @@ CONFIG_VT82C686=y CONFIG_MIPS_BOSTON=y CONFIG_FITLOADER=y CONFIG_PCI_EXPRESS_XILINX=y +CONFIG_ATI_VGA=y diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 52acb7cf39..75024b298e 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -56,6 +56,7 @@ CONFIG_DEC_PCI=y CONFIG_IDE_MACIO=y CONFIG_MAC_OLDWORLD=y CONFIG_MAC_NEWWORLD=y +CONFIG_ATI_VGA=y # For PReP CONFIG_PREP=y diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 7c4ae9a0fd..e58947e6f3 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -53,3 +53,5 @@ virtio-gpu-3d.o-cflags := $(VIRGL_CFLAGS) virtio-gpu-3d.o-libs += $(VIRGL_LIBS) obj-$(CONFIG_DPCD) += dpcd.o obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dp.o + +obj-$(CONFIG_ATI_VGA) += ati.o ati_2d.o diff --git a/hw/display/ati.c b/hw/display/ati.c new file mode 100644 index 0000000000..18458110b2 --- /dev/null +++ b/hw/display/ati.c @@ -0,0 +1,582 @@ +/* + * QEMU ATI SVGA emulation + * + * Copyright (c) 2019 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + */ + +/* + * WARNING: + * This is very incomplete and only enough to get Linux console output yet. + * At the moment it's little more than a frame buffer with minimal functions, + * other more advanced features of the hardware are yet to be implemented. + * We only aim for Rage 128 Pro (and some RV100) and 2D only at first, + * No 3D at all yet (maybe after 2D works, but feel free to improve it) + */ + +#include "ati_int.h" +#include "ati_regs.h" +#include "vga_regs.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "ui/console.h" +#include "trace.h" + +#define PCI_VENDOR_ID_ATI 0x1002 +/* Rage128 Pro GL */ +#define PCI_DEVICE_ID_ATI_RAGE128_PF 0x5046 +/* Radeon RV100 (VE) */ +#define PCI_DEVICE_ID_ATI_RADEON_QY 0x5159 + +enum { VGA_MODE, EXT_MODE }; + +static void ati_vga_switch_mode(ATIVGAState *s) +{ + DPRINTF("%d -> %d\n", + s->mode, !!(s->regs.crtc_gen_cntl & CRTC2_EXT_DISP_EN)); + if (s->regs.crtc_gen_cntl & CRTC2_EXT_DISP_EN) { + /* Extended mode enabled */ + s->mode = EXT_MODE; + if (s->regs.crtc_gen_cntl & CRTC2_EN) { + /* CRT controller enabled, use CRTC values */ + uint32_t offs = s->regs.crtc_offset & 0x07ffffff; + int stride = (s->regs.crtc_pitch & 0x7ff) * 8; + int bpp = 0; + int h, v; + + if (s->regs.crtc_h_total_disp == 0) { + s->regs.crtc_h_total_disp = ((640 / 8) - 1) << 16; + } + if (s->regs.crtc_v_total_disp == 0) { + s->regs.crtc_v_total_disp = (480 - 1) << 16; + } + h = ((s->regs.crtc_h_total_disp >> 16) + 1) * 8; + v = (s->regs.crtc_v_total_disp >> 16) + 1; + switch (s->regs.crtc_gen_cntl & CRTC_PIX_WIDTH_MASK) { + case CRTC_PIX_WIDTH_4BPP: + bpp = 4; + break; + case CRTC_PIX_WIDTH_8BPP: + bpp = 8; + break; + case CRTC_PIX_WIDTH_15BPP: + bpp = 15; + break; + case CRTC_PIX_WIDTH_16BPP: + bpp = 16; + break; + case CRTC_PIX_WIDTH_24BPP: + bpp = 24; + break; + case CRTC_PIX_WIDTH_32BPP: + bpp = 32; + break; + default: + qemu_log_mask(LOG_UNIMP, "Unsupported bpp value"); + } + assert(bpp != 0); + DPRINTF("Switching to %dx%d %d %d @ %x\n", h, v, stride, bpp, offs); + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_ENABLE); + vbe_ioport_write_data(&s->vga, 0, VBE_DISPI_DISABLED); + /* reset VBE regs then set up mode */ + s->vga.vbe_regs[VBE_DISPI_INDEX_XRES] = h; + s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] = v; + s->vga.vbe_regs[VBE_DISPI_INDEX_BPP] = bpp; + /* enable mode via ioport so it updates vga regs */ + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_ENABLE); + vbe_ioport_write_data(&s->vga, 0, VBE_DISPI_ENABLED | + VBE_DISPI_LFB_ENABLED | VBE_DISPI_NOCLEARMEM | + (s->regs.dac_cntl & DAC_8BIT_EN ? VBE_DISPI_8BIT_DAC : 0)); + /* now set offset and stride after enable as that resets these */ + if (stride) { + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_VIRT_WIDTH); + vbe_ioport_write_data(&s->vga, 0, stride); + if (offs % stride == 0) { + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_Y_OFFSET); + vbe_ioport_write_data(&s->vga, 0, offs / stride); + } else { + /* FIXME what to do with this? */ + error_report("VGA offset is not multiple of pitch, " + "expect bad picture"); + } + } + } + } else { + /* VGA mode enabled */ + s->mode = VGA_MODE; + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_ENABLE); + vbe_ioport_write_data(&s->vga, 0, VBE_DISPI_DISABLED); + } +} + +static uint64_t ati_reg_read_offs(uint64_t reg, int offs, unsigned int size) +{ + uint64_t val = 0; + uint32_t mask; + int i; + + for (i = 0; i < size; i++) { + mask = 0xffUL << (offs + i) * 8; + val |= reg & mask; + } + val >>= offs * 8; + return val; +} + +static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size) +{ + ATIVGAState *s = opaque; + uint64_t val = 0; + + switch (addr) { + case MM_INDEX: + val = s->regs.mm_index; + break; + case MM_DATA ... MM_DATA + 3: + /* indexed access to regs or memory */ + if (s->regs.mm_index & 0x80000000) { + if (s->regs.mm_index <= s->vga.vram_size - size) { + int i = size - 1; + while (i >= 0) { + val <<= 8; + val |= s->vga.vram_ptr[s->regs.mm_index + i--]; + } + } + } else { + val = ati_mm_read(s, s->regs.mm_index + addr - MM_DATA, size); + } + break; + case BIOS_0_SCRATCH: + val = s->regs.bios_0_scratch; + break; + case CRTC_GEN_CNTL ... CRTC_GEN_CNTL + 3: + if (addr == CRTC_GEN_CNTL && size == 4) { + val = s->regs.crtc_gen_cntl; + } else { + val = ati_reg_read_offs(s->regs.crtc_gen_cntl, + addr - CRTC_GEN_CNTL, size); + } + break; + case CRTC_EXT_CNTL ... CRTC_EXT_CNTL + 3: + if (addr == CRTC_EXT_CNTL && size == 4) { + val = s->regs.crtc_ext_cntl; + } else { + val = ati_reg_read_offs(s->regs.crtc_ext_cntl, + addr - CRTC_EXT_CNTL, size); + } + break; + case DAC_CNTL: + val = s->regs.dac_cntl; + break; +/* case GPIO_MONID: FIXME hook up DDC I2C here */ + case PALETTE_INDEX: + /* FIXME unaligned access */ + val = vga_ioport_read(&s->vga, VGA_PEL_IR) << 16; + val |= vga_ioport_read(&s->vga, VGA_PEL_IW) & 0xff; + break; + case PALETTE_DATA: + val = vga_ioport_read(&s->vga, VGA_PEL_D); + break; + case CNFG_MEMSIZE: + val = s->vga.vram_size; + break; + case MC_STATUS: + val = 5; + break; + case RBBM_STATUS: + case GUI_STAT: + val = 64; /* free CMDFIFO entries */ + break; + case CRTC_H_TOTAL_DISP: + val = s->regs.crtc_h_total_disp; + break; + case CRTC_H_SYNC_STRT_WID: + val = s->regs.crtc_h_sync_strt_wid; + break; + case CRTC_V_TOTAL_DISP: + val = s->regs.crtc_v_total_disp; + break; + case CRTC_V_SYNC_STRT_WID: + val = s->regs.crtc_v_sync_strt_wid; + break; + case CRTC_OFFSET: + val = s->regs.crtc_offset; + break; + case CRTC_OFFSET_CNTL: + val = s->regs.crtc_offset_cntl; + break; + case CRTC_PITCH: + val = s->regs.crtc_pitch; + break; + case 0xf00 ... 0xfff: + val = pci_default_read_config(&s->dev, addr - 0xf00, size); + break; + case DST_OFFSET: + val = s->regs.dst_offset; + break; + case DST_PITCH: + val = s->regs.dst_pitch; + break; + case DST_WIDTH: + val = s->regs.dst_width; + break; + case DST_HEIGHT: + val = s->regs.dst_height; + break; + case SRC_X: + val = s->regs.src_x; + break; + case SRC_Y: + val = s->regs.src_y; + break; + case DST_X: + val = s->regs.dst_x; + break; + case DST_Y: + val = s->regs.dst_y; + break; + case DP_GUI_MASTER_CNTL: + val = s->regs.dp_gui_master_cntl; + break; + case DP_BRUSH_BKGD_CLR: + val = s->regs.dp_brush_bkgd_clr; + break; + case DP_BRUSH_FRGD_CLR: + val = s->regs.dp_brush_frgd_clr; + break; + case DP_SRC_FRGD_CLR: + val = s->regs.dp_src_frgd_clr; + break; + case DP_SRC_BKGD_CLR: + val = s->regs.dp_src_bkgd_clr; + break; + case DP_CNTL: + val = s->regs.dp_cntl; + break; + case DP_WRITE_MASK: + val = s->regs.dp_write_mask; + break; + case DEFAULT_OFFSET: + val = s->regs.default_offset; + break; + case DEFAULT_PITCH: + val = s->regs.default_pitch; + break; + case DEFAULT_SC_BOTTOM_RIGHT: + val = s->regs.default_sc_bottom_right; + break; + default: + break; + } + trace_ati_mm_read(size, addr, val); + + return val; +} + +static void ati_reg_write_offs(uint32_t *reg, int offs, + uint64_t data, unsigned int size) +{ + int shift, i; + uint32_t mask; + + for (i = 0; i < size; i++) { + shift = (offs + i) * 8; + mask = 0xffUL << shift; + *reg &= ~mask; + *reg |= (data & 0xff) << shift; + data >>= 8; + } +} + +static void ati_mm_write(void *opaque, hwaddr addr, + uint64_t data, unsigned int size) +{ + ATIVGAState *s = opaque; + int i; + + trace_ati_mm_write(size, addr, data); + switch (addr) { + case MM_INDEX: + s->regs.mm_index = data; + break; + case MM_DATA ... MM_DATA + 3: + /* indexed access to regs or memory */ + if (s->regs.mm_index & 0x80000000) { + if (s->regs.mm_index <= s->vga.vram_size - size) { + int i = 0; + while (i < size) { + s->vga.vram_ptr[s->regs.mm_index + i] = data & 0xff; + data >>= 8; + } + } + } else { + ati_mm_write(s, s->regs.mm_index + addr - MM_DATA, data, size); + } + break; + case BIOS_0_SCRATCH: + s->regs.bios_0_scratch = data; + break; + case CRTC_GEN_CNTL ... CRTC_GEN_CNTL + 3: + if (addr == CRTC_GEN_CNTL && size == 4) { + s->regs.crtc_gen_cntl = data; + } else { + ati_reg_write_offs(&s->regs.crtc_gen_cntl, + addr - CRTC_GEN_CNTL, data, size); + } + break; + case CRTC_EXT_CNTL ... CRTC_EXT_CNTL + 3: + if (addr == CRTC_EXT_CNTL && size == 4) { + s->regs.crtc_ext_cntl = data; + } else { + ati_reg_write_offs(&s->regs.crtc_ext_cntl, + addr - CRTC_EXT_CNTL, data, size); + } + if (s->regs.crtc_ext_cntl & CRT_CRTC_DISPLAY_DIS) { + DPRINTF("Display disabled\n"); + s->vga.ar_index &= ~0x20; + } else { + DPRINTF("Display enabled\n"); + s->vga.ar_index |= 0x20; + } + if (!!(s->regs.crtc_gen_cntl & CRTC2_EXT_DISP_EN) != s->mode) { + ati_vga_switch_mode(s); + } + break; + case DAC_CNTL: + s->regs.dac_cntl = data & 0xffffe3ff; + s->vga.dac_8bit = !!(data & DAC_8BIT_EN); + break; +/* case GPIO_MONID: FIXME hook up DDC I2C here */ + case PALETTE_INDEX ... PALETTE_INDEX + 3: + if (size == 4) { + vga_ioport_write(&s->vga, VGA_PEL_IR, (data >> 16) & 0xff); + vga_ioport_write(&s->vga, VGA_PEL_IW, data & 0xff); + } else { + if (addr == PALETTE_INDEX) { + vga_ioport_write(&s->vga, VGA_PEL_IW, data & 0xff); + } else { + vga_ioport_write(&s->vga, VGA_PEL_IR, data & 0xff); + } + } + break; + case PALETTE_DATA: + for (i = 0; i < size && i < 3; i++) { + vga_ioport_write(&s->vga, VGA_PEL_D, data & 0xff); + data >>= 8; + } + break; + case CRTC_H_TOTAL_DISP: + s->regs.crtc_h_total_disp = data & 0x07ff07ff; + break; + case CRTC_H_SYNC_STRT_WID: + s->regs.crtc_h_sync_strt_wid = data & 0x17bf1fff; + break; + case CRTC_V_TOTAL_DISP: + s->regs.crtc_v_total_disp = data & 0x0fff0fff; + break; + case CRTC_V_SYNC_STRT_WID: + s->regs.crtc_v_sync_strt_wid = data & 0x9f0fff; + break; + case CRTC_OFFSET: + s->regs.crtc_offset = data & 0xc7ffffff; + break; + case CRTC_OFFSET_CNTL: + s->regs.crtc_offset_cntl = data; /* FIXME */ + break; + case CRTC_PITCH: + s->regs.crtc_pitch = data & 0x07ff07ff; + break; + case 0xf00 ... 0xfff: + /* read-only copy of PCI config space so ignore writes */ + break; + case DST_OFFSET: + s->regs.dst_offset = data & 0xfffffc00; + break; + case DST_PITCH: + s->regs.dst_pitch = data & 0x3fff; + break; + case DST_WIDTH: + s->regs.dst_width = data & 0x3fff; + ati_2d_blit(s); + break; + case DST_HEIGHT: + s->regs.dst_height = data & 0x3fff; + break; + case SRC_X: + s->regs.src_x = data & 0x3fff; + break; + case SRC_Y: + s->regs.src_y = data & 0x3fff; + break; + case DST_X: + s->regs.dst_x = data & 0x3fff; + break; + case DST_Y: + s->regs.dst_y = data & 0x3fff; + break; + case SRC_Y_X: + s->regs.src_x = data & 0x3fff; + s->regs.src_y = (data >> 16) & 0x3fff; + break; + case DST_Y_X: + s->regs.dst_x = data & 0x3fff; + s->regs.dst_y = (data >> 16) & 0x3fff; + break; + case DST_HEIGHT_WIDTH: + s->regs.dst_width = data & 0x3fff; + s->regs.dst_height = (data >> 16) & 0x3fff; + ati_2d_blit(s); + break; + case DP_GUI_MASTER_CNTL: + s->regs.dp_gui_master_cntl = data; + break; + case DST_WIDTH_X: + s->regs.dst_x = data & 0x3fff; + s->regs.dst_width = (data >> 16) & 0x3fff; + ati_2d_blit(s); + break; + case SRC_X_Y: + s->regs.src_y = data & 0x3fff; + s->regs.src_x = (data >> 16) & 0x3fff; + break; + case DST_X_Y: + s->regs.dst_y = data & 0x3fff; + s->regs.dst_x = (data >> 16) & 0x3fff; + break; + case DST_HEIGHT_Y: + s->regs.dst_y = data & 0x3fff; + s->regs.dst_height = (data >> 16) & 0x3fff; + break; + case DP_BRUSH_BKGD_CLR: + s->regs.dp_brush_bkgd_clr = data; + break; + case DP_BRUSH_FRGD_CLR: + s->regs.dp_brush_frgd_clr = data; + break; + case DP_CNTL: + s->regs.dp_cntl = data; + break; + case DP_WRITE_MASK: + s->regs.dp_write_mask = data; + break; + case DEFAULT_OFFSET: + data &= (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF ? + 0x03fffc00 : 0xfffffc00); + s->regs.default_offset = data; + break; + case DEFAULT_PITCH: + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + s->regs.default_pitch = data & 0x103ff; + } + break; + case DEFAULT_SC_BOTTOM_RIGHT: + s->regs.default_sc_bottom_right = data & 0x3fff3fff; + break; + default: + break; + } +} + +static const MemoryRegionOps ati_mm_ops = { + .read = ati_mm_read, + .write = ati_mm_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ati_vga_realize(PCIDevice *dev, Error **errp) +{ + ATIVGAState *s = ATI_VGA(dev); + VGACommonState *vga = &s->vga; + + if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF && + s->dev_id != PCI_DEVICE_ID_ATI_RADEON_QY) { + error_setg(errp, "Unknown ATI VGA device id, " + "only 0x5046 and 0x5159 are supported"); + return; + } + if (s->dev_id == PCI_DEVICE_ID_ATI_RADEON_QY && + s->vga.vram_size_mb < 16) { + warn_report("Too small video memory for device id"); + s->vga.vram_size_mb = 16; + } + + /* init vga compat bits */ + vga_common_init(vga, OBJECT(s)); + vga_init(vga, OBJECT(s), pci_address_space(dev), + pci_address_space_io(dev), true); + vga->con = graphic_console_init(DEVICE(s), 0, s->vga.hw_ops, &s->vga); + + /* mmio register space */ + memory_region_init_io(&s->mm, OBJECT(s), &ati_mm_ops, s, + "ati.mmregs", 0x4000); + /* io space is alias to beginning of mmregs */ + memory_region_init_alias(&s->io, OBJECT(s), "ati.io", &s->mm, 0, 0x100); + + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram); + pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io); + pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mm); +} + +static void ati_vga_reset(DeviceState *dev) +{ + ATIVGAState *s = ATI_VGA(dev); + PCIDevice *pd = PCI_DEVICE(dev); + + pci_set_word(pd->config + PCI_DEVICE_ID, s->dev_id); + /* reset vga */ + vga_common_reset(&s->vga); + s->mode = VGA_MODE; +} + +static void ati_vga_exit(PCIDevice *dev) +{ + ATIVGAState *s = ATI_VGA(dev); + + graphic_console_close(s->vga.con); +} + +static Property ati_vga_properties[] = { + DEFINE_PROP_UINT32("vgamem_mb", ATIVGAState, vga.vram_size_mb, 16), + DEFINE_PROP_UINT16("device_id", ATIVGAState, dev_id, + PCI_DEVICE_ID_ATI_RAGE128_PF), + DEFINE_PROP_END_OF_LIST() +}; + +static void ati_vga_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + dc->reset = ati_vga_reset; + dc->props = ati_vga_properties; + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + + k->class_id = PCI_CLASS_DISPLAY_VGA; + k->vendor_id = PCI_VENDOR_ID_ATI; + k->device_id = PCI_DEVICE_ID_ATI_RAGE128_PF; + k->romfile = "vgabios-stdvga.bin"; + k->realize = ati_vga_realize; + k->exit = ati_vga_exit; +} + +static const TypeInfo ati_vga_info = { + .name = TYPE_ATI_VGA, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(ATIVGAState), + .class_init = ati_vga_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static void ati_vga_register_types(void) +{ + type_register_static(&ati_vga_info); +} + +type_init(ati_vga_register_types) diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c new file mode 100644 index 0000000000..ca3d296b23 --- /dev/null +++ b/hw/display/ati_2d.c @@ -0,0 +1,44 @@ +/* + * QEMU ATI SVGA emulation + * 2D engine functions + * + * Copyright (c) 2019 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + */ + +#include "ati_int.h" +#include "ati_regs.h" +#include "qemu/log.h" + +/* + * NOTE: + * This is 2D _acceleration_ and supposed to be fast. Therefore, don't try to + * reinvent the wheel (unlikely to get better with a naive implementation than + * existing libraries) and avoid (poorly) reimplementing gfx primitives. + * That is unnecessary and would become a performance problem. Instead, try to + * map to and reuse existing optimised facilities (e.g. pixman) wherever + * possible. + */ + +void ati_2d_blit(ATIVGAState *s) +{ + /* FIXME it is really much more complex than this and will need to be */ + /* rewritten but for now as a start just to get some console output: */ + if ((s->regs.dp_gui_master_cntl & GMC_ROP3_MASK) == ROP3_SRCCOPY) { + DisplaySurface *ds = qemu_console_surface(s->vga.con); + DPRINTF("%p %p, %d %d, (%d,%d) -> (%d,%d) %dx%d\n", ds->image, + ds->image, surface_stride(ds), surface_bits_per_pixel(ds), + s->regs.src_x, s->regs.src_y, s->regs.dst_x, s->regs.dst_y, + s->regs.dst_width, s->regs.dst_height); + pixman_image_composite(PIXMAN_OP_SRC, ds->image, NULL, ds->image, + s->regs.src_x, s->regs.src_y, 0, 0, + s->regs.dst_x, s->regs.dst_y, + s->regs.dst_width, s->regs.dst_height); + memory_region_set_dirty(&s->vga.vram, s->vga.vbe_start_addr + + s->regs.dst_y * surface_stride(ds), + s->regs.dst_height * surface_stride(ds)); + } else { + qemu_log_mask(LOG_UNIMP, "Unimplemented ati_2d blit op"); + } +} diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h new file mode 100644 index 0000000000..85d045517c --- /dev/null +++ b/hw/display/ati_int.h @@ -0,0 +1,67 @@ +/* + * QEMU ATI SVGA emulation + * + * Copyright (c) 2019 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + */ + +#include "qemu/osdep.h" +#include "hw/pci/pci.h" +#include "vga_int.h" + +#undef DEBUG_ATI + +#ifdef DEBUG_ATI +#define DPRINTF(fmt, ...) printf("%s: " fmt, __func__, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#define TYPE_ATI_VGA "ati-vga" +#define ATI_VGA(obj) OBJECT_CHECK(ATIVGAState, (obj), TYPE_ATI_VGA) + +typedef struct ATIVGARegs { + uint32_t mm_index; + uint32_t bios_0_scratch; + uint32_t crtc_gen_cntl; + uint32_t crtc_ext_cntl; + uint32_t dac_cntl; + uint32_t crtc_h_total_disp; + uint32_t crtc_h_sync_strt_wid; + uint32_t crtc_v_total_disp; + uint32_t crtc_v_sync_strt_wid; + uint32_t crtc_offset; + uint32_t crtc_offset_cntl; + uint32_t crtc_pitch; + uint32_t dst_offset; + uint32_t dst_pitch; + uint32_t dst_width; + uint32_t dst_height; + uint32_t src_x; + uint32_t src_y; + uint32_t dst_x; + uint32_t dst_y; + uint32_t dp_gui_master_cntl; + uint32_t dp_brush_bkgd_clr; + uint32_t dp_brush_frgd_clr; + uint32_t dp_src_frgd_clr; + uint32_t dp_src_bkgd_clr; + uint32_t dp_cntl; + uint32_t dp_write_mask; + uint32_t default_offset; + uint32_t default_pitch; + uint32_t default_sc_bottom_right; +} ATIVGARegs; + +typedef struct ATIVGAState { + PCIDevice dev; + VGACommonState vga; + uint16_t dev_id; + uint16_t mode; + MemoryRegion io; + MemoryRegion mm; + ATIVGARegs regs; +} ATIVGAState; + +void ati_2d_blit(ATIVGAState *s); diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h new file mode 100644 index 0000000000..dc5ff53ceb --- /dev/null +++ b/hw/display/ati_regs.h @@ -0,0 +1,452 @@ +/* + * ATI VGA register definitions + * + * based on: + * linux/include/video/aty128.h + * Register definitions for ATI Rage128 boards + * Anthony Tong <atong@uiuc.edu>, 1999 + * Brad Douglas <brad@neruo.com>, 2000 + * + * and linux/include/video/radeon.h + * + * This work is licensed under the GNU GPL license version 2. + */ + +/* + * Register mapping: + * 0x0000-0x00ff Misc regs also accessible via io and mmio space + * 0x0100-0x0eff Misc regs only accessible via mmio + * 0x0f00-0x0fff Read-only copy of PCI config regs + * 0x1000-0x13ff Concurrent Command Engine (CCE) regs + * 0x1400-0x1fff GUI (drawing engine) regs + */ + +#ifndef ATI_REGS_H +#define ATI_REGS_H + +#define MM_INDEX 0x0000 +#define MM_DATA 0x0004 +#define CLOCK_CNTL_INDEX 0x0008 +#define CLOCK_CNTL_DATA 0x000c +#define BIOS_0_SCRATCH 0x0010 +#define BUS_CNTL 0x0030 +#define BUS_CNTL1 0x0034 +#define GEN_INT_CNTL 0x0040 +#define CRTC_GEN_CNTL 0x0050 +#define CRTC_EXT_CNTL 0x0054 +#define DAC_CNTL 0x0058 +#define GPIO_MONID 0x0068 +#define I2C_CNTL_1 0x0094 +#define PALETTE_INDEX 0x00b0 +#define PALETTE_DATA 0x00b4 +#define CNFG_CNTL 0x00e0 +#define GEN_RESET_CNTL 0x00f0 +#define CNFG_MEMSIZE 0x00f8 +#define MEM_CNTL 0x0140 +#define MC_FB_LOCATION 0x0148 +#define MC_AGP_LOCATION 0x014C +#define MC_STATUS 0x0150 +#define MEM_POWER_MISC 0x015c +#define AGP_BASE 0x0170 +#define AGP_CNTL 0x0174 +#define AGP_APER_OFFSET 0x0178 +#define PCI_GART_PAGE 0x017c +#define PC_NGUI_MODE 0x0180 +#define PC_NGUI_CTLSTAT 0x0184 +#define MPP_TB_CONFIG 0x01C0 +#define MPP_GP_CONFIG 0x01C8 +#define VIPH_CONTROL 0x01D0 +#define CRTC_H_TOTAL_DISP 0x0200 +#define CRTC_H_SYNC_STRT_WID 0x0204 +#define CRTC_V_TOTAL_DISP 0x0208 +#define CRTC_V_SYNC_STRT_WID 0x020c +#define CRTC_VLINE_CRNT_VLINE 0x0210 +#define CRTC_CRNT_FRAME 0x0214 +#define CRTC_GUI_TRIG_VLINE 0x0218 +#define CRTC_OFFSET 0x0224 +#define CRTC_OFFSET_CNTL 0x0228 +#define CRTC_PITCH 0x022c +#define OVR_CLR 0x0230 +#define OVR_WID_LEFT_RIGHT 0x0234 +#define OVR_WID_TOP_BOTTOM 0x0238 +#define LVDS_GEN_CNTL 0x02d0 +#define DDA_CONFIG 0x02e0 +#define DDA_ON_OFF 0x02e4 +#define VGA_DDA_CONFIG 0x02e8 +#define VGA_DDA_ON_OFF 0x02ec +#define CRTC2_H_TOTAL_DISP 0x0300 +#define CRTC2_H_SYNC_STRT_WID 0x0304 +#define CRTC2_V_TOTAL_DISP 0x0308 +#define CRTC2_V_SYNC_STRT_WID 0x030c +#define CRTC2_VLINE_CRNT_VLINE 0x0310 +#define CRTC2_CRNT_FRAME 0x0314 +#define CRTC2_GUI_TRIG_VLINE 0x0318 +#define CRTC2_OFFSET 0x0324 +#define CRTC2_OFFSET_CNTL 0x0328 +#define CRTC2_PITCH 0x032c +#define DDA2_CONFIG 0x03e0 +#define DDA2_ON_OFF 0x03e4 +#define CRTC2_GEN_CNTL 0x03f8 +#define CRTC2_STATUS 0x03fc +#define OV0_SCALE_CNTL 0x0420 +#define SUBPIC_CNTL 0x0540 +#define PM4_BUFFER_OFFSET 0x0700 +#define PM4_BUFFER_CNTL 0x0704 +#define PM4_BUFFER_WM_CNTL 0x0708 +#define PM4_BUFFER_DL_RPTR_ADDR 0x070c +#define PM4_BUFFER_DL_RPTR 0x0710 +#define PM4_BUFFER_DL_WPTR 0x0714 +#define PM4_VC_FPU_SETUP 0x071c +#define PM4_FPU_CNTL 0x0720 +#define PM4_VC_FORMAT 0x0724 +#define PM4_VC_CNTL 0x0728 +#define PM4_VC_I01 0x072c +#define PM4_VC_VLOFF 0x0730 +#define PM4_VC_VLSIZE 0x0734 +#define PM4_IW_INDOFF 0x0738 +#define PM4_IW_INDSIZE 0x073c +#define PM4_FPU_FPX0 0x0740 +#define PM4_FPU_FPY0 0x0744 +#define PM4_FPU_FPX1 0x0748 +#define PM4_FPU_FPY1 0x074c +#define PM4_FPU_FPX2 0x0750 +#define PM4_FPU_FPY2 0x0754 +#define PM4_FPU_FPY3 0x0758 +#define PM4_FPU_FPY4 0x075c +#define PM4_FPU_FPY5 0x0760 +#define PM4_FPU_FPY6 0x0764 +#define PM4_FPU_FPR 0x0768 +#define PM4_FPU_FPG 0x076c +#define PM4_FPU_FPB 0x0770 +#define PM4_FPU_FPA 0x0774 +#define PM4_FPU_INTXY0 0x0780 +#define PM4_FPU_INTXY1 0x0784 +#define PM4_FPU_INTXY2 0x0788 +#define PM4_FPU_INTARGB 0x078c +#define PM4_FPU_FPTWICEAREA 0x0790 +#define PM4_FPU_DMAJOR01 0x0794 +#define PM4_FPU_DMAJOR12 0x0798 +#define PM4_FPU_DMAJOR02 0x079c +#define PM4_FPU_STAT 0x07a0 +#define PM4_STAT 0x07b8 +#define PM4_TEST_CNTL 0x07d0 +#define PM4_MICROCODE_ADDR 0x07d4 +#define PM4_MICROCODE_RADDR 0x07d8 +#define PM4_MICROCODE_DATAH 0x07dc +#define PM4_MICROCODE_DATAL 0x07e0 +#define PM4_CMDFIFO_ADDR 0x07e4 +#define PM4_CMDFIFO_DATAH 0x07e8 +#define PM4_CMDFIFO_DATAL 0x07ec +#define PM4_BUFFER_ADDR 0x07f0 +#define PM4_BUFFER_DATAH 0x07f4 +#define PM4_BUFFER_DATAL 0x07f8 +#define PM4_MICRO_CNTL 0x07fc +#define CAP0_TRIG_CNTL 0x0950 +#define CAP1_TRIG_CNTL 0x09c0 + +#define RBBM_STATUS 0x0E40 +#define RBBM_STATUS_alt_1 0x1740 + +/* + * GUI Block Memory Mapped Registers + * These registers are FIFOed. + */ +#define PM4_FIFO_DATA_EVEN 0x1000 +#define PM4_FIFO_DATA_ODD 0x1004 + +#define DST_OFFSET 0x1404 +#define DST_PITCH 0x1408 +#define DST_WIDTH 0x140c +#define DST_HEIGHT 0x1410 +#define SRC_X 0x1414 +#define SRC_Y 0x1418 +#define DST_X 0x141c +#define DST_Y 0x1420 +#define SRC_PITCH_OFFSET 0x1428 +#define DST_PITCH_OFFSET 0x142c +#define SRC_Y_X 0x1434 +#define DST_Y_X 0x1438 +#define DST_HEIGHT_WIDTH 0x143c +#define DP_GUI_MASTER_CNTL 0x146c +#define BRUSH_SCALE 0x1470 +#define BRUSH_Y_X 0x1474 +#define DP_BRUSH_BKGD_CLR 0x1478 +#define DP_BRUSH_FRGD_CLR 0x147c +#define DST_WIDTH_X 0x1588 +#define DST_HEIGHT_WIDTH_8 0x158c +#define SRC_X_Y 0x1590 +#define DST_X_Y 0x1594 +#define DST_WIDTH_HEIGHT 0x1598 +#define DST_WIDTH_X_INCY 0x159c +#define DST_HEIGHT_Y 0x15a0 +#define DST_X_SUB 0x15a4 +#define DST_Y_SUB 0x15a8 +#define SRC_OFFSET 0x15ac +#define SRC_PITCH 0x15b0 +#define DST_HEIGHT_WIDTH_BW 0x15b4 +#define CLR_CMP_CNTL 0x15c0 +#define CLR_CMP_CLR_SRC 0x15c4 +#define CLR_CMP_CLR_DST 0x15c8 +#define CLR_CMP_MASK 0x15cc +#define DP_SRC_FRGD_CLR 0x15d8 +#define DP_SRC_BKGD_CLR 0x15dc +#define DST_BRES_ERR 0x1628 +#define DST_BRES_INC 0x162c +#define DST_BRES_DEC 0x1630 +#define DST_BRES_LNTH 0x1634 +#define DST_BRES_LNTH_SUB 0x1638 +#define SC_LEFT 0x1640 +#define SC_RIGHT 0x1644 +#define SC_TOP 0x1648 +#define SC_BOTTOM 0x164c +#define SRC_SC_RIGHT 0x1654 +#define SRC_SC_BOTTOM 0x165c +#define GUI_DEBUG0 0x16a0 +#define GUI_DEBUG1 0x16a4 +#define GUI_TIMEOUT 0x16b0 +#define GUI_TIMEOUT0 0x16b4 +#define GUI_TIMEOUT1 0x16b8 +#define GUI_PROBE 0x16bc +#define DP_CNTL 0x16c0 +#define DP_DATATYPE 0x16c4 +#define DP_MIX 0x16c8 +#define DP_WRITE_MASK 0x16cc +#define DP_CNTL_XDIR_YDIR_YMAJOR 0x16d0 +#define DEFAULT_OFFSET 0x16e0 +#define DEFAULT_PITCH 0x16e4 +#define DEFAULT_SC_BOTTOM_RIGHT 0x16e8 +#define SC_TOP_LEFT 0x16ec +#define SC_BOTTOM_RIGHT 0x16f0 +#define SRC_SC_BOTTOM_RIGHT 0x16f4 +#define WAIT_UNTIL 0x1720 +#define CACHE_CNTL 0x1724 +#define GUI_STAT 0x1740 +#define PC_GUI_MODE 0x1744 +#define PC_GUI_CTLSTAT 0x1748 +#define PC_DEBUG_MODE 0x1760 +#define BRES_DST_ERR_DEC 0x1780 +#define TRAIL_BRES_T12_ERR_DEC 0x1784 +#define TRAIL_BRES_T12_INC 0x1788 +#define DP_T12_CNTL 0x178c +#define DST_BRES_T1_LNTH 0x1790 +#define DST_BRES_T2_LNTH 0x1794 +#define SCALE_SRC_HEIGHT_WIDTH 0x1994 +#define SCALE_OFFSET_0 0x1998 +#define SCALE_PITCH 0x199c +#define SCALE_X_INC 0x19a0 +#define SCALE_Y_INC 0x19a4 +#define SCALE_HACC 0x19a8 +#define SCALE_VACC 0x19ac +#define SCALE_DST_X_Y 0x19b0 +#define SCALE_DST_HEIGHT_WIDTH 0x19b4 +#define SCALE_3D_CNTL 0x1a00 +#define SCALE_3D_DATATYPE 0x1a20 +#define SETUP_CNTL 0x1bc4 +#define SOLID_COLOR 0x1bc8 +#define WINDOW_XY_OFFSET 0x1bcc +#define DRAW_LINE_POINT 0x1bd0 +#define SETUP_CNTL_PM4 0x1bd4 +#define DST_PITCH_OFFSET_C 0x1c80 +#define DP_GUI_MASTER_CNTL_C 0x1c84 +#define SC_TOP_LEFT_C 0x1c88 +#define SC_BOTTOM_RIGHT_C 0x1c8c + +#define CLR_CMP_MASK_3D 0x1A28 +#define MISC_3D_STATE_CNTL_REG 0x1CA0 +#define MC_SRC1_CNTL 0x19D8 +#define TEX_CNTL 0x1800 + +/* CONSTANTS */ +#define GUI_ACTIVE 0x80000000 +#define ENGINE_IDLE 0x0 + +#define PLL_WR_EN 0x00000080 + +#define CLK_PIN_CNTL 0x0001 +#define PPLL_CNTL 0x0002 +#define PPLL_REF_DIV 0x0003 +#define PPLL_DIV_0 0x0004 +#define PPLL_DIV_1 0x0005 +#define PPLL_DIV_2 0x0006 +#define PPLL_DIV_3 0x0007 +#define VCLK_ECP_CNTL 0x0008 +#define HTOTAL_CNTL 0x0009 +#define X_MPLL_REF_FB_DIV 0x000a +#define XPLL_CNTL 0x000b +#define XDLL_CNTL 0x000c +#define XCLK_CNTL 0x000d +#define MPLL_CNTL 0x000e +#define MCLK_CNTL 0x000f +#define AGP_PLL_CNTL 0x0010 +#define FCP_CNTL 0x0012 +#define PLL_TEST_CNTL 0x0013 +#define P2PLL_CNTL 0x002a +#define P2PLL_REF_DIV 0x002b +#define P2PLL_DIV_0 0x002b +#define POWER_MANAGEMENT 0x002f + +#define PPLL_RESET 0x01 +#define PPLL_ATOMIC_UPDATE_EN 0x10000 +#define PPLL_VGA_ATOMIC_UPDATE_EN 0x20000 +#define PPLL_REF_DIV_MASK 0x3FF +#define PPLL_FB3_DIV_MASK 0x7FF +#define PPLL_POST3_DIV_MASK 0x70000 +#define PPLL_ATOMIC_UPDATE_R 0x8000 +#define PPLL_ATOMIC_UPDATE_W 0x8000 +#define MEM_CFG_TYPE_MASK 0x3 +#define XCLK_SRC_SEL_MASK 0x7 +#define XPLL_FB_DIV_MASK 0xFF00 +#define X_MPLL_REF_DIV_MASK 0xFF + +/* Config control values (CONFIG_CNTL) */ +#define CFG_VGA_IO_DIS 0x00000400 + +/* CRTC control values (CRTC_GEN_CNTL) */ +#define CRTC_CSYNC_EN 0x00000010 + +#define CRTC2_DBL_SCAN_EN 0x00000001 +#define CRTC2_DISPLAY_DIS 0x00800000 +#define CRTC2_FIFO_EXTSENSE 0x00200000 +#define CRTC2_ICON_EN 0x00100000 +#define CRTC2_CUR_EN 0x00010000 +#define CRTC2_EXT_DISP_EN 0x01000000 +#define CRTC2_EN 0x02000000 +#define CRTC2_DISP_REQ_EN_B 0x04000000 + +#define CRTC_PIX_WIDTH_MASK 0x00000700 +#define CRTC_PIX_WIDTH_4BPP 0x00000100 +#define CRTC_PIX_WIDTH_8BPP 0x00000200 +#define CRTC_PIX_WIDTH_15BPP 0x00000300 +#define CRTC_PIX_WIDTH_16BPP 0x00000400 +#define CRTC_PIX_WIDTH_24BPP 0x00000500 +#define CRTC_PIX_WIDTH_32BPP 0x00000600 + +/* DAC_CNTL bit constants */ +#define DAC_8BIT_EN 0x00000100 +#define DAC_MASK 0xFF000000 +#define DAC_BLANKING 0x00000004 +#define DAC_RANGE_CNTL 0x00000003 +#define DAC_CLK_SEL 0x00000010 +#define DAC_PALETTE_ACCESS_CNTL 0x00000020 +#define DAC_PALETTE2_SNOOP_EN 0x00000040 +#define DAC_PDWN 0x00008000 + +/* CRTC_EXT_CNTL */ +#define CRT_CRTC_DISPLAY_DIS 0x00000400 +#define CRT_CRTC_ON 0x00008000 + +/* GEN_RESET_CNTL bit constants */ +#define SOFT_RESET_GUI 0x00000001 +#define SOFT_RESET_VCLK 0x00000100 +#define SOFT_RESET_PCLK 0x00000200 +#define SOFT_RESET_ECP 0x00000400 +#define SOFT_RESET_DISPENG_XCLK 0x00000800 + +/* PC_GUI_CTLSTAT bit constants */ +#define PC_BUSY_INIT 0x10000000 +#define PC_BUSY_GUI 0x20000000 +#define PC_BUSY_NGUI 0x40000000 +#define PC_BUSY 0x80000000 + +#define BUS_MASTER_DIS 0x00000040 +#define PM4_BUFFER_CNTL_NONPM4 0x00000000 + +/* DP_DATATYPE bit constants */ +#define DST_8BPP 0x00000002 +#define DST_15BPP 0x00000003 +#define DST_16BPP 0x00000004 +#define DST_24BPP 0x00000005 +#define DST_32BPP 0x00000006 + +#define BRUSH_SOLIDCOLOR 0x00000d00 + +/* DP_GUI_MASTER_CNTL bit constants */ +#define GMC_SRC_PITCH_OFFSET_DEFAULT 0x00000000 +#define GMC_DST_PITCH_OFFSET_DEFAULT 0x00000000 +#define GMC_SRC_CLIP_DEFAULT 0x00000000 +#define GMC_DST_CLIP_DEFAULT 0x00000000 +#define GMC_BRUSH_SOLIDCOLOR 0x000000d0 +#define GMC_SRC_DSTCOLOR 0x00003000 +#define GMC_BYTE_ORDER_MSB_TO_LSB 0x00000000 +#define GMC_DP_SRC_RECT 0x02000000 +#define GMC_3D_FCN_EN_CLR 0x00000000 +#define GMC_AUX_CLIP_CLEAR 0x20000000 +#define GMC_DST_CLR_CMP_FCN_CLEAR 0x10000000 +#define GMC_WRITE_MASK_SET 0x40000000 +#define GMC_DP_CONVERSION_TEMP_6500 0x00000000 + +/* DP_GUI_MASTER_CNTL ROP3 named constants */ +#define GMC_ROP3_MASK 0x00ff0000 +#define ROP3_PATCOPY 0x00f00000 +#define ROP3_SRCCOPY 0x00cc0000 + +#define SRC_DSTCOLOR 0x00030000 + +/* DP_CNTL bit constants */ +#define DST_X_RIGHT_TO_LEFT 0x00000000 +#define DST_X_LEFT_TO_RIGHT 0x00000001 +#define DST_Y_BOTTOM_TO_TOP 0x00000000 +#define DST_Y_TOP_TO_BOTTOM 0x00000002 +#define DST_X_MAJOR 0x00000000 +#define DST_Y_MAJOR 0x00000004 +#define DST_X_TILE 0x00000008 +#define DST_Y_TILE 0x00000010 +#define DST_LAST_PEL 0x00000020 +#define DST_TRAIL_X_RIGHT_TO_LEFT 0x00000000 +#define DST_TRAIL_X_LEFT_TO_RIGHT 0x00000040 +#define DST_TRAP_FILL_RIGHT_TO_LEFT 0x00000000 +#define DST_TRAP_FILL_LEFT_TO_RIGHT 0x00000080 +#define DST_BRES_SIGN 0x00000100 +#define DST_HOST_BIG_ENDIAN_EN 0x00000200 +#define DST_POLYLINE_NONLAST 0x00008000 +#define DST_RASTER_STALL 0x00010000 +#define DST_POLY_EDGE 0x00040000 + +/* DP_MIX bit constants */ +#define DP_SRC_RECT 0x00000200 +#define DP_SRC_HOST 0x00000300 +#define DP_SRC_HOST_BYTEALIGN 0x00000400 + +/* LVDS_GEN_CNTL constants */ +#define LVDS_BL_MOD_LEVEL_MASK 0x0000ff00 +#define LVDS_BL_MOD_LEVEL_SHIFT 8 +#define LVDS_BL_MOD_EN 0x00010000 +#define LVDS_DIGION 0x00040000 +#define LVDS_BLON 0x00080000 +#define LVDS_ON 0x00000001 +#define LVDS_DISPLAY_DIS 0x00000002 +#define LVDS_PANEL_TYPE_2PIX_PER_CLK 0x00000004 +#define LVDS_PANEL_24BITS_TFT 0x00000008 +#define LVDS_FRAME_MOD_NO 0x00000000 +#define LVDS_FRAME_MOD_2_LEVELS 0x00000010 +#define LVDS_FRAME_MOD_4_LEVELS 0x00000020 +#define LVDS_RST_FM 0x00000040 +#define LVDS_EN 0x00000080 + +/* CRTC2_GEN_CNTL constants */ +#define CRTC2_EN 0x02000000 + +/* POWER_MANAGEMENT constants */ +#define PWR_MGT_ON 0x00000001 +#define PWR_MGT_MODE_MASK 0x00000006 +#define PWR_MGT_MODE_PIN 0x00000000 +#define PWR_MGT_MODE_REGISTER 0x00000002 +#define PWR_MGT_MODE_TIMER 0x00000004 +#define PWR_MGT_MODE_PCI 0x00000006 +#define PWR_MGT_AUTO_PWR_UP_EN 0x00000008 +#define PWR_MGT_ACTIVITY_PIN_ON 0x00000010 +#define PWR_MGT_STANDBY_POL 0x00000020 +#define PWR_MGT_SUSPEND_POL 0x00000040 +#define PWR_MGT_SELF_REFRESH 0x00000080 +#define PWR_MGT_ACTIVITY_PIN_EN 0x00000100 +#define PWR_MGT_KEYBD_SNOOP 0x00000200 +#define PWR_MGT_TRISTATE_MEM_EN 0x00000800 +#define PWR_MGT_SELW4MS 0x00001000 +#define PWR_MGT_SLOWDOWN_MCLK 0x00002000 + +#define PMI_PMSCR_REG 0x60 + +/* used by ATI bug fix for hardware ROM */ +#define RAGE128_MPP_TB_CONFIG 0x01c0 + +#endif /* ATI_REGS_H */ diff --git a/hw/display/trace-events b/hw/display/trace-events index 387c6b8931..6b85cf2f64 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -137,3 +137,7 @@ vga_cirrus_write_blt(uint32_t offset, uint32_t val) "offset 0x%x, val 0x%x" sii9022_read_reg(uint8_t addr, uint8_t val) "addr 0x%02x, val 0x%02x" sii9022_write_reg(uint8_t addr, uint8_t val) "addr 0x%02x, val 0x%02x" sii9022_switch_mode(const char *mode) "mode: %s" + +# hw/display/ati*.c +ati_mm_read(unsigned int size, uint64_t addr, uint64_t val) "%d 0x%"HWADDR_PRIx" -> 0x%"PRIx64 +ati_mm_write(unsigned int size, uint64_t addr, uint64_t val) "%d 0x%"HWADDR_PRIx" <- 0x%"PRIx64
At least two machines, the PPC mac99 and MIPS fulong2e, have an ATI gfx chip by default (Rage 128 Pro and M6/RV100 respectively) and guests running on these and the PMON2000 firmware of the fulong2e expect this to be available. Fortunately these are very similar chips so they can be mostly emulated in the same device model. This patch adds basic emulation of these ATI VGA chips. While this is incomplete and currently only enough to run the MIPS firmware and get console output with Linux, it allows the fulong2e board to work more like the real hardware and having it in QEMU in this state provides a way to experiment with it and allows others to contribute to improve it (e.g. implement more 2D features to get X running). It is only enabled for mips64le and ppc now but only the fulong2e (which currently has no display output at all) is set to use it by default (in a separate patch). Tested with Debian 8.11.0 powerpc on mac99 and the pmon_2e.bin fulong2e firmware from https://mirrors.cloud.tencent.com/loongson/pmon/ Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> --- default-configs/mips64el-softmmu.mak | 1 + default-configs/ppc-softmmu.mak | 1 + hw/display/Makefile.objs | 2 + hw/display/ati.c | 582 +++++++++++++++++++++++++++++++++++ hw/display/ati_2d.c | 44 +++ hw/display/ati_int.h | 67 ++++ hw/display/ati_regs.h | 452 +++++++++++++++++++++++++++ hw/display/trace-events | 4 + 8 files changed, 1153 insertions(+) create mode 100644 hw/display/ati.c create mode 100644 hw/display/ati_2d.c create mode 100644 hw/display/ati_int.h create mode 100644 hw/display/ati_regs.h