root / hw / ssd0323.c @ e57ec016
History | View | Annotate | Download (7.7 kB)
1 | 9ee6e8bb | pbrook | /*
|
---|---|---|---|
2 | 9ee6e8bb | pbrook | * SSD0323 OLED controller with OSRAM Pictiva 128x64 display.
|
3 | 9ee6e8bb | pbrook | *
|
4 | 9ee6e8bb | pbrook | * Copyright (c) 2006-2007 CodeSourcery.
|
5 | 9ee6e8bb | pbrook | * Written by Paul Brook
|
6 | 9ee6e8bb | pbrook | *
|
7 | 9ee6e8bb | pbrook | * This code is licenced under the GPL.
|
8 | 9ee6e8bb | pbrook | */
|
9 | 9ee6e8bb | pbrook | |
10 | 9ee6e8bb | pbrook | /* The controller can support a variety of different displays, but we only
|
11 | 9ee6e8bb | pbrook | implement one. Most of the commends relating to brightness and geometry
|
12 | 9ee6e8bb | pbrook | setup are ignored. */
|
13 | 87ecb68b | pbrook | #include "hw.h" |
14 | 87ecb68b | pbrook | #include "devices.h" |
15 | 87ecb68b | pbrook | #include "console.h" |
16 | 9ee6e8bb | pbrook | |
17 | 9ee6e8bb | pbrook | //#define DEBUG_SSD0323 1
|
18 | 9ee6e8bb | pbrook | |
19 | 9ee6e8bb | pbrook | #ifdef DEBUG_SSD0323
|
20 | 9ee6e8bb | pbrook | #define DPRINTF(fmt, args...) \
|
21 | 9ee6e8bb | pbrook | do { printf("ssd0323: " fmt , ##args); } while (0) |
22 | 9ee6e8bb | pbrook | #define BADF(fmt, args...) \
|
23 | 9ee6e8bb | pbrook | do { fprintf(stderr, "ssd0323: error: " fmt , ##args); exit(1);} while (0) |
24 | 9ee6e8bb | pbrook | #else
|
25 | 9ee6e8bb | pbrook | #define DPRINTF(fmt, args...) do {} while(0) |
26 | 9ee6e8bb | pbrook | #define BADF(fmt, args...) \
|
27 | 9ee6e8bb | pbrook | do { fprintf(stderr, "ssd0323: error: " fmt , ##args);} while (0) |
28 | 9ee6e8bb | pbrook | #endif
|
29 | 9ee6e8bb | pbrook | |
30 | 9ee6e8bb | pbrook | /* Scaling factor for pixels. */
|
31 | 9ee6e8bb | pbrook | #define MAGNIFY 4 |
32 | 9ee6e8bb | pbrook | |
33 | 7ac56ff0 | pbrook | #define REMAP_SWAP_COLUMN 0x01 |
34 | 7ac56ff0 | pbrook | #define REMAP_SWAP_NYBBLE 0x02 |
35 | 7ac56ff0 | pbrook | #define REMAP_VERTICAL 0x04 |
36 | 7ac56ff0 | pbrook | #define REMAP_SWAP_COM 0x10 |
37 | 7ac56ff0 | pbrook | #define REMAP_SPLIT_COM 0x40 |
38 | 7ac56ff0 | pbrook | |
39 | 9ee6e8bb | pbrook | enum ssd0323_mode
|
40 | 9ee6e8bb | pbrook | { |
41 | 9ee6e8bb | pbrook | SSD0323_CMD, |
42 | 9ee6e8bb | pbrook | SSD0323_DATA |
43 | 9ee6e8bb | pbrook | }; |
44 | 9ee6e8bb | pbrook | |
45 | 9ee6e8bb | pbrook | typedef struct { |
46 | 9ee6e8bb | pbrook | DisplayState *ds; |
47 | 9ee6e8bb | pbrook | |
48 | 9ee6e8bb | pbrook | int cmd_len;
|
49 | 9ee6e8bb | pbrook | int cmd;
|
50 | 9ee6e8bb | pbrook | int cmd_data[8]; |
51 | 9ee6e8bb | pbrook | int row;
|
52 | 9ee6e8bb | pbrook | int row_start;
|
53 | 9ee6e8bb | pbrook | int row_end;
|
54 | 9ee6e8bb | pbrook | int col;
|
55 | 9ee6e8bb | pbrook | int col_start;
|
56 | 9ee6e8bb | pbrook | int col_end;
|
57 | 9ee6e8bb | pbrook | int redraw;
|
58 | 7ac56ff0 | pbrook | int remap;
|
59 | 9ee6e8bb | pbrook | enum ssd0323_mode mode;
|
60 | 9ee6e8bb | pbrook | uint8_t framebuffer[128 * 80 / 2]; |
61 | 9ee6e8bb | pbrook | } ssd0323_state; |
62 | 9ee6e8bb | pbrook | |
63 | 9ee6e8bb | pbrook | int ssd0323_xfer_ssi(void *opaque, int data) |
64 | 9ee6e8bb | pbrook | { |
65 | 9ee6e8bb | pbrook | ssd0323_state *s = (ssd0323_state *)opaque; |
66 | 9ee6e8bb | pbrook | switch (s->mode) {
|
67 | 9ee6e8bb | pbrook | case SSD0323_DATA:
|
68 | 9ee6e8bb | pbrook | DPRINTF("data 0x%02x\n", data);
|
69 | 9ee6e8bb | pbrook | s->framebuffer[s->col + s->row * 64] = data;
|
70 | 7ac56ff0 | pbrook | if (s->remap & REMAP_VERTICAL) {
|
71 | 9ee6e8bb | pbrook | s->row++; |
72 | 7ac56ff0 | pbrook | if (s->row > s->row_end) {
|
73 | 7ac56ff0 | pbrook | s->row = s->row_start; |
74 | 7ac56ff0 | pbrook | s->col++; |
75 | 7ac56ff0 | pbrook | } |
76 | 7ac56ff0 | pbrook | if (s->col > s->col_end) {
|
77 | 7ac56ff0 | pbrook | s->col = s->col_start; |
78 | 7ac56ff0 | pbrook | } |
79 | 7ac56ff0 | pbrook | } else {
|
80 | 7ac56ff0 | pbrook | s->col++; |
81 | 7ac56ff0 | pbrook | if (s->col > s->col_end) {
|
82 | 7ac56ff0 | pbrook | s->row++; |
83 | 7ac56ff0 | pbrook | s->col = s->col_start; |
84 | 7ac56ff0 | pbrook | } |
85 | 7ac56ff0 | pbrook | if (s->row > s->row_end) {
|
86 | 7ac56ff0 | pbrook | s->row = s->row_start; |
87 | 7ac56ff0 | pbrook | } |
88 | 9ee6e8bb | pbrook | } |
89 | 9ee6e8bb | pbrook | s->redraw = 1;
|
90 | 9ee6e8bb | pbrook | break;
|
91 | 9ee6e8bb | pbrook | case SSD0323_CMD:
|
92 | 9ee6e8bb | pbrook | DPRINTF("cmd 0x%02x\n", data);
|
93 | 9ee6e8bb | pbrook | if (s->cmd_len == 0) { |
94 | 9ee6e8bb | pbrook | s->cmd = data; |
95 | 9ee6e8bb | pbrook | } else {
|
96 | 9ee6e8bb | pbrook | s->cmd_data[s->cmd_len - 1] = data;
|
97 | 9ee6e8bb | pbrook | } |
98 | 9ee6e8bb | pbrook | s->cmd_len++; |
99 | 9ee6e8bb | pbrook | switch (s->cmd) {
|
100 | 9ee6e8bb | pbrook | #define DATA(x) if (s->cmd_len <= (x)) return 0 |
101 | 9ee6e8bb | pbrook | case 0x15: /* Set column. */ |
102 | 9ee6e8bb | pbrook | DATA(2);
|
103 | 7ac56ff0 | pbrook | s->col = s->col_start = s->cmd_data[0] % 64; |
104 | 9ee6e8bb | pbrook | s->col_end = s->cmd_data[1] % 64; |
105 | 9ee6e8bb | pbrook | break;
|
106 | 9ee6e8bb | pbrook | case 0x75: /* Set row. */ |
107 | 9ee6e8bb | pbrook | DATA(2);
|
108 | 7ac56ff0 | pbrook | s->row = s->row_start = s->cmd_data[0] % 80; |
109 | 9ee6e8bb | pbrook | s->row_end = s->cmd_data[1] % 80; |
110 | 9ee6e8bb | pbrook | break;
|
111 | 9ee6e8bb | pbrook | case 0x81: /* Set contrast */ |
112 | 9ee6e8bb | pbrook | DATA(1);
|
113 | 9ee6e8bb | pbrook | break;
|
114 | 9ee6e8bb | pbrook | case 0x84: case 0x85: case 0x86: /* Max current. */ |
115 | 9ee6e8bb | pbrook | DATA(0);
|
116 | 9ee6e8bb | pbrook | break;
|
117 | 9ee6e8bb | pbrook | case 0xa0: /* Set remapping. */ |
118 | 9ee6e8bb | pbrook | /* FIXME: Implement this. */
|
119 | 9ee6e8bb | pbrook | DATA(1);
|
120 | 7ac56ff0 | pbrook | s->remap = s->cmd_data[0];
|
121 | 9ee6e8bb | pbrook | break;
|
122 | 9ee6e8bb | pbrook | case 0xa1: /* Set display start line. */ |
123 | 9ee6e8bb | pbrook | case 0xa2: /* Set display offset. */ |
124 | 9ee6e8bb | pbrook | /* FIXME: Implement these. */
|
125 | 9ee6e8bb | pbrook | DATA(1);
|
126 | 9ee6e8bb | pbrook | break;
|
127 | 9ee6e8bb | pbrook | case 0xa4: /* Normal mode. */ |
128 | 9ee6e8bb | pbrook | case 0xa5: /* All on. */ |
129 | 9ee6e8bb | pbrook | case 0xa6: /* All off. */ |
130 | 9ee6e8bb | pbrook | case 0xa7: /* Inverse. */ |
131 | 9ee6e8bb | pbrook | /* FIXME: Implement these. */
|
132 | 9ee6e8bb | pbrook | DATA(0);
|
133 | 9ee6e8bb | pbrook | break;
|
134 | 9ee6e8bb | pbrook | case 0xa8: /* Set multiplex ratio. */ |
135 | 9ee6e8bb | pbrook | case 0xad: /* Set DC-DC converter. */ |
136 | 9ee6e8bb | pbrook | DATA(1);
|
137 | 9ee6e8bb | pbrook | /* Ignored. Don't care. */
|
138 | 9ee6e8bb | pbrook | break;
|
139 | 9ee6e8bb | pbrook | case 0xae: /* Display off. */ |
140 | 9ee6e8bb | pbrook | case 0xaf: /* Display on. */ |
141 | 9ee6e8bb | pbrook | DATA(0);
|
142 | 9ee6e8bb | pbrook | /* TODO: Implement power control. */
|
143 | 9ee6e8bb | pbrook | break;
|
144 | 9ee6e8bb | pbrook | case 0xb1: /* Set phase length. */ |
145 | 9ee6e8bb | pbrook | case 0xb2: /* Set row period. */ |
146 | 9ee6e8bb | pbrook | case 0xb3: /* Set clock rate. */ |
147 | 9ee6e8bb | pbrook | case 0xbc: /* Set precharge. */ |
148 | 9ee6e8bb | pbrook | case 0xbe: /* Set VCOMH. */ |
149 | 9ee6e8bb | pbrook | case 0xbf: /* Set segment low. */ |
150 | 9ee6e8bb | pbrook | DATA(1);
|
151 | 9ee6e8bb | pbrook | /* Ignored. Don't care. */
|
152 | 9ee6e8bb | pbrook | break;
|
153 | 9ee6e8bb | pbrook | case 0xb8: /* Set grey scale table. */ |
154 | 9ee6e8bb | pbrook | /* FIXME: Implement this. */
|
155 | 9ee6e8bb | pbrook | DATA(8);
|
156 | 9ee6e8bb | pbrook | break;
|
157 | 9ee6e8bb | pbrook | case 0xe3: /* NOP. */ |
158 | 9ee6e8bb | pbrook | DATA(0);
|
159 | 9ee6e8bb | pbrook | break;
|
160 | 9ee6e8bb | pbrook | default:
|
161 | 9ee6e8bb | pbrook | BADF("Unknown command: 0x%x\n", data);
|
162 | 9ee6e8bb | pbrook | } |
163 | 9ee6e8bb | pbrook | s->cmd_len = 0;
|
164 | 9ee6e8bb | pbrook | return 0; |
165 | 9ee6e8bb | pbrook | } |
166 | 9ee6e8bb | pbrook | return 0; |
167 | 9ee6e8bb | pbrook | } |
168 | 9ee6e8bb | pbrook | |
169 | 9ee6e8bb | pbrook | static void ssd0323_update_display(void *opaque) |
170 | 9ee6e8bb | pbrook | { |
171 | 9ee6e8bb | pbrook | ssd0323_state *s = (ssd0323_state *)opaque; |
172 | 9ee6e8bb | pbrook | uint8_t *dest; |
173 | 9ee6e8bb | pbrook | uint8_t *src; |
174 | 9ee6e8bb | pbrook | int x;
|
175 | 9ee6e8bb | pbrook | int y;
|
176 | 9ee6e8bb | pbrook | int i;
|
177 | 9ee6e8bb | pbrook | int line;
|
178 | 9ee6e8bb | pbrook | char *colors[16]; |
179 | 9ee6e8bb | pbrook | char colortab[MAGNIFY * 64]; |
180 | 9ee6e8bb | pbrook | char *p;
|
181 | 9ee6e8bb | pbrook | int dest_width;
|
182 | 9ee6e8bb | pbrook | |
183 | 9ee6e8bb | pbrook | if (s->redraw) {
|
184 | 9ee6e8bb | pbrook | switch (s->ds->depth) {
|
185 | 9ee6e8bb | pbrook | case 0: |
186 | 9ee6e8bb | pbrook | return;
|
187 | 9ee6e8bb | pbrook | case 15: |
188 | 9ee6e8bb | pbrook | dest_width = 2;
|
189 | 9ee6e8bb | pbrook | break;
|
190 | 9ee6e8bb | pbrook | case 16: |
191 | 9ee6e8bb | pbrook | dest_width = 2;
|
192 | 9ee6e8bb | pbrook | break;
|
193 | 9ee6e8bb | pbrook | case 24: |
194 | 9ee6e8bb | pbrook | dest_width = 3;
|
195 | 9ee6e8bb | pbrook | break;
|
196 | 9ee6e8bb | pbrook | case 32: |
197 | 9ee6e8bb | pbrook | dest_width = 4;
|
198 | 9ee6e8bb | pbrook | break;
|
199 | 9ee6e8bb | pbrook | default:
|
200 | 9ee6e8bb | pbrook | BADF("Bad color depth\n");
|
201 | 9ee6e8bb | pbrook | return;
|
202 | 9ee6e8bb | pbrook | } |
203 | 9ee6e8bb | pbrook | p = colortab; |
204 | 9ee6e8bb | pbrook | for (i = 0; i < 16; i++) { |
205 | 9ee6e8bb | pbrook | int n;
|
206 | 9ee6e8bb | pbrook | colors[i] = p; |
207 | 9ee6e8bb | pbrook | switch (s->ds->depth) {
|
208 | 9ee6e8bb | pbrook | case 15: |
209 | 9ee6e8bb | pbrook | n = i * 2 + (i >> 3); |
210 | 9ee6e8bb | pbrook | p[0] = n | (n << 5); |
211 | 9ee6e8bb | pbrook | p[1] = (n << 2) | (n >> 3); |
212 | 9ee6e8bb | pbrook | break;
|
213 | 9ee6e8bb | pbrook | case 16: |
214 | 9ee6e8bb | pbrook | n = i * 2 + (i >> 3); |
215 | 9ee6e8bb | pbrook | p[0] = n | (n << 6) | ((n << 1) & 0x20); |
216 | 9ee6e8bb | pbrook | p[1] = (n << 3) | (n >> 2); |
217 | 9ee6e8bb | pbrook | break;
|
218 | 9ee6e8bb | pbrook | case 24: |
219 | 9ee6e8bb | pbrook | case 32: |
220 | 9ee6e8bb | pbrook | n = (i << 4) | i;
|
221 | 9ee6e8bb | pbrook | p[0] = p[1] = p[2] = n; |
222 | 9ee6e8bb | pbrook | break;
|
223 | 9ee6e8bb | pbrook | default:
|
224 | 9ee6e8bb | pbrook | BADF("Bad color depth\n");
|
225 | 9ee6e8bb | pbrook | return;
|
226 | 9ee6e8bb | pbrook | } |
227 | 9ee6e8bb | pbrook | p += dest_width; |
228 | 9ee6e8bb | pbrook | } |
229 | 7ac56ff0 | pbrook | /* TODO: Implement row/column remapping. */
|
230 | 9ee6e8bb | pbrook | dest = s->ds->data; |
231 | 9ee6e8bb | pbrook | for (y = 0; y < 64; y++) { |
232 | 9ee6e8bb | pbrook | line = y; |
233 | 9ee6e8bb | pbrook | src = s->framebuffer + 64 * line;
|
234 | 9ee6e8bb | pbrook | for (x = 0; x < 64; x++) { |
235 | 9ee6e8bb | pbrook | int val;
|
236 | 9ee6e8bb | pbrook | val = *src >> 4;
|
237 | 9ee6e8bb | pbrook | for (i = 0; i < MAGNIFY; i++) { |
238 | 9ee6e8bb | pbrook | memcpy(dest, colors[val], dest_width); |
239 | 9ee6e8bb | pbrook | dest += dest_width; |
240 | 9ee6e8bb | pbrook | } |
241 | 9ee6e8bb | pbrook | val = *src & 0xf;
|
242 | 9ee6e8bb | pbrook | for (i = 0; i < MAGNIFY; i++) { |
243 | 9ee6e8bb | pbrook | memcpy(dest, colors[val], dest_width); |
244 | 9ee6e8bb | pbrook | dest += dest_width; |
245 | 9ee6e8bb | pbrook | } |
246 | 9ee6e8bb | pbrook | src++; |
247 | 9ee6e8bb | pbrook | } |
248 | 9ee6e8bb | pbrook | for (i = 1; i < MAGNIFY; i++) { |
249 | 9ee6e8bb | pbrook | memcpy(dest, dest - dest_width * MAGNIFY * 128,
|
250 | 9ee6e8bb | pbrook | dest_width * 128 * MAGNIFY);
|
251 | 9ee6e8bb | pbrook | dest += dest_width * 128 * MAGNIFY;
|
252 | 9ee6e8bb | pbrook | } |
253 | 9ee6e8bb | pbrook | } |
254 | 9ee6e8bb | pbrook | } |
255 | 9ee6e8bb | pbrook | dpy_update(s->ds, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY); |
256 | 9ee6e8bb | pbrook | } |
257 | 9ee6e8bb | pbrook | |
258 | 9ee6e8bb | pbrook | static void ssd0323_invalidate_display(void * opaque) |
259 | 9ee6e8bb | pbrook | { |
260 | 9ee6e8bb | pbrook | ssd0323_state *s = (ssd0323_state *)opaque; |
261 | 9ee6e8bb | pbrook | s->redraw = 1;
|
262 | 9ee6e8bb | pbrook | } |
263 | 9ee6e8bb | pbrook | |
264 | 9ee6e8bb | pbrook | /* Command/data input. */
|
265 | 9ee6e8bb | pbrook | static void ssd0323_cd(void *opaque, int n, int level) |
266 | 9ee6e8bb | pbrook | { |
267 | 9ee6e8bb | pbrook | ssd0323_state *s = (ssd0323_state *)opaque; |
268 | 9ee6e8bb | pbrook | DPRINTF("%s mode\n", level ? "Data" : "Command"); |
269 | 9ee6e8bb | pbrook | s->mode = level ? SSD0323_DATA : SSD0323_CMD; |
270 | 9ee6e8bb | pbrook | } |
271 | 9ee6e8bb | pbrook | |
272 | 9ee6e8bb | pbrook | void *ssd0323_init(DisplayState *ds, qemu_irq *cmd_p)
|
273 | 9ee6e8bb | pbrook | { |
274 | 9ee6e8bb | pbrook | ssd0323_state *s; |
275 | 9ee6e8bb | pbrook | qemu_irq *cmd; |
276 | 9ee6e8bb | pbrook | |
277 | 9ee6e8bb | pbrook | s = (ssd0323_state *)qemu_mallocz(sizeof(ssd0323_state));
|
278 | 9ee6e8bb | pbrook | s->ds = ds; |
279 | 9ee6e8bb | pbrook | graphic_console_init(ds, ssd0323_update_display, ssd0323_invalidate_display, |
280 | 9ee6e8bb | pbrook | NULL, s);
|
281 | 9ee6e8bb | pbrook | dpy_resize(s->ds, 128 * MAGNIFY, 64 * MAGNIFY); |
282 | 9ee6e8bb | pbrook | s->col_end = 63;
|
283 | 9ee6e8bb | pbrook | s->row_end = 79;
|
284 | 9ee6e8bb | pbrook | |
285 | 9ee6e8bb | pbrook | cmd = qemu_allocate_irqs(ssd0323_cd, s, 1);
|
286 | 9ee6e8bb | pbrook | *cmd_p = *cmd; |
287 | 9ee6e8bb | pbrook | |
288 | 9ee6e8bb | pbrook | return s;
|
289 | 9ee6e8bb | pbrook | } |