Statistics
| Branch: | Revision:

root / hw / tcx.c @ 4b48bf05

History | View | Annotate | Download (18.5 kB)

1
/*
2
 * QEMU TCX Frame buffer
3
 *
4
 * Copyright (c) 2003-2005 Fabrice Bellard
5
 *
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
 */
24

    
25
#include "sun4m.h"
26
#include "console.h"
27
#include "pixel_ops.h"
28
#include "sysbus.h"
29
#include "qdev-addr.h"
30

    
31
#define MAXX 1024
32
#define MAXY 768
33
#define TCX_DAC_NREGS 16
34
#define TCX_THC_NREGS_8  0x081c
35
#define TCX_THC_NREGS_24 0x1000
36
#define TCX_TEC_NREGS    0x1000
37

    
38
typedef struct TCXState {
39
    SysBusDevice busdev;
40
    target_phys_addr_t addr;
41
    DisplayState *ds;
42
    uint8_t *vram;
43
    uint32_t *vram24, *cplane;
44
    ram_addr_t vram_offset, vram24_offset, cplane_offset;
45
    uint32_t vram_size;
46
    uint16_t width, height, depth;
47
    uint8_t r[256], g[256], b[256];
48
    uint32_t palette[256];
49
    uint8_t dac_index, dac_state;
50
} TCXState;
51

    
52
static void tcx_screen_dump(void *opaque, const char *filename);
53
static void tcx24_screen_dump(void *opaque, const char *filename);
54

    
55
static void tcx_set_dirty(TCXState *s)
56
{
57
    unsigned int i;
58

    
59
    for (i = 0; i < MAXX * MAXY; i += TARGET_PAGE_SIZE) {
60
        cpu_physical_memory_set_dirty(s->vram_offset + i);
61
    }
62
}
63

    
64
static void tcx24_set_dirty(TCXState *s)
65
{
66
    unsigned int i;
67

    
68
    for (i = 0; i < MAXX * MAXY * 4; i += TARGET_PAGE_SIZE) {
69
        cpu_physical_memory_set_dirty(s->vram24_offset + i);
70
        cpu_physical_memory_set_dirty(s->cplane_offset + i);
71
    }
72
}
73

    
74
static void update_palette_entries(TCXState *s, int start, int end)
75
{
76
    int i;
77
    for(i = start; i < end; i++) {
78
        switch(ds_get_bits_per_pixel(s->ds)) {
79
        default:
80
        case 8:
81
            s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
82
            break;
83
        case 15:
84
            s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
85
            break;
86
        case 16:
87
            s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
88
            break;
89
        case 32:
90
            if (is_surface_bgr(s->ds->surface))
91
                s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
92
            else
93
                s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
94
            break;
95
        }
96
    }
97
    if (s->depth == 24) {
98
        tcx24_set_dirty(s);
99
    } else {
100
        tcx_set_dirty(s);
101
    }
102
}
103

    
104
static void tcx_draw_line32(TCXState *s1, uint8_t *d,
105
                            const uint8_t *s, int width)
106
{
107
    int x;
108
    uint8_t val;
109
    uint32_t *p = (uint32_t *)d;
110

    
111
    for(x = 0; x < width; x++) {
112
        val = *s++;
113
        *p++ = s1->palette[val];
114
    }
115
}
116

    
117
static void tcx_draw_line16(TCXState *s1, uint8_t *d,
118
                            const uint8_t *s, int width)
119
{
120
    int x;
121
    uint8_t val;
122
    uint16_t *p = (uint16_t *)d;
123

    
124
    for(x = 0; x < width; x++) {
125
        val = *s++;
126
        *p++ = s1->palette[val];
127
    }
128
}
129

    
130
static void tcx_draw_line8(TCXState *s1, uint8_t *d,
131
                           const uint8_t *s, int width)
132
{
133
    int x;
134
    uint8_t val;
135

    
136
    for(x = 0; x < width; x++) {
137
        val = *s++;
138
        *d++ = s1->palette[val];
139
    }
140
}
141

    
142
/*
143
  XXX Could be much more optimal:
144
  * detect if line/page/whole screen is in 24 bit mode
145
  * if destination is also BGR, use memcpy
146
  */
147
static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
148
                                     const uint8_t *s, int width,
149
                                     const uint32_t *cplane,
150
                                     const uint32_t *s24)
