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