]>
Commit | Line | Data |
---|---|---|
9ee6e8bb PB |
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 "vl.h" | |
14 | ||
15 | //#define DEBUG_SSD0303 1 | |
16 | ||
17 | #ifdef DEBUG_SSD0303 | |
18 | #define DPRINTF(fmt, args...) \ | |
19 | do { printf("ssd0303: " fmt , ##args); } while (0) | |
20 | #define BADF(fmt, args...) \ | |
21 | do { fprintf(stderr, "ssd0303: error: " fmt , ##args); exit(1);} while (0) | |
22 | #else | |
23 | #define DPRINTF(fmt, args...) do {} while(0) | |
24 | #define BADF(fmt, args...) \ | |
25 | do { fprintf(stderr, "ssd0303: error: " fmt , ##args);} while (0) | |
26 | #endif | |
27 | ||
28 | /* Scaling factor for pixels. */ | |
29 | #define MAGNIFY 4 | |
30 | ||
31 | enum ssd0303_mode | |
32 | { | |
33 | SSD0303_IDLE, | |
34 | SSD0303_DATA, | |
35 | SSD0303_CMD | |
36 | }; | |
37 | ||
38 | enum ssd0303_cmd { | |
39 | SSD0303_CMD_NONE, | |
40 | SSD0303_CMD_SKIP1 | |
41 | }; | |
42 | ||
43 | typedef struct { | |
44 | i2c_slave i2c; | |
45 | DisplayState *ds; | |
46 | int row; | |
47 | int col; | |
48 | int start_line; | |
49 | int mirror; | |
50 | int flash; | |
51 | int enabled; | |
52 | int inverse; | |
53 | int redraw; | |
54 | enum ssd0303_mode mode; | |
55 | enum ssd0303_cmd cmd_state; | |
56 | uint8_t framebuffer[132*8]; | |
57 | } ssd0303_state; | |
58 | ||
59 | static int ssd0303_recv(i2c_slave *i2c) | |
60 | { | |
61 | BADF("Reads not implemented\n"); | |
62 | return -1; | |
63 | } | |
64 | ||
65 | static int ssd0303_send(i2c_slave *i2c, uint8_t data) | |
66 | { | |
67 | ssd0303_state *s = (ssd0303_state *)i2c; | |
68 | enum ssd0303_cmd old_cmd_state; | |
69 | switch (s->mode) { | |
70 | case SSD0303_IDLE: | |
71 | DPRINTF("byte 0x%02x\n", data); | |
72 | if (data == 0x80) | |
73 | s->mode = SSD0303_CMD; | |
74 | else if (data == 0x40) | |
75 | s->mode = SSD0303_DATA; | |
76 | else | |
77 | BADF("Unexpected byte 0x%x\n", data); | |
78 | break; | |
79 | case SSD0303_DATA: | |
80 | DPRINTF("data 0x%02x\n", data); | |
81 | if (s->col < 132) { | |
82 | s->framebuffer[s->col + s->row * 132] = data; | |
83 | s->col++; | |
84 | s->redraw = 1; | |
85 | } | |
86 | break; | |
87 | case SSD0303_CMD: | |
88 | old_cmd_state = s->cmd_state; | |
89 | s->cmd_state = SSD0303_CMD_NONE; | |
90 | switch (old_cmd_state) { | |
91 | case SSD0303_CMD_NONE: | |
92 | DPRINTF("cmd 0x%02x\n", data); | |
93 | s->mode = SSD0303_IDLE; | |
94 | switch (data) { | |
95 | case 0x00 ... 0x0f: /* Set lower colum address. */ | |
96 | s->col = (s->col & 0xf0) | (data & 0xf); | |
97 | break; | |
98 | case 0x10 ... 0x20: /* Set higher column address. */ | |
99 | s->col = (s->col & 0x0f) | ((data & 0xf) << 4); | |
100 | break; | |
101 | case 0x40 ... 0x7f: /* Set start line. */ | |
102 | s->start_line = 0; | |
103 | break; | |
104 | case 0x81: /* Set contrast (Ignored). */ | |
105 | s->cmd_state = SSD0303_CMD_SKIP1; | |
106 | break; | |
107 | case 0xa0: /* Mirror off. */ | |
108 | s->mirror = 0; | |
109 | break; | |
110 | case 0xa1: /* Mirror off. */ | |
111 | s->mirror = 1; | |
112 | break; | |
113 | case 0xa4: /* Entire display off. */ | |
114 | s->flash = 0; | |
115 | break; | |
116 | case 0xa5: /* Entire display on. */ | |
117 | s->flash = 1; | |
118 | break; | |
119 | case 0xa6: /* Inverse off. */ | |
120 | s->inverse = 0; | |
121 | break; | |
122 | case 0xa7: /* Inverse on. */ | |
123 | s->inverse = 1; | |
124 | break; | |
125 | case 0xa8: /* Set multipled ratio (Ignored). */ | |
126 | s->cmd_state = SSD0303_CMD_SKIP1; | |
127 | break; | |
128 | case 0xad: /* DC-DC power control. */ | |
129 | s->cmd_state = SSD0303_CMD_SKIP1; | |
130 | break; | |
131 | case 0xae: /* Display off. */ | |
132 | s->enabled = 0; | |
133 | break; | |
134 | case 0xaf: /* Display on. */ | |
135 | s->enabled = 1; | |
136 | break; | |
137 | case 0xb0 ... 0xbf: /* Set Page address. */ | |
138 | s->row = data & 7; | |
139 | break; | |
140 | case 0xc0 ... 0xc8: /* Set COM output direction (Ignored). */ | |
141 | break; | |
142 | case 0xd3: /* Set display offset (Ignored). */ | |
143 | s->cmd_state = SSD0303_CMD_SKIP1; | |
144 | break; | |
145 | case 0xd5: /* Set display clock (Ignored). */ | |
146 | s->cmd_state = SSD0303_CMD_SKIP1; | |
147 | break; | |
148 | case 0xd8: /* Set color and power mode (Ignored). */ | |
149 | s->cmd_state = SSD0303_CMD_SKIP1; | |
150 | break; | |
151 | case 0xd9: /* Set pre-charge period (Ignored). */ | |
152 | s->cmd_state = SSD0303_CMD_SKIP1; | |
153 | break; | |
154 | case 0xda: /* Set COM pin configuration (Ignored). */ | |
155 | s->cmd_state = SSD0303_CMD_SKIP1; | |
156 | break; | |
157 | case 0xdb: /* Set VCOM dselect level (Ignored). */ | |
158 | s->cmd_state = SSD0303_CMD_SKIP1; | |
159 | break; | |
160 | case 0xe3: /* no-op. */ | |
161 | break; | |
162 | default: | |
163 | BADF("Unknown command: 0x%x\n", data); | |
164 | } | |
165 | break; | |
166 | case SSD0303_CMD_SKIP1: | |
167 | DPRINTF("skip 0x%02x\n", data); | |
168 | break; | |
169 | } | |
170 | break; | |
171 | } | |
172 | return 0; | |
173 | } | |
174 | ||
175 | static void ssd0303_event(i2c_slave *i2c, enum i2c_event event) | |
176 | { | |
177 | ssd0303_state *s = (ssd0303_state *)i2c; | |
178 | switch (event) { | |
179 | case I2C_FINISH: | |
180 | s->mode = SSD0303_IDLE; | |
181 | break; | |
182 | case I2C_START_RECV: | |
183 | case I2C_START_SEND: | |
184 | case I2C_NACK: | |
185 | /* Nothing to do. */ | |
186 | break; | |
187 | } | |
188 | } | |
189 | ||
190 | static void ssd0303_update_display(void *opaque) | |
191 | { | |
192 | ssd0303_state *s = (ssd0303_state *)opaque; | |
193 | uint8_t *dest; | |
194 | uint8_t *src; | |
195 | int x; | |
196 | int y; | |
197 | int line; | |
198 | char *colors[2]; | |
199 | char colortab[MAGNIFY * 8]; | |
200 | int dest_width; | |
201 | uint8_t mask; | |
202 | ||
203 | if (s->redraw) { | |
204 | switch (s->ds->depth) { | |
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; | |
222 | } | |
223 | dest_width *= MAGNIFY; | |
224 | memset(colortab, 0xff, dest_width); | |
225 | memset(colortab + dest_width, 0, dest_width); | |
226 | if (s->flash) { | |
227 | colors[0] = colortab; | |
228 | colors[1] = colortab; | |
229 | } else if (s->inverse) { | |
230 | colors[0] = colortab; | |
231 | colors[1] = colortab + dest_width; | |
232 | } else { | |
233 | colors[0] = colortab + dest_width; | |
234 | colors[1] = colortab; | |
235 | } | |
236 | dest = s->ds->data; | |
237 | for (y = 0; y < 16; y++) { | |
238 | line = (y + s->start_line) & 63; | |
239 | src = s->framebuffer + 132 * (line >> 3) + 36; | |
240 | mask = 1 << (line & 7); | |
241 | for (x = 0; x < 96; x++) { | |
242 | memcpy(dest, colors[(*src & mask) != 0], dest_width); | |
243 | dest += dest_width; | |
244 | src++; | |
245 | } | |
246 | for (x = 1; x < MAGNIFY; x++) { | |
247 | memcpy(dest, dest - dest_width * 96, dest_width * 96); | |
248 | dest += dest_width * 96; | |
249 | } | |
250 | } | |
251 | } | |
252 | dpy_update(s->ds, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY); | |
253 | } | |
254 | ||
255 | static void ssd0303_invalidate_display(void * opaque) | |
256 | { | |
257 | ssd0303_state *s = (ssd0303_state *)opaque; | |
258 | s->redraw = 1; | |
259 | } | |
260 | ||
261 | void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address) | |
262 | { | |
263 | ssd0303_state *s; | |
264 | ||
265 | s = (ssd0303_state *)i2c_slave_init(bus, address, sizeof(ssd0303_state)); | |
266 | s->ds = ds; | |
267 | s->i2c.event = ssd0303_event; | |
268 | s->i2c.recv = ssd0303_recv; | |
269 | s->i2c.send = ssd0303_send; | |
270 | graphic_console_init(ds, ssd0303_update_display, ssd0303_invalidate_display, | |
271 | NULL, s); | |
272 | dpy_resize(s->ds, 96 * MAGNIFY, 16 * MAGNIFY); | |
273 | } |