Annotation of qemu/hw/ssd0323.c, revision 1.1.1.2
1.1 root 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 licenced 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: #include "hw.h"
14: #include "devices.h"
15: #include "console.h"
16:
17: //#define DEBUG_SSD0323 1
18:
19: #ifdef DEBUG_SSD0323
20: #define DPRINTF(fmt, args...) \
21: do { printf("ssd0323: " fmt , ##args); } while (0)
22: #define BADF(fmt, args...) \
23: do { fprintf(stderr, "ssd0323: error: " fmt , ##args); exit(1);} while (0)
24: #else
25: #define DPRINTF(fmt, args...) do {} while(0)
26: #define BADF(fmt, args...) \
27: do { fprintf(stderr, "ssd0323: error: " fmt , ##args);} while (0)
28: #endif
29:
30: /* Scaling factor for pixels. */
31: #define MAGNIFY 4
32:
33: #define REMAP_SWAP_COLUMN 0x01
34: #define REMAP_SWAP_NYBBLE 0x02
35: #define REMAP_VERTICAL 0x04
36: #define REMAP_SWAP_COM 0x10
37: #define REMAP_SPLIT_COM 0x40
38:
39: enum ssd0323_mode
40: {
41: SSD0323_CMD,
42: SSD0323_DATA
43: };
44:
45: typedef struct {
46: DisplayState *ds;
47:
48: int cmd_len;
49: int cmd;
50: int cmd_data[8];
51: int row;
52: int row_start;
53: int row_end;
54: int col;
55: int col_start;
56: int col_end;
57: int redraw;
58: int remap;
59: enum ssd0323_mode mode;
60: uint8_t framebuffer[128 * 80 / 2];
61: } ssd0323_state;
62:
63: int ssd0323_xfer_ssi(void *opaque, int data)
64: {
65: ssd0323_state *s = (ssd0323_state *)opaque;
66: switch (s->mode) {
67: case SSD0323_DATA:
68: DPRINTF("data 0x%02x\n", data);
69: s->framebuffer[s->col + s->row * 64] = data;
70: if (s->remap & REMAP_VERTICAL) {
71: s->row++;
72: if (s->row > s->row_end) {
73: s->row = s->row_start;
74: s->col++;
75: }
76: if (s->col > s->col_end) {
77: s->col = s->col_start;
78: }
79: } else {
80: s->col++;
81: if (s->col > s->col_end) {
82: s->row++;
83: s->col = s->col_start;
84: }
85: if (s->row > s->row_end) {
86: s->row = s->row_start;
87: }
88: }
89: s->redraw = 1;
90: break;
91: case SSD0323_CMD:
92: DPRINTF("cmd 0x%02x\n", data);
93: if (s->cmd_len == 0) {
94: s->cmd = data;
95: } else {
96: s->cmd_data[s->cmd_len - 1] = data;
97: }
98: s->cmd_len++;
99: switch (s->cmd) {
100: #define DATA(x) if (s->cmd_len <= (x)) return 0
101: case 0x15: /* Set column. */
102: DATA(2);
103: s->col = s->col_start = s->cmd_data[0] % 64;
104: s->col_end = s->cmd_data[1] % 64;
105: break;
106: case 0x75: /* Set row. */
107: DATA(2);
108: s->row = s->row_start = s->cmd_data[0] % 80;
109: s->row_end = s->cmd_data[1] % 80;
110: break;
111: case 0x81: /* Set contrast */
112: DATA(1);
113: break;
114: case 0x84: case 0x85: case 0x86: /* Max current. */
115: DATA(0);
116: break;
117: case 0xa0: /* Set remapping. */
118: /* FIXME: Implement this. */
119: DATA(1);
120: s->remap = s->cmd_data[0];
121: break;
122: case 0xa1: /* Set display start line. */
123: case 0xa2: /* Set display offset. */
124: /* FIXME: Implement these. */
125: DATA(1);
126: break;
127: case 0xa4: /* Normal mode. */
128: case 0xa5: /* All on. */
129: case 0xa6: /* All off. */
130: case 0xa7: /* Inverse. */
131: /* FIXME: Implement these. */
132: DATA(0);
133: break;
134: case 0xa8: /* Set multiplex ratio. */
135: case 0xad: /* Set DC-DC converter. */
136: DATA(1);
137: /* Ignored. Don't care. */
138: break;
139: case 0xae: /* Display off. */
140: case 0xaf: /* Display on. */
141: DATA(0);
142: /* TODO: Implement power control. */
143: break;
144: case 0xb1: /* Set phase length. */
145: case 0xb2: /* Set row period. */
146: case 0xb3: /* Set clock rate. */
147: case 0xbc: /* Set precharge. */
148: case 0xbe: /* Set VCOMH. */
149: case 0xbf: /* Set segment low. */
150: DATA(1);
151: /* Ignored. Don't care. */
152: break;
153: case 0xb8: /* Set grey scale table. */
154: /* FIXME: Implement this. */
155: DATA(8);
156: break;
157: case 0xe3: /* NOP. */
158: DATA(0);
159: break;
160: case 0xff: /* Nasty hack because we don't handle chip selects
161: properly. */
162: break;
163: default:
164: BADF("Unknown command: 0x%x\n", data);
165: }
166: s->cmd_len = 0;
167: return 0;
168: }
169: return 0;
170: }
171:
172: static void ssd0323_update_display(void *opaque)
173: {
174: ssd0323_state *s = (ssd0323_state *)opaque;
175: uint8_t *dest;
176: uint8_t *src;
177: int x;
178: int y;
179: int i;
180: int line;
181: char *colors[16];
182: char colortab[MAGNIFY * 64];
183: char *p;
184: int dest_width;
185:
1.1.1.2 ! root 186: if (!s->redraw)
! 187: return;
! 188:
! 189: switch (ds_get_bits_per_pixel(s->ds)) {
! 190: case 0:
! 191: return;
! 192: case 15:
! 193: dest_width = 2;
! 194: break;
! 195: case 16:
! 196: dest_width = 2;
! 197: break;
! 198: case 24:
! 199: dest_width = 3;
! 200: break;
! 201: case 32:
! 202: dest_width = 4;
! 203: break;
! 204: default:
! 205: BADF("Bad color depth\n");
! 206: return;
! 207: }
! 208: p = colortab;
! 209: for (i = 0; i < 16; i++) {
! 210: int n;
! 211: colors[i] = p;
! 212: switch (ds_get_bits_per_pixel(s->ds)) {
1.1 root 213: case 15:
1.1.1.2 ! root 214: n = i * 2 + (i >> 3);
! 215: p[0] = n | (n << 5);
! 216: p[1] = (n << 2) | (n >> 3);
1.1 root 217: break;
218: case 16:
1.1.1.2 ! root 219: n = i * 2 + (i >> 3);
! 220: p[0] = n | (n << 6) | ((n << 1) & 0x20);
! 221: p[1] = (n << 3) | (n >> 2);
1.1 root 222: break;
223: case 24:
224: case 32:
1.1.1.2 ! root 225: n = (i << 4) | i;
! 226: p[0] = p[1] = p[2] = n;
1.1 root 227: break;
228: default:
229: BADF("Bad color depth\n");
230: return;
231: }
1.1.1.2 ! root 232: p += dest_width;
! 233: }
! 234: /* TODO: Implement row/column remapping. */
! 235: dest = ds_get_data(s->ds);
! 236: for (y = 0; y < 64; y++) {
! 237: line = y;
! 238: src = s->framebuffer + 64 * line;
! 239: for (x = 0; x < 64; x++) {
! 240: int val;
! 241: val = *src >> 4;
! 242: for (i = 0; i < MAGNIFY; i++) {
! 243: memcpy(dest, colors[val], dest_width);
! 244: dest += dest_width;
1.1 root 245: }
1.1.1.2 ! root 246: val = *src & 0xf;
! 247: for (i = 0; i < MAGNIFY; i++) {
! 248: memcpy(dest, colors[val], dest_width);
! 249: dest += dest_width;
1.1 root 250: }
1.1.1.2 ! root 251: src++;
! 252: }
! 253: for (i = 1; i < MAGNIFY; i++) {
! 254: memcpy(dest, dest - dest_width * MAGNIFY * 128,
! 255: dest_width * 128 * MAGNIFY);
! 256: dest += dest_width * 128 * MAGNIFY;
1.1 root 257: }
258: }
1.1.1.2 ! root 259: s->redraw = 0;
1.1 root 260: dpy_update(s->ds, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
261: }
262:
263: static void ssd0323_invalidate_display(void * opaque)
264: {
265: ssd0323_state *s = (ssd0323_state *)opaque;
266: s->redraw = 1;
267: }
268:
269: /* Command/data input. */
270: static void ssd0323_cd(void *opaque, int n, int level)
271: {
272: ssd0323_state *s = (ssd0323_state *)opaque;
273: DPRINTF("%s mode\n", level ? "Data" : "Command");
274: s->mode = level ? SSD0323_DATA : SSD0323_CMD;
275: }
276:
1.1.1.2 ! root 277: static void ssd0323_save(QEMUFile *f, void *opaque)
! 278: {
! 279: ssd0323_state *s = (ssd0323_state *)opaque;
! 280: int i;
! 281:
! 282: qemu_put_be32(f, s->cmd_len);
! 283: qemu_put_be32(f, s->cmd);
! 284: for (i = 0; i < 8; i++)
! 285: qemu_put_be32(f, s->cmd_data[i]);
! 286: qemu_put_be32(f, s->row);
! 287: qemu_put_be32(f, s->row_start);
! 288: qemu_put_be32(f, s->row_end);
! 289: qemu_put_be32(f, s->col);
! 290: qemu_put_be32(f, s->col_start);
! 291: qemu_put_be32(f, s->col_end);
! 292: qemu_put_be32(f, s->redraw);
! 293: qemu_put_be32(f, s->remap);
! 294: qemu_put_be32(f, s->mode);
! 295: qemu_put_buffer(f, s->framebuffer, sizeof(s->framebuffer));
! 296: }
! 297:
! 298: static int ssd0323_load(QEMUFile *f, void *opaque, int version_id)
! 299: {
! 300: ssd0323_state *s = (ssd0323_state *)opaque;
! 301: int i;
! 302:
! 303: if (version_id != 1)
! 304: return -EINVAL;
! 305:
! 306: s->cmd_len = qemu_get_be32(f);
! 307: s->cmd = qemu_get_be32(f);
! 308: for (i = 0; i < 8; i++)
! 309: s->cmd_data[i] = qemu_get_be32(f);
! 310: s->row = qemu_get_be32(f);
! 311: s->row_start = qemu_get_be32(f);
! 312: s->row_end = qemu_get_be32(f);
! 313: s->col = qemu_get_be32(f);
! 314: s->col_start = qemu_get_be32(f);
! 315: s->col_end = qemu_get_be32(f);
! 316: s->redraw = qemu_get_be32(f);
! 317: s->remap = qemu_get_be32(f);
! 318: s->mode = qemu_get_be32(f);
! 319: qemu_get_buffer(f, s->framebuffer, sizeof(s->framebuffer));
! 320:
! 321: return 0;
! 322: }
! 323:
! 324: void *ssd0323_init(qemu_irq *cmd_p)
1.1 root 325: {
326: ssd0323_state *s;
327: qemu_irq *cmd;
328:
329: s = (ssd0323_state *)qemu_mallocz(sizeof(ssd0323_state));
330: s->col_end = 63;
331: s->row_end = 79;
1.1.1.2 ! root 332: s->ds = graphic_console_init(ssd0323_update_display,
! 333: ssd0323_invalidate_display,
! 334: NULL, NULL, s);
! 335: qemu_console_resize(s->ds, 128 * MAGNIFY, 64 * MAGNIFY);
1.1 root 336:
337: cmd = qemu_allocate_irqs(ssd0323_cd, s, 1);
338: *cmd_p = *cmd;
339:
1.1.1.2 ! root 340: register_savevm("ssd0323_oled", -1, 1, ssd0323_save, ssd0323_load, s);
! 341:
1.1 root 342: return s;
343: }
unix.superglobalmegacorp.com