hw/misc/a9scu: Report unimplemented accesses with qemu_log_mask(UNIMP)
[qemu/ar7.git] / hw / display / ssd0323.c
blob037da81127594364e87902f2d765af2e99364495
1 /*
2 * SSD0323 OLED controller with OSRAM Pictiva 128x64 display.
4 * Copyright (c) 2006-2007 CodeSourcery.
5 * Written by Paul Brook
7 * This code is licensed under the GPL.
8 */
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. */
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"
21 //#define DEBUG_SSD0323 1
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
36 /* Scaling factor for pixels. */
37 #define MAGNIFY 4
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
45 enum ssd0323_mode
47 SSD0323_CMD,
48 SSD0323_DATA
51 struct ssd0323_state {
52 SSISlave ssidev;
53 QemuConsole *con;
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];
69 typedef struct ssd0323_state ssd0323_state;
71 #define TYPE_SSD0323 "ssd0323"
72 DECLARE_INSTANCE_CHECKER(ssd0323_state, SSD0323,
73 TYPE_SSD0323)
76 static uint32_t ssd0323_transfer(SSISlave *dev, uint32_t data)
78 ssd0323_state *s = SSD0323(dev);
80 switch (s->mode) {
81 case SSD0323_DATA:
82 DPRINTF("data 0x%02x\n", data);
83 s->framebuffer[s->col + s->row * 64] = data;
84 if (s->remap & REMAP_VERTICAL) {
85 s->row++;
86 if (s->row > s->row_end) {
87 s->row = s->row_start;
88 s->col++;
90 if (s->col > s->col_end) {
91 s->col = s->col_start;
93 } else {
94 s->col++;
95 if (s->col > s->col_end) {
96 s->row++;
97 s->col = s->col_start;
99 if (s->row > s->row_end) {
100 s->row = s->row_start;
103 s->redraw = 1;
104 break;
105 case SSD0323_CMD:
106 DPRINTF("cmd 0x%02x\n", data);
107 if (s->cmd_len == 0) {
108 s->cmd = data;
109 } else {
110 s->cmd_data[s->cmd_len - 1] = data;
112 s->cmd_len++;
113 switch (s->cmd) {
114 #define DATA(x) if (s->cmd_len <= (x)) return 0
115 case 0x15: /* Set column. */
116 DATA(2);
117 s->col = s->col_start = s->cmd_data[0] % 64;
118 s->col_end = s->cmd_data[1] % 64;
119 break;
120 case 0x75: /* Set row. */
121 DATA(2);
122 s->row = s->row_start = s->cmd_data[0] % 80;
123 s->row_end = s->cmd_data[1] % 80;
124 break;
125 case 0x81: /* Set contrast */
126 DATA(1);
127 break;
128 case 0x84: case 0x85: case 0x86: /* Max current. */
129 DATA(0);
130 break;
131 case 0xa0: /* Set remapping. */
132 /* FIXME: Implement this. */
133 DATA(1);
134 s->remap = s->cmd_data[0];
135 break;
136 case 0xa1: /* Set display start line. */
137 case 0xa2: /* Set display offset. */
138 /* FIXME: Implement these. */
139 DATA(1);
140 break;
141 case 0xa4: /* Normal mode. */
142 case 0xa5: /* All on. */
143 case 0xa6: /* All off. */
144 case 0xa7: /* Inverse. */
145 /* FIXME: Implement these. */
146 DATA(0);
147 break;
148 case 0xa8: /* Set multiplex ratio. */
149 case 0xad: /* Set DC-DC converter. */
150 DATA(1);
151 /* Ignored. Don't care. */
152 break;
153 case 0xae: /* Display off. */
154 case 0xaf: /* Display on. */
155 DATA(0);
156 /* TODO: Implement power control. */
157 break;
158 case 0xb1: /* Set phase length. */
159 case 0xb2: /* Set row period. */
160 case 0xb3: /* Set clock rate. */
161 case 0xbc: /* Set precharge. */
162 case 0xbe: /* Set VCOMH. */
163 case 0xbf: /* Set segment low. */
164 DATA(1);
165 /* Ignored. Don't care. */
166 break;
167 case 0xb8: /* Set grey scale table. */
168 /* FIXME: Implement this. */
169 DATA(8);
170 break;
171 case 0xe3: /* NOP. */
172 DATA(0);
173 break;
174 case 0xff: /* Nasty hack because we don't handle chip selects
175 properly. */
176 break;
177 default:
178 BADF("Unknown command: 0x%x\n", data);
180 s->cmd_len = 0;
181 return 0;
183 return 0;
186 static void ssd0323_update_display(void *opaque)
188 ssd0323_state *s = (ssd0323_state *)opaque;
189 DisplaySurface *surface = qemu_console_surface(s->con);
190 uint8_t *dest;
191 uint8_t *src;
192 int x;
193 int y;
194 int i;
195 int line;
196 char *colors[16];
197 char colortab[MAGNIFY * 64];
198 char *p;
199 int dest_width;
201 if (!s->redraw)
202 return;
204 switch (surface_bits_per_pixel(surface)) {
205 case 0:
206 return;
207 case 15:
208 dest_width = 2;
209 break;
210 case 16:
211 dest_width = 2;
212 break;
213 case 24:
214 dest_width = 3;
215 break;
216 case 32:
217 dest_width = 4;
218 break;
219 default:
220 BADF("Bad color depth\n");
221 return;
223 p = colortab;
224 for (i = 0; i < 16; i++) {
225 int n;
226 colors[i] = p;
227 switch (surface_bits_per_pixel(surface)) {
228 case 15:
229 n = i * 2 + (i >> 3);
230 p[0] = n | (n << 5);
231 p[1] = (n << 2) | (n >> 3);
232 break;
233 case 16:
234 n = i * 2 + (i >> 3);
235 p[0] = n | (n << 6) | ((n << 1) & 0x20);
236 p[1] = (n << 3) | (n >> 2);
237 break;
238 case 24:
239 case 32:
240 n = (i << 4) | i;
241 p[0] = p[1] = p[2] = n;
242 break;
243 default:
244 BADF("Bad color depth\n");
245 return;
247 p += dest_width;
249 /* TODO: Implement row/column remapping. */
250 dest = surface_data(surface);
251 for (y = 0; y < 64; y++) {
252 line = y;
253 src = s->framebuffer + 64 * line;
254 for (x = 0; x < 64; x++) {
255 int val;
256 val = *src >> 4;
257 for (i = 0; i < MAGNIFY; i++) {
258 memcpy(dest, colors[val], dest_width);
259 dest += dest_width;
261 val = *src & 0xf;
262 for (i = 0; i < MAGNIFY; i++) {
263 memcpy(dest, colors[val], dest_width);
264 dest += dest_width;
266 src++;
268 for (i = 1; i < MAGNIFY; i++) {
269 memcpy(dest, dest - dest_width * MAGNIFY * 128,
270 dest_width * 128 * MAGNIFY);
271 dest += dest_width * 128 * MAGNIFY;
274 s->redraw = 0;
275 dpy_gfx_update(s->con, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
278 static void ssd0323_invalidate_display(void * opaque)
280 ssd0323_state *s = (ssd0323_state *)opaque;
281 s->redraw = 1;
284 /* Command/data input. */
285 static void ssd0323_cd(void *opaque, int n, int level)
287 ssd0323_state *s = (ssd0323_state *)opaque;
288 DPRINTF("%s mode\n", level ? "Data" : "Command");
289 s->mode = level ? SSD0323_DATA : SSD0323_CMD;
292 static int ssd0323_post_load(void *opaque, int version_id)
294 ssd0323_state *s = (ssd0323_state *)opaque;
296 if (s->cmd_len > ARRAY_SIZE(s->cmd_data)) {
297 return -EINVAL;
299 if (s->row < 0 || s->row >= 80) {
300 return -EINVAL;
302 if (s->row_start < 0 || s->row_start >= 80) {
303 return -EINVAL;
305 if (s->row_end < 0 || s->row_end >= 80) {
306 return -EINVAL;
308 if (s->col < 0 || s->col >= 64) {
309 return -EINVAL;
311 if (s->col_start < 0 || s->col_start >= 64) {
312 return -EINVAL;
314 if (s->col_end < 0 || s->col_end >= 64) {
315 return -EINVAL;
317 if (s->mode != SSD0323_CMD && s->mode != SSD0323_DATA) {
318 return -EINVAL;
321 return 0;
324 static const VMStateDescription vmstate_ssd0323 = {
325 .name = "ssd0323_oled",
326 .version_id = 2,
327 .minimum_version_id = 2,
328 .post_load = ssd0323_post_load,
329 .fields = (VMStateField []) {
330 VMSTATE_UINT32(cmd_len, ssd0323_state),
331 VMSTATE_INT32(cmd, ssd0323_state),
332 VMSTATE_INT32_ARRAY(cmd_data, ssd0323_state, 8),
333 VMSTATE_INT32(row, ssd0323_state),
334 VMSTATE_INT32(row_start, ssd0323_state),
335 VMSTATE_INT32(row_end, ssd0323_state),
336 VMSTATE_INT32(col, ssd0323_state),
337 VMSTATE_INT32(col_start, ssd0323_state),
338 VMSTATE_INT32(col_end, ssd0323_state),
339 VMSTATE_INT32(redraw, ssd0323_state),
340 VMSTATE_INT32(remap, ssd0323_state),
341 VMSTATE_UINT32(mode, ssd0323_state),
342 VMSTATE_BUFFER(framebuffer, ssd0323_state),
343 VMSTATE_SSI_SLAVE(ssidev, ssd0323_state),
344 VMSTATE_END_OF_LIST()
348 static const GraphicHwOps ssd0323_ops = {
349 .invalidate = ssd0323_invalidate_display,
350 .gfx_update = ssd0323_update_display,
353 static void ssd0323_realize(SSISlave *d, Error **errp)
355 DeviceState *dev = DEVICE(d);
356 ssd0323_state *s = SSD0323(d);
358 s->col_end = 63;
359 s->row_end = 79;
360 s->con = graphic_console_init(dev, 0, &ssd0323_ops, s);
361 qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY);
363 qdev_init_gpio_in(dev, ssd0323_cd, 1);
366 static void ssd0323_class_init(ObjectClass *klass, void *data)
368 DeviceClass *dc = DEVICE_CLASS(klass);
369 SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
371 k->realize = ssd0323_realize;
372 k->transfer = ssd0323_transfer;
373 k->cs_polarity = SSI_CS_HIGH;
374 dc->vmsd = &vmstate_ssd0323;
377 static const TypeInfo ssd0323_info = {
378 .name = TYPE_SSD0323,
379 .parent = TYPE_SSI_SLAVE,
380 .instance_size = sizeof(ssd0323_state),
381 .class_init = ssd0323_class_init,
384 static void ssd03232_register_types(void)
386 type_register_static(&ssd0323_info);
389 type_init(ssd03232_register_types)