xref: /openbmc/qemu/hw/display/ssd0323.c (revision d328fef93ae757a0dd65ed786a4086e27952eef3)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * SSD0323 OLED controller with OSRAM Pictiva 128x64 display.
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2006-2007 CodeSourcery.
549ab747fSPaolo Bonzini  * Written by Paul Brook
649ab747fSPaolo Bonzini  *
749ab747fSPaolo Bonzini  * This code is licensed under the GPL.
849ab747fSPaolo Bonzini  */
949ab747fSPaolo Bonzini 
1049ab747fSPaolo Bonzini /* The controller can support a variety of different displays, but we only
1133a52307SMichael Tokarev    implement one.  Most of the commands relating to brightness and geometry
1249ab747fSPaolo Bonzini    setup are ignored. */
130b8fa32fSMarkus Armbruster 
1447df5154SPeter Maydell #include "qemu/osdep.h"
158fd06719SAlistair Francis #include "hw/ssi/ssi.h"
16d6454270SMarkus Armbruster #include "migration/vmstate.h"
170b8fa32fSMarkus Armbruster #include "qemu/module.h"
1849ab747fSPaolo Bonzini #include "ui/console.h"
19db1015e9SEduardo Habkost #include "qom/object.h"
2049ab747fSPaolo Bonzini 
2149ab747fSPaolo Bonzini //#define DEBUG_SSD0323 1
2249ab747fSPaolo Bonzini 
2349ab747fSPaolo Bonzini #ifdef DEBUG_SSD0323
2449ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) \
2549ab747fSPaolo Bonzini do { printf("ssd0323: " fmt , ## __VA_ARGS__); } while (0)
2649ab747fSPaolo Bonzini #define BADF(fmt, ...) \
2749ab747fSPaolo Bonzini do { \
2849ab747fSPaolo Bonzini     fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); abort(); \
2949ab747fSPaolo Bonzini } while (0)
3049ab747fSPaolo Bonzini #else
3149ab747fSPaolo Bonzini #define DPRINTF(fmt, ...) do {} while(0)
3249ab747fSPaolo Bonzini #define BADF(fmt, ...) \
3349ab747fSPaolo Bonzini do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__);} while (0)
3449ab747fSPaolo Bonzini #endif
3549ab747fSPaolo Bonzini 
3649ab747fSPaolo Bonzini /* Scaling factor for pixels.  */
3749ab747fSPaolo Bonzini #define MAGNIFY 4
3849ab747fSPaolo Bonzini 
3949ab747fSPaolo Bonzini #define REMAP_SWAP_COLUMN 0x01
4049ab747fSPaolo Bonzini #define REMAP_SWAP_NYBBLE 0x02
4149ab747fSPaolo Bonzini #define REMAP_VERTICAL    0x04
4249ab747fSPaolo Bonzini #define REMAP_SWAP_COM    0x10
4349ab747fSPaolo Bonzini #define REMAP_SPLIT_COM   0x40
4449ab747fSPaolo Bonzini 
4549ab747fSPaolo Bonzini enum ssd0323_mode
4649ab747fSPaolo Bonzini {
4749ab747fSPaolo Bonzini     SSD0323_CMD,
4849ab747fSPaolo Bonzini     SSD0323_DATA
4949ab747fSPaolo Bonzini };
5049ab747fSPaolo Bonzini 
51db1015e9SEduardo Habkost struct ssd0323_state {
52ec7e429bSPhilippe Mathieu-Daudé     SSIPeripheral ssidev;
5349ab747fSPaolo Bonzini     QemuConsole *con;
5449ab747fSPaolo Bonzini 
55e7f76c52SDr. David Alan Gilbert     uint32_t cmd_len;
56e7f76c52SDr. David Alan Gilbert     int32_t cmd;
57e7f76c52SDr. David Alan Gilbert     int32_t cmd_data[8];
58e7f76c52SDr. David Alan Gilbert     int32_t row;
59e7f76c52SDr. David Alan Gilbert     int32_t row_start;
60e7f76c52SDr. David Alan Gilbert     int32_t row_end;
61e7f76c52SDr. David Alan Gilbert     int32_t col;
62e7f76c52SDr. David Alan Gilbert     int32_t col_start;
63e7f76c52SDr. David Alan Gilbert     int32_t col_end;
64e7f76c52SDr. David Alan Gilbert     int32_t redraw;
65e7f76c52SDr. David Alan Gilbert     int32_t remap;
66e7f76c52SDr. David Alan Gilbert     uint32_t mode;
6749ab747fSPaolo Bonzini     uint8_t framebuffer[128 * 80 / 2];
68db1015e9SEduardo Habkost };
6949ab747fSPaolo Bonzini 
70213f63dfSPeter Maydell #define TYPE_SSD0323 "ssd0323"
OBJECT_DECLARE_SIMPLE_TYPE(ssd0323_state,SSD0323)718063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(ssd0323_state, SSD0323)
72213f63dfSPeter Maydell 
73213f63dfSPeter Maydell 
74ec7e429bSPhilippe Mathieu-Daudé static uint32_t ssd0323_transfer(SSIPeripheral *dev, uint32_t data)
7549ab747fSPaolo Bonzini {
76213f63dfSPeter Maydell     ssd0323_state *s = SSD0323(dev);
7749ab747fSPaolo Bonzini 
7849ab747fSPaolo Bonzini     switch (s->mode) {
7949ab747fSPaolo Bonzini     case SSD0323_DATA:
8049ab747fSPaolo Bonzini         DPRINTF("data 0x%02x\n", data);
8149ab747fSPaolo Bonzini         s->framebuffer[s->col + s->row * 64] = data;
8249ab747fSPaolo Bonzini         if (s->remap & REMAP_VERTICAL) {
8349ab747fSPaolo Bonzini             s->row++;
8449ab747fSPaolo Bonzini             if (s->row > s->row_end) {
8549ab747fSPaolo Bonzini                 s->row = s->row_start;
8649ab747fSPaolo Bonzini                 s->col++;
8749ab747fSPaolo Bonzini             }
8849ab747fSPaolo Bonzini             if (s->col > s->col_end) {
8949ab747fSPaolo Bonzini                 s->col = s->col_start;
9049ab747fSPaolo Bonzini             }
9149ab747fSPaolo Bonzini         } else {
9249ab747fSPaolo Bonzini             s->col++;
9349ab747fSPaolo Bonzini             if (s->col > s->col_end) {
9449ab747fSPaolo Bonzini                 s->row++;
9549ab747fSPaolo Bonzini                 s->col = s->col_start;
9649ab747fSPaolo Bonzini             }
9749ab747fSPaolo Bonzini             if (s->row > s->row_end) {
9849ab747fSPaolo Bonzini                 s->row = s->row_start;
9949ab747fSPaolo Bonzini             }
10049ab747fSPaolo Bonzini         }
10149ab747fSPaolo Bonzini         s->redraw = 1;
10249ab747fSPaolo Bonzini         break;
10349ab747fSPaolo Bonzini     case SSD0323_CMD:
10449ab747fSPaolo Bonzini         DPRINTF("cmd 0x%02x\n", data);
10549ab747fSPaolo Bonzini         if (s->cmd_len == 0) {
10649ab747fSPaolo Bonzini             s->cmd = data;
10749ab747fSPaolo Bonzini         } else {
10849ab747fSPaolo Bonzini             s->cmd_data[s->cmd_len - 1] = data;
10949ab747fSPaolo Bonzini         }
11049ab747fSPaolo Bonzini         s->cmd_len++;
11149ab747fSPaolo Bonzini         switch (s->cmd) {
11249ab747fSPaolo Bonzini #define DATA(x) if (s->cmd_len <= (x)) return 0
11349ab747fSPaolo Bonzini         case 0x15: /* Set column.  */
11449ab747fSPaolo Bonzini             DATA(2);
11549ab747fSPaolo Bonzini             s->col = s->col_start = s->cmd_data[0] % 64;
11649ab747fSPaolo Bonzini             s->col_end = s->cmd_data[1] % 64;
11749ab747fSPaolo Bonzini             break;
11849ab747fSPaolo Bonzini         case 0x75: /* Set row.  */
11949ab747fSPaolo Bonzini             DATA(2);
12049ab747fSPaolo Bonzini             s->row = s->row_start = s->cmd_data[0] % 80;
12149ab747fSPaolo Bonzini             s->row_end = s->cmd_data[1] % 80;
12249ab747fSPaolo Bonzini             break;
12349ab747fSPaolo Bonzini         case 0x81: /* Set contrast */
12449ab747fSPaolo Bonzini             DATA(1);
12549ab747fSPaolo Bonzini             break;
12649ab747fSPaolo Bonzini         case 0x84: case 0x85: case 0x86: /* Max current.  */
12749ab747fSPaolo Bonzini             DATA(0);
12849ab747fSPaolo Bonzini             break;
12949ab747fSPaolo Bonzini         case 0xa0: /* Set remapping.  */
13049ab747fSPaolo Bonzini             /* FIXME: Implement this.  */
13149ab747fSPaolo Bonzini             DATA(1);
13249ab747fSPaolo Bonzini             s->remap = s->cmd_data[0];
13349ab747fSPaolo Bonzini             break;
13449ab747fSPaolo Bonzini         case 0xa1: /* Set display start line.  */
13549ab747fSPaolo Bonzini         case 0xa2: /* Set display offset.  */
13649ab747fSPaolo Bonzini             /* FIXME: Implement these.  */
13749ab747fSPaolo Bonzini             DATA(1);
13849ab747fSPaolo Bonzini             break;
13949ab747fSPaolo Bonzini         case 0xa4: /* Normal mode.  */
14049ab747fSPaolo Bonzini         case 0xa5: /* All on.  */
14149ab747fSPaolo Bonzini         case 0xa6: /* All off.  */
14249ab747fSPaolo Bonzini         case 0xa7: /* Inverse.  */
14349ab747fSPaolo Bonzini             /* FIXME: Implement these.  */
14449ab747fSPaolo Bonzini             DATA(0);
14549ab747fSPaolo Bonzini             break;
14649ab747fSPaolo Bonzini         case 0xa8: /* Set multiplex ratio.  */
14749ab747fSPaolo Bonzini         case 0xad: /* Set DC-DC converter.  */
14849ab747fSPaolo Bonzini             DATA(1);
14949ab747fSPaolo Bonzini             /* Ignored.  Don't care.  */
15049ab747fSPaolo Bonzini             break;
15149ab747fSPaolo Bonzini         case 0xae: /* Display off.  */
15249ab747fSPaolo Bonzini         case 0xaf: /* Display on.  */
15349ab747fSPaolo Bonzini             DATA(0);
15449ab747fSPaolo Bonzini             /* TODO: Implement power control.  */
15549ab747fSPaolo Bonzini             break;
15649ab747fSPaolo Bonzini         case 0xb1: /* Set phase length.  */
15749ab747fSPaolo Bonzini         case 0xb2: /* Set row period.  */
15849ab747fSPaolo Bonzini         case 0xb3: /* Set clock rate.  */
15949ab747fSPaolo Bonzini         case 0xbc: /* Set precharge.  */
16049ab747fSPaolo Bonzini         case 0xbe: /* Set VCOMH.  */
16149ab747fSPaolo Bonzini         case 0xbf: /* Set segment low.  */
16249ab747fSPaolo Bonzini             DATA(1);
16349ab747fSPaolo Bonzini             /* Ignored.  Don't care.  */
16449ab747fSPaolo Bonzini             break;
16549ab747fSPaolo Bonzini         case 0xb8: /* Set grey scale table.  */
16649ab747fSPaolo Bonzini             /* FIXME: Implement this.  */
16749ab747fSPaolo Bonzini             DATA(8);
16849ab747fSPaolo Bonzini             break;
16949ab747fSPaolo Bonzini         case 0xe3: /* NOP.  */
17049ab747fSPaolo Bonzini             DATA(0);
17149ab747fSPaolo Bonzini             break;
17249ab747fSPaolo Bonzini         case 0xff: /* Nasty hack because we don't handle chip selects
17349ab747fSPaolo Bonzini                       properly.  */
17449ab747fSPaolo Bonzini             break;
17549ab747fSPaolo Bonzini         default:
17649ab747fSPaolo Bonzini             BADF("Unknown command: 0x%x\n", data);
17749ab747fSPaolo Bonzini         }
17849ab747fSPaolo Bonzini         s->cmd_len = 0;
17949ab747fSPaolo Bonzini         return 0;
18049ab747fSPaolo Bonzini     }
18149ab747fSPaolo Bonzini     return 0;
18249ab747fSPaolo Bonzini }
18349ab747fSPaolo Bonzini 
ssd0323_update_display(void * opaque)18449ab747fSPaolo Bonzini static void ssd0323_update_display(void *opaque)
18549ab747fSPaolo Bonzini {
18649ab747fSPaolo Bonzini     ssd0323_state *s = (ssd0323_state *)opaque;
18749ab747fSPaolo Bonzini     DisplaySurface *surface = qemu_console_surface(s->con);
18849ab747fSPaolo Bonzini     uint8_t *dest;
18949ab747fSPaolo Bonzini     uint8_t *src;
19049ab747fSPaolo Bonzini     int x;
19149ab747fSPaolo Bonzini     int y;
19249ab747fSPaolo Bonzini     int i;
19349ab747fSPaolo Bonzini     int line;
19449ab747fSPaolo Bonzini     char *colors[16];
19549ab747fSPaolo Bonzini     char colortab[MAGNIFY * 64];
19649ab747fSPaolo Bonzini     char *p;
19749ab747fSPaolo Bonzini     int dest_width;
19849ab747fSPaolo Bonzini 
19949ab747fSPaolo Bonzini     if (!s->redraw)
20049ab747fSPaolo Bonzini         return;
20149ab747fSPaolo Bonzini 
20249ab747fSPaolo Bonzini     switch (surface_bits_per_pixel(surface)) {
20349ab747fSPaolo Bonzini     case 0:
20449ab747fSPaolo Bonzini         return;
20549ab747fSPaolo Bonzini     case 15:
20649ab747fSPaolo Bonzini         dest_width = 2;
20749ab747fSPaolo Bonzini         break;
20849ab747fSPaolo Bonzini     case 16:
20949ab747fSPaolo Bonzini         dest_width = 2;
21049ab747fSPaolo Bonzini         break;
21149ab747fSPaolo Bonzini     case 24:
21249ab747fSPaolo Bonzini         dest_width = 3;
21349ab747fSPaolo Bonzini         break;
21449ab747fSPaolo Bonzini     case 32:
21549ab747fSPaolo Bonzini         dest_width = 4;
21649ab747fSPaolo Bonzini         break;
21749ab747fSPaolo Bonzini     default:
21849ab747fSPaolo Bonzini         BADF("Bad color depth\n");
21949ab747fSPaolo Bonzini         return;
22049ab747fSPaolo Bonzini     }
22149ab747fSPaolo Bonzini     p = colortab;
22249ab747fSPaolo Bonzini     for (i = 0; i < 16; i++) {
22349ab747fSPaolo Bonzini         int n;
22449ab747fSPaolo Bonzini         colors[i] = p;
22549ab747fSPaolo Bonzini         switch (surface_bits_per_pixel(surface)) {
22649ab747fSPaolo Bonzini         case 15:
22749ab747fSPaolo Bonzini             n = i * 2 + (i >> 3);
22849ab747fSPaolo Bonzini             p[0] = n | (n << 5);
22949ab747fSPaolo Bonzini             p[1] = (n << 2) | (n >> 3);
23049ab747fSPaolo Bonzini             break;
23149ab747fSPaolo Bonzini         case 16:
23249ab747fSPaolo Bonzini             n = i * 2 + (i >> 3);
23349ab747fSPaolo Bonzini             p[0] = n | (n << 6) | ((n << 1) & 0x20);
23449ab747fSPaolo Bonzini             p[1] = (n << 3) | (n >> 2);
23549ab747fSPaolo Bonzini             break;
23649ab747fSPaolo Bonzini         case 24:
23749ab747fSPaolo Bonzini         case 32:
23849ab747fSPaolo Bonzini             n = (i << 4) | i;
23949ab747fSPaolo Bonzini             p[0] = p[1] = p[2] = n;
24049ab747fSPaolo Bonzini             break;
24149ab747fSPaolo Bonzini         default:
24249ab747fSPaolo Bonzini             BADF("Bad color depth\n");
24349ab747fSPaolo Bonzini             return;
24449ab747fSPaolo Bonzini         }
24549ab747fSPaolo Bonzini         p += dest_width;
24649ab747fSPaolo Bonzini     }
24749ab747fSPaolo Bonzini     /* TODO: Implement row/column remapping.  */
24849ab747fSPaolo Bonzini     dest = surface_data(surface);
24949ab747fSPaolo Bonzini     for (y = 0; y < 64; y++) {
25049ab747fSPaolo Bonzini         line = y;
25149ab747fSPaolo Bonzini         src = s->framebuffer + 64 * line;
25249ab747fSPaolo Bonzini         for (x = 0; x < 64; x++) {
25349ab747fSPaolo Bonzini             int val;
25449ab747fSPaolo Bonzini             val = *src >> 4;
25549ab747fSPaolo Bonzini             for (i = 0; i < MAGNIFY; i++) {
25649ab747fSPaolo Bonzini                 memcpy(dest, colors[val], dest_width);
25749ab747fSPaolo Bonzini                 dest += dest_width;
25849ab747fSPaolo Bonzini             }
25949ab747fSPaolo Bonzini             val = *src & 0xf;
26049ab747fSPaolo Bonzini             for (i = 0; i < MAGNIFY; i++) {
26149ab747fSPaolo Bonzini                 memcpy(dest, colors[val], dest_width);
26249ab747fSPaolo Bonzini                 dest += dest_width;
26349ab747fSPaolo Bonzini             }
26449ab747fSPaolo Bonzini             src++;
26549ab747fSPaolo Bonzini         }
26649ab747fSPaolo Bonzini         for (i = 1; i < MAGNIFY; i++) {
26749ab747fSPaolo Bonzini             memcpy(dest, dest - dest_width * MAGNIFY * 128,
26849ab747fSPaolo Bonzini                    dest_width * 128 * MAGNIFY);
26949ab747fSPaolo Bonzini             dest += dest_width * 128 * MAGNIFY;
27049ab747fSPaolo Bonzini         }
27149ab747fSPaolo Bonzini     }
27249ab747fSPaolo Bonzini     s->redraw = 0;
27349ab747fSPaolo Bonzini     dpy_gfx_update(s->con, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
27449ab747fSPaolo Bonzini }
27549ab747fSPaolo Bonzini 
ssd0323_invalidate_display(void * opaque)27649ab747fSPaolo Bonzini static void ssd0323_invalidate_display(void * opaque)
27749ab747fSPaolo Bonzini {
27849ab747fSPaolo Bonzini     ssd0323_state *s = (ssd0323_state *)opaque;
27949ab747fSPaolo Bonzini     s->redraw = 1;
28049ab747fSPaolo Bonzini }
28149ab747fSPaolo Bonzini 
28249ab747fSPaolo Bonzini /* Command/data input.  */
ssd0323_cd(void * opaque,int n,int level)28349ab747fSPaolo Bonzini static void ssd0323_cd(void *opaque, int n, int level)
28449ab747fSPaolo Bonzini {
28549ab747fSPaolo Bonzini     ssd0323_state *s = (ssd0323_state *)opaque;
28649ab747fSPaolo Bonzini     DPRINTF("%s mode\n", level ? "Data" : "Command");
28749ab747fSPaolo Bonzini     s->mode = level ? SSD0323_DATA : SSD0323_CMD;
28849ab747fSPaolo Bonzini }
28949ab747fSPaolo Bonzini 
ssd0323_post_load(void * opaque,int version_id)290e7f76c52SDr. David Alan Gilbert static int ssd0323_post_load(void *opaque, int version_id)
29149ab747fSPaolo Bonzini {
29249ab747fSPaolo Bonzini     ssd0323_state *s = (ssd0323_state *)opaque;
29349ab747fSPaolo Bonzini 
294e7f76c52SDr. David Alan Gilbert     if (s->cmd_len > ARRAY_SIZE(s->cmd_data)) {
295ead7a57dSMichael S. Tsirkin         return -EINVAL;
296ead7a57dSMichael S. Tsirkin     }
297ead7a57dSMichael S. Tsirkin     if (s->row < 0 || s->row >= 80) {
298ead7a57dSMichael S. Tsirkin         return -EINVAL;
299ead7a57dSMichael S. Tsirkin     }
300ead7a57dSMichael S. Tsirkin     if (s->row_start < 0 || s->row_start >= 80) {
301ead7a57dSMichael S. Tsirkin         return -EINVAL;
302ead7a57dSMichael S. Tsirkin     }
303ead7a57dSMichael S. Tsirkin     if (s->row_end < 0 || s->row_end >= 80) {
304ead7a57dSMichael S. Tsirkin         return -EINVAL;
305ead7a57dSMichael S. Tsirkin     }
306ead7a57dSMichael S. Tsirkin     if (s->col < 0 || s->col >= 64) {
307ead7a57dSMichael S. Tsirkin         return -EINVAL;
308ead7a57dSMichael S. Tsirkin     }
309ead7a57dSMichael S. Tsirkin     if (s->col_start < 0 || s->col_start >= 64) {
310ead7a57dSMichael S. Tsirkin         return -EINVAL;
311ead7a57dSMichael S. Tsirkin     }
312ead7a57dSMichael S. Tsirkin     if (s->col_end < 0 || s->col_end >= 64) {
313ead7a57dSMichael S. Tsirkin         return -EINVAL;
314ead7a57dSMichael S. Tsirkin     }
315ead7a57dSMichael S. Tsirkin     if (s->mode != SSD0323_CMD && s->mode != SSD0323_DATA) {
316ead7a57dSMichael S. Tsirkin         return -EINVAL;
317ead7a57dSMichael S. Tsirkin     }
31849ab747fSPaolo Bonzini 
31949ab747fSPaolo Bonzini     return 0;
32049ab747fSPaolo Bonzini }
32149ab747fSPaolo Bonzini 
322e7f76c52SDr. David Alan Gilbert static const VMStateDescription vmstate_ssd0323 = {
323e7f76c52SDr. David Alan Gilbert     .name = "ssd0323_oled",
324e7f76c52SDr. David Alan Gilbert     .version_id = 2,
325e7f76c52SDr. David Alan Gilbert     .minimum_version_id = 2,
326e7f76c52SDr. David Alan Gilbert     .post_load = ssd0323_post_load,
327*f0613160SRichard Henderson     .fields = (const VMStateField []) {
328e7f76c52SDr. David Alan Gilbert         VMSTATE_UINT32(cmd_len, ssd0323_state),
329e7f76c52SDr. David Alan Gilbert         VMSTATE_INT32(cmd, ssd0323_state),
330e7f76c52SDr. David Alan Gilbert         VMSTATE_INT32_ARRAY(cmd_data, ssd0323_state, 8),
331e7f76c52SDr. David Alan Gilbert         VMSTATE_INT32(row, ssd0323_state),
332e7f76c52SDr. David Alan Gilbert         VMSTATE_INT32(row_start, ssd0323_state),
333e7f76c52SDr. David Alan Gilbert         VMSTATE_INT32(row_end, ssd0323_state),
334e7f76c52SDr. David Alan Gilbert         VMSTATE_INT32(col, ssd0323_state),
335e7f76c52SDr. David Alan Gilbert         VMSTATE_INT32(col_start, ssd0323_state),
336e7f76c52SDr. David Alan Gilbert         VMSTATE_INT32(col_end, ssd0323_state),
337e7f76c52SDr. David Alan Gilbert         VMSTATE_INT32(redraw, ssd0323_state),
338e7f76c52SDr. David Alan Gilbert         VMSTATE_INT32(remap, ssd0323_state),
339e7f76c52SDr. David Alan Gilbert         VMSTATE_UINT32(mode, ssd0323_state),
340e7f76c52SDr. David Alan Gilbert         VMSTATE_BUFFER(framebuffer, ssd0323_state),
341ec7e429bSPhilippe Mathieu-Daudé         VMSTATE_SSI_PERIPHERAL(ssidev, ssd0323_state),
342e7f76c52SDr. David Alan Gilbert         VMSTATE_END_OF_LIST()
343e7f76c52SDr. David Alan Gilbert     }
344e7f76c52SDr. David Alan Gilbert };
345e7f76c52SDr. David Alan Gilbert 
346380cd056SGerd Hoffmann static const GraphicHwOps ssd0323_ops = {
347380cd056SGerd Hoffmann     .invalidate  = ssd0323_invalidate_display,
348380cd056SGerd Hoffmann     .gfx_update  = ssd0323_update_display,
349380cd056SGerd Hoffmann };
350380cd056SGerd Hoffmann 
ssd0323_realize(SSIPeripheral * d,Error ** errp)351ec7e429bSPhilippe Mathieu-Daudé static void ssd0323_realize(SSIPeripheral *d, Error **errp)
35249ab747fSPaolo Bonzini {
3531a7d9ee6SPeter Crosthwaite     DeviceState *dev = DEVICE(d);
354213f63dfSPeter Maydell     ssd0323_state *s = SSD0323(d);
35549ab747fSPaolo Bonzini 
35649ab747fSPaolo Bonzini     s->col_end = 63;
35749ab747fSPaolo Bonzini     s->row_end = 79;
3581a7d9ee6SPeter Crosthwaite     s->con = graphic_console_init(dev, 0, &ssd0323_ops, s);
35949ab747fSPaolo Bonzini     qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY);
36049ab747fSPaolo Bonzini 
3611a7d9ee6SPeter Crosthwaite     qdev_init_gpio_in(dev, ssd0323_cd, 1);
36249ab747fSPaolo Bonzini }
36349ab747fSPaolo Bonzini 
ssd0323_class_init(ObjectClass * klass,void * data)36449ab747fSPaolo Bonzini static void ssd0323_class_init(ObjectClass *klass, void *data)
36549ab747fSPaolo Bonzini {
366e7f76c52SDr. David Alan Gilbert     DeviceClass *dc = DEVICE_CLASS(klass);
367ec7e429bSPhilippe Mathieu-Daudé     SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
36849ab747fSPaolo Bonzini 
3697673bb4cSCédric Le Goater     k->realize = ssd0323_realize;
37049ab747fSPaolo Bonzini     k->transfer = ssd0323_transfer;
37149ab747fSPaolo Bonzini     k->cs_polarity = SSI_CS_HIGH;
372e7f76c52SDr. David Alan Gilbert     dc->vmsd = &vmstate_ssd0323;
37313527115SGan Qixin     set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
37449ab747fSPaolo Bonzini }
37549ab747fSPaolo Bonzini 
37649ab747fSPaolo Bonzini static const TypeInfo ssd0323_info = {
377213f63dfSPeter Maydell     .name          = TYPE_SSD0323,
378ec7e429bSPhilippe Mathieu-Daudé     .parent        = TYPE_SSI_PERIPHERAL,
37949ab747fSPaolo Bonzini     .instance_size = sizeof(ssd0323_state),
38049ab747fSPaolo Bonzini     .class_init    = ssd0323_class_init,
38149ab747fSPaolo Bonzini };
38249ab747fSPaolo Bonzini 
ssd03232_register_types(void)38349ab747fSPaolo Bonzini static void ssd03232_register_types(void)
38449ab747fSPaolo Bonzini {
38549ab747fSPaolo Bonzini     type_register_static(&ssd0323_info);
38649ab747fSPaolo Bonzini }
38749ab747fSPaolo Bonzini 
38849ab747fSPaolo Bonzini type_init(ssd03232_register_types)
389