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