]>
Commit | Line | Data |
---|---|---|
8ac919a0 LV |
1 | /* |
2 | * QEMU Motorola 680x0 Macintosh Video Card Emulation | |
3 | * Copyright (c) 2012-2018 Laurent Vivier | |
4 | * | |
5 | * some parts from QEMU G364 framebuffer Emulator. | |
6 | * Copyright (c) 2007-2011 Herve Poussineau | |
7 | * | |
8 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
9 | * See the COPYING file in the top-level directory. | |
10 | * | |
11 | */ | |
12 | ||
13 | #include "qemu/osdep.h" | |
14 | #include "qemu/units.h" | |
15 | #include "hw/sysbus.h" | |
16 | #include "ui/console.h" | |
17 | #include "ui/pixel_ops.h" | |
18 | #include "hw/nubus/nubus.h" | |
19 | #include "hw/display/macfb.h" | |
20 | #include "qapi/error.h" | |
21 | #include "hw/qdev-properties.h" | |
22 | #include "migration/vmstate.h" | |
4ec27073 | 23 | #include "trace.h" |
8ac919a0 | 24 | |
df8abbba | 25 | #define VIDEO_BASE 0x0 |
8ac919a0 LV |
26 | #define DAFB_BASE 0x00800000 |
27 | ||
28 | #define MACFB_PAGE_SIZE 4096 | |
29 | #define MACFB_VRAM_SIZE (4 * MiB) | |
30 | ||
df8abbba MCA |
31 | #define DAFB_MODE_VADDR1 0x0 |
32 | #define DAFB_MODE_VADDR2 0x4 | |
33 | #define DAFB_MODE_CTRL1 0x8 | |
34 | #define DAFB_MODE_CTRL2 0xc | |
e6108b96 | 35 | #define DAFB_MODE_SENSE 0x1c |
c7a2f7ba MCA |
36 | #define DAFB_INTR_MASK 0x104 |
37 | #define DAFB_INTR_STAT 0x108 | |
38 | #define DAFB_INTR_CLEAR 0x10c | |
2fca4e7a | 39 | #define DAFB_LUT_INDEX 0x200 |
ced64254 | 40 | #define DAFB_LUT 0x210 |
e6108b96 | 41 | |
c7a2f7ba MCA |
42 | #define DAFB_INTR_VBL 0x4 |
43 | ||
44 | /* Vertical Blank period (60.15Hz) */ | |
45 | #define DAFB_INTR_VBL_PERIOD_NS 16625800 | |
e6108b96 MCA |
46 | |
47 | /* | |
48 | * Quadra sense codes taken from Apple Technical Note HW26: | |
49 | * "Macintosh Quadra Built-In Video". The sense codes and | |
50 | * extended sense codes have different meanings: | |
51 | * | |
52 | * Sense: | |
53 | * bit 2: SENSE2 (pin 10) | |
54 | * bit 1: SENSE1 (pin 7) | |
55 | * bit 0: SENSE0 (pin 4) | |
56 | * | |
57 | * 0 = pin tied to ground | |
58 | * 1 = pin unconnected | |
59 | * | |
60 | * Extended Sense: | |
61 | * bit 2: pins 4-10 | |
62 | * bit 1: pins 10-7 | |
63 | * bit 0: pins 7-4 | |
64 | * | |
65 | * 0 = pins tied together | |
66 | * 1 = pins unconnected | |
67 | * | |
68 | * Reads from the sense register appear to be active low, i.e. a 1 indicates | |
69 | * that the pin is tied to ground, a 0 indicates the pin is disconnected. | |
70 | * | |
71 | * Writes to the sense register appear to activate pulldowns i.e. a 1 enables | |
72 | * a pulldown on a particular pin. | |
73 | * | |
74 | * The MacOS toolbox appears to use a series of reads and writes to first | |
75 | * determine if extended sense is to be used, and then check which pins are | |
76 | * tied together in order to determine the display type. | |
77 | */ | |
78 | ||
79 | typedef struct MacFbSense { | |
80 | uint8_t type; | |
81 | uint8_t sense; | |
82 | uint8_t ext_sense; | |
83 | } MacFbSense; | |
84 | ||
85 | static MacFbSense macfb_sense_table[] = { | |
86 | { MACFB_DISPLAY_APPLE_21_COLOR, 0x0, 0 }, | |
87 | { MACFB_DISPLAY_APPLE_PORTRAIT, 0x1, 0 }, | |
88 | { MACFB_DISPLAY_APPLE_12_RGB, 0x2, 0 }, | |
89 | { MACFB_DISPLAY_APPLE_2PAGE_MONO, 0x3, 0 }, | |
90 | { MACFB_DISPLAY_NTSC_UNDERSCAN, 0x4, 0 }, | |
91 | { MACFB_DISPLAY_NTSC_OVERSCAN, 0x4, 0 }, | |
92 | { MACFB_DISPLAY_APPLE_12_MONO, 0x6, 0 }, | |
93 | { MACFB_DISPLAY_APPLE_13_RGB, 0x6, 0 }, | |
94 | { MACFB_DISPLAY_16_COLOR, 0x7, 0x3 }, | |
95 | { MACFB_DISPLAY_PAL1_UNDERSCAN, 0x7, 0x0 }, | |
96 | { MACFB_DISPLAY_PAL1_OVERSCAN, 0x7, 0x0 }, | |
97 | { MACFB_DISPLAY_PAL2_UNDERSCAN, 0x7, 0x6 }, | |
98 | { MACFB_DISPLAY_PAL2_OVERSCAN, 0x7, 0x6 }, | |
99 | { MACFB_DISPLAY_VGA, 0x7, 0x5 }, | |
100 | { MACFB_DISPLAY_SVGA, 0x7, 0x5 }, | |
101 | }; | |
8ac919a0 | 102 | |
df8abbba MCA |
103 | static MacFbMode macfb_mode_table[] = { |
104 | { MACFB_DISPLAY_VGA, 1, 0x100, 0x71e, 640, 480, 0x400, 0x1000 }, | |
105 | { MACFB_DISPLAY_VGA, 2, 0x100, 0x70e, 640, 480, 0x400, 0x1000 }, | |
106 | { MACFB_DISPLAY_VGA, 4, 0x100, 0x706, 640, 480, 0x400, 0x1000 }, | |
107 | { MACFB_DISPLAY_VGA, 8, 0x100, 0x702, 640, 480, 0x400, 0x1000 }, | |
108 | { MACFB_DISPLAY_VGA, 24, 0x100, 0x7ff, 640, 480, 0x1000, 0x1000 }, | |
109 | { MACFB_DISPLAY_VGA, 1, 0xd0 , 0x70e, 800, 600, 0x340, 0xe00 }, | |
110 | { MACFB_DISPLAY_VGA, 2, 0xd0 , 0x706, 800, 600, 0x340, 0xe00 }, | |
111 | { MACFB_DISPLAY_VGA, 4, 0xd0 , 0x702, 800, 600, 0x340, 0xe00 }, | |
112 | { MACFB_DISPLAY_VGA, 8, 0xd0, 0x700, 800, 600, 0x340, 0xe00 }, | |
113 | { MACFB_DISPLAY_VGA, 24, 0x340, 0x100, 800, 600, 0xd00, 0xe00 }, | |
114 | { MACFB_DISPLAY_APPLE_21_COLOR, 1, 0x90, 0x506, 1152, 870, 0x240, 0x80 }, | |
115 | { MACFB_DISPLAY_APPLE_21_COLOR, 2, 0x90, 0x502, 1152, 870, 0x240, 0x80 }, | |
116 | { MACFB_DISPLAY_APPLE_21_COLOR, 4, 0x90, 0x500, 1152, 870, 0x240, 0x80 }, | |
117 | { MACFB_DISPLAY_APPLE_21_COLOR, 8, 0x120, 0x5ff, 1152, 870, 0x480, 0x80 }, | |
118 | }; | |
8ac919a0 LV |
119 | |
120 | typedef void macfb_draw_line_func(MacfbState *s, uint8_t *d, uint32_t addr, | |
121 | int width); | |
122 | ||
123 | static inline uint8_t macfb_read_byte(MacfbState *s, uint32_t addr) | |
124 | { | |
125 | return s->vram[addr & s->vram_bit_mask]; | |
126 | } | |
127 | ||
128 | /* 1-bit color */ | |
129 | static void macfb_draw_line1(MacfbState *s, uint8_t *d, uint32_t addr, | |
130 | int width) | |
131 | { | |
132 | uint8_t r, g, b; | |
133 | int x; | |
134 | ||
135 | for (x = 0; x < width; x++) { | |
136 | int bit = x & 7; | |
137 | int idx = (macfb_read_byte(s, addr) >> (7 - bit)) & 1; | |
57eeaf44 MCA |
138 | r = s->color_palette[idx * 3]; |
139 | g = s->color_palette[idx * 3 + 1]; | |
140 | b = s->color_palette[idx * 3 + 2]; | |
8ac919a0 LV |
141 | addr += (bit == 7); |
142 | ||
143 | *(uint32_t *)d = rgb_to_pixel32(r, g, b); | |
144 | d += 4; | |
145 | } | |
146 | } | |
147 | ||
148 | /* 2-bit color */ | |
149 | static void macfb_draw_line2(MacfbState *s, uint8_t *d, uint32_t addr, | |
150 | int width) | |
151 | { | |
152 | uint8_t r, g, b; | |
153 | int x; | |
154 | ||
155 | for (x = 0; x < width; x++) { | |
156 | int bit = (x & 3); | |
157 | int idx = (macfb_read_byte(s, addr) >> ((3 - bit) << 1)) & 3; | |
158 | r = s->color_palette[idx * 3]; | |
159 | g = s->color_palette[idx * 3 + 1]; | |
160 | b = s->color_palette[idx * 3 + 2]; | |
161 | addr += (bit == 3); | |
162 | ||
163 | *(uint32_t *)d = rgb_to_pixel32(r, g, b); | |
164 | d += 4; | |
165 | } | |
166 | } | |
167 | ||
168 | /* 4-bit color */ | |
169 | static void macfb_draw_line4(MacfbState *s, uint8_t *d, uint32_t addr, | |
170 | int width) | |
171 | { | |
172 | uint8_t r, g, b; | |
173 | int x; | |
174 | ||
175 | for (x = 0; x < width; x++) { | |
176 | int bit = x & 1; | |
177 | int idx = (macfb_read_byte(s, addr) >> ((1 - bit) << 2)) & 15; | |
178 | r = s->color_palette[idx * 3]; | |
179 | g = s->color_palette[idx * 3 + 1]; | |
180 | b = s->color_palette[idx * 3 + 2]; | |
181 | addr += (bit == 1); | |
182 | ||
183 | *(uint32_t *)d = rgb_to_pixel32(r, g, b); | |
184 | d += 4; | |
185 | } | |
186 | } | |
187 | ||
188 | /* 8-bit color */ | |
189 | static void macfb_draw_line8(MacfbState *s, uint8_t *d, uint32_t addr, | |
190 | int width) | |
191 | { | |
192 | uint8_t r, g, b; | |
193 | int x; | |
194 | ||
195 | for (x = 0; x < width; x++) { | |
196 | r = s->color_palette[macfb_read_byte(s, addr) * 3]; | |
197 | g = s->color_palette[macfb_read_byte(s, addr) * 3 + 1]; | |
198 | b = s->color_palette[macfb_read_byte(s, addr) * 3 + 2]; | |
199 | addr++; | |
200 | ||
201 | *(uint32_t *)d = rgb_to_pixel32(r, g, b); | |
202 | d += 4; | |
203 | } | |
204 | } | |
205 | ||
206 | /* 16-bit color */ | |
207 | static void macfb_draw_line16(MacfbState *s, uint8_t *d, uint32_t addr, | |
208 | int width) | |
209 | { | |
210 | uint8_t r, g, b; | |
211 | int x; | |
212 | ||
213 | for (x = 0; x < width; x++) { | |
214 | uint16_t pixel; | |
215 | pixel = (macfb_read_byte(s, addr) << 8) | macfb_read_byte(s, addr + 1); | |
216 | r = ((pixel >> 10) & 0x1f) << 3; | |
217 | g = ((pixel >> 5) & 0x1f) << 3; | |
218 | b = (pixel & 0x1f) << 3; | |
219 | addr += 2; | |
220 | ||
221 | *(uint32_t *)d = rgb_to_pixel32(r, g, b); | |
222 | d += 4; | |
223 | } | |
224 | } | |
225 | ||
226 | /* 24-bit color */ | |
227 | static void macfb_draw_line24(MacfbState *s, uint8_t *d, uint32_t addr, | |
228 | int width) | |
229 | { | |
230 | uint8_t r, g, b; | |
231 | int x; | |
232 | ||
233 | for (x = 0; x < width; x++) { | |
432d59c5 MCA |
234 | r = macfb_read_byte(s, addr + 1); |
235 | g = macfb_read_byte(s, addr + 2); | |
236 | b = macfb_read_byte(s, addr + 3); | |
237 | addr += 4; | |
8ac919a0 LV |
238 | |
239 | *(uint32_t *)d = rgb_to_pixel32(r, g, b); | |
240 | d += 4; | |
241 | } | |
242 | } | |
243 | ||
244 | ||
245 | enum { | |
246 | MACFB_DRAW_LINE1, | |
247 | MACFB_DRAW_LINE2, | |
248 | MACFB_DRAW_LINE4, | |
249 | MACFB_DRAW_LINE8, | |
250 | MACFB_DRAW_LINE16, | |
251 | MACFB_DRAW_LINE24, | |
252 | MACFB_DRAW_LINE_NB, | |
253 | }; | |
254 | ||
255 | static macfb_draw_line_func * const | |
256 | macfb_draw_line_table[MACFB_DRAW_LINE_NB] = { | |
257 | macfb_draw_line1, | |
258 | macfb_draw_line2, | |
259 | macfb_draw_line4, | |
260 | macfb_draw_line8, | |
261 | macfb_draw_line16, | |
262 | macfb_draw_line24, | |
263 | }; | |
264 | ||
265 | static int macfb_check_dirty(MacfbState *s, DirtyBitmapSnapshot *snap, | |
266 | ram_addr_t addr, int len) | |
267 | { | |
268 | return memory_region_snapshot_get_dirty(&s->mem_vram, snap, addr, len); | |
269 | } | |
270 | ||
271 | static void macfb_draw_graphic(MacfbState *s) | |
272 | { | |
273 | DisplaySurface *surface = qemu_console_surface(s->con); | |
274 | DirtyBitmapSnapshot *snap = NULL; | |
275 | ram_addr_t page; | |
276 | uint32_t v = 0; | |
277 | int y, ymin; | |
df8abbba | 278 | int macfb_stride = s->mode->stride; |
8ac919a0 LV |
279 | macfb_draw_line_func *macfb_draw_line; |
280 | ||
281 | switch (s->depth) { | |
282 | case 1: | |
283 | v = MACFB_DRAW_LINE1; | |
284 | break; | |
285 | case 2: | |
286 | v = MACFB_DRAW_LINE2; | |
287 | break; | |
288 | case 4: | |
289 | v = MACFB_DRAW_LINE4; | |
290 | break; | |
291 | case 8: | |
292 | v = MACFB_DRAW_LINE8; | |
293 | break; | |
294 | case 16: | |
295 | v = MACFB_DRAW_LINE16; | |
296 | break; | |
297 | case 24: | |
298 | v = MACFB_DRAW_LINE24; | |
299 | break; | |
300 | } | |
301 | ||
302 | macfb_draw_line = macfb_draw_line_table[v]; | |
303 | assert(macfb_draw_line != NULL); | |
304 | ||
305 | snap = memory_region_snapshot_and_clear_dirty(&s->mem_vram, 0x0, | |
306 | memory_region_size(&s->mem_vram), | |
307 | DIRTY_MEMORY_VGA); | |
308 | ||
309 | ymin = -1; | |
df8abbba | 310 | page = s->mode->offset; |
8ac919a0 LV |
311 | for (y = 0; y < s->height; y++, page += macfb_stride) { |
312 | if (macfb_check_dirty(s, snap, page, macfb_stride)) { | |
313 | uint8_t *data_display; | |
314 | ||
315 | data_display = surface_data(surface) + y * surface_stride(surface); | |
316 | macfb_draw_line(s, data_display, page, s->width); | |
317 | ||
318 | if (ymin < 0) { | |
319 | ymin = y; | |
320 | } | |
321 | } else { | |
322 | if (ymin >= 0) { | |
323 | dpy_gfx_update(s->con, 0, ymin, s->width, y - ymin); | |
324 | ymin = -1; | |
325 | } | |
326 | } | |
327 | } | |
328 | ||
329 | if (ymin >= 0) { | |
330 | dpy_gfx_update(s->con, 0, ymin, s->width, y - ymin); | |
331 | } | |
332 | ||
333 | g_free(snap); | |
334 | } | |
335 | ||
336 | static void macfb_invalidate_display(void *opaque) | |
337 | { | |
338 | MacfbState *s = opaque; | |
339 | ||
340 | memory_region_set_dirty(&s->mem_vram, 0, MACFB_VRAM_SIZE); | |
341 | } | |
342 | ||
e6108b96 MCA |
343 | static uint32_t macfb_sense_read(MacfbState *s) |
344 | { | |
345 | MacFbSense *macfb_sense; | |
346 | uint8_t sense; | |
347 | ||
4317c518 MCA |
348 | assert(s->type < ARRAY_SIZE(macfb_sense_table)); |
349 | macfb_sense = &macfb_sense_table[s->type]; | |
e6108b96 MCA |
350 | if (macfb_sense->sense == 0x7) { |
351 | /* Extended sense */ | |
352 | sense = 0; | |
353 | if (!(macfb_sense->ext_sense & 1)) { | |
354 | /* Pins 7-4 together */ | |
df8abbba MCA |
355 | if (~s->regs[DAFB_MODE_SENSE >> 2] & 3) { |
356 | sense = (~s->regs[DAFB_MODE_SENSE >> 2] & 7) | 3; | |
e6108b96 MCA |
357 | } |
358 | } | |
359 | if (!(macfb_sense->ext_sense & 2)) { | |
360 | /* Pins 10-7 together */ | |
df8abbba MCA |
361 | if (~s->regs[DAFB_MODE_SENSE >> 2] & 6) { |
362 | sense = (~s->regs[DAFB_MODE_SENSE >> 2] & 7) | 6; | |
e6108b96 MCA |
363 | } |
364 | } | |
365 | if (!(macfb_sense->ext_sense & 4)) { | |
366 | /* Pins 4-10 together */ | |
df8abbba MCA |
367 | if (~s->regs[DAFB_MODE_SENSE >> 2] & 5) { |
368 | sense = (~s->regs[DAFB_MODE_SENSE >> 2] & 7) | 5; | |
e6108b96 MCA |
369 | } |
370 | } | |
371 | } else { | |
372 | /* Normal sense */ | |
df8abbba MCA |
373 | sense = (~macfb_sense->sense & 7) | |
374 | (~s->regs[DAFB_MODE_SENSE >> 2] & 7); | |
e6108b96 MCA |
375 | } |
376 | ||
377 | trace_macfb_sense_read(sense); | |
378 | return sense; | |
379 | } | |
380 | ||
381 | static void macfb_sense_write(MacfbState *s, uint32_t val) | |
382 | { | |
df8abbba | 383 | s->regs[DAFB_MODE_SENSE >> 2] = val; |
e6108b96 MCA |
384 | |
385 | trace_macfb_sense_write(val); | |
386 | return; | |
387 | } | |
388 | ||
df8abbba MCA |
389 | static void macfb_update_mode(MacfbState *s) |
390 | { | |
391 | s->width = s->mode->width; | |
392 | s->height = s->mode->height; | |
393 | s->depth = s->mode->depth; | |
394 | ||
395 | trace_macfb_update_mode(s->width, s->height, s->depth); | |
396 | macfb_invalidate_display(s); | |
397 | } | |
398 | ||
399 | static void macfb_mode_write(MacfbState *s) | |
400 | { | |
401 | MacFbMode *macfb_mode; | |
402 | int i; | |
403 | ||
404 | for (i = 0; i < ARRAY_SIZE(macfb_mode_table); i++) { | |
405 | macfb_mode = &macfb_mode_table[i]; | |
406 | ||
407 | if (s->type != macfb_mode->type) { | |
408 | continue; | |
409 | } | |
410 | ||
411 | if ((s->regs[DAFB_MODE_CTRL1 >> 2] & 0xff) == | |
412 | (macfb_mode->mode_ctrl1 & 0xff) && | |
413 | (s->regs[DAFB_MODE_CTRL2 >> 2] & 0xff) == | |
414 | (macfb_mode->mode_ctrl2 & 0xff)) { | |
415 | s->mode = macfb_mode; | |
416 | macfb_update_mode(s); | |
417 | break; | |
418 | } | |
419 | } | |
420 | } | |
421 | ||
422 | static MacFbMode *macfb_find_mode(MacfbDisplayType display_type, | |
423 | uint16_t width, uint16_t height, | |
424 | uint8_t depth) | |
425 | { | |
426 | MacFbMode *macfb_mode; | |
427 | int i; | |
428 | ||
429 | for (i = 0; i < ARRAY_SIZE(macfb_mode_table); i++) { | |
430 | macfb_mode = &macfb_mode_table[i]; | |
431 | ||
432 | if (display_type == macfb_mode->type && width == macfb_mode->width && | |
433 | height == macfb_mode->height && depth == macfb_mode->depth) { | |
434 | return macfb_mode; | |
435 | } | |
436 | } | |
437 | ||
438 | return NULL; | |
439 | } | |
440 | ||
441 | static gchar *macfb_mode_list(void) | |
442 | { | |
5db83c7e | 443 | GString *list = g_string_new(""); |
df8abbba MCA |
444 | MacFbMode *macfb_mode; |
445 | int i; | |
446 | ||
447 | for (i = 0; i < ARRAY_SIZE(macfb_mode_table); i++) { | |
448 | macfb_mode = &macfb_mode_table[i]; | |
449 | ||
5db83c7e | 450 | g_string_append_printf(list, " %dx%dx%d\n", macfb_mode->width, |
df8abbba | 451 | macfb_mode->height, macfb_mode->depth); |
df8abbba MCA |
452 | } |
453 | ||
5db83c7e | 454 | return g_string_free(list, FALSE); |
df8abbba MCA |
455 | } |
456 | ||
457 | ||
8ac919a0 LV |
458 | static void macfb_update_display(void *opaque) |
459 | { | |
460 | MacfbState *s = opaque; | |
461 | DisplaySurface *surface = qemu_console_surface(s->con); | |
462 | ||
463 | qemu_flush_coalesced_mmio_buffer(); | |
464 | ||
465 | if (s->width == 0 || s->height == 0) { | |
466 | return; | |
467 | } | |
468 | ||
469 | if (s->width != surface_width(surface) || | |
470 | s->height != surface_height(surface)) { | |
471 | qemu_console_resize(s->con, s->width, s->height); | |
472 | } | |
473 | ||
474 | macfb_draw_graphic(s); | |
475 | } | |
476 | ||
c7a2f7ba MCA |
477 | static void macfb_update_irq(MacfbState *s) |
478 | { | |
47181251 MCA |
479 | uint32_t irq_state = s->regs[DAFB_INTR_STAT >> 2] & |
480 | s->regs[DAFB_INTR_MASK >> 2]; | |
c7a2f7ba MCA |
481 | |
482 | if (irq_state) { | |
483 | qemu_irq_raise(s->irq); | |
484 | } else { | |
485 | qemu_irq_lower(s->irq); | |
486 | } | |
487 | } | |
488 | ||
489 | static int64_t macfb_next_vbl(void) | |
490 | { | |
491 | return (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + DAFB_INTR_VBL_PERIOD_NS) / | |
492 | DAFB_INTR_VBL_PERIOD_NS * DAFB_INTR_VBL_PERIOD_NS; | |
493 | } | |
494 | ||
495 | static void macfb_vbl_timer(void *opaque) | |
496 | { | |
497 | MacfbState *s = opaque; | |
498 | int64_t next_vbl; | |
499 | ||
47181251 | 500 | s->regs[DAFB_INTR_STAT >> 2] |= DAFB_INTR_VBL; |
c7a2f7ba MCA |
501 | macfb_update_irq(s); |
502 | ||
503 | /* 60 Hz irq */ | |
504 | next_vbl = macfb_next_vbl(); | |
505 | timer_mod(s->vbl_timer, next_vbl); | |
506 | } | |
507 | ||
8ac919a0 LV |
508 | static void macfb_reset(MacfbState *s) |
509 | { | |
510 | int i; | |
511 | ||
512 | s->palette_current = 0; | |
513 | for (i = 0; i < 256; i++) { | |
514 | s->color_palette[i * 3] = 255 - i; | |
515 | s->color_palette[i * 3 + 1] = 255 - i; | |
516 | s->color_palette[i * 3 + 2] = 255 - i; | |
517 | } | |
518 | memset(s->vram, 0, MACFB_VRAM_SIZE); | |
519 | macfb_invalidate_display(s); | |
520 | } | |
521 | ||
522 | static uint64_t macfb_ctrl_read(void *opaque, | |
523 | hwaddr addr, | |
524 | unsigned int size) | |
525 | { | |
e6108b96 | 526 | MacfbState *s = opaque; |
4ec27073 MCA |
527 | uint64_t val = 0; |
528 | ||
e6108b96 | 529 | switch (addr) { |
df8abbba MCA |
530 | case DAFB_MODE_VADDR1: |
531 | case DAFB_MODE_VADDR2: | |
532 | case DAFB_MODE_CTRL1: | |
533 | case DAFB_MODE_CTRL2: | |
c7a2f7ba | 534 | case DAFB_INTR_STAT: |
47181251 | 535 | val = s->regs[addr >> 2]; |
c7a2f7ba | 536 | break; |
e6108b96 MCA |
537 | case DAFB_MODE_SENSE: |
538 | val = macfb_sense_read(s); | |
539 | break; | |
95f39432 MCA |
540 | case DAFB_LUT ... DAFB_LUT + 3: |
541 | val = s->color_palette[s->palette_current]; | |
542 | s->palette_current = (s->palette_current + 1) % | |
543 | ARRAY_SIZE(s->color_palette); | |
544 | break; | |
dd2a56ab MCA |
545 | default: |
546 | if (addr < MACFB_CTRL_TOPADDR) { | |
547 | val = s->regs[addr >> 2]; | |
548 | } | |
e6108b96 MCA |
549 | } |
550 | ||
4ec27073 MCA |
551 | trace_macfb_ctrl_read(addr, val, size); |
552 | return val; | |
8ac919a0 LV |
553 | } |
554 | ||
555 | static void macfb_ctrl_write(void *opaque, | |
556 | hwaddr addr, | |
557 | uint64_t val, | |
558 | unsigned int size) | |
559 | { | |
560 | MacfbState *s = opaque; | |
c7a2f7ba MCA |
561 | int64_t next_vbl; |
562 | ||
8ac919a0 | 563 | switch (addr) { |
df8abbba MCA |
564 | case DAFB_MODE_VADDR1: |
565 | case DAFB_MODE_VADDR2: | |
566 | s->regs[addr >> 2] = val; | |
567 | break; | |
568 | case DAFB_MODE_CTRL1 ... DAFB_MODE_CTRL1 + 3: | |
569 | case DAFB_MODE_CTRL2 ... DAFB_MODE_CTRL2 + 3: | |
570 | s->regs[addr >> 2] = val; | |
571 | if (val) { | |
572 | macfb_mode_write(s); | |
573 | } | |
574 | break; | |
e6108b96 MCA |
575 | case DAFB_MODE_SENSE: |
576 | macfb_sense_write(s, val); | |
577 | break; | |
c7a2f7ba | 578 | case DAFB_INTR_MASK: |
47181251 | 579 | s->regs[addr >> 2] = val; |
c7a2f7ba MCA |
580 | if (val & DAFB_INTR_VBL) { |
581 | next_vbl = macfb_next_vbl(); | |
582 | timer_mod(s->vbl_timer, next_vbl); | |
583 | } else { | |
584 | timer_del(s->vbl_timer); | |
585 | } | |
586 | break; | |
587 | case DAFB_INTR_CLEAR: | |
47181251 | 588 | s->regs[DAFB_INTR_STAT >> 2] &= ~DAFB_INTR_VBL; |
c7a2f7ba MCA |
589 | macfb_update_irq(s); |
590 | break; | |
2fca4e7a MCA |
591 | case DAFB_LUT_INDEX: |
592 | s->palette_current = (val & 0xff) * 3; | |
8ac919a0 | 593 | break; |
ced64254 MCA |
594 | case DAFB_LUT ... DAFB_LUT + 3: |
595 | s->color_palette[s->palette_current] = val & 0xff; | |
14d0ddfc MCA |
596 | s->palette_current = (s->palette_current + 1) % |
597 | ARRAY_SIZE(s->color_palette); | |
8ac919a0 LV |
598 | if (s->palette_current % 3) { |
599 | macfb_invalidate_display(s); | |
600 | } | |
601 | break; | |
dd2a56ab MCA |
602 | default: |
603 | if (addr < MACFB_CTRL_TOPADDR) { | |
604 | s->regs[addr >> 2] = val; | |
605 | } | |
8ac919a0 | 606 | } |
4ec27073 MCA |
607 | |
608 | trace_macfb_ctrl_write(addr, val, size); | |
8ac919a0 LV |
609 | } |
610 | ||
611 | static const MemoryRegionOps macfb_ctrl_ops = { | |
612 | .read = macfb_ctrl_read, | |
613 | .write = macfb_ctrl_write, | |
614 | .endianness = DEVICE_BIG_ENDIAN, | |
615 | .impl.min_access_size = 1, | |
616 | .impl.max_access_size = 4, | |
617 | }; | |
618 | ||
619 | static int macfb_post_load(void *opaque, int version_id) | |
620 | { | |
df8abbba | 621 | macfb_mode_write(opaque); |
8ac919a0 LV |
622 | return 0; |
623 | } | |
624 | ||
625 | static const VMStateDescription vmstate_macfb = { | |
626 | .name = "macfb", | |
627 | .version_id = 1, | |
628 | .minimum_version_id = 1, | |
8ac919a0 | 629 | .post_load = macfb_post_load, |
f0613160 | 630 | .fields = (const VMStateField[]) { |
aba7432b | 631 | VMSTATE_UINT8(type, MacfbState), |
8ac919a0 LV |
632 | VMSTATE_UINT8_ARRAY(color_palette, MacfbState, 256 * 3), |
633 | VMSTATE_UINT32(palette_current, MacfbState), | |
df8abbba | 634 | VMSTATE_UINT32_ARRAY(regs, MacfbState, MACFB_NUM_REGS), |
aba7432b | 635 | VMSTATE_TIMER_PTR(vbl_timer, MacfbState), |
8ac919a0 LV |
636 | VMSTATE_END_OF_LIST() |
637 | } | |
638 | }; | |
639 | ||
640 | static const GraphicHwOps macfb_ops = { | |
641 | .invalidate = macfb_invalidate_display, | |
642 | .gfx_update = macfb_update_display, | |
643 | }; | |
644 | ||
c047862a | 645 | static bool macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp) |
8ac919a0 LV |
646 | { |
647 | DisplaySurface *surface; | |
648 | ||
df8abbba MCA |
649 | s->mode = macfb_find_mode(s->type, s->width, s->height, s->depth); |
650 | if (!s->mode) { | |
651 | gchar *list; | |
652 | error_setg(errp, "unknown display mode: width %d, height %d, depth %d", | |
653 | s->width, s->height, s->depth); | |
5db83c7e | 654 | list = macfb_mode_list(); |
df8abbba MCA |
655 | error_append_hint(errp, "Available modes:\n%s", list); |
656 | g_free(list); | |
657 | ||
c047862a | 658 | return false; |
8ac919a0 LV |
659 | } |
660 | ||
e20d0b84 MCA |
661 | /* |
662 | * Set mode control registers to match the mode found above so that | |
663 | * macfb_mode_write() does the right thing if no MacOS toolbox ROM | |
664 | * is present to initialise them | |
665 | */ | |
666 | s->regs[DAFB_MODE_CTRL1 >> 2] = s->mode->mode_ctrl1; | |
667 | s->regs[DAFB_MODE_CTRL2 >> 2] = s->mode->mode_ctrl2; | |
668 | ||
8ac919a0 LV |
669 | s->con = graphic_console_init(dev, 0, &macfb_ops, s); |
670 | surface = qemu_console_surface(s->con); | |
671 | ||
672 | if (surface_bits_per_pixel(surface) != 32) { | |
673 | error_setg(errp, "unknown host depth %d", | |
674 | surface_bits_per_pixel(surface)); | |
c047862a | 675 | return false; |
8ac919a0 LV |
676 | } |
677 | ||
b9fc4f6e PMD |
678 | memory_region_init_io(&s->mem_ctrl, OBJECT(dev), &macfb_ctrl_ops, s, |
679 | "macfb-ctrl", 0x1000); | |
8ac919a0 | 680 | |
3b10b567 MCA |
681 | memory_region_init_ram(&s->mem_vram, OBJECT(dev), "macfb-vram", |
682 | MACFB_VRAM_SIZE, &error_abort); | |
4e136629 | 683 | memory_region_set_log(&s->mem_vram, true, DIRTY_MEMORY_VGA); |
8ac919a0 LV |
684 | s->vram = memory_region_get_ram_ptr(&s->mem_vram); |
685 | s->vram_bit_mask = MACFB_VRAM_SIZE - 1; | |
c047862a | 686 | |
c7a2f7ba | 687 | s->vbl_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, macfb_vbl_timer, s); |
df8abbba | 688 | macfb_update_mode(s); |
c047862a | 689 | return true; |
8ac919a0 LV |
690 | } |
691 | ||
692 | static void macfb_sysbus_realize(DeviceState *dev, Error **errp) | |
693 | { | |
694 | MacfbSysBusState *s = MACFB(dev); | |
695 | MacfbState *ms = &s->macfb; | |
696 | ||
c047862a | 697 | if (!macfb_common_realize(dev, ms, errp)) { |
7437b13e MCA |
698 | return; |
699 | } | |
700 | ||
8ac919a0 LV |
701 | sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_ctrl); |
702 | sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_vram); | |
c7a2f7ba MCA |
703 | |
704 | qdev_init_gpio_out(dev, &ms->irq, 1); | |
705 | } | |
706 | ||
707 | static void macfb_nubus_set_irq(void *opaque, int n, int level) | |
708 | { | |
709 | MacfbNubusState *s = NUBUS_MACFB(opaque); | |
710 | NubusDevice *nd = NUBUS_DEVICE(s); | |
711 | ||
712 | nubus_set_irq(nd, level); | |
8ac919a0 LV |
713 | } |
714 | ||
8ac919a0 LV |
715 | static void macfb_nubus_realize(DeviceState *dev, Error **errp) |
716 | { | |
d470fd6a | 717 | ERRP_GUARD(); |
8ac919a0 LV |
718 | NubusDevice *nd = NUBUS_DEVICE(dev); |
719 | MacfbNubusState *s = NUBUS_MACFB(dev); | |
00104015 | 720 | MacfbNubusDeviceClass *ndc = NUBUS_MACFB_GET_CLASS(dev); |
8ac919a0 LV |
721 | MacfbState *ms = &s->macfb; |
722 | ||
723 | ndc->parent_realize(dev, errp); | |
7437b13e MCA |
724 | if (*errp) { |
725 | return; | |
726 | } | |
8ac919a0 | 727 | |
c047862a | 728 | if (!macfb_common_realize(dev, ms, errp)) { |
7437b13e MCA |
729 | return; |
730 | } | |
731 | ||
8ac919a0 LV |
732 | memory_region_add_subregion(&nd->slot_mem, DAFB_BASE, &ms->mem_ctrl); |
733 | memory_region_add_subregion(&nd->slot_mem, VIDEO_BASE, &ms->mem_vram); | |
c7a2f7ba MCA |
734 | |
735 | ms->irq = qemu_allocate_irq(macfb_nubus_set_irq, s, 0); | |
736 | } | |
737 | ||
738 | static void macfb_nubus_unrealize(DeviceState *dev) | |
739 | { | |
740 | MacfbNubusState *s = NUBUS_MACFB(dev); | |
741 | MacfbNubusDeviceClass *ndc = NUBUS_MACFB_GET_CLASS(dev); | |
742 | MacfbState *ms = &s->macfb; | |
743 | ||
744 | ndc->parent_unrealize(dev); | |
745 | ||
746 | qemu_free_irq(ms->irq); | |
8ac919a0 LV |
747 | } |
748 | ||
749 | static void macfb_sysbus_reset(DeviceState *d) | |
750 | { | |
751 | MacfbSysBusState *s = MACFB(d); | |
752 | macfb_reset(&s->macfb); | |
753 | } | |
754 | ||
755 | static void macfb_nubus_reset(DeviceState *d) | |
756 | { | |
757 | MacfbNubusState *s = NUBUS_MACFB(d); | |
758 | macfb_reset(&s->macfb); | |
759 | } | |
760 | ||
761 | static Property macfb_sysbus_properties[] = { | |
762 | DEFINE_PROP_UINT32("width", MacfbSysBusState, macfb.width, 640), | |
763 | DEFINE_PROP_UINT32("height", MacfbSysBusState, macfb.height, 480), | |
764 | DEFINE_PROP_UINT8("depth", MacfbSysBusState, macfb.depth, 8), | |
4317c518 MCA |
765 | DEFINE_PROP_UINT8("display", MacfbSysBusState, macfb.type, |
766 | MACFB_DISPLAY_VGA), | |
8ac919a0 LV |
767 | DEFINE_PROP_END_OF_LIST(), |
768 | }; | |
769 | ||
580399c2 MCA |
770 | static const VMStateDescription vmstate_macfb_sysbus = { |
771 | .name = "macfb-sysbus", | |
772 | .version_id = 1, | |
773 | .minimum_version_id = 1, | |
f0613160 | 774 | .fields = (const VMStateField[]) { |
580399c2 MCA |
775 | VMSTATE_STRUCT(macfb, MacfbSysBusState, 1, vmstate_macfb, MacfbState), |
776 | VMSTATE_END_OF_LIST() | |
777 | } | |
778 | }; | |
779 | ||
8ac919a0 LV |
780 | static Property macfb_nubus_properties[] = { |
781 | DEFINE_PROP_UINT32("width", MacfbNubusState, macfb.width, 640), | |
782 | DEFINE_PROP_UINT32("height", MacfbNubusState, macfb.height, 480), | |
783 | DEFINE_PROP_UINT8("depth", MacfbNubusState, macfb.depth, 8), | |
4317c518 MCA |
784 | DEFINE_PROP_UINT8("display", MacfbNubusState, macfb.type, |
785 | MACFB_DISPLAY_VGA), | |
8ac919a0 LV |
786 | DEFINE_PROP_END_OF_LIST(), |
787 | }; | |
788 | ||
580399c2 MCA |
789 | static const VMStateDescription vmstate_macfb_nubus = { |
790 | .name = "macfb-nubus", | |
791 | .version_id = 1, | |
792 | .minimum_version_id = 1, | |
f0613160 | 793 | .fields = (const VMStateField[]) { |
580399c2 MCA |
794 | VMSTATE_STRUCT(macfb, MacfbNubusState, 1, vmstate_macfb, MacfbState), |
795 | VMSTATE_END_OF_LIST() | |
796 | } | |
797 | }; | |
798 | ||
8ac919a0 LV |
799 | static void macfb_sysbus_class_init(ObjectClass *klass, void *data) |
800 | { | |
801 | DeviceClass *dc = DEVICE_CLASS(klass); | |
802 | ||
803 | dc->realize = macfb_sysbus_realize; | |
804 | dc->desc = "SysBus Macintosh framebuffer"; | |
805 | dc->reset = macfb_sysbus_reset; | |
580399c2 | 806 | dc->vmsd = &vmstate_macfb_sysbus; |
4f67d30b | 807 | device_class_set_props(dc, macfb_sysbus_properties); |
8ac919a0 LV |
808 | } |
809 | ||
810 | static void macfb_nubus_class_init(ObjectClass *klass, void *data) | |
811 | { | |
812 | DeviceClass *dc = DEVICE_CLASS(klass); | |
00104015 | 813 | MacfbNubusDeviceClass *ndc = NUBUS_MACFB_CLASS(klass); |
8ac919a0 LV |
814 | |
815 | device_class_set_parent_realize(dc, macfb_nubus_realize, | |
816 | &ndc->parent_realize); | |
c7a2f7ba MCA |
817 | device_class_set_parent_unrealize(dc, macfb_nubus_unrealize, |
818 | &ndc->parent_unrealize); | |
8ac919a0 LV |
819 | dc->desc = "Nubus Macintosh framebuffer"; |
820 | dc->reset = macfb_nubus_reset; | |
580399c2 | 821 | dc->vmsd = &vmstate_macfb_nubus; |
69b66e49 | 822 | set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); |
4f67d30b | 823 | device_class_set_props(dc, macfb_nubus_properties); |
8ac919a0 LV |
824 | } |
825 | ||
5e78c98b | 826 | static const TypeInfo macfb_sysbus_info = { |
8ac919a0 LV |
827 | .name = TYPE_MACFB, |
828 | .parent = TYPE_SYS_BUS_DEVICE, | |
829 | .instance_size = sizeof(MacfbSysBusState), | |
830 | .class_init = macfb_sysbus_class_init, | |
831 | }; | |
832 | ||
5e78c98b | 833 | static const TypeInfo macfb_nubus_info = { |
8ac919a0 LV |
834 | .name = TYPE_NUBUS_MACFB, |
835 | .parent = TYPE_NUBUS_DEVICE, | |
836 | .instance_size = sizeof(MacfbNubusState), | |
837 | .class_init = macfb_nubus_class_init, | |
838 | .class_size = sizeof(MacfbNubusDeviceClass), | |
839 | }; | |
840 | ||
841 | static void macfb_register_types(void) | |
842 | { | |
843 | type_register_static(&macfb_sysbus_info); | |
844 | type_register_static(&macfb_nubus_info); | |
845 | } | |
846 | ||
847 | type_init(macfb_register_types) |