151
{
152
    int x, bgr, r, g, b;
153
    uint8_t val, *p8;
154
    uint32_t *p = (uint32_t *)d;
155
    uint32_t dval;
156

    
157
    bgr = is_surface_bgr(s1->ds->surface);
158
    for(x = 0; x < width; x++, s++, s24++) {
159
        if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) {
160
            // 24-bit direct, BGR order
161
            p8 = (uint8_t *)s24;
162
            p8++;
163
            b = *p8++;
164
            g = *p8++;
165
            r = *p8++;
166
            if (bgr)
167
                dval = rgb_to_pixel32bgr(r, g, b);
168
            else
169
                dval = rgb_to_pixel32(r, g, b);
170
        } else {
171
            val = *s;
172
            dval = s1->palette[val];
173
        }
174
        *p++ = dval;
175
    }
176
}
177

    
178
static inline int check_dirty(ram_addr_t page, ram_addr_t page24,
179
                              ram_addr_t cpage)
180
{
181
    int ret;
182
    unsigned int off;
183

    
184
    ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
185
    for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) {
186
        ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG);
187
        ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG);
188
    }
189
    return ret;
190
}
191

    
192
static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
193
                               ram_addr_t page_max, ram_addr_t page24,
194
                              ram_addr_t cpage)
195
{
196
    cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
197
                                    VGA_DIRTY_FLAG);
198
    page_min -= ts->vram_offset;
199
    page_max -= ts->vram_offset;
200
    cpu_physical_memory_reset_dirty(page24 + page_min * 4,
201
                                    page24 + page_max * 4 + TARGET_PAGE_SIZE,
202
                                    VGA_DIRTY_FLAG);
203
    cpu_physical_memory_reset_dirty(cpage + page_min * 4,
204
                                    cpage + page_max * 4 + TARGET_PAGE_SIZE,
205
                                    VGA_DIRTY_FLAG);
206
}
207

    
208
/* Fixed line length 1024 allows us to do nice tricks not possible on
209
   VGA... */
210
static void tcx_update_display(void *opaque)
211
{
212
    TCXState *ts = opaque;
213
    ram_addr_t page, page_min, page_max;
214
    int y, y_start, dd, ds;
215
    uint8_t *d, *s;
216
    void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
217

    
218
    if (ds_get_bits_per_pixel(ts->ds) == 0)
219
        return;
220
    page = ts->vram_offset;
221
    y_start = -1;
222
    page_min = -1;
223
    page_max = 0;
224
    d = ds_get_data(ts->ds);
225
    s = ts->vram;
226
    dd = ds_get_linesize(ts->ds);
227
    ds = 1024;
228

    
229
    switch (ds_get_bits_per_pixel(ts->ds)) {
230
    case 32:
231
        f = tcx_draw_line32;
232
        break;
233
    case 15:
234
    case 16:
235
        f = tcx_draw_line16;
236
        break;
237
    default:
238
    case 8:
239
        f = tcx_draw_line8;
240
        break;
241
    case 0:
242
        return;
243
    }
244

    
245
    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
246
        if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
247
            if (y_start < 0)
248
                y_start = y;
249
            if (page < page_min)
250
                page_min = page;
251
            if (page > page_max)
252
                page_max = page;
253
            f(ts, d, s, ts->width);
254
            d += dd;
255
            s += ds;
256
            f(ts, d, s, ts->width);
257
            d += dd;
258
            s += ds;
259
            f(ts, d, s, ts->width);
260
            d += dd;
261
            s += ds;
262
            f(ts, d, s, ts->width);
263
            d += dd;
264
            s += ds;
265
        } else {
266
            if (y_start >= 0) {
267
                /* flush to display */
268
                dpy_update(ts->ds, 0, y_start,
269
                           ts->width, y - y_start);
270
                y_start = -1;
271
            }
272
            d += dd * 4;
273
            s += ds * 4;
274
        }
275
    }
276
    if (y_start >= 0) {
277
        /* flush to display */
278
        dpy_update(ts->ds, 0, y_start,
279
                   ts->width, y - y_start);
280
    }
281
    /* reset modified pages */
282
    if (page_max >= page_min) {
283
        cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
284
                                        VGA_DIRTY_FLAG);
285
    }
