|
|
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: *
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 "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 {
45: i2c_slave i2c;
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:
60: static int ssd0303_recv(i2c_slave *i2c)
61: {
62: BADF("Reads not implemented\n");
63: return -1;
64: }
65:
66: static int ssd0303_send(i2c_slave *i2c, uint8_t data)
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) {
96: case 0x00 ... 0x0f: /* Set lower colum address. */
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;
126: case 0xa8: /* Set multipled ratio (Ignored). */
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:
176: static void ssd0303_event(i2c_slave *i2c, enum i2c_event event)
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.2 root 264: static void ssd0303_save(QEMUFile *f, void *opaque)
265: {
266: ssd0303_state *s = (ssd0303_state *)opaque;
267:
268: qemu_put_be32(f, s->row);
269: qemu_put_be32(f, s->col);
270: qemu_put_be32(f, s->start_line);
271: qemu_put_be32(f, s->mirror);
272: qemu_put_be32(f, s->flash);
273: qemu_put_be32(f, s->enabled);
274: qemu_put_be32(f, s->inverse);
275: qemu_put_be32(f, s->redraw);
276: qemu_put_be32(f, s->mode);
277: qemu_put_be32(f, s->cmd_state);
278: qemu_put_buffer(f, s->framebuffer, sizeof(s->framebuffer));
279:
280: i2c_slave_save(f, &s->i2c);
281: }
282:
283: static int ssd0303_load(QEMUFile *f, void *opaque, int version_id)
284: {
285: ssd0303_state *s = (ssd0303_state *)opaque;
286:
287: if (version_id != 1)
288: return -EINVAL;
289:
290: s->row = qemu_get_be32(f);
291: s->col = qemu_get_be32(f);
292: s->start_line = qemu_get_be32(f);
293: s->mirror = qemu_get_be32(f);
294: s->flash = qemu_get_be32(f);
295: s->enabled = qemu_get_be32(f);
296: s->inverse = qemu_get_be32(f);
297: s->redraw = qemu_get_be32(f);
298: s->mode = qemu_get_be32(f);
299: s->cmd_state = qemu_get_be32(f);
300: qemu_get_buffer(f, s->framebuffer, sizeof(s->framebuffer));
301:
302: i2c_slave_load(f, &s->i2c);
303:
304: return 0;
305: }
306:
1.1.1.3 ! root 307: static void ssd0303_init(i2c_slave *i2c)
1.1 root 308: {
1.1.1.3 ! root 309: ssd0303_state *s = FROM_I2C_SLAVE(ssd0303_state, i2c);
1.1 root 310:
1.1.1.2 root 311: s->ds = graphic_console_init(ssd0303_update_display,
312: ssd0303_invalidate_display,
313: NULL, NULL, s);
314: qemu_console_resize(s->ds, 96 * MAGNIFY, 16 * MAGNIFY);
315: register_savevm("ssd0303_oled", -1, 1, ssd0303_save, ssd0303_load, s);
1.1 root 316: }
1.1.1.3 ! root 317:
! 318: static I2CSlaveInfo ssd0303_info = {
! 319: .qdev.name = "ssd0303",
! 320: .qdev.size = sizeof(ssd0303_state),
! 321: .init = ssd0303_init,
! 322: .event = ssd0303_event,
! 323: .recv = ssd0303_recv,
! 324: .send = ssd0303_send
! 325: };
! 326:
! 327: static void ssd0303_register_devices(void)
! 328: {
! 329: i2c_register_slave(&ssd0303_info);
! 330: }
! 331:
! 332: device_init(ssd0303_register_devices)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.