]> git.proxmox.com Git - mirror_qemu.git/blame - hw/display/tcx.c
Merge remote-tracking branch 'remotes/berrange-gitlab/tags/misc-next-pull-request...
[mirror_qemu.git] / hw / display / tcx.c
CommitLineData
420557e8 1/*
6f7e9aec 2 * QEMU TCX Frame buffer
5fafdf24 3 *
6f7e9aec 4 * Copyright (c) 2003-2005 Fabrice Bellard
5fafdf24 5 *
420557e8
FB
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
f40070c3 24
47df5154 25#include "qemu/osdep.h"
a8d25326 26#include "qemu-common.h"
da34e65c 27#include "qapi/error.h"
28ecbaee
PB
28#include "ui/console.h"
29#include "ui/pixel_ops.h"
da87dd7b 30#include "hw/loader.h"
a27bd6c7 31#include "hw/qdev-properties.h"
83c9f4ca 32#include "hw/sysbus.h"
d6454270 33#include "migration/vmstate.h"
d49b6836 34#include "qemu/error-report.h"
0b8fa32f 35#include "qemu/module.h"
db1015e9 36#include "qom/object.h"
420557e8 37
da87dd7b
MCA
38#define TCX_ROM_FILE "QEMU,tcx.bin"
39#define FCODE_MAX_ROM_SIZE 0x10000
40
420557e8
FB
41#define MAXX 1024
42#define MAXY 768
55d7bfe2
MCA
43#define TCX_DAC_NREGS 16
44#define TCX_THC_NREGS 0x1000
45#define TCX_DHC_NREGS 0x4000
8508b89e 46#define TCX_TEC_NREGS 0x1000
55d7bfe2
MCA
47#define TCX_ALT_NREGS 0x8000
48#define TCX_STIP_NREGS 0x800000
49#define TCX_BLIT_NREGS 0x800000
50#define TCX_RSTIP_NREGS 0x800000
51#define TCX_RBLIT_NREGS 0x800000
52
53#define TCX_THC_MISC 0x818
54#define TCX_THC_CURSXY 0x8fc
55#define TCX_THC_CURSMASK 0x900
56#define TCX_THC_CURSBITS 0x980
420557e8 57
01774ddb 58#define TYPE_TCX "SUNW,tcx"
8063396b 59OBJECT_DECLARE_SIMPLE_TYPE(TCXState, TCX)
01774ddb 60
db1015e9 61struct TCXState {
01774ddb
AF
62 SysBusDevice parent_obj;
63
c78f7137 64 QemuConsole *con;
55d7bfe2 65 qemu_irq irq;
8d5f07fa 66 uint8_t *vram;
eee0b836 67 uint32_t *vram24, *cplane;
da87dd7b
MCA
68 hwaddr prom_addr;
69 MemoryRegion rom;
d08151bf
AK
70 MemoryRegion vram_mem;
71 MemoryRegion vram_8bit;
72 MemoryRegion vram_24bit;
55d7bfe2
MCA
73 MemoryRegion stip;
74 MemoryRegion blit;
d08151bf 75 MemoryRegion vram_cplane;
55d7bfe2
MCA
76 MemoryRegion rstip;
77 MemoryRegion rblit;
d08151bf 78 MemoryRegion tec;
55d7bfe2
MCA
79 MemoryRegion dac;
80 MemoryRegion thc;
81 MemoryRegion dhc;
82 MemoryRegion alt;
d08151bf 83 MemoryRegion thc24;
55d7bfe2 84
d08151bf 85 ram_addr_t vram24_offset, cplane_offset;
55d7bfe2 86 uint32_t tmpblit;
ee6847d1 87 uint32_t vram_size;
55d7bfe2
MCA
88 uint32_t palette[260];
89 uint8_t r[260], g[260], b[260];
427a66c3 90 uint16_t width, height, depth;
6f7e9aec 91 uint8_t dac_index, dac_state;
55d7bfe2
MCA
92 uint32_t thcmisc;
93 uint32_t cursmask[32];
94 uint32_t cursbits[32];
95 uint16_t cursx;
96 uint16_t cursy;
db1015e9 97};
420557e8 98
9800b3c2 99static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len)
d3ffcafe 100{
9800b3c2 101 memory_region_set_dirty(&s->vram_mem, addr, len);
4b865c28
MCA
102
103 if (s->depth == 24) {
104 memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
105 len * 4);
106 memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
107 len * 4);
108 }
d3ffcafe
BS
109}
110
2dd285b5
MCA
111static int tcx_check_dirty(TCXState *s, DirtyBitmapSnapshot *snap,
112 ram_addr_t addr, int len)
d3ffcafe 113{
55d7bfe2
MCA
114 int ret;
115
2dd285b5 116 ret = memory_region_snapshot_get_dirty(&s->vram_mem, snap, addr, len);
427ee02b
MCA
117
118 if (s->depth == 24) {
2dd285b5
MCA
119 ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
120 s->vram24_offset + addr * 4, len * 4);
121 ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
122 s->cplane_offset + addr * 4, len * 4);
427ee02b
MCA
123 }
124
55d7bfe2
MCA
125 return ret;
126}
127
21206a10
FB
128static void update_palette_entries(TCXState *s, int start, int end)
129{
c78f7137 130 DisplaySurface *surface = qemu_console_surface(s->con);
21206a10 131 int i;
c78f7137
GH
132
133 for (i = start; i < end; i++) {
ee72bed0
MCA
134 if (is_surface_bgr(surface)) {
135 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
136 } else {
137 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
21206a10
FB
138 }
139 }
9800b3c2 140 tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
21206a10
FB
141}
142
5fafdf24 143static void tcx_draw_line32(TCXState *s1, uint8_t *d,
f930d07e 144 const uint8_t *s, int width)
420557e8 145{
e80cfcfc
FB
146 int x;
147 uint8_t val;
8bdc2159 148 uint32_t *p = (uint32_t *)d;
e80cfcfc 149
55d7bfe2 150 for (x = 0; x < width; x++) {
f930d07e 151 val = *s++;
8bdc2159 152 *p++ = s1->palette[val];
e80cfcfc 153 }
420557e8
FB
154}
155
55d7bfe2
MCA
156static void tcx_draw_cursor32(TCXState *s1, uint8_t *d,
157 int y, int width)
158{
159 int x, len;
160 uint32_t mask, bits;
161 uint32_t *p = (uint32_t *)d;
162
163 y = y - s1->cursy;
164 mask = s1->cursmask[y];
165 bits = s1->cursbits[y];
166 len = MIN(width - s1->cursx, 32);
167 p = &p[s1->cursx];
168 for (x = 0; x < len; x++) {
169 if (mask & 0x80000000) {
170 if (bits & 0x80000000) {
171 *p = s1->palette[259];
172 } else {
173 *p = s1->palette[258];
174 }
175 }
176 p++;
177 mask <<= 1;
178 bits <<= 1;
179 }
180}
181
688ea2eb
BS
182/*
183 XXX Could be much more optimal:
184 * detect if line/page/whole screen is in 24 bit mode
185 * if destination is also BGR, use memcpy
186 */
eee0b836
BS
187static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
188 const uint8_t *s, int width,
189 const uint32_t *cplane,
190 const uint32_t *s24)
191{
c78f7137 192 DisplaySurface *surface = qemu_console_surface(s1->con);
7b5d76da 193 int x, bgr, r, g, b;
688ea2eb 194 uint8_t val, *p8;
eee0b836
BS
195 uint32_t *p = (uint32_t *)d;
196 uint32_t dval;
c78f7137 197 bgr = is_surface_bgr(surface);
eee0b836 198 for(x = 0; x < width; x++, s++, s24++) {
55d7bfe2
MCA
199 if (be32_to_cpu(*cplane) & 0x03000000) {
200 /* 24-bit direct, BGR order */
688ea2eb
BS
201 p8 = (uint8_t *)s24;
202 p8++;
203 b = *p8++;
204 g = *p8++;
f7e683b8 205 r = *p8;
7b5d76da
AL
206 if (bgr)
207 dval = rgb_to_pixel32bgr(r, g, b);
208 else
209 dval = rgb_to_pixel32(r, g, b);
eee0b836 210 } else {
55d7bfe2 211 /* 8-bit pseudocolor */
eee0b836
BS
212 val = *s;
213 dval = s1->palette[val];
214 }
215 *p++ = dval;
55d7bfe2 216 cplane++;
eee0b836
BS
217 }
218}
219
e80cfcfc
FB
220/* Fixed line length 1024 allows us to do nice tricks not possible on
221 VGA... */
55d7bfe2 222
95219897 223static void tcx_update_display(void *opaque)
420557e8 224{
e80cfcfc 225 TCXState *ts = opaque;
c78f7137 226 DisplaySurface *surface = qemu_console_surface(ts->con);
2dd285b5
MCA
227 ram_addr_t page;
228 DirtyBitmapSnapshot *snap = NULL;
550be127 229 int y, y_start, dd, ds;
e80cfcfc 230 uint8_t *d, *s;
e80cfcfc 231
ee72bed0 232 if (surface_bits_per_pixel(surface) != 32) {
f930d07e 233 return;
c78f7137
GH
234 }
235
d08151bf 236 page = 0;
e80cfcfc 237 y_start = -1;
c78f7137 238 d = surface_data(surface);
6f7e9aec 239 s = ts->vram;
c78f7137 240 dd = surface_stride(surface);
e80cfcfc
FB
241 ds = 1024;
242
2dd285b5
MCA
243 snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
244 memory_region_size(&ts->vram_mem),
245 DIRTY_MEMORY_VGA);
246
0a97c6c4 247 for (y = 0; y < ts->height; y++, page += ds) {
2dd285b5 248 if (tcx_check_dirty(ts, snap, page, ds)) {
f930d07e 249 if (y_start < 0)
e80cfcfc 250 y_start = y;
55d7bfe2 251
ee72bed0 252 tcx_draw_line32(ts, d, s, ts->width);
55d7bfe2 253 if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
ee72bed0 254 tcx_draw_cursor32(ts, d, y, ts->width);
55d7bfe2 255 }
f930d07e 256 } else {
e80cfcfc
FB
257 if (y_start >= 0) {
258 /* flush to display */
c78f7137 259 dpy_gfx_update(ts->con, 0, y_start,
a93a4a22 260 ts->width, y - y_start);
e80cfcfc
FB
261 y_start = -1;
262 }
f930d07e 263 }
0a97c6c4
MCA
264 s += ds;
265 d += dd;
e80cfcfc
FB
266 }
267 if (y_start >= 0) {
f930d07e 268 /* flush to display */
c78f7137 269 dpy_gfx_update(ts->con, 0, y_start,
a93a4a22 270 ts->width, y - y_start);
e80cfcfc 271 }
2dd285b5 272 g_free(snap);
420557e8
FB
273}
274
eee0b836
BS
275static void tcx24_update_display(void *opaque)
276{
277 TCXState *ts = opaque;
c78f7137 278 DisplaySurface *surface = qemu_console_surface(ts->con);
2dd285b5
MCA
279 ram_addr_t page;
280 DirtyBitmapSnapshot *snap = NULL;
eee0b836
BS
281 int y, y_start, dd, ds;
282 uint8_t *d, *s;
283 uint32_t *cptr, *s24;
284
c78f7137 285 if (surface_bits_per_pixel(surface) != 32) {
eee0b836 286 return;
c78f7137
GH
287 }
288
d08151bf 289 page = 0;
eee0b836 290 y_start = -1;
c78f7137 291 d = surface_data(surface);
eee0b836
BS
292 s = ts->vram;
293 s24 = ts->vram24;
294 cptr = ts->cplane;
c78f7137 295 dd = surface_stride(surface);
eee0b836
BS
296 ds = 1024;
297
2dd285b5
MCA
298 snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
299 memory_region_size(&ts->vram_mem),
300 DIRTY_MEMORY_VGA);
301
d18e1012 302 for (y = 0; y < ts->height; y++, page += ds) {
2dd285b5 303 if (tcx_check_dirty(ts, snap, page, ds)) {
eee0b836
BS
304 if (y_start < 0)
305 y_start = y;
2dd285b5 306
eee0b836 307 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
55d7bfe2
MCA
308 if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
309 tcx_draw_cursor32(ts, d, y, ts->width);
310 }
eee0b836
BS
311 } else {
312 if (y_start >= 0) {
313 /* flush to display */
c78f7137 314 dpy_gfx_update(ts->con, 0, y_start,
a93a4a22 315 ts->width, y - y_start);
eee0b836
BS
316 y_start = -1;
317 }
eee0b836 318 }
d18e1012
MCA
319 d += dd;
320 s += ds;
321 cptr += ds;
322 s24 += ds;
eee0b836
BS
323 }
324 if (y_start >= 0) {
325 /* flush to display */
c78f7137 326 dpy_gfx_update(ts->con, 0, y_start,
a93a4a22 327 ts->width, y - y_start);
eee0b836 328 }
2dd285b5 329 g_free(snap);
eee0b836
BS
330}
331
95219897 332static void tcx_invalidate_display(void *opaque)
420557e8 333{
e80cfcfc 334 TCXState *s = opaque;
e80cfcfc 335
9800b3c2 336 tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
c78f7137 337 qemu_console_resize(s->con, s->width, s->height);
420557e8
FB
338}
339
eee0b836
BS
340static void tcx24_invalidate_display(void *opaque)
341{
342 TCXState *s = opaque;
eee0b836 343
9800b3c2 344 tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
c78f7137 345 qemu_console_resize(s->con, s->width, s->height);
eee0b836
BS
346}
347
e59fb374 348static int vmstate_tcx_post_load(void *opaque, int version_id)
420557e8
FB
349{
350 TCXState *s = opaque;
3b46e624 351
21206a10 352 update_palette_entries(s, 0, 256);
9800b3c2 353 tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
e80cfcfc 354 return 0;
420557e8
FB
355}
356
c0c41a4b
BS
357static const VMStateDescription vmstate_tcx = {
358 .name ="tcx",
359 .version_id = 4,
360 .minimum_version_id = 4,
752ff2fa 361 .post_load = vmstate_tcx_post_load,
35d08458 362 .fields = (VMStateField[]) {
c0c41a4b
BS
363 VMSTATE_UINT16(height, TCXState),
364 VMSTATE_UINT16(width, TCXState),
365 VMSTATE_UINT16(depth, TCXState),
366 VMSTATE_BUFFER(r, TCXState),
367 VMSTATE_BUFFER(g, TCXState),
368 VMSTATE_BUFFER(b, TCXState),
369 VMSTATE_UINT8(dac_index, TCXState),
370 VMSTATE_UINT8(dac_state, TCXState),
371 VMSTATE_END_OF_LIST()
372 }
373};
374
7f23f812 375static void tcx_reset(DeviceState *d)
420557e8 376{
01774ddb 377 TCXState *s = TCX(d);
e80cfcfc
FB
378
379 /* Initialize palette */
55d7bfe2
MCA
380 memset(s->r, 0, 260);
381 memset(s->g, 0, 260);
382 memset(s->b, 0, 260);
e80cfcfc 383 s->r[255] = s->g[255] = s->b[255] = 255;
55d7bfe2
MCA
384 s->r[256] = s->g[256] = s->b[256] = 255;
385 s->r[258] = s->g[258] = s->b[258] = 255;
386 update_palette_entries(s, 0, 260);
e80cfcfc 387 memset(s->vram, 0, MAXX*MAXY);
d08151bf
AK
388 memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4),
389 DIRTY_MEMORY_VGA);
6f7e9aec
FB
390 s->dac_index = 0;
391 s->dac_state = 0;
55d7bfe2
MCA
392 s->cursx = 0xf000; /* Put cursor off screen */
393 s->cursy = 0xf000;
6f7e9aec
FB
394}
395
a8170e5e 396static uint64_t tcx_dac_readl(void *opaque, hwaddr addr,
d08151bf 397 unsigned size)
6f7e9aec 398{
55d7bfe2
MCA
399 TCXState *s = opaque;
400 uint32_t val = 0;
401
402 switch (s->dac_state) {
403 case 0:
404 val = s->r[s->dac_index] << 24;
405 s->dac_state++;
406 break;
407 case 1:
408 val = s->g[s->dac_index] << 24;
409 s->dac_state++;
410 break;
411 case 2:
412 val = s->b[s->dac_index] << 24;
413 s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
ada44065 414 /* fall through */
55d7bfe2
MCA
415 default:
416 s->dac_state = 0;
417 break;
418 }
419
420 return val;
6f7e9aec
FB
421}
422
a8170e5e 423static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val,
d08151bf 424 unsigned size)
6f7e9aec
FB
425{
426 TCXState *s = opaque;
55d7bfe2 427 unsigned index;
6f7e9aec 428
e64d7d59 429 switch (addr) {
55d7bfe2 430 case 0: /* Address */
f930d07e
BS
431 s->dac_index = val >> 24;
432 s->dac_state = 0;
433 break;
55d7bfe2
MCA
434 case 4: /* Pixel colours */
435 case 12: /* Overlay (cursor) colours */
436 if (addr & 8) {
437 index = (s->dac_index & 3) + 256;
438 } else {
439 index = s->dac_index;
440 }
f930d07e
BS
441 switch (s->dac_state) {
442 case 0:
55d7bfe2
MCA
443 s->r[index] = val >> 24;
444 update_palette_entries(s, index, index + 1);
f930d07e
BS
445 s->dac_state++;
446 break;
447 case 1:
55d7bfe2
MCA
448 s->g[index] = val >> 24;
449 update_palette_entries(s, index, index + 1);
f930d07e
BS
450 s->dac_state++;
451 break;
452 case 2:
55d7bfe2
MCA
453 s->b[index] = val >> 24;
454 update_palette_entries(s, index, index + 1);
455 s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
ada44065 456 /* fall through */
f930d07e
BS
457 default:
458 s->dac_state = 0;
459 break;
460 }
461 break;
55d7bfe2 462 default: /* Control registers */
f930d07e 463 break;
6f7e9aec 464 }
420557e8
FB
465}
466
d08151bf
AK
467static const MemoryRegionOps tcx_dac_ops = {
468 .read = tcx_dac_readl,
469 .write = tcx_dac_writel,
470 .endianness = DEVICE_NATIVE_ENDIAN,
471 .valid = {
472 .min_access_size = 4,
473 .max_access_size = 4,
474 },
6f7e9aec
FB
475};
476
55d7bfe2
MCA
477static uint64_t tcx_stip_readl(void *opaque, hwaddr addr,
478 unsigned size)
479{
480 return 0;
481}
482
483static void tcx_stip_writel(void *opaque, hwaddr addr,
484 uint64_t val, unsigned size)
485{
486 TCXState *s = opaque;
487 int i;
488 uint32_t col;
489
490 if (!(addr & 4)) {
491 s->tmpblit = val;
492 } else {
493 addr = (addr >> 3) & 0xfffff;
494 col = cpu_to_be32(s->tmpblit);
495 if (s->depth == 24) {
496 for (i = 0; i < 32; i++) {
497 if (val & 0x80000000) {
498 s->vram[addr + i] = s->tmpblit;
499 s->vram24[addr + i] = col;
500 }
501 val <<= 1;
502 }
503 } else {
504 for (i = 0; i < 32; i++) {
505 if (val & 0x80000000) {
506 s->vram[addr + i] = s->tmpblit;
507 }
508 val <<= 1;
509 }
510 }
97394580 511 tcx_set_dirty(s, addr, 32);
55d7bfe2
MCA
512 }
513}
514
515static void tcx_rstip_writel(void *opaque, hwaddr addr,
516 uint64_t val, unsigned size)
517{
518 TCXState *s = opaque;
519 int i;
520 uint32_t col;
521
522 if (!(addr & 4)) {
523 s->tmpblit = val;
524 } else {
525 addr = (addr >> 3) & 0xfffff;
526 col = cpu_to_be32(s->tmpblit);
527 if (s->depth == 24) {
528 for (i = 0; i < 32; i++) {
529 if (val & 0x80000000) {
530 s->vram[addr + i] = s->tmpblit;
531 s->vram24[addr + i] = col;
532 s->cplane[addr + i] = col;
533 }
534 val <<= 1;
535 }
536 } else {
537 for (i = 0; i < 32; i++) {
538 if (val & 0x80000000) {
539 s->vram[addr + i] = s->tmpblit;
540 }
541 val <<= 1;
542 }
543 }
97394580 544 tcx_set_dirty(s, addr, 32);
55d7bfe2
MCA
545 }
546}
547
548static const MemoryRegionOps tcx_stip_ops = {
549 .read = tcx_stip_readl,
550 .write = tcx_stip_writel,
551 .endianness = DEVICE_NATIVE_ENDIAN,
ae5643ec 552 .impl = {
55d7bfe2
MCA
553 .min_access_size = 4,
554 .max_access_size = 4,
555 },
ae5643ec
PMD
556 .valid = {
557 .min_access_size = 4,
558 .max_access_size = 8,
559 },
55d7bfe2
MCA
560};
561
562static const MemoryRegionOps tcx_rstip_ops = {
563 .read = tcx_stip_readl,
564 .write = tcx_rstip_writel,
565 .endianness = DEVICE_NATIVE_ENDIAN,
ae5643ec 566 .impl = {
55d7bfe2
MCA
567 .min_access_size = 4,
568 .max_access_size = 4,
569 },
ae5643ec
PMD
570 .valid = {
571 .min_access_size = 4,
572 .max_access_size = 8,
573 },
55d7bfe2
MCA
574};
575
576static uint64_t tcx_blit_readl(void *opaque, hwaddr addr,
577 unsigned size)
578{
579 return 0;
580}
581
582static void tcx_blit_writel(void *opaque, hwaddr addr,
583 uint64_t val, unsigned size)
584{
585 TCXState *s = opaque;
586 uint32_t adsr, len;
587 int i;
588
589 if (!(addr & 4)) {
590 s->tmpblit = val;
591 } else {
592 addr = (addr >> 3) & 0xfffff;
593 adsr = val & 0xffffff;
594 len = ((val >> 24) & 0x1f) + 1;
595 if (adsr == 0xffffff) {
596 memset(&s->vram[addr], s->tmpblit, len);
597 if (s->depth == 24) {
598 val = s->tmpblit & 0xffffff;
599 val = cpu_to_be32(val);
600 for (i = 0; i < len; i++) {
601 s->vram24[addr + i] = val;
602 }
603 }
604 } else {
605 memcpy(&s->vram[addr], &s->vram[adsr], len);
606 if (s->depth == 24) {
607 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
608 }
609 }
97394580 610 tcx_set_dirty(s, addr, len);
55d7bfe2
MCA
611 }
612}
613
614static void tcx_rblit_writel(void *opaque, hwaddr addr,
615 uint64_t val, unsigned size)
616{
617 TCXState *s = opaque;
618 uint32_t adsr, len;
619 int i;
620
621 if (!(addr & 4)) {
622 s->tmpblit = val;
623 } else {
624 addr = (addr >> 3) & 0xfffff;
625 adsr = val & 0xffffff;
626 len = ((val >> 24) & 0x1f) + 1;
627 if (adsr == 0xffffff) {
628 memset(&s->vram[addr], s->tmpblit, len);
629 if (s->depth == 24) {
630 val = s->tmpblit & 0xffffff;
631 val = cpu_to_be32(val);
632 for (i = 0; i < len; i++) {
633 s->vram24[addr + i] = val;
634 s->cplane[addr + i] = val;
635 }
636 }
637 } else {
638 memcpy(&s->vram[addr], &s->vram[adsr], len);
639 if (s->depth == 24) {
640 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
641 memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4);
642 }
643 }
97394580 644 tcx_set_dirty(s, addr, len);
55d7bfe2
MCA
645 }
646}
647
648static const MemoryRegionOps tcx_blit_ops = {
649 .read = tcx_blit_readl,
650 .write = tcx_blit_writel,
651 .endianness = DEVICE_NATIVE_ENDIAN,
652 .valid = {
653 .min_access_size = 4,
654 .max_access_size = 4,
655 },
656};
657
658static const MemoryRegionOps tcx_rblit_ops = {
659 .read = tcx_blit_readl,
660 .write = tcx_rblit_writel,
661 .endianness = DEVICE_NATIVE_ENDIAN,
ae5643ec 662 .impl = {
55d7bfe2
MCA
663 .min_access_size = 4,
664 .max_access_size = 4,
665 },
ae5643ec
PMD
666 .valid = {
667 .min_access_size = 4,
668 .max_access_size = 8,
669 },
55d7bfe2
MCA
670};
671
672static void tcx_invalidate_cursor_position(TCXState *s)
673{
674 int ymin, ymax, start, end;
675
676 /* invalidate only near the cursor */
677 ymin = s->cursy;
678 if (ymin >= s->height) {
679 return;
680 }
681 ymax = MIN(s->height, ymin + 32);
682 start = ymin * 1024;
683 end = ymax * 1024;
684
97394580 685 tcx_set_dirty(s, start, end - start);
55d7bfe2
MCA
686}
687
688static uint64_t tcx_thc_readl(void *opaque, hwaddr addr,
689 unsigned size)
690{
691 TCXState *s = opaque;
692 uint64_t val;
693
694 if (addr == TCX_THC_MISC) {
695 val = s->thcmisc | 0x02000000;
696 } else {
697 val = 0;
698 }
699 return val;
700}
701
702static void tcx_thc_writel(void *opaque, hwaddr addr,
703 uint64_t val, unsigned size)
704{
705 TCXState *s = opaque;
706
707 if (addr == TCX_THC_CURSXY) {
708 tcx_invalidate_cursor_position(s);
709 s->cursx = val >> 16;
710 s->cursy = val;
711 tcx_invalidate_cursor_position(s);
712 } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) {
713 s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val;
714 tcx_invalidate_cursor_position(s);
715 } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) {
716 s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val;
717 tcx_invalidate_cursor_position(s);
718 } else if (addr == TCX_THC_MISC) {
719 s->thcmisc = val;
720 }
721
722}
723
724static const MemoryRegionOps tcx_thc_ops = {
725 .read = tcx_thc_readl,
726 .write = tcx_thc_writel,
727 .endianness = DEVICE_NATIVE_ENDIAN,
728 .valid = {
729 .min_access_size = 4,
730 .max_access_size = 4,
731 },
732};
733
734static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr,
d08151bf 735 unsigned size)
8508b89e
BS
736{
737 return 0;
738}
739
55d7bfe2 740static void tcx_dummy_writel(void *opaque, hwaddr addr,
d08151bf 741 uint64_t val, unsigned size)
8508b89e 742{
55d7bfe2 743 return;
8508b89e
BS
744}
745
55d7bfe2
MCA
746static const MemoryRegionOps tcx_dummy_ops = {
747 .read = tcx_dummy_readl,
748 .write = tcx_dummy_writel,
d08151bf
AK
749 .endianness = DEVICE_NATIVE_ENDIAN,
750 .valid = {
751 .min_access_size = 4,
752 .max_access_size = 4,
753 },
8508b89e
BS
754};
755
380cd056
GH
756static const GraphicHwOps tcx_ops = {
757 .invalidate = tcx_invalidate_display,
758 .gfx_update = tcx_update_display,
759};
760
761static const GraphicHwOps tcx24_ops = {
762 .invalidate = tcx24_invalidate_display,
763 .gfx_update = tcx24_update_display,
764};
765
01b91ac2
MCA
766static void tcx_initfn(Object *obj)
767{
768 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
769 TCXState *s = TCX(obj);
770
52013bce
PMD
771 memory_region_init_rom_nomigrate(&s->rom, obj, "tcx.prom",
772 FCODE_MAX_ROM_SIZE, &error_fatal);
01b91ac2
MCA
773 sysbus_init_mmio(sbd, &s->rom);
774
55d7bfe2 775 /* 2/STIP : Stippler */
b21de199 776 memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip",
55d7bfe2
MCA
777 TCX_STIP_NREGS);
778 sysbus_init_mmio(sbd, &s->stip);
779
780 /* 3/BLIT : Blitter */
b21de199 781 memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit",
55d7bfe2
MCA
782 TCX_BLIT_NREGS);
783 sysbus_init_mmio(sbd, &s->blit);
784
785 /* 5/RSTIP : Raw Stippler */
b21de199 786 memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip",
55d7bfe2
MCA
787 TCX_RSTIP_NREGS);
788 sysbus_init_mmio(sbd, &s->rstip);
789
790 /* 6/RBLIT : Raw Blitter */
b21de199 791 memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit",
55d7bfe2
MCA
792 TCX_RBLIT_NREGS);
793 sysbus_init_mmio(sbd, &s->rblit);
794
795 /* 7/TEC : ??? */
b21de199
TH
796 memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec",
797 TCX_TEC_NREGS);
55d7bfe2
MCA
798 sysbus_init_mmio(sbd, &s->tec);
799
800 /* 8/CMAP : DAC */
b21de199
TH
801 memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac",
802 TCX_DAC_NREGS);
01b91ac2
MCA
803 sysbus_init_mmio(sbd, &s->dac);
804
55d7bfe2 805 /* 9/THC : Cursor */
b21de199 806 memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc",
55d7bfe2
MCA
807 TCX_THC_NREGS);
808 sysbus_init_mmio(sbd, &s->thc);
01b91ac2 809
55d7bfe2 810 /* 11/DHC : ??? */
b21de199 811 memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc",
55d7bfe2
MCA
812 TCX_DHC_NREGS);
813 sysbus_init_mmio(sbd, &s->dhc);
814
815 /* 12/ALT : ??? */
b21de199 816 memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt",
55d7bfe2
MCA
817 TCX_ALT_NREGS);
818 sysbus_init_mmio(sbd, &s->alt);
01b91ac2
MCA
819}
820
d4ad9dec 821static void tcx_realizefn(DeviceState *dev, Error **errp)
f40070c3 822{
d4ad9dec 823 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
01774ddb 824 TCXState *s = TCX(dev);
d08151bf 825 ram_addr_t vram_offset = 0;
da87dd7b 826 int size, ret;
dc828ca1 827 uint8_t *vram_base;
da87dd7b 828 char *fcode_filename;
dc828ca1 829
1cfe48c1 830 memory_region_init_ram_nomigrate(&s->vram_mem, OBJECT(s), "tcx.vram",
f8ed85ac 831 s->vram_size * (1 + 4 + 4), &error_fatal);
c5705a77 832 vmstate_register_ram_global(&s->vram_mem);
74259ae5 833 memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
d08151bf 834 vram_base = memory_region_get_ram_ptr(&s->vram_mem);
eee0b836 835
55d7bfe2 836 /* 10/ROM : FCode ROM */
da87dd7b 837 vmstate_register_ram_global(&s->rom);
da87dd7b
MCA
838 fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE);
839 if (fcode_filename) {
74976386 840 ret = load_image_mr(fcode_filename, &s->rom);
8684e85c 841 g_free(fcode_filename);
da87dd7b 842 if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
0765691e 843 warn_report("tcx: could not load prom '%s'", TCX_ROM_FILE);
da87dd7b
MCA
844 }
845 }
846
55d7bfe2 847 /* 0/DFB8 : 8-bit plane */
eee0b836 848 s->vram = vram_base;
ee6847d1 849 size = s->vram_size;
3eadad55 850 memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit",
d08151bf 851 &s->vram_mem, vram_offset, size);
d4ad9dec 852 sysbus_init_mmio(sbd, &s->vram_8bit);
eee0b836
BS
853 vram_offset += size;
854 vram_base += size;
e80cfcfc 855
55d7bfe2
MCA
856 /* 1/DFB24 : 24bit plane */
857 size = s->vram_size * 4;
858 s->vram24 = (uint32_t *)vram_base;
859 s->vram24_offset = vram_offset;
860 memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit",
861 &s->vram_mem, vram_offset, size);
862 sysbus_init_mmio(sbd, &s->vram_24bit);
863 vram_offset += size;
864 vram_base += size;
865
866 /* 4/RDFB32 : Raw Framebuffer */
867 size = s->vram_size * 4;
868 s->cplane = (uint32_t *)vram_base;
869 s->cplane_offset = vram_offset;
870 memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane",
871 &s->vram_mem, vram_offset, size);
872 sysbus_init_mmio(sbd, &s->vram_cplane);
f40070c3 873
55d7bfe2
MCA
874 /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */
875 if (s->depth == 8) {
876 memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s,
877 "tcx.thc24", TCX_THC_NREGS);
878 sysbus_init_mmio(sbd, &s->thc24);
879 }
880
881 sysbus_init_irq(sbd, &s->irq);
f40070c3 882
55d7bfe2 883 if (s->depth == 8) {
8e5c952b 884 s->con = graphic_console_init(dev, 0, &tcx_ops, s);
55d7bfe2 885 } else {
8e5c952b 886 s->con = graphic_console_init(dev, 0, &tcx24_ops, s);
eee0b836 887 }
55d7bfe2 888 s->thcmisc = 0;
e80cfcfc 889
c78f7137 890 qemu_console_resize(s->con, s->width, s->height);
420557e8
FB
891}
892
999e12bb 893static Property tcx_properties[] = {
c7bcc85d 894 DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1),
999e12bb
AL
895 DEFINE_PROP_UINT16("width", TCXState, width, -1),
896 DEFINE_PROP_UINT16("height", TCXState, height, -1),
897 DEFINE_PROP_UINT16("depth", TCXState, depth, -1),
898 DEFINE_PROP_END_OF_LIST(),
899};
900
901static void tcx_class_init(ObjectClass *klass, void *data)
902{
39bffca2 903 DeviceClass *dc = DEVICE_CLASS(klass);
999e12bb 904
d4ad9dec 905 dc->realize = tcx_realizefn;
39bffca2
AL
906 dc->reset = tcx_reset;
907 dc->vmsd = &vmstate_tcx;
4f67d30b 908 device_class_set_props(dc, tcx_properties);
999e12bb
AL
909}
910
8c43a6f0 911static const TypeInfo tcx_info = {
01774ddb 912 .name = TYPE_TCX,
39bffca2
AL
913 .parent = TYPE_SYS_BUS_DEVICE,
914 .instance_size = sizeof(TCXState),
01b91ac2 915 .instance_init = tcx_initfn,
39bffca2 916 .class_init = tcx_class_init,
ee6847d1
GH
917};
918
83f7d43a 919static void tcx_register_types(void)
f40070c3 920{
39bffca2 921 type_register_static(&tcx_info);
f40070c3
BS
922}
923
83f7d43a 924type_init(tcx_register_types)