xref: /qemu/hw/display/ati_2d.c (revision 177d9e0d)
1 /*
2  * QEMU ATI SVGA emulation
3  * 2D engine functions
4  *
5  * Copyright (c) 2019 BALATON Zoltan
6  *
7  * This work is licensed under the GNU GPL license version 2 or later.
8  */
9 
10 #include "qemu/osdep.h"
11 #include "ati_int.h"
12 #include "ati_regs.h"
13 #include "qemu/log.h"
14 #include "ui/pixel_ops.h"
15 
16 /*
17  * NOTE:
18  * This is 2D _acceleration_ and supposed to be fast. Therefore, don't try to
19  * reinvent the wheel (unlikely to get better with a naive implementation than
20  * existing libraries) and avoid (poorly) reimplementing gfx primitives.
21  * That is unnecessary and would become a performance problem. Instead, try to
22  * map to and reuse existing optimised facilities (e.g. pixman) wherever
23  * possible.
24  */
25 
26 static int ati_bpp_from_datatype(ATIVGAState *s)
27 {
28     switch (s->regs.dp_datatype & 0xf) {
29     case 2:
30         return 8;
31     case 3:
32     case 4:
33         return 16;
34     case 5:
35         return 24;
36     case 6:
37         return 32;
38     default:
39         qemu_log_mask(LOG_UNIMP, "Unknown dst datatype %d\n",
40                       s->regs.dp_datatype & 0xf);
41         return 0;
42     }
43 }
44 
45 void ati_2d_blt(ATIVGAState *s)
46 {
47     /* FIXME it is probably more complex than this and may need to be */
48     /* rewritten but for now as a start just to get some output: */
49     DisplaySurface *ds = qemu_console_surface(s->vga.con);
50     DPRINTF("%p %u ds: %p %d %d rop: %x\n", s->vga.vram_ptr,
51             s->vga.vbe_start_addr, surface_data(ds), surface_stride(ds),
52             surface_bits_per_pixel(ds),
53             (s->regs.dp_mix & GMC_ROP3_MASK) >> 16);
54     DPRINTF("%d %d, %d %d, (%d,%d) -> (%d,%d) %dx%d\n", s->regs.src_offset,
55             s->regs.dst_offset, s->regs.src_pitch, s->regs.dst_pitch,
56             s->regs.src_x, s->regs.src_y, s->regs.dst_x, s->regs.dst_y,
57             s->regs.dst_width, s->regs.dst_height);
58     switch (s->regs.dp_mix & GMC_ROP3_MASK) {
59     case ROP3_SRCCOPY:
60     {
61         uint8_t *src_bits, *dst_bits, *end;
62         int src_stride, dst_stride, bpp = ati_bpp_from_datatype(s);
63         src_bits = s->vga.vram_ptr + s->regs.src_offset;
64         dst_bits = s->vga.vram_ptr + s->regs.dst_offset;
65         src_stride = s->regs.src_pitch;
66         dst_stride = s->regs.dst_pitch;
67 
68         if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
69             src_bits += s->regs.crtc_offset & 0x07ffffff;
70             dst_bits += s->regs.crtc_offset & 0x07ffffff;
71             src_stride *= bpp;
72             dst_stride *= bpp;
73         }
74         src_stride /= sizeof(uint32_t);
75         dst_stride /= sizeof(uint32_t);
76 
77         DPRINTF("pixman_blt(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)\n",
78                 src_bits, dst_bits, src_stride, dst_stride, bpp, bpp,
79                 s->regs.src_x, s->regs.src_y, s->regs.dst_x, s->regs.dst_y,
80                 s->regs.dst_width, s->regs.dst_height);
81         end = s->vga.vram_ptr + s->vga.vram_size;
82         if (src_bits >= end || dst_bits >= end ||
83             src_bits + s->regs.src_x + (s->regs.src_y + s->regs.dst_height) *
84             src_stride * sizeof(uint32_t) >= end ||
85             dst_bits + s->regs.dst_x + (s->regs.dst_y + s->regs.dst_height) *
86             dst_stride * sizeof(uint32_t) >= end) {
87             qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n");
88             return;
89         }
90         pixman_blt((uint32_t *)src_bits, (uint32_t *)dst_bits,
91                    src_stride, dst_stride, bpp, bpp,
92                    s->regs.src_x, s->regs.src_y,
93                    s->regs.dst_x, s->regs.dst_y,
94                    s->regs.dst_width, s->regs.dst_height);
95         if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr &&
96             dst_bits < s->vga.vram_ptr + s->vga.vbe_start_addr +
97             s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] * s->vga.vbe_line_offset) {
98             memory_region_set_dirty(&s->vga.vram, s->vga.vbe_start_addr +
99                                     s->regs.dst_offset +
100                                     s->regs.dst_y * surface_stride(ds),
101                                     s->regs.dst_height * surface_stride(ds));
102         }
103         s->regs.dst_x += s->regs.dst_width;
104         s->regs.dst_y += s->regs.dst_height;
105         break;
106     }
107     case ROP3_PATCOPY:
108     case ROP3_BLACKNESS:
109     case ROP3_WHITENESS:
110     {
111         uint8_t *dst_bits, *end;
112         int dst_stride, bpp = ati_bpp_from_datatype(s);
113         uint32_t filler = 0;
114         dst_bits = s->vga.vram_ptr + s->regs.dst_offset;
115         dst_stride = s->regs.dst_pitch;
116 
117         if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) {
118             dst_bits += s->regs.crtc_offset & 0x07ffffff;
119             dst_stride *= bpp;
120         }
121         dst_stride /= sizeof(uint32_t);
122 
123         switch (s->regs.dp_mix & GMC_ROP3_MASK) {
124         case ROP3_PATCOPY:
125             filler = bswap32(s->regs.dp_brush_frgd_clr);
126             break;
127         case ROP3_BLACKNESS:
128             filler = rgb_to_pixel32(s->vga.palette[0], s->vga.palette[1],
129                                     s->vga.palette[2]) << 8 | 0xff;
130             break;
131         case ROP3_WHITENESS:
132             filler = rgb_to_pixel32(s->vga.palette[3], s->vga.palette[4],
133                                     s->vga.palette[5]) << 8 | 0xff;
134             break;
135         }
136 
137         DPRINTF("pixman_fill(%p, %d, %d, %d, %d, %d, %d, %x)\n",
138                 dst_bits, dst_stride, bpp,
139                 s->regs.dst_x, s->regs.dst_y,
140                 s->regs.dst_width, s->regs.dst_height,
141                 filler);
142         end = s->vga.vram_ptr + s->vga.vram_size;
143         if (dst_bits >= end ||
144             dst_bits + s->regs.dst_x + (s->regs.dst_y + s->regs.dst_height) *
145             dst_stride * sizeof(uint32_t) >= end) {
146             qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n");
147             return;
148         }
149         pixman_fill((uint32_t *)dst_bits, dst_stride, bpp,
150                    s->regs.dst_x, s->regs.dst_y,
151                    s->regs.dst_width, s->regs.dst_height,
152                    filler);
153         if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr &&
154             dst_bits < s->vga.vram_ptr + s->vga.vbe_start_addr +
155             s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] * s->vga.vbe_line_offset) {
156             memory_region_set_dirty(&s->vga.vram, s->vga.vbe_start_addr +
157                                     s->regs.dst_offset +
158                                     s->regs.dst_y * surface_stride(ds),
159                                     s->regs.dst_height * surface_stride(ds));
160         }
161         s->regs.dst_y += s->regs.dst_height;
162         break;
163     }
164     default:
165         qemu_log_mask(LOG_UNIMP, "Unimplemented ati_2d blt op %x\n",
166                       (s->regs.dp_mix & GMC_ROP3_MASK) >> 16);
167     }
168 }
169