xref: /openbmc/qemu/hw/display/cg3.c (revision 650d103d)
1 /*
2  * QEMU CG3 Frame buffer
3  *
4  * Copyright (c) 2012 Bob Breuer
5  * Copyright (c) 2013 Mark Cave-Ayland
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 
26 #include "qemu/osdep.h"
27 #include "qemu-common.h"
28 #include "qapi/error.h"
29 #include "qemu/error-report.h"
30 #include "ui/console.h"
31 #include "hw/sysbus.h"
32 #include "migration/vmstate.h"
33 #include "hw/irq.h"
34 #include "hw/loader.h"
35 #include "qemu/log.h"
36 #include "qemu/module.h"
37 
38 /* Change to 1 to enable debugging */
39 #define DEBUG_CG3 0
40 
41 #define CG3_ROM_FILE  "QEMU,cgthree.bin"
42 #define FCODE_MAX_ROM_SIZE 0x10000
43 
44 #define CG3_REG_SIZE            0x20
45 
46 #define CG3_REG_BT458_ADDR      0x0
47 #define CG3_REG_BT458_COLMAP    0x4
48 #define CG3_REG_FBC_CTRL        0x10
49 #define CG3_REG_FBC_STATUS      0x11
50 #define CG3_REG_FBC_CURSTART    0x12
51 #define CG3_REG_FBC_CUREND      0x13
52 #define CG3_REG_FBC_VCTRL       0x14
53 
54 /* Control register flags */
55 #define CG3_CR_ENABLE_INTS      0x80
56 
57 /* Status register flags */
58 #define CG3_SR_PENDING_INT      0x80
59 #define CG3_SR_1152_900_76_B    0x60
60 #define CG3_SR_ID_COLOR         0x01
61 
62 #define CG3_VRAM_SIZE 0x100000
63 #define CG3_VRAM_OFFSET 0x800000
64 
65 #define DPRINTF(fmt, ...) do { \
66     if (DEBUG_CG3) { \
67         printf("CG3: " fmt , ## __VA_ARGS__); \
68     } \
69 } while (0)
70 
71 #define TYPE_CG3 "cgthree"
72 #define CG3(obj) OBJECT_CHECK(CG3State, (obj), TYPE_CG3)
73 
74 typedef struct CG3State {
75     SysBusDevice parent_obj;
76 
77     QemuConsole *con;
78     qemu_irq irq;
79     hwaddr prom_addr;
80     MemoryRegion vram_mem;
81     MemoryRegion rom;
82     MemoryRegion reg;
83     uint32_t vram_size;
84     int full_update;
85     uint8_t regs[16];
86     uint8_t r[256], g[256], b[256];
87     uint16_t width, height, depth;
88     uint8_t dac_index, dac_state;
89 } CG3State;
90 
91 static void cg3_update_display(void *opaque)
92 {
93     CG3State *s = opaque;
94     DisplaySurface *surface = qemu_console_surface(s->con);
95     const uint8_t *pix;
96     uint32_t *data;
97     uint32_t dval;
98     int x, y, y_start;
99     unsigned int width, height;
100     ram_addr_t page;
101     DirtyBitmapSnapshot *snap = NULL;
102 
103     if (surface_bits_per_pixel(surface) != 32) {
104         return;
105     }
106     width = s->width;
107     height = s->height;
108 
109     y_start = -1;
110     pix = memory_region_get_ram_ptr(&s->vram_mem);
111     data = (uint32_t *)surface_data(surface);
112 
113     if (!s->full_update) {
114         snap = memory_region_snapshot_and_clear_dirty(&s->vram_mem, 0x0,
115                                               memory_region_size(&s->vram_mem),
116                                               DIRTY_MEMORY_VGA);
117     }
118 
119     for (y = 0; y < height; y++) {
120         int update;
121 
122         page = (ram_addr_t)y * width;
123 
124         if (s->full_update) {
125             update = 1;
126         } else {
127             update = memory_region_snapshot_get_dirty(&s->vram_mem, snap, page,
128                                                       width);
129         }
130 
131         if (update) {
132             if (y_start < 0) {
133                 y_start = y;
134             }
135 
136             for (x = 0; x < width; x++) {
137                 dval = *pix++;
138                 dval = (s->r[dval] << 16) | (s->g[dval] << 8) | s->b[dval];
139                 *data++ = dval;
140             }
141         } else {
142             if (y_start >= 0) {
143                 dpy_gfx_update(s->con, 0, y_start, width, y - y_start);
144                 y_start = -1;
145             }
146             pix += width;
147             data += width;
148         }
149     }
150     s->full_update = 0;
151     if (y_start >= 0) {
152         dpy_gfx_update(s->con, 0, y_start, width, y - y_start);
153     }
154     /* vsync interrupt? */
155     if (s->regs[0] & CG3_CR_ENABLE_INTS) {
156         s->regs[1] |= CG3_SR_PENDING_INT;
157         qemu_irq_raise(s->irq);
158     }
159     g_free(snap);
160 }
161 
162 static void cg3_invalidate_display(void *opaque)
163 {
164     CG3State *s = opaque;
165 
166     memory_region_set_dirty(&s->vram_mem, 0, CG3_VRAM_SIZE);
167 }
168 
169 static uint64_t cg3_reg_read(void *opaque, hwaddr addr, unsigned size)
170 {
171     CG3State *s = opaque;
172     int val;
173 
174     switch (addr) {
175     case CG3_REG_BT458_ADDR:
176     case CG3_REG_BT458_COLMAP:
177         val = 0;
178         break;
179     case CG3_REG_FBC_CTRL:
180         val = s->regs[0];
181         break;
182     case CG3_REG_FBC_STATUS:
183         /* monitor ID 6, board type = 1 (color) */
184         val = s->regs[1] | CG3_SR_1152_900_76_B | CG3_SR_ID_COLOR;
185         break;
186     case CG3_REG_FBC_CURSTART ... CG3_REG_SIZE - 1:
187         val = s->regs[addr - 0x10];
188         break;
189     default:
190         qemu_log_mask(LOG_UNIMP,
191                   "cg3: Unimplemented register read "
192                   "reg 0x%" HWADDR_PRIx " size 0x%x\n",
193                   addr, size);
194         val = 0;
195         break;
196     }
197     DPRINTF("read %02x from reg %" HWADDR_PRIx "\n", val, addr);
198     return val;
199 }
200 
201 static void cg3_reg_write(void *opaque, hwaddr addr, uint64_t val,
202                           unsigned size)
203 {
204     CG3State *s = opaque;
205     uint8_t regval;
206     int i;
207 
208     DPRINTF("write %" PRIx64 " to reg %" HWADDR_PRIx " size %d\n",
209             val, addr, size);
210 
211     switch (addr) {
212     case CG3_REG_BT458_ADDR:
213         s->dac_index = val;
214         s->dac_state = 0;
215         break;
216     case CG3_REG_BT458_COLMAP:
217         /* This register can be written to as either a long word or a byte */
218         if (size == 1) {
219             val <<= 24;
220         }
221 
222         for (i = 0; i < size; i++) {
223             regval = val >> 24;
224 
225             switch (s->dac_state) {
226             case 0:
227                 s->r[s->dac_index] = regval;
228                 s->dac_state++;
229                 break;
230             case 1:
231                 s->g[s->dac_index] = regval;
232                 s->dac_state++;
233                 break;
234             case 2:
235                 s->b[s->dac_index] = regval;
236                 /* Index autoincrement */
237                 s->dac_index = (s->dac_index + 1) & 0xff;
238                 /* fall through */
239             default:
240                 s->dac_state = 0;
241                 break;
242             }
243             val <<= 8;
244         }
245         s->full_update = 1;
246         break;
247     case CG3_REG_FBC_CTRL:
248         s->regs[0] = val;
249         break;
250     case CG3_REG_FBC_STATUS:
251         if (s->regs[1] & CG3_SR_PENDING_INT) {
252             /* clear interrupt */
253             s->regs[1] &= ~CG3_SR_PENDING_INT;
254             qemu_irq_lower(s->irq);
255         }
256         break;
257     case CG3_REG_FBC_CURSTART ... CG3_REG_SIZE - 1:
258         s->regs[addr - 0x10] = val;
259         break;
260     default:
261         qemu_log_mask(LOG_UNIMP,
262                   "cg3: Unimplemented register write "
263                   "reg 0x%" HWADDR_PRIx " size 0x%x value 0x%" PRIx64 "\n",
264                   addr, size, val);
265         break;
266     }
267 }
268 
269 static const MemoryRegionOps cg3_reg_ops = {
270     .read = cg3_reg_read,
271     .write = cg3_reg_write,
272     .endianness = DEVICE_NATIVE_ENDIAN,
273     .valid = {
274         .min_access_size = 1,
275         .max_access_size = 4,
276     },
277 };
278 
279 static const GraphicHwOps cg3_ops = {
280     .invalidate = cg3_invalidate_display,
281     .gfx_update = cg3_update_display,
282 };
283 
284 static void cg3_initfn(Object *obj)
285 {
286     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
287     CG3State *s = CG3(obj);
288 
289     memory_region_init_ram_nomigrate(&s->rom, obj, "cg3.prom", FCODE_MAX_ROM_SIZE,
290                            &error_fatal);
291     memory_region_set_readonly(&s->rom, true);
292     sysbus_init_mmio(sbd, &s->rom);
293 
294     memory_region_init_io(&s->reg, obj, &cg3_reg_ops, s, "cg3.reg",
295                           CG3_REG_SIZE);
296     sysbus_init_mmio(sbd, &s->reg);
297 }
298 
299 static void cg3_realizefn(DeviceState *dev, Error **errp)
300 {
301     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
302     CG3State *s = CG3(dev);
303     int ret;
304     char *fcode_filename;
305 
306     /* FCode ROM */
307     vmstate_register_ram_global(&s->rom);
308     fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, CG3_ROM_FILE);
309     if (fcode_filename) {
310         ret = load_image_mr(fcode_filename, &s->rom);
311         g_free(fcode_filename);
312         if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
313             warn_report("cg3: could not load prom '%s'", CG3_ROM_FILE);
314         }
315     }
316 
317     memory_region_init_ram(&s->vram_mem, NULL, "cg3.vram", s->vram_size,
318                            &error_fatal);
319     memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
320     sysbus_init_mmio(sbd, &s->vram_mem);
321 
322     sysbus_init_irq(sbd, &s->irq);
323 
324     s->con = graphic_console_init(DEVICE(dev), 0, &cg3_ops, s);
325     qemu_console_resize(s->con, s->width, s->height);
326 }
327 
328 static int vmstate_cg3_post_load(void *opaque, int version_id)
329 {
330     CG3State *s = opaque;
331 
332     cg3_invalidate_display(s);
333 
334     return 0;
335 }
336 
337 static const VMStateDescription vmstate_cg3 = {
338     .name = "cg3",
339     .version_id = 1,
340     .minimum_version_id = 1,
341     .post_load = vmstate_cg3_post_load,
342     .fields = (VMStateField[]) {
343         VMSTATE_UINT16(height, CG3State),
344         VMSTATE_UINT16(width, CG3State),
345         VMSTATE_UINT16(depth, CG3State),
346         VMSTATE_BUFFER(r, CG3State),
347         VMSTATE_BUFFER(g, CG3State),
348         VMSTATE_BUFFER(b, CG3State),
349         VMSTATE_UINT8(dac_index, CG3State),
350         VMSTATE_UINT8(dac_state, CG3State),
351         VMSTATE_END_OF_LIST()
352     }
353 };
354 
355 static void cg3_reset(DeviceState *d)
356 {
357     CG3State *s = CG3(d);
358 
359     /* Initialize palette */
360     memset(s->r, 0, 256);
361     memset(s->g, 0, 256);
362     memset(s->b, 0, 256);
363 
364     s->dac_state = 0;
365     s->full_update = 1;
366     qemu_irq_lower(s->irq);
367 }
368 
369 static Property cg3_properties[] = {
370     DEFINE_PROP_UINT32("vram-size",    CG3State, vram_size, -1),
371     DEFINE_PROP_UINT16("width",        CG3State, width,     -1),
372     DEFINE_PROP_UINT16("height",       CG3State, height,    -1),
373     DEFINE_PROP_UINT16("depth",        CG3State, depth,     -1),
374     DEFINE_PROP_END_OF_LIST(),
375 };
376 
377 static void cg3_class_init(ObjectClass *klass, void *data)
378 {
379     DeviceClass *dc = DEVICE_CLASS(klass);
380 
381     dc->realize = cg3_realizefn;
382     dc->reset = cg3_reset;
383     dc->vmsd = &vmstate_cg3;
384     dc->props = cg3_properties;
385 }
386 
387 static const TypeInfo cg3_info = {
388     .name          = TYPE_CG3,
389     .parent        = TYPE_SYS_BUS_DEVICE,
390     .instance_size = sizeof(CG3State),
391     .instance_init = cg3_initfn,
392     .class_init    = cg3_class_init,
393 };
394 
395 static void cg3_register_types(void)
396 {
397     type_register_static(&cg3_info);
398 }
399 
400 type_init(cg3_register_types)
401