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