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