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