xref: /openbmc/qemu/hw/display/ssd0303.c (revision e1fe50dc)
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 licensed 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/i2c/i2c.h"
14 #include "ui/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     I2CSlave i2c;
46     QemuConsole *con;
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(I2CSlave *i2c)
61 {
62     BADF("Reads not implemented\n");
63     return -1;
64 }
65 
66 static int ssd0303_send(I2CSlave *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 column 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 multiplied 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(I2CSlave *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     DisplaySurface *surface = qemu_console_surface(s->con);
195     uint8_t *dest;
196     uint8_t *src;
197     int x;
198     int y;
199     int line;
200     char *colors[2];
201     char colortab[MAGNIFY * 8];
202     int dest_width;
203     uint8_t mask;
204 
205     if (!s->redraw)
206         return;
207 
208     switch (surface_bits_per_pixel(surface)) {
209     case 0:
210         return;
211     case 15:
212         dest_width = 2;
213         break;
214     case 16:
215         dest_width = 2;
216         break;
217     case 24:
218         dest_width = 3;
219         break;
220     case 32:
221         dest_width = 4;
222         break;
223     default:
224         BADF("Bad color depth\n");
225         return;
226     }
227     dest_width *= MAGNIFY;
228     memset(colortab, 0xff, dest_width);
229     memset(colortab + dest_width, 0, dest_width);
230     if (s->flash) {
231         colors[0] = colortab;
232         colors[1] = colortab;
233     } else if (s->inverse) {
234         colors[0] = colortab;
235         colors[1] = colortab + dest_width;
236     } else {
237         colors[0] = colortab + dest_width;
238         colors[1] = colortab;
239     }
240     dest = surface_data(surface);
241     for (y = 0; y < 16; y++) {
242         line = (y + s->start_line) & 63;
243         src = s->framebuffer + 132 * (line >> 3) + 36;
244         mask = 1 << (line & 7);
245         for (x = 0; x < 96; x++) {
246             memcpy(dest, colors[(*src & mask) != 0], dest_width);
247             dest += dest_width;
248             src++;
249         }
250         for (x = 1; x < MAGNIFY; x++) {
251             memcpy(dest, dest - dest_width * 96, dest_width * 96);
252             dest += dest_width * 96;
253         }
254     }
255     s->redraw = 0;
256     dpy_gfx_update(s->con, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
257 }
258 
259 static void ssd0303_invalidate_display(void * opaque)
260 {
261     ssd0303_state *s = (ssd0303_state *)opaque;
262     s->redraw = 1;
263 }
264 
265 static const VMStateDescription vmstate_ssd0303 = {
266     .name = "ssd0303_oled",
267     .version_id = 1,
268     .minimum_version_id = 1,
269     .minimum_version_id_old = 1,
270     .fields      = (VMStateField []) {
271         VMSTATE_INT32(row, ssd0303_state),
272         VMSTATE_INT32(col, ssd0303_state),
273         VMSTATE_INT32(start_line, ssd0303_state),
274         VMSTATE_INT32(mirror, ssd0303_state),
275         VMSTATE_INT32(flash, ssd0303_state),
276         VMSTATE_INT32(enabled, ssd0303_state),
277         VMSTATE_INT32(inverse, ssd0303_state),
278         VMSTATE_INT32(redraw, ssd0303_state),
279         VMSTATE_UINT32(mode, ssd0303_state),
280         VMSTATE_UINT32(cmd_state, ssd0303_state),
281         VMSTATE_BUFFER(framebuffer, ssd0303_state),
282         VMSTATE_I2C_SLAVE(i2c, ssd0303_state),
283         VMSTATE_END_OF_LIST()
284     }
285 };
286 
287 static const GraphicHwOps ssd0303_ops = {
288     .invalidate  = ssd0303_invalidate_display,
289     .gfx_update  = ssd0303_update_display,
290 };
291 
292 static int ssd0303_init(I2CSlave *i2c)
293 {
294     ssd0303_state *s = FROM_I2C_SLAVE(ssd0303_state, i2c);
295 
296     s->con = graphic_console_init(&ssd0303_ops, s);
297     qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY);
298     return 0;
299 }
300 
301 static void ssd0303_class_init(ObjectClass *klass, void *data)
302 {
303     DeviceClass *dc = DEVICE_CLASS(klass);
304     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
305 
306     k->init = ssd0303_init;
307     k->event = ssd0303_event;
308     k->recv = ssd0303_recv;
309     k->send = ssd0303_send;
310     dc->vmsd = &vmstate_ssd0303;
311 }
312 
313 static const TypeInfo ssd0303_info = {
314     .name          = "ssd0303",
315     .parent        = TYPE_I2C_SLAVE,
316     .instance_size = sizeof(ssd0303_state),
317     .class_init    = ssd0303_class_init,
318 };
319 
320 static void ssd0303_register_types(void)
321 {
322     type_register_static(&ssd0303_info);
323 }
324 
325 type_init(ssd0303_register_types)
326