xref: /qemu/hw/display/ssd0323.c (revision db1015e92e04835c9eb50c29625fe566d1202dbd)
1 /*
2  * SSD0323 OLED controller with OSRAM Pictiva 128x64 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/ssi/ssi.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_SSD0323 1
22 
23 #ifdef DEBUG_SSD0323
24 #define DPRINTF(fmt, ...) \
25 do { printf("ssd0323: " fmt , ## __VA_ARGS__); } while (0)
26 #define BADF(fmt, ...) \
27 do { \
28     fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); abort(); \
29 } while (0)
30 #else
31 #define DPRINTF(fmt, ...) do {} while(0)
32 #define BADF(fmt, ...) \
33 do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__);} while (0)
34 #endif
35 
36 /* Scaling factor for pixels.  */
37 #define MAGNIFY 4
38 
39 #define REMAP_SWAP_COLUMN 0x01
40 #define REMAP_SWAP_NYBBLE 0x02
41 #define REMAP_VERTICAL    0x04
42 #define REMAP_SWAP_COM    0x10
43 #define REMAP_SPLIT_COM   0x40
44 
45 enum ssd0323_mode
46 {
47     SSD0323_CMD,
48     SSD0323_DATA
49 };
50 
51 struct ssd0323_state {
52     SSISlave ssidev;
53     QemuConsole *con;
54 
55     uint32_t cmd_len;
56     int32_t cmd;
57     int32_t cmd_data[8];
58     int32_t row;
59     int32_t row_start;
60     int32_t row_end;
61     int32_t col;
62     int32_t col_start;
63     int32_t col_end;
64     int32_t redraw;
65     int32_t remap;
66     uint32_t mode;
67     uint8_t framebuffer[128 * 80 / 2];
68 };
69 typedef struct ssd0323_state ssd0323_state;
70 
71 #define TYPE_SSD0323 "ssd0323"
72 #define SSD0323(obj) OBJECT_CHECK(ssd0323_state, (obj), TYPE_SSD0323)
73 
74 
75 static uint32_t ssd0323_transfer(SSISlave *dev, uint32_t data)
76 {
77     ssd0323_state *s = SSD0323(dev);
78 
79     switch (s->mode) {
80     case SSD0323_DATA:
81         DPRINTF("data 0x%02x\n", data);
82         s->framebuffer[s->col + s->row * 64] = data;
83         if (s->remap & REMAP_VERTICAL) {
84             s->row++;
85             if (s->row > s->row_end) {
86                 s->row = s->row_start;
87                 s->col++;
88             }
89             if (s->col > s->col_end) {
90                 s->col = s->col_start;
91             }
92         } else {
93             s->col++;
94             if (s->col > s->col_end) {
95                 s->row++;
96                 s->col = s->col_start;
97             }
98             if (s->row > s->row_end) {
99                 s->row = s->row_start;
100             }
101         }
102         s->redraw = 1;
103         break;
104     case SSD0323_CMD:
105         DPRINTF("cmd 0x%02x\n", data);
106         if (s->cmd_len == 0) {
107             s->cmd = data;
108         } else {
109             s->cmd_data[s->cmd_len - 1] = data;
110         }
111         s->cmd_len++;
112         switch (s->cmd) {
113 #define DATA(x) if (s->cmd_len <= (x)) return 0
114         case 0x15: /* Set column.  */
115             DATA(2);
116             s->col = s->col_start = s->cmd_data[0] % 64;
117             s->col_end = s->cmd_data[1] % 64;
118             break;
119         case 0x75: /* Set row.  */
120             DATA(2);
121             s->row = s->row_start = s->cmd_data[0] % 80;
122             s->row_end = s->cmd_data[1] % 80;
123             break;
124         case 0x81: /* Set contrast */
125             DATA(1);
126             break;
127         case 0x84: case 0x85: case 0x86: /* Max current.  */
128             DATA(0);
129             break;
130         case 0xa0: /* Set remapping.  */
131             /* FIXME: Implement this.  */
132             DATA(1);
133             s->remap = s->cmd_data[0];
134             break;
135         case 0xa1: /* Set display start line.  */
136         case 0xa2: /* Set display offset.  */
137             /* FIXME: Implement these.  */
138             DATA(1);
139             break;
140         case 0xa4: /* Normal mode.  */
141         case 0xa5: /* All on.  */
142         case 0xa6: /* All off.  */
143         case 0xa7: /* Inverse.  */
144             /* FIXME: Implement these.  */
145             DATA(0);
146             break;
147         case 0xa8: /* Set multiplex ratio.  */
148         case 0xad: /* Set DC-DC converter.  */
149             DATA(1);
150             /* Ignored.  Don't care.  */
151             break;
152         case 0xae: /* Display off.  */
153         case 0xaf: /* Display on.  */
154             DATA(0);
155             /* TODO: Implement power control.  */
156             break;
157         case 0xb1: /* Set phase length.  */
158         case 0xb2: /* Set row period.  */
159         case 0xb3: /* Set clock rate.  */
160         case 0xbc: /* Set precharge.  */
161         case 0xbe: /* Set VCOMH.  */
162         case 0xbf: /* Set segment low.  */
163             DATA(1);
164             /* Ignored.  Don't care.  */
165             break;
166         case 0xb8: /* Set grey scale table.  */
167             /* FIXME: Implement this.  */
168             DATA(8);
169             break;
170         case 0xe3: /* NOP.  */
171             DATA(0);
172             break;
173         case 0xff: /* Nasty hack because we don't handle chip selects
174                       properly.  */
175             break;
176         default:
177             BADF("Unknown command: 0x%x\n", data);
178         }
179         s->cmd_len = 0;
180         return 0;
181     }
182     return 0;
183 }
184 
185 static void ssd0323_update_display(void *opaque)
186 {
187     ssd0323_state *s = (ssd0323_state *)opaque;
188     DisplaySurface *surface = qemu_console_surface(s->con);
189     uint8_t *dest;
190     uint8_t *src;
191     int x;
192     int y;
193     int i;
194     int line;
195     char *colors[16];
196     char colortab[MAGNIFY * 64];
197     char *p;
198     int dest_width;
199 
200     if (!s->redraw)
201         return;
202 
203     switch (surface_bits_per_pixel(surface)) {
204     case 0:
205         return;
206     case 15:
207         dest_width = 2;
208         break;
209     case 16:
210         dest_width = 2;
211         break;
212     case 24:
213         dest_width = 3;
214         break;
215     case 32:
216         dest_width = 4;
217         break;
218     default:
219         BADF("Bad color depth\n");
220         return;
221     }
222     p = colortab;
223     for (i = 0; i < 16; i++) {
224         int n;
225         colors[i] = p;
226         switch (surface_bits_per_pixel(surface)) {
227         case 15:
228             n = i * 2 + (i >> 3);
229             p[0] = n | (n << 5);
230             p[1] = (n << 2) | (n >> 3);
231             break;
232         case 16:
233             n = i * 2 + (i >> 3);
234             p[0] = n | (n << 6) | ((n << 1) & 0x20);
235             p[1] = (n << 3) | (n >> 2);
236             break;
237         case 24:
238         case 32:
239             n = (i << 4) | i;
240             p[0] = p[1] = p[2] = n;
241             break;
242         default:
243             BADF("Bad color depth\n");
244             return;
245         }
246         p += dest_width;
247     }
248     /* TODO: Implement row/column remapping.  */
249     dest = surface_data(surface);
250     for (y = 0; y < 64; y++) {
251         line = y;
252         src = s->framebuffer + 64 * line;
253         for (x = 0; x < 64; x++) {
254             int val;
255             val = *src >> 4;
256             for (i = 0; i < MAGNIFY; i++) {
257                 memcpy(dest, colors[val], dest_width);
258                 dest += dest_width;
259             }
260             val = *src & 0xf;
261             for (i = 0; i < MAGNIFY; i++) {
262                 memcpy(dest, colors[val], dest_width);
263                 dest += dest_width;
264             }
265             src++;
266         }
267         for (i = 1; i < MAGNIFY; i++) {
268             memcpy(dest, dest - dest_width * MAGNIFY * 128,
269                    dest_width * 128 * MAGNIFY);
270             dest += dest_width * 128 * MAGNIFY;
271         }
272     }
273     s->redraw = 0;
274     dpy_gfx_update(s->con, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
275 }
276 
277 static void ssd0323_invalidate_display(void * opaque)
278 {
279     ssd0323_state *s = (ssd0323_state *)opaque;
280     s->redraw = 1;
281 }
282 
283 /* Command/data input.  */
284 static void ssd0323_cd(void *opaque, int n, int level)
285 {
286     ssd0323_state *s = (ssd0323_state *)opaque;
287     DPRINTF("%s mode\n", level ? "Data" : "Command");
288     s->mode = level ? SSD0323_DATA : SSD0323_CMD;
289 }
290 
291 static int ssd0323_post_load(void *opaque, int version_id)
292 {
293     ssd0323_state *s = (ssd0323_state *)opaque;
294 
295     if (s->cmd_len > ARRAY_SIZE(s->cmd_data)) {
296         return -EINVAL;
297     }
298     if (s->row < 0 || s->row >= 80) {
299         return -EINVAL;
300     }
301     if (s->row_start < 0 || s->row_start >= 80) {
302         return -EINVAL;
303     }
304     if (s->row_end < 0 || s->row_end >= 80) {
305         return -EINVAL;
306     }
307     if (s->col < 0 || s->col >= 64) {
308         return -EINVAL;
309     }
310     if (s->col_start < 0 || s->col_start >= 64) {
311         return -EINVAL;
312     }
313     if (s->col_end < 0 || s->col_end >= 64) {
314         return -EINVAL;
315     }
316     if (s->mode != SSD0323_CMD && s->mode != SSD0323_DATA) {
317         return -EINVAL;
318     }
319 
320     return 0;
321 }
322 
323 static const VMStateDescription vmstate_ssd0323 = {
324     .name = "ssd0323_oled",
325     .version_id = 2,
326     .minimum_version_id = 2,
327     .post_load = ssd0323_post_load,
328     .fields      = (VMStateField []) {
329         VMSTATE_UINT32(cmd_len, ssd0323_state),
330         VMSTATE_INT32(cmd, ssd0323_state),
331         VMSTATE_INT32_ARRAY(cmd_data, ssd0323_state, 8),
332         VMSTATE_INT32(row, ssd0323_state),
333         VMSTATE_INT32(row_start, ssd0323_state),
334         VMSTATE_INT32(row_end, ssd0323_state),
335         VMSTATE_INT32(col, ssd0323_state),
336         VMSTATE_INT32(col_start, ssd0323_state),
337         VMSTATE_INT32(col_end, ssd0323_state),
338         VMSTATE_INT32(redraw, ssd0323_state),
339         VMSTATE_INT32(remap, ssd0323_state),
340         VMSTATE_UINT32(mode, ssd0323_state),
341         VMSTATE_BUFFER(framebuffer, ssd0323_state),
342         VMSTATE_SSI_SLAVE(ssidev, ssd0323_state),
343         VMSTATE_END_OF_LIST()
344     }
345 };
346 
347 static const GraphicHwOps ssd0323_ops = {
348     .invalidate  = ssd0323_invalidate_display,
349     .gfx_update  = ssd0323_update_display,
350 };
351 
352 static void ssd0323_realize(SSISlave *d, Error **errp)
353 {
354     DeviceState *dev = DEVICE(d);
355     ssd0323_state *s = SSD0323(d);
356 
357     s->col_end = 63;
358     s->row_end = 79;
359     s->con = graphic_console_init(dev, 0, &ssd0323_ops, s);
360     qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY);
361 
362     qdev_init_gpio_in(dev, ssd0323_cd, 1);
363 }
364 
365 static void ssd0323_class_init(ObjectClass *klass, void *data)
366 {
367     DeviceClass *dc = DEVICE_CLASS(klass);
368     SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
369 
370     k->realize = ssd0323_realize;
371     k->transfer = ssd0323_transfer;
372     k->cs_polarity = SSI_CS_HIGH;
373     dc->vmsd = &vmstate_ssd0323;
374 }
375 
376 static const TypeInfo ssd0323_info = {
377     .name          = TYPE_SSD0323,
378     .parent        = TYPE_SSI_SLAVE,
379     .instance_size = sizeof(ssd0323_state),
380     .class_init    = ssd0323_class_init,
381 };
382 
383 static void ssd03232_register_types(void)
384 {
385     type_register_static(&ssd0323_info);
386 }
387 
388 type_init(ssd03232_register_types)
389