Break up vl.h.
[qemu] / hw / ssd0323.c
1 /*
2  * SSD0323 OLED controller with OSRAM Pictiva 128x64 display.
3  *
4  * Copyright (c) 2006-2007 CodeSourcery.
5  * Written by Paul Brook
6  *
7  * This code is licenced under the GPL.
8  */
9
10 /* The controller can support a variety of different displays, but we only
11    implement one.  Most of the commends relating to brightness and geometry
12    setup are ignored. */
13 #include "hw.h"
14 #include "devices.h"
15 #include "console.h"
16
17 //#define DEBUG_SSD0323 1
18
19 #ifdef DEBUG_SSD0323
20 #define DPRINTF(fmt, args...) \
21 do { printf("ssd0323: " fmt , ##args); } while (0)
22 #define BADF(fmt, args...) \
23 do { fprintf(stderr, "ssd0323: error: " fmt , ##args); exit(1);} while (0)
24 #else
25 #define DPRINTF(fmt, args...) do {} while(0)
26 #define BADF(fmt, args...) \
27 do { fprintf(stderr, "ssd0323: error: " fmt , ##args);} while (0)
28 #endif
29
30 /* Scaling factor for pixels.  */
31 #define MAGNIFY 4
32
33 enum ssd0323_mode
34 {
35     SSD0323_CMD,
36     SSD0323_DATA
37 };
38
39 typedef struct {
40     DisplayState *ds;
41
42     int cmd_len;
43     int cmd;
44     int cmd_data[8];
45     int row;
46     int row_start;
47     int row_end;
48     int col;
49     int col_start;
50     int col_end;
51     int redraw;
52     enum ssd0323_mode mode;
53     uint8_t framebuffer[128 * 80 / 2];
54 } ssd0323_state;
55
56 int ssd0323_xfer_ssi(void *opaque, int data)
57 {
58     ssd0323_state *s = (ssd0323_state *)opaque;
59     switch (s->mode) {
60     case SSD0323_DATA:
61         DPRINTF("data 0x%02x\n", data);
62         s->framebuffer[s->col + s->row * 64] = data;
63         s->col++;
64         if (s->col > s->col_end) {
65             s->row++;
66             s->col = s->col_start;
67         }
68         if (s->row > s->row_end) {
69             s->row = s->row_start;
70         }
71         s->redraw = 1;
72         break;
73     case SSD0323_CMD:
74         DPRINTF("cmd 0x%02x\n", data);
75         if (s->cmd_len == 0) {
76             s->cmd = data;
77         } else {
78             s->cmd_data[s->cmd_len - 1] = data;
79         }
80         s->cmd_len++;
81         switch (s->cmd) {
82 #define DATA(x) if (s->cmd_len <= (x)) return 0
83         case 0x15: /* Set column.  */
84             DATA(2);
85             s->col_start = s->cmd_data[0] % 64;
86             s->col_end = s->cmd_data[1] % 64;
87             break;
88         case 0x75: /* Set row.  */
89             DATA(2);
90             s->row_start = s->cmd_data[0] % 80;
91             s->row_end = s->cmd_data[1] % 80;
92             break;
93         case 0x81: /* Set contrast */
94             DATA(1);
95             break;
96         case 0x84: case 0x85: case 0x86: /* Max current.  */
97             DATA(0);
98             break;
99         case 0xa0: /* Set remapping.  */
100             /* FIXME: Implement this.  */
101             DATA(1);
102             break;
103         case 0xa1: /* Set display start line.  */
104         case 0xa2: /* Set display offset.  */
105             /* FIXME: Implement these.  */
106             DATA(1);
107             break;
108         case 0xa4: /* Normal mode.  */
109         case 0xa5: /* All on.  */
110         case 0xa6: /* All off.  */
111         case 0xa7: /* Inverse.  */
112             /* FIXME: Implement these.  */
113             DATA(0);
114             break;
115         case 0xa8: /* Set multiplex ratio.  */
116         case 0xad: /* Set DC-DC converter.  */
117             DATA(1);
118             /* Ignored.  Don't care.  */
119             break;
120         case 0xae: /* Display off.  */
121         case 0xaf: /* Display on.  */
122             DATA(0);
123             /* TODO: Implement power control.  */
124             break;
125         case 0xb1: /* Set phase length.  */
126         case 0xb2: /* Set row period.  */
127         case 0xb3: /* Set clock rate.  */
128         case 0xbc: /* Set precharge.  */
129         case 0xbe: /* Set VCOMH.  */
130         case 0xbf: /* Set segment low.  */
131             DATA(1);
132             /* Ignored.  Don't care.  */
133             break;
134         case 0xb8: /* Set grey scale table.  */
135             /* FIXME: Implement this.  */
136             DATA(8);
137             break;
138         case 0xe3: /* NOP.  */
139             DATA(0);
140             break;
141         default:
142             BADF("Unknown command: 0x%x\n", data);
143         }
144         s->cmd_len = 0;
145         return 0;
146     }
147     return 0;
148 }
149
150 static void ssd0323_update_display(void *opaque)
151 {
152     ssd0323_state *s = (ssd0323_state *)opaque;
153     uint8_t *dest;
154     uint8_t *src;
155     int x;
156     int y;
157     int i;
158     int line;
159     char *colors[16];
160     char colortab[MAGNIFY * 64];
161     char *p;
162     int dest_width;
163
164     if (s->redraw) {
165         switch (s->ds->depth) {
166         case 0:
167             return;
168         case 15:
169             dest_width = 2;
170             break;
171         case 16:
172             dest_width = 2;
173             break;
174         case 24:
175             dest_width = 3;
176             break;
177         case 32:
178             dest_width = 4;
179             break;
180         default:
181             BADF("Bad color depth\n");
182             return;
183         }
184         p = colortab;
185         for (i = 0; i < 16; i++) {
186             int n;
187             colors[i] = p;
188             switch (s->ds->depth) {
189             case 15:
190                 n = i * 2 + (i >> 3);
191                 p[0] = n | (n << 5);
192                 p[1] = (n << 2) | (n >> 3);
193                 break;
194             case 16:
195                 n = i * 2 + (i >> 3);
196                 p[0] = n | (n << 6) | ((n << 1) & 0x20);
197                 p[1] = (n << 3) | (n >> 2);
198                 break;
199             case 24:
200             case 32:
201                 n = (i << 4) | i;
202                 p[0] = p[1] = p[2] = n;
203                 break;
204             default:
205                 BADF("Bad color depth\n");
206                 return;
207             }
208             p += dest_width;
209         }
210         dest = s->ds->data;
211         for (y = 0; y < 64; y++) {
212             line = y;
213             src = s->framebuffer + 64 * line;
214             for (x = 0; x < 64; x++) {
215                 int val;
216                 val = *src >> 4;
217                 for (i = 0; i < MAGNIFY; i++) {
218                     memcpy(dest, colors[val], dest_width);
219                     dest += dest_width;
220                 }
221                 val = *src & 0xf;
222                 for (i = 0; i < MAGNIFY; i++) {
223                     memcpy(dest, colors[val], dest_width);
224                     dest += dest_width;
225                 }
226                 src++;
227             }
228             for (i = 1; i < MAGNIFY; i++) {
229                 memcpy(dest, dest - dest_width * MAGNIFY * 128,
230                        dest_width * 128 * MAGNIFY);
231                 dest += dest_width * 128 * MAGNIFY;
232             }
233         }
234     }
235     dpy_update(s->ds, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
236 }
237
238 static void ssd0323_invalidate_display(void * opaque)
239 {
240     ssd0323_state *s = (ssd0323_state *)opaque;
241     s->redraw = 1;
242 }
243
244 /* Command/data input.  */
245 static void ssd0323_cd(void *opaque, int n, int level)
246 {
247     ssd0323_state *s = (ssd0323_state *)opaque;
248     DPRINTF("%s mode\n", level ? "Data" : "Command");
249     s->mode = level ? SSD0323_DATA : SSD0323_CMD;
250 }
251
252 void *ssd0323_init(DisplayState *ds, qemu_irq *cmd_p)
253 {
254     ssd0323_state *s;
255     qemu_irq *cmd;
256
257     s = (ssd0323_state *)qemu_mallocz(sizeof(ssd0323_state));
258     s->ds = ds;
259     graphic_console_init(ds, ssd0323_update_display, ssd0323_invalidate_display,
260                          NULL, s);
261     dpy_resize(s->ds, 128 * MAGNIFY, 64 * MAGNIFY);
262     s->col_end = 63;
263     s->row_end = 79;
264
265     cmd = qemu_allocate_irqs(ssd0323_cd, s, 1);
266     *cmd_p = *cmd;
267
268     return s;
269 }