@samitouri / QOSamiQemu / commits / 4bbe831f1e

ati-vga: Fix setting CRTC_OFFSET

Offset (display start address) should also be updated when changing the register value not only on mode change. Fix the register write mask to hard code bits 0:2 to 0 as the chip docs say and update the start address on register write. This fixes virtual screen panning for screens larger than displayed resolution. As this register allows values that cannot be handled by the VBE_DISPI X and Y offsets (which is restricted by line length) we add a function to set it directly not through the VBE offsets. Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu> Tested-by: Chad Jablonski <chad@jablonski.xyz> Reviewed-by: Chad Jablonski <chad@jablonski.xyz> Message-ID: <2b8af6022aba06aa98a249ae67922de29d82d86f.1775228029.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>

BALATON Zoltan committed Apr 3, 2026 at 16:59 UTC 4bbe831f1eafc9f176e5613fa72008d797de0225
1 file changed +17 -17
hw/display/ati.c
+17 -17
@@ -48,6 +48,19 @@ static const struct {
48
49 enum { VGA_MODE, EXT_MODE };
50
51 +static void ati_vga_set_offset(VGACommonState *vga, uint32_t offs)
52 +{
53 + int bypp = DIV_ROUND_UP(vga->vbe_regs[VBE_DISPI_INDEX_BPP], BITS_PER_BYTE);
54 +
55 + if (!bypp ||
56 + vga->vbe_regs[VBE_DISPI_INDEX_YRES] *
57 + vga->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] * bypp + offs >
58 + vga->vbe_size) {
59 + return;
60 + }
61 + vga->vbe_start_addr = offs / 4;
62 +}
63 +
64 static void ati_vga_switch_mode(ATIVGAState *s)
65 {
66 DPRINTF("%d -> %d\n",
@@ -109,26 +122,12 @@ static void ati_vga_switch_mode(ATIVGAState *s)
122 vbe_ioport_write_data(&s->vga, 0, VBE_DISPI_ENABLED |
123 VBE_DISPI_LFB_ENABLED | VBE_DISPI_NOCLEARMEM |
124 (s->regs.dac_cntl & DAC_8BIT_EN ? VBE_DISPI_8BIT_DAC : 0));
112 - /* now set offset and stride after enable as that resets these */
125 + /* now set offset and stride because enable resets these */
126 if (stride) {
114 - int bypp = DIV_ROUND_UP(bpp, BITS_PER_BYTE);
115 -
127 vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_VIRT_WIDTH);
128 vbe_ioport_write_data(&s->vga, 0, stride);
118 - stride *= bypp;
119 - if (offs % stride) {
120 - DPRINTF("CRTC offset is not multiple of pitch\n");
121 - vbe_ioport_write_index(&s->vga, 0,
122 - VBE_DISPI_INDEX_X_OFFSET);
123 - vbe_ioport_write_data(&s->vga, 0, offs % stride / bypp);
124 - }
125 - vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_Y_OFFSET);
126 - vbe_ioport_write_data(&s->vga, 0, offs / stride);
127 - DPRINTF("VBE offset (%d,%d), vbe_start_addr=%x\n",
128 - s->vga.vbe_regs[VBE_DISPI_INDEX_X_OFFSET],
129 - s->vga.vbe_regs[VBE_DISPI_INDEX_Y_OFFSET],
130 - s->vga.vbe_start_addr);
129 }
130 + ati_vga_set_offset(&s->vga, offs);
131 }
132 } else {
133 /* VGA mode enabled */
@@ -737,7 +736,8 @@ static void ati_mm_write(void *opaque, hwaddr addr,
736 s->regs.crtc_v_sync_strt_wid = data & 0x9f0fff;
737 break;
738 case CRTC_OFFSET:
740 - s->regs.crtc_offset = data & 0xc7ffffff;
739 + s->regs.crtc_offset = data & 0x87fffff8;
740 + ati_vga_set_offset(&s->vga, s->regs.crtc_offset & 0x07ffffff);
741 break;
742 case CRTC_OFFSET_CNTL:
743 s->regs.crtc_offset_cntl = data; /* FIXME */