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