support xattr syscall in qemu-arm
[external/qemu.git] / hw / tcx.c
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 "console.h"
26 #include "pixel_ops.h"
27 #include "sysbus.h"
28 #include "qdev-addr.h"
29
30 #define MAXX 1024
31 #define MAXY 768
32 #define TCX_DAC_NREGS 16
33 #define TCX_THC_NREGS_8  0x081c
34 #define TCX_THC_NREGS_24 0x1000
35 #define TCX_TEC_NREGS    0x1000
36
37 typedef struct TCXState {
38     SysBusDevice busdev;
39     target_phys_addr_t addr;
40     DisplayState *ds;
41     uint8_t *vram;
42     uint32_t *vram24, *cplane;
43     ram_addr_t vram_offset, vram24_offset, cplane_offset;
44     uint32_t vram_size;
45     uint16_t width, height, depth;
46     uint8_t r[256], g[256], b[256];
47     uint32_t palette[256];
48     uint8_t dac_index, dac_state;
49 } TCXState;
50
51 static void tcx_screen_dump(void *opaque, const char *filename);
52 static void tcx24_screen_dump(void *opaque, const char *filename);
53
54 static void tcx_set_dirty(TCXState *s)
55 {
56     unsigned int i;
57
58     for (i = 0; i < MAXX * MAXY; i += TARGET_PAGE_SIZE) {
59         cpu_physical_memory_set_dirty(s->vram_offset + i);
60     }
61 }
62
63 static void tcx24_set_dirty(TCXState *s)
64 {
65     unsigned int i;
66
67     for (i = 0; i < MAXX * MAXY * 4; i += TARGET_PAGE_SIZE) {
68         cpu_physical_memory_set_dirty(s->vram24_offset + i);
69         cpu_physical_memory_set_dirty(s->cplane_offset + i);
70     }
71 }
72
73 static void update_palette_entries(TCXState *s, int start, int end)
74 {
75     int i;
76     for(i = start; i < end; i++) {
77         switch(ds_get_bits_per_pixel(s->ds)) {
78         default:
79         case 8:
80             s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
81             break;
82         case 15:
83             s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
84             break;
85         case 16:
86             s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
87             break;
88         case 32:
89             if (is_surface_bgr(s->ds->surface))
90                 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
91             else
92                 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
93             break;
94         }
95     }
96     if (s->depth == 24) {
97         tcx24_set_dirty(s);
98     } else {
99         tcx_set_dirty(s);
100     }
101 }
102
103 static void tcx_draw_line32(TCXState *s1, uint8_t *d,
104                             const uint8_t *s, int width)
105 {
106     int x;
107     uint8_t val;
108     uint32_t *p = (uint32_t *)d;
109
110     for(x = 0; x < width; x++) {
111         val = *s++;
112         *p++ = s1->palette[val];
113     }
114 }
115
116 static void tcx_draw_line16(TCXState *s1, uint8_t *d,
117                             const uint8_t *s, int width)
118 {
119     int x;
120     uint8_t val;
121     uint16_t *p = (uint16_t *)d;
122
123     for(x = 0; x < width; x++) {
124         val = *s++;
125         *p++ = s1->palette[val];
126     }
127 }
128
129 static void tcx_draw_line8(TCXState *s1, uint8_t *d,
130                            const uint8_t *s, int width)
131 {
132     int x;
133     uint8_t val;
134
135     for(x = 0; x < width; x++) {
136         val = *s++;
137         *d++ = s1->palette[val];
138     }
139 }
140
141 /*
142   XXX Could be much more optimal:
143   * detect if line/page/whole screen is in 24 bit mode
144   * if destination is also BGR, use memcpy
145   */
146 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
147                                      const uint8_t *s, int width,
148                                      const uint32_t *cplane,
149                                      const uint32_t *s24)
150 {
151     int x, bgr, r, g, b;
152     uint8_t val, *p8;
153     uint32_t *p = (uint32_t *)d;
154     uint32_t dval;
155
156     bgr = is_surface_bgr(s1->ds->surface);
157     for(x = 0; x < width; x++, s++, s24++) {
158         if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) {
159             // 24-bit direct, BGR order
160             p8 = (uint8_t *)s24;
161             p8++;
162             b = *p8++;
163             g = *p8++;
164             r = *p8;
165             if (bgr)
166                 dval = rgb_to_pixel32bgr(r, g, b);
167             else
168                 dval = rgb_to_pixel32(r, g, b);
169         } else {
170             val = *s;
171             dval = s1->palette[val];
172         }
173         *p++ = dval;
174     }
175 }
176
177 static inline int check_dirty(ram_addr_t page, ram_addr_t page24,
178                               ram_addr_t cpage)
179 {
180     int ret;
181     unsigned int off;
182
183     ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
184     for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) {
185         ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG);
186         ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG);
187     }
188     return ret;
189 }
190
191 static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
192                                ram_addr_t page_max, ram_addr_t page24,
193                               ram_addr_t cpage)
194 {
195     cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
196                                     VGA_DIRTY_FLAG);
197     page_min -= ts->vram_offset;
198     page_max -= ts->vram_offset;
199     cpu_physical_memory_reset_dirty(page24 + page_min * 4,
200                                     page24 + page_max * 4 + TARGET_PAGE_SIZE,
201                                     VGA_DIRTY_FLAG);
202     cpu_physical_memory_reset_dirty(cpage + page_min * 4,
203                                     cpage + page_max * 4 + TARGET_PAGE_SIZE,
204                                     VGA_DIRTY_FLAG);
205 }
206
207 /* Fixed line length 1024 allows us to do nice tricks not possible on
208    VGA... */
209 static void tcx_update_display(void *opaque)
210 {
211     TCXState *ts = opaque;
212     ram_addr_t page, page_min, page_max;
213     int y, y_start, dd, ds;
214     uint8_t *d, *s;
215     void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
216
217     if (ds_get_bits_per_pixel(ts->ds) == 0)
218         return;
219     page = ts->vram_offset;
220     y_start = -1;
221     page_min = -1;
222     page_max = 0;
223     d = ds_get_data(ts->ds);
224     s = ts->vram;
225     dd = ds_get_linesize(ts->ds);
226     ds = 1024;
227
228     switch (ds_get_bits_per_pixel(ts->ds)) {
229     case 32:
230         f = tcx_draw_line32;
231         break;
232     case 15:
233     case 16:
234         f = tcx_draw_line16;
235         break;
236     default:
237     case 8:
238         f = tcx_draw_line8;
239         break;
240     case 0:
241         return;
242     }
243
244     for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
245         if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
246             if (y_start < 0)
247                 y_start = y;
248             if (page < page_min)
249                 page_min = page;
250             if (page > page_max)
251                 page_max = page;
252             f(ts, d, s, ts->width);
253             d += dd;
254             s += ds;
255             f(ts, d, s, ts->width);
256             d += dd;
257             s += ds;
258             f(ts, d, s, ts->width);
259             d += dd;
260             s += ds;
261             f(ts, d, s, ts->width);
262             d += dd;
263             s += ds;
264         } else {
265             if (y_start >= 0) {
266                 /* flush to display */
267                 dpy_update(ts->ds, 0, y_start,
268                            ts->width, y - y_start);
269                 y_start = -1;
270             }
271             d += dd * 4;
272             s += ds * 4;
273         }
274     }
275     if (y_start >= 0) {
276         /* flush to display */
277         dpy_update(ts->ds, 0, y_start,
278                    ts->width, y - y_start);
279     }
280     /* reset modified pages */
281     if (page_max >= page_min) {
282         cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
283                                         VGA_DIRTY_FLAG);
284     }
285 }
286
287 static void tcx24_update_display(void *opaque)
288 {
289     TCXState *ts = opaque;
290     ram_addr_t page, page_min, page_max, cpage, page24;
291     int y, y_start, dd, ds;
292     uint8_t *d, *s;
293     uint32_t *cptr, *s24;
294
295     if (ds_get_bits_per_pixel(ts->ds) != 32)
296             return;
297     page = ts->vram_offset;
298     page24 = ts->vram24_offset;
299     cpage = ts->cplane_offset;
300     y_start = -1;
301     page_min = -1;
302     page_max = 0;
303     d = ds_get_data(ts->ds);
304     s = ts->vram;
305     s24 = ts->vram24;
306     cptr = ts->cplane;
307     dd = ds_get_linesize(ts->ds);
308     ds = 1024;
309
310     for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
311             page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
312         if (check_dirty(page, page24, cpage)) {
313             if (y_start < 0)
314                 y_start = y;
315             if (page < page_min)
316                 page_min = page;
317             if (page > page_max)
318                 page_max = page;
319             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
320             d += dd;
321             s += ds;
322             cptr += ds;
323             s24 += ds;
324             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
325             d += dd;
326             s += ds;
327             cptr += ds;
328             s24 += ds;
329             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
330             d += dd;
331             s += ds;
332             cptr += ds;
333             s24 += ds;
334             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
335             d += dd;
336             s += ds;
337             cptr += ds;
338             s24 += ds;
339         } else {
340             if (y_start >= 0) {
341                 /* flush to display */
342                 dpy_update(ts->ds, 0, y_start,
343                            ts->width, y - y_start);
344                 y_start = -1;
345             }
346             d += dd * 4;
347             s += ds * 4;
348             cptr += ds * 4;
349             s24 += ds * 4;
350         }
351     }
352     if (y_start >= 0) {
353         /* flush to display */
354         dpy_update(ts->ds, 0, y_start,
355                    ts->width, y - y_start);
356     }
357     /* reset modified pages */
358     if (page_max >= page_min) {
359         reset_dirty(ts, page_min, page_max, page24, cpage);
360     }
361 }
362
363 static void tcx_invalidate_display(void *opaque)
364 {
365     TCXState *s = opaque;
366
367     tcx_set_dirty(s);
368     qemu_console_resize(s->ds, s->width, s->height);
369 }
370
371 static void tcx24_invalidate_display(void *opaque)
372 {
373     TCXState *s = opaque;
374
375     tcx_set_dirty(s);
376     tcx24_set_dirty(s);
377     qemu_console_resize(s->ds, s->width, s->height);
378 }
379
380 static int vmstate_tcx_post_load(void *opaque, int version_id)
381 {
382     TCXState *s = opaque;
383
384     update_palette_entries(s, 0, 256);
385     if (s->depth == 24) {
386         tcx24_set_dirty(s);
387     } else {
388         tcx_set_dirty(s);
389     }
390
391     return 0;
392 }
393
394 static const VMStateDescription vmstate_tcx = {
395     .name ="tcx",
396     .version_id = 4,
397     .minimum_version_id = 4,
398     .minimum_version_id_old = 4,
399     .post_load = vmstate_tcx_post_load,
400     .fields      = (VMStateField []) {
401         VMSTATE_UINT16(height, TCXState),
402         VMSTATE_UINT16(width, TCXState),
403         VMSTATE_UINT16(depth, TCXState),
404         VMSTATE_BUFFER(r, TCXState),
405         VMSTATE_BUFFER(g, TCXState),
406         VMSTATE_BUFFER(b, TCXState),
407         VMSTATE_UINT8(dac_index, TCXState),
408         VMSTATE_UINT8(dac_state, TCXState),
409         VMSTATE_END_OF_LIST()
410     }
411 };
412
413 static void tcx_reset(DeviceState *d)
414 {
415     TCXState *s = container_of(d, TCXState, busdev.qdev);
416
417     /* Initialize palette */
418     memset(s->r, 0, 256);
419     memset(s->g, 0, 256);
420     memset(s->b, 0, 256);
421     s->r[255] = s->g[255] = s->b[255] = 255;
422     update_palette_entries(s, 0, 256);
423     memset(s->vram, 0, MAXX*MAXY);
424     cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset +
425                                     MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG);
426     s->dac_index = 0;
427     s->dac_state = 0;
428 }
429
430 static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
431 {
432     return 0;
433 }
434
435 static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
436 {
437     TCXState *s = opaque;
438
439     switch (addr) {
440     case 0:
441         s->dac_index = val >> 24;
442         s->dac_state = 0;
443         break;
444     case 4:
445         switch (s->dac_state) {
446         case 0:
447             s->r[s->dac_index] = val >> 24;
448             update_palette_entries(s, s->dac_index, s->dac_index + 1);
449             s->dac_state++;
450             break;
451         case 1:
452             s->g[s->dac_index] = val >> 24;
453             update_palette_entries(s, s->dac_index, s->dac_index + 1);
454             s->dac_state++;
455             break;
456         case 2:
457             s->b[s->dac_index] = val >> 24;
458             update_palette_entries(s, s->dac_index, s->dac_index + 1);
459             s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
460         default:
461             s->dac_state = 0;
462             break;
463         }
464         break;
465     default:
466         break;
467     }
468     return;
469 }
470
471 static CPUReadMemoryFunc * const tcx_dac_read[3] = {
472     NULL,
473     NULL,
474     tcx_dac_readl,
475 };
476
477 static CPUWriteMemoryFunc * const tcx_dac_write[3] = {
478     NULL,
479     NULL,
480     tcx_dac_writel,
481 };
482
483 static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
484 {
485     return 0;
486 }
487
488 static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
489                              uint32_t val)
490 {
491 }
492
493 static CPUReadMemoryFunc * const tcx_dummy_read[3] = {
494     NULL,
495     NULL,
496     tcx_dummy_readl,
497 };
498
499 static CPUWriteMemoryFunc * const tcx_dummy_write[3] = {
500     NULL,
501     NULL,
502     tcx_dummy_writel,
503 };
504
505 static int tcx_init1(SysBusDevice *dev)
506 {
507     TCXState *s = FROM_SYSBUS(TCXState, dev);
508     int io_memory, dummy_memory;
509     ram_addr_t vram_offset;
510     int size;
511     uint8_t *vram_base;
512
513     vram_offset = qemu_ram_alloc(NULL, "tcx.vram", s->vram_size * (1 + 4 + 4));
514     vram_base = qemu_get_ram_ptr(vram_offset);
515     s->vram_offset = vram_offset;
516
517     /* 8-bit plane */
518     s->vram = vram_base;
519     size = s->vram_size;
520     sysbus_init_mmio(dev, size, s->vram_offset);
521     vram_offset += size;
522     vram_base += size;
523
524     /* DAC */
525     io_memory = cpu_register_io_memory(tcx_dac_read, tcx_dac_write, s,
526                                        DEVICE_NATIVE_ENDIAN);
527     sysbus_init_mmio(dev, TCX_DAC_NREGS, io_memory);
528
529     /* TEC (dummy) */
530     dummy_memory = cpu_register_io_memory(tcx_dummy_read, tcx_dummy_write,
531                                           s, DEVICE_NATIVE_ENDIAN);
532     sysbus_init_mmio(dev, TCX_TEC_NREGS, dummy_memory);
533     /* THC: NetBSD writes here even with 8-bit display: dummy */
534     sysbus_init_mmio(dev, TCX_THC_NREGS_24, dummy_memory);
535
536     if (s->depth == 24) {
537         /* 24-bit plane */
538         size = s->vram_size * 4;
539         s->vram24 = (uint32_t *)vram_base;
540         s->vram24_offset = vram_offset;
541         sysbus_init_mmio(dev, size, vram_offset);
542         vram_offset += size;
543         vram_base += size;
544
545         /* Control plane */
546         size = s->vram_size * 4;
547         s->cplane = (uint32_t *)vram_base;
548         s->cplane_offset = vram_offset;
549         sysbus_init_mmio(dev, size, vram_offset);
550
551         s->ds = graphic_console_init(tcx24_update_display,
552                                      tcx24_invalidate_display,
553                                      tcx24_screen_dump, NULL, s);
554     } else {
555         /* THC 8 bit (dummy) */
556         sysbus_init_mmio(dev, TCX_THC_NREGS_8, dummy_memory);
557
558         s->ds = graphic_console_init(tcx_update_display,
559                                      tcx_invalidate_display,
560                                      tcx_screen_dump, NULL, s);
561     }
562
563     qemu_console_resize(s->ds, s->width, s->height);
564     return 0;
565 }
566
567 static void tcx_screen_dump(void *opaque, const char *filename)
568 {
569     TCXState *s = opaque;
570     FILE *f;
571     uint8_t *d, *d1, v;
572     int y, x;
573
574     f = fopen(filename, "wb");
575     if (!f)
576         return;
577     fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
578     d1 = s->vram;
579     for(y = 0; y < s->height; y++) {
580         d = d1;
581         for(x = 0; x < s->width; x++) {
582             v = *d;
583             fputc(s->r[v], f);
584             fputc(s->g[v], f);
585             fputc(s->b[v], f);
586             d++;
587         }
588         d1 += MAXX;
589     }
590     fclose(f);
591     return;
592 }
593
594 static void tcx24_screen_dump(void *opaque, const char *filename)
595 {
596     TCXState *s = opaque;
597     FILE *f;
598     uint8_t *d, *d1, v;
599     uint32_t *s24, *cptr, dval;
600     int y, x;
601
602     f = fopen(filename, "wb");
603     if (!f)
604         return;
605     fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
606     d1 = s->vram;
607     s24 = s->vram24;
608     cptr = s->cplane;
609     for(y = 0; y < s->height; y++) {
610         d = d1;
611         for(x = 0; x < s->width; x++, d++, s24++) {
612             if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
613                 dval = *s24 & 0x00ffffff;
614                 fputc((dval >> 16) & 0xff, f);
615                 fputc((dval >> 8) & 0xff, f);
616                 fputc(dval & 0xff, f);
617             } else {
618                 v = *d;
619                 fputc(s->r[v], f);
620                 fputc(s->g[v], f);
621                 fputc(s->b[v], f);
622             }
623         }
624         d1 += MAXX;
625     }
626     fclose(f);
627     return;
628 }
629
630 static SysBusDeviceInfo tcx_info = {
631     .init = tcx_init1,
632     .qdev.name  = "SUNW,tcx",
633     .qdev.size  = sizeof(TCXState),
634     .qdev.reset = tcx_reset,
635     .qdev.vmsd  = &vmstate_tcx,
636     .qdev.props = (Property[]) {
637         DEFINE_PROP_TADDR("addr",      TCXState, addr,      -1),
638         DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1),
639         DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
640         DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
641         DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
642         DEFINE_PROP_END_OF_LIST(),
643     }
644 };
645
646 static void tcx_register_devices(void)
647 {
648     sysbus_register_withprop(&tcx_info);
649 }
650
651 device_init(tcx_register_devices)