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