286
}
287

    
288
static void tcx24_update_display(void *opaque)
289
{
290
    TCXState *ts = opaque;
291
    ram_addr_t page, page_min, page_max, cpage, page24;
292
    int y, y_start, dd, ds;
293
    uint8_t *d, *s;
294
    uint32_t *cptr, *s24;
295

    
296
    if (ds_get_bits_per_pixel(ts->ds) != 32)
297
            return;
298
    page = ts->vram_offset;
299
    page24 = ts->vram24_offset;
300
    cpage = ts->cplane_offset;
301
    y_start = -1;
302
    page_min = -1;
303
    page_max = 0;
304
    d = ds_get_data(ts->ds);
305
    s = ts->vram;
306
    s24 = ts->vram24;
307
    cptr = ts->cplane;
308
    dd = ds_get_linesize(ts->ds);
309
    ds = 1024;
310

    
311
    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
312
            page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
313
        if (check_dirty(page, page24, cpage)) {
314
            if (y_start < 0)
315
                y_start = y;
316
            if (page < page_min)
317
                page_min = page;
318
            if (page > page_max)
319
                page_max = page;
320
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
321
            d += dd;
322
            s += ds;
323
            cptr += ds;
324
            s24 += ds;
325
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
326
            d += dd;
327
            s += ds;
328
            cptr += ds;
329
            s24 += ds;
330
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
331
            d += dd;
332
            s += ds;
333
            cptr += ds;
334
            s24 += ds;
335
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
336
            d += dd;
337
            s += ds;
338
            cptr += ds;
339
            s24 += ds;
340
        } else {
341
            if (y_start >= 0) {
342
                /* flush to display */
343
                dpy_update(ts->ds, 0, y_start,
344
                           ts->width, y - y_start);
345
                y_start = -1;
346
            }
347
            d += dd * 4;
348
            s += ds * 4;
349
            cptr += ds * 4;
350
            s24 += ds * 4;
351
        }
352
    }
353
    if (y_start >= 0) {
354
        /* flush to display */
355
        dpy_update(ts->ds, 0, y_start,
356
                   ts->width, y - y_start);
357
    }
358
    /* reset modified pages */
359
    if (page_max >= page_min) {
360
        reset_dirty(ts, page_min, page_max, page24, cpage);
361
    }
362
}
363

    
364
static void tcx_invalidate_display(void *opaque)
365
{
366
    TCXState *s = opaque;
367

    
368
    tcx_set_dirty(s);
369
    qemu_console_resize(s->ds, s->width, s->height);
370
}
371

    
372
static void tcx24_invalidate_display(void *opaque)
373
{
374
    TCXState *s = opaque;
375

    
376
    tcx_set_dirty(s);
377
    tcx24_set_dirty(s);
378
    qemu_console_resize(s->ds, s->width, s->height);
379
}
380

    
381
static void tcx_save(QEMUFile *f, void *opaque)
382
{
383
    TCXState *s = opaque;
384

    
385
    qemu_put_be16s(f, &s->height);
386
    qemu_put_be16s(f, &s->width);
387
    qemu_put_be16s(f, &s->depth);
388
    qemu_put_buffer(f, s->r, 256);
389
    qemu_put_buffer(f, s->g, 256);
390
    qemu_put_buffer(f, s->b, 256);
391
    qemu_put_8s(f, &s->dac_index);
392
    qemu_put_8s(f, &s->dac_state);
393
}
394

    
395
static int tcx_load(QEMUFile *f, void *opaque, int version_id)
396
{
397
    TCXState *s = opaque;
398
    uint32_t dummy;
399

    
400
    if (version_id != 3 && version_id != 4)
401
        return -EINVAL;
402

    
403
    if (version_id == 3) {
404
        qemu_get_be32s(f, &dummy);
405
        qemu_get_be32s(f, &dummy);
406
        qemu_get_be32s(f, &dummy);
407
    }
408
    qemu_get_be16s(f, &s->height);
409
    qemu_get_be16s(f, &s->width);
410
    qemu_get_be16s(f, &s->depth);
411
    qemu_get_buffer(f, s->r, 256);
412
    qemu_get_buffer(f, s->g, 256);
413
    qemu_get_buffer(f, s->b, 256);
414
    qemu_get_8s(f, &s->dac_index);
415
    qemu_get_8s(f, &s->dac_state);
416
    update_palette_entries(s, 0, 256);
417
    if (s->depth == 24) {
418
        tcx24_set_dirty(s);
419
    } else {
420
        tcx_set_dirty(s);
421
    }
422

    
423
    return 0;
424
}
425

    
426
static void tcx_reset(void *opaque)
427
{
428
    TCXState *s = opaque;
429

    
430
    /* Initialize palette */
431
    memset(s->r, 0, 256);
432
    memset(s->g, 0, 256);
433
    memset(s->b, 0, 256);
434
    s->r[255] = s->g[255] = s->b[255] = 255;
435
    update_palette_entries(s, 0, 256);
436
    memset(s->vram, 0, MAXX*MAXY);
437
    cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset +
438
                                    MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG);
