qdev: move name+size into DeviceInfo (v2)
[qemu] / hw / ssd0303.c
1 /*
2  * SSD0303 OLED controller with OSRAM Pictiva 96x16 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 "i2c.h"
14 #include "console.h"
15
16 //#define DEBUG_SSD0303 1
17
18 #ifdef DEBUG_SSD0303
19 #define DPRINTF(fmt, ...) \
20 do { printf("ssd0303: " fmt , ## __VA_ARGS__); } while (0)
21 #define BADF(fmt, ...) \
22 do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
23 #else
24 #define DPRINTF(fmt, ...) do {} while(0)
25 #define BADF(fmt, ...) \
26 do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__);} while (0)
27 #endif
28
29 /* Scaling factor for pixels.  */
30 #define MAGNIFY 4
31
32 enum ssd0303_mode
33 {
34     SSD0303_IDLE,
35     SSD0303_DATA,
36     SSD0303_CMD
37 };
38
39 enum ssd0303_cmd {
40     SSD0303_CMD_NONE,
41     SSD0303_CMD_SKIP1
42 };
43
44 typedef struct {
45     i2c_slave i2c;
46     DisplayState *ds;
47     int row;
48     int col;
49     int start_line;
50     int mirror;
51     int flash;
52     int enabled;
53     int inverse;
54     int redraw;
55     enum ssd0303_mode mode;
56     enum ssd0303_cmd cmd_state;
57     uint8_t framebuffer[132*8];
58 } ssd0303_state;
59
60 static int ssd0303_recv(i2c_slave *i2c)
61 {
62     BADF("Reads not implemented\n");
63     return -1;
64 }
65
66 static int ssd0303_send(i2c_slave *i2c, uint8_t data)
67 {
68     ssd0303_state *s = (ssd0303_state *)i2c;
69     enum ssd0303_cmd old_cmd_state;
70     switch (s->mode) {
71     case SSD0303_IDLE:
72         DPRINTF("byte 0x%02x\n", data);
73         if (data == 0x80)
74             s->mode = SSD0303_CMD;
75         else if (data == 0x40)
76             s->mode = SSD0303_DATA;
77         else
78             BADF("Unexpected byte 0x%x\n", data);
79         break;
80     case SSD0303_DATA:
81         DPRINTF("data 0x%02x\n", data);
82         if (s->col < 132) {
83             s->framebuffer[s->col + s->row * 132] = data;
84             s->col++;
85             s->redraw = 1;
86         }
87         break;
88     case SSD0303_CMD:
89         old_cmd_state = s->cmd_state;
90         s->cmd_state = SSD0303_CMD_NONE;
91         switch (old_cmd_state) {
92         case SSD0303_CMD_NONE:
93             DPRINTF("cmd 0x%02x\n", data);
94             s->mode = SSD0303_IDLE;
95             switch (data) {
96             case 0x00 ... 0x0f: /* Set lower colum address.  */
97                 s->col = (s->col & 0xf0) | (data & 0xf);
98                 break;
99             case 0x10 ... 0x20: /* Set higher column address.  */
100                 s->col = (s->col & 0x0f) | ((data & 0xf) << 4);
101                 break;
102             case 0x40 ... 0x7f: /* Set start line.  */
103                 s->start_line = 0;
104                 break;
105             case 0x81: /* Set contrast (Ignored).  */
106                 s->cmd_state = SSD0303_CMD_SKIP1;
107                 break;
108             case 0xa0: /* Mirror off.  */
109                 s->mirror = 0;
110                 break;
111             case 0xa1: /* Mirror off.  */
112                 s->mirror = 1;
113                 break;
114             case 0xa4: /* Entire display off.  */
115                 s->flash = 0;
116                 break;
117             case 0xa5: /* Entire display on.  */
118                 s->flash = 1;
119                 break;
120             case 0xa6: /* Inverse off.  */
121                 s->inverse = 0;
122                 break;
123             case 0xa7: /* Inverse on.  */
124                 s->inverse = 1;
125                 break;
126             case 0xa8: /* Set multipled ratio (Ignored).  */
127                 s->cmd_state = SSD0303_CMD_SKIP1;
128                 break;
129             case 0xad: /* DC-DC power control.  */
130                 s->cmd_state = SSD0303_CMD_SKIP1;
131                 break;
132             case 0xae: /* Display off.  */
133                 s->enabled = 0;
134                 break;
135             case 0xaf: /* Display on.  */
136                 s->enabled = 1;
137                 break;
138             case 0xb0 ... 0xbf: /* Set Page address.  */
139                 s->row = data & 7;
140                 break;
141             case 0xc0 ... 0xc8: /* Set COM output direction (Ignored).  */
142                 break;
143             case 0xd3: /* Set display offset (Ignored).  */
144                 s->cmd_state = SSD0303_CMD_SKIP1;
145                 break;
146             case 0xd5: /* Set display clock (Ignored).  */
147                 s->cmd_state = SSD0303_CMD_SKIP1;
148                 break;
149             case 0xd8: /* Set color and power mode (Ignored).  */
150                 s->cmd_state = SSD0303_CMD_SKIP1;
151                 break;
152             case 0xd9: /* Set pre-charge period (Ignored).  */
153                 s->cmd_state = SSD0303_CMD_SKIP1;
154                 break;
155             case 0xda: /* Set COM pin configuration (Ignored).  */
156                 s->cmd_state = SSD0303_CMD_SKIP1;
157                 break;
158             case 0xdb: /* Set VCOM dselect level (Ignored).  */
159                 s->cmd_state = SSD0303_CMD_SKIP1;
160                 break;
161             case 0xe3: /* no-op.  */
162                 break;
163             default:
164                 BADF("Unknown command: 0x%x\n", data);
165             }
166             break;
167         case SSD0303_CMD_SKIP1:
168             DPRINTF("skip 0x%02x\n", data);
169             break;
170         }
171         break;
172     }
173     return 0;
174 }
175
176 static void ssd0303_event(i2c_slave *i2c, enum i2c_event event)
177 {
178     ssd0303_state *s = (ssd0303_state *)i2c;
179     switch (event) {
180     case I2C_FINISH:
181         s->mode = SSD0303_IDLE;
182         break;
183     case I2C_START_RECV:
184     case I2C_START_SEND:
185     case I2C_NACK:
186         /* Nothing to do.  */
187         break;
188     }
189 }
190
191 static void ssd0303_update_display(void *opaque)
192 {
193     ssd0303_state *s = (ssd0303_state *)opaque;
194     uint8_t *dest;
195     uint8_t *src;
196     int x;
197     int y;
198     int line;
199     char *colors[2];
200     char colortab[MAGNIFY * 8];
201     int dest_width;
202     uint8_t mask;
203
204     if (!s->redraw)
205         return;
206
207     switch (ds_get_bits_per_pixel(s->ds)) {
208     case 0:
209         return;
210     case 15:
211         dest_width = 2;
212         break;
213     case 16:
214         dest_width = 2;
215         break;
216     case 24:
217         dest_width = 3;
218         break;
219     case 32:
220         dest_width = 4;
221         break;
222     default:
223         BADF("Bad color depth\n");
224         return;
225     }
226     dest_width *= MAGNIFY;
227     memset(colortab, 0xff, dest_width);
228     memset(colortab + dest_width, 0, dest_width);
229     if (s->flash) {
230         colors[0] = colortab;
231         colors[1] = colortab;
232     } else if (s->inverse) {
233         colors[0] = colortab;
234         colors[1] = colortab + dest_width;
235     } else {
236         colors[0] = colortab + dest_width;
237         colors[1] = colortab;
238     }
239     dest = ds_get_data(s->ds);
240     for (y = 0; y < 16; y++) {
241         line = (y + s->start_line) & 63;
242         src = s->framebuffer + 132 * (line >> 3) + 36;
243         mask = 1 << (line & 7);
244         for (x = 0; x < 96; x++) {
245             memcpy(dest, colors[(*src & mask) != 0], dest_width);
246             dest += dest_width;
247             src++;
248         }
249         for (x = 1; x < MAGNIFY; x++) {
250             memcpy(dest, dest - dest_width * 96, dest_width * 96);
251             dest += dest_width * 96;
252         }
253     }
254     s->redraw = 0;
255     dpy_update(s->ds, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
256 }
257
258 static void ssd0303_invalidate_display(void * opaque)
259 {
260     ssd0303_state *s = (ssd0303_state *)opaque;
261     s->redraw = 1;
262 }
263
264 static void ssd0303_save(QEMUFile *f, void *opaque)
265 {
266     ssd0303_state *s = (ssd0303_state *)opaque;
267
268     qemu_put_be32(f, s->row);
269     qemu_put_be32(f, s->col);
270     qemu_put_be32(f, s->start_line);
271     qemu_put_be32(f, s->mirror);
272     qemu_put_be32(f, s->flash);
273     qemu_put_be32(f, s->enabled);
274     qemu_put_be32(f, s->inverse);
275     qemu_put_be32(f, s->redraw);
276     qemu_put_be32(f, s->mode);
277     qemu_put_be32(f, s->cmd_state);
278     qemu_put_buffer(f, s->framebuffer, sizeof(s->framebuffer));
279
280     i2c_slave_save(f, &s->i2c);
281 }
282
283 static int ssd0303_load(QEMUFile *f, void *opaque, int version_id)
284 {
285     ssd0303_state *s = (ssd0303_state *)opaque;
286
287     if (version_id != 1)
288         return -EINVAL;
289
290     s->row = qemu_get_be32(f);
291     s->col = qemu_get_be32(f);
292     s->start_line = qemu_get_be32(f);
293     s->mirror = qemu_get_be32(f);
294     s->flash = qemu_get_be32(f);
295     s->enabled = qemu_get_be32(f);
296     s->inverse = qemu_get_be32(f);
297     s->redraw = qemu_get_be32(f);
298     s->mode = qemu_get_be32(f);
299     s->cmd_state = qemu_get_be32(f);
300     qemu_get_buffer(f, s->framebuffer, sizeof(s->framebuffer));
301
302     i2c_slave_load(f, &s->i2c);
303
304     return 0;
305 }
306
307 static void ssd0303_init(i2c_slave *i2c)
308 {
309     ssd0303_state *s = FROM_I2C_SLAVE(ssd0303_state, i2c);
310
311     s->ds = graphic_console_init(ssd0303_update_display,
312                                  ssd0303_invalidate_display,
313                                  NULL, NULL, s);
314     qemu_console_resize(s->ds, 96 * MAGNIFY, 16 * MAGNIFY);
315     register_savevm("ssd0303_oled", -1, 1, ssd0303_save, ssd0303_load, s);
316 }
317
318 static I2CSlaveInfo ssd0303_info = {
319     .qdev.name = "ssd0303",
320     .qdev.size = sizeof(ssd0303_state),
321     .init = ssd0303_init,
322     .event = ssd0303_event,
323     .recv = ssd0303_recv,
324     .send = ssd0303_send
325 };
326
327 static void ssd0303_register_devices(void)
328 {
329     i2c_register_slave(&ssd0303_info);
330 }
331
332 device_init(ssd0303_register_devices)