439
    s->dac_index = 0;
440
    s->dac_state = 0;
441
}
442

    
443
static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
444
{
445
    return 0;
446
}
447

    
448
static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
449
{
450
    TCXState *s = opaque;
451

    
452
    switch (addr) {
453
    case 0:
454
        s->dac_index = val >> 24;
455
        s->dac_state = 0;
456
        break;
457
    case 4:
458
        switch (s->dac_state) {
459
        case 0:
460
            s->r[s->dac_index] = val >> 24;
461
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
462
            s->dac_state++;
463
            break;
464
        case 1:
465
            s->g[s->dac_index] = val >> 24;
466
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
467
            s->dac_state++;
468
            break;
469
        case 2:
470
            s->b[s->dac_index] = val >> 24;
471
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
472
            s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
473
        default:
474
            s->dac_state = 0;
475
            break;
476
        }
477
        break;
478
    default:
479
        break;
480
    }
481
    return;
482
}
483

    
484
static CPUReadMemoryFunc *tcx_dac_read[3] = {
485
    NULL,
486
    NULL,
487
    tcx_dac_readl,
488
};
489

    
490
static CPUWriteMemoryFunc *tcx_dac_write[3] = {
491
    NULL,
492
    NULL,
493
    tcx_dac_writel,
494
};
495

    
496
static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
497
{
498
    return 0;
499
}
500

    
501
static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
502
                             uint32_t val)
503
{
504
}
505

    
506
static CPUReadMemoryFunc *tcx_dummy_read[3] = {
507
    NULL,
508
    NULL,
509
    tcx_dummy_readl,
510
};
511

    
512
static CPUWriteMemoryFunc *tcx_dummy_write[3] = {
513
    NULL,
514
    NULL,
515
    tcx_dummy_writel,
516
};
517

    
518
static void tcx_init1(SysBusDevice *dev)
519
{
520
    TCXState *s = FROM_SYSBUS(TCXState, dev);
521
    int io_memory, dummy_memory;
522
    ram_addr_t vram_offset;
523
    int size;
524
    uint8_t *vram_base;
525

    
526
    vram_offset = qemu_ram_alloc(s->vram_size * (1 + 4 + 4));
527
    vram_base = qemu_get_ram_ptr(vram_offset);
528
    s->vram_offset = vram_offset;
529

    
530
    /* 8-bit plane */
531
    s->vram = vram_base;
532
    size = s->vram_size;
533
    sysbus_init_mmio(dev, size, s->vram_offset);
534
    vram_offset += size;
535
    vram_base += size;
536

    
537
    /* DAC */
538
    io_memory = cpu_register_io_memory(tcx_dac_read, tcx_dac_write, s);
539
    sysbus_init_mmio(dev, TCX_DAC_NREGS, io_memory);
540

    
541
    /* TEC (dummy) */
542
    dummy_memory = cpu_register_io_memory(tcx_dummy_read, tcx_dummy_write,
543
                                          s);
544
    sysbus_init_mmio(dev, TCX_TEC_NREGS, dummy_memory);
545
    /* THC: NetBSD writes here even with 8-bit display: dummy */
546
    sysbus_init_mmio(dev, TCX_THC_NREGS_24, dummy_memory);
547

    
548
    if (s->depth == 24) {
549
        /* 24-bit plane */
550
        size = s->vram_size * 4;
551
        s->vram24 = (uint32_t *)vram_base;
552
        s->vram24_offset = vram_offset;
553
        sysbus_init_mmio(dev, size, vram_offset);
554
        vram_offset += size;
555
        vram_base += size;
556

    
557
        /* Control plane */
558
        size = s->vram_size * 4;
559
        s->cplane = (uint32_t *)vram_base;
560
        s->cplane_offset = vram_offset;
561
        sysbus_init_mmio(dev, size, vram_offset);
562

    
563
        s->ds = graphic_console_init(tcx24_update_display,
564
                                     tcx24_invalidate_display,
565
                                     tcx24_screen_dump, NULL, s);
566
    } else {
567
        /* THC 8 bit (dummy) */
568
        sysbus_init_mmio(dev, TCX_THC_NREGS_8, dummy_memory);
569

    
570
        s->ds = graphic_console_init(tcx_update_display,
571
                                     tcx_invalidate_display,
572
                                     tcx_screen_dump, NULL, s);
573
    }
574

    
575
    register_savevm("tcx", -1, 4, tcx_save, tcx_load, s);
576
    qemu_register_reset(tcx_reset, s);
577
    tcx_reset(s);
578
    qemu_console_resize(s->ds, s->width, s->height);
579
}
580

    
581
static void tcx_screen_dump(void *opaque, const char *filename)
582
{
583
    TCXState *s = opaque;
584
    FILE *f;
585
    uint8_t *d, *d1, v;
586
    int y, x;
587

    
588
    f = fopen(filename, "wb");
589
    if (!f)
590
        return;
591
    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
592
    d1 = s->vram;
593
    for(y = 0; y < s->height; y++) {
594
        d = d1;
595
        for(x = 0; x < s->width; x++) {
596
            v = *d;
597
            fputc(s->r[v], f);
598
            fputc(s->g[v], f);
599
            fputc(s->b[v], f);
600
            d++;
601
        }
602
        d1 += MAXX;
603
    }
604
    fclose(f);
605
    return;
606
}
607

    
608
static void tcx24_screen_dump(void *opaque, const char *filename)
609
{
610
    TCXState *s = opaque;
611
    FILE *f;
612
    uint8_t *d, *d1, v;
613
    uint32_t *s24, *cptr, dval;
614
    int y, x;
615

    
616
    f = fopen(filename, "wb");
617
    if (!f)
618
        return;
619
    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
620
    d1 = s->vram;
621
    s24 = s->vram24;
622
    cptr = s->cplane;
623
    for(y = 0; y < s->height; y++) {
624
        d = d1;
625
        for(x = 0; x < s->width; x++, d++, s24++) {
626
            if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
627
                dval = *s24 & 0x00ffffff;
628
                fputc((dval >> 16) & 0xff, f);
629
                fputc((dval >> 8) & 0xff, f);
630
                fputc(dval & 0xff, f);
631
            } else {
632
                v = *d;
633
                fputc(s->r[v], f);
634
                fputc(s->g[v], f);
635
                fputc(s->b[v], f);
636
            }
637
        }
638
        d1 += MAXX;
639
    }
640
    fclose(f);
641
    return;
642
}
643

    
644
static SysBusDeviceInfo tcx_info = {
645
    .init = tcx_init1,
646
    .qdev.name  = "SUNW,tcx",
647
    .qdev.size  = sizeof(TCXState),
648
    .qdev.props = (Property[]) {
649
        {
650
            .name   = "addr",
651
            .info   = &qdev_prop_taddr,
652
            .offset = offsetof(TCXState, addr),
653
            .defval = (target_phys_addr_t[]) { -1 },
654
        },{
655
            .name   = "vram_size",
656
            .info   = &qdev_prop_hex32,
657
            .offset = offsetof(TCXState, vram_size),
658
            .defval = (uint32_t[]) { -1 },
659
        },{
660
            .name   = "width",
661
            .info   = &qdev_prop_uint16,
662
            .offset = offsetof(TCXState, width),
663
            .defval = (uint16_t[]) { -1 },
664
        },{
665
            .name   = "height",
666
            .info   = &qdev_prop_uint16,
667
            .offset = offsetof(TCXState, height),
668
            .defval = (uint16_t[]) { -1 },
669
        },{
670
            .name   = "depth",
671
            .info   = &qdev_prop_uint16,
672
            .offset = offsetof(TCXState, depth),
673
            .defval = (uint16_t[]) { -1 },
674
        },
675
        {/* end of list */}
676
    }
677
};
678

    
679
static void tcx_register_devices(void)
680
{
681
    sysbus_register_withprop(&tcx_info);
682
}
683

    
684
device_init(tcx_register_devices)