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 licensed 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 14 #include "qemu/osdep.h" 15 #include "hw/ssi/ssi.h" 16 #include "migration/vmstate.h" 17 #include "qemu/module.h" 18 #include "ui/console.h" 19 20 //#define DEBUG_SSD0323 1 21 22 #ifdef DEBUG_SSD0323 23 #define DPRINTF(fmt, ...) \ 24 do { printf("ssd0323: " fmt , ## __VA_ARGS__); } while (0) 25 #define BADF(fmt, ...) \ 26 do { \ 27 fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); abort(); \ 28 } while (0) 29 #else 30 #define DPRINTF(fmt, ...) do {} while(0) 31 #define BADF(fmt, ...) \ 32 do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__);} while (0) 33 #endif 34 35 /* Scaling factor for pixels. */ 36 #define MAGNIFY 4 37 38 #define REMAP_SWAP_COLUMN 0x01 39 #define REMAP_SWAP_NYBBLE 0x02 40 #define REMAP_VERTICAL 0x04 41 #define REMAP_SWAP_COM 0x10 42 #define REMAP_SPLIT_COM 0x40 43 44 enum ssd0323_mode 45 { 46 SSD0323_CMD, 47 SSD0323_DATA 48 }; 49 50 typedef struct { 51 SSISlave ssidev; 52 QemuConsole *con; 53 54 uint32_t cmd_len; 55 int32_t cmd; 56 int32_t cmd_data[8]; 57 int32_t row; 58 int32_t row_start; 59 int32_t row_end; 60 int32_t col; 61 int32_t col_start; 62 int32_t col_end; 63 int32_t redraw; 64 int32_t remap; 65 uint32_t mode; 66 uint8_t framebuffer[128 * 80 / 2]; 67 } ssd0323_state; 68 69 static uint32_t ssd0323_transfer(SSISlave *dev, uint32_t data) 70 { 71 ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev); 72 73 switch (s->mode) { 74 case SSD0323_DATA: 75 DPRINTF("data 0x%02x\n", data); 76 s->framebuffer[s->col + s->row * 64] = data; 77 if (s->remap & REMAP_VERTICAL) { 78 s->row++; 79 if (s->row > s->row_end) { 80 s->row = s->row_start; 81 s->col++; 82 } 83 if (s->col > s->col_end) { 84 s->col = s->col_start; 85 } 86 } else { 87 s->col++; 88 if (s->col > s->col_end) { 89 s->row++; 90 s->col = s->col_start; 91 } 92 if (s->row > s->row_end) { 93 s->row = s->row_start; 94 } 95 } 96 s->redraw = 1; 97 break; 98 case SSD0323_CMD: 99 DPRINTF("cmd 0x%02x\n", data); 100 if (s->cmd_len == 0) { 101 s->cmd = data; 102 } else { 103 s->cmd_data[s->cmd_len - 1] = data; 104 } 105 s->cmd_len++; 106 switch (s->cmd) { 107 #define DATA(x) if (s->cmd_len <= (x)) return 0 108 case 0x15: /* Set column. */ 109 DATA(2); 110 s->col = s->col_start = s->cmd_data[0] % 64; 111 s->col_end = s->cmd_data[1] % 64; 112 break; 113 case 0x75: /* Set row. */ 114 DATA(2); 115 s->row = s->row_start = s->cmd_data[0] % 80; 116 s->row_end = s->cmd_data[1] % 80; 117 break; 118 case 0x81: /* Set contrast */ 119 DATA(1); 120 break; 121 case 0x84: case 0x85: case 0x86: /* Max current. */ 122 DATA(0); 123 break; 124 case 0xa0: /* Set remapping. */ 125 /* FIXME: Implement this. */ 126 DATA(1); 127 s->remap = s->cmd_data[0]; 128 break; 129 case 0xa1: /* Set display start line. */ 130 case 0xa2: /* Set display offset. */ 131 /* FIXME: Implement these. */ 132 DATA(1); 133 break; 134 case 0xa4: /* Normal mode. */ 135 case 0xa5: /* All on. */ 136 case 0xa6: /* All off. */ 137 case 0xa7: /* Inverse. */ 138 /* FIXME: Implement these. */ 139 DATA(0); 140 break; 141 case 0xa8: /* Set multiplex ratio. */ 142 case 0xad: /* Set DC-DC converter. */ 143 DATA(1); 144 /* Ignored. Don't care. */ 145 break; 146 case 0xae: /* Display off. */ 147 case 0xaf: /* Display on. */ 148 DATA(0); 149 /* TODO: Implement power control. */ 150 break; 151 case 0xb1: /* Set phase length. */ 152 case 0xb2: /* Set row period. */ 153 case 0xb3: /* Set clock rate. */ 154 case 0xbc: /* Set precharge. */ 155 case 0xbe: /* Set VCOMH. */ 156 case 0xbf: /* Set segment low. */ 157 DATA(1); 158 /* Ignored. Don't care. */ 159 break; 160 case 0xb8: /* Set grey scale table. */ 161 /* FIXME: Implement this. */ 162 DATA(8); 163 break; 164 case 0xe3: /* NOP. */ 165 DATA(0); 166 break; 167 case 0xff: /* Nasty hack because we don't handle chip selects 168 properly. */ 169 break; 170 default: 171 BADF("Unknown command: 0x%x\n", data); 172 } 173 s->cmd_len = 0; 174 return 0; 175 } 176 return 0; 177 } 178 179 static void ssd0323_update_display(void *opaque) 180 { 181 ssd0323_state *s = (ssd0323_state *)opaque; 182 DisplaySurface *surface = qemu_console_surface(s->con); 183 uint8_t *dest; 184 uint8_t *src; 185 int x; 186 int y; 187 int i; 188 int line; 189 char *colors[16]; 190 char colortab[MAGNIFY * 64]; 191 char *p; 192 int dest_width; 193 194 if (!s->redraw) 195 return; 196 197 switch (surface_bits_per_pixel(surface)) { 198 case 0: 199 return; 200 case 15: 201 dest_width = 2; 202 break; 203 case 16: 204 dest_width = 2; 205 break; 206 case 24: 207 dest_width = 3; 208 break; 209 case 32: 210 dest_width = 4; 211 break; 212 default: 213 BADF("Bad color depth\n"); 214 return; 215 } 216 p = colortab; 217 for (i = 0; i < 16; i++) { 218 int n; 219 colors[i] = p; 220 switch (surface_bits_per_pixel(surface)) { 221 case 15: 222 n = i * 2 + (i >> 3); 223 p[0] = n | (n << 5); 224 p[1] = (n << 2) | (n >> 3); 225 break; 226 case 16: 227 n = i * 2 + (i >> 3); 228 p[0] = n | (n << 6) | ((n << 1) & 0x20); 229 p[1] = (n << 3) | (n >> 2); 230 break; 231 case 24: 232 case 32: 233 n = (i << 4) | i; 234 p[0] = p[1] = p[2] = n; 235 break; 236 default: 237 BADF("Bad color depth\n"); 238 return; 239 } 240 p += dest_width; 241 } 242 /* TODO: Implement row/column remapping. */ 243 dest = surface_data(surface); 244 for (y = 0; y < 64; y++) { 245 line = y; 246 src = s->framebuffer + 64 * line; 247 for (x = 0; x < 64; x++) { 248 int val; 249 val = *src >> 4; 250 for (i = 0; i < MAGNIFY; i++) { 251 memcpy(dest, colors[val], dest_width); 252 dest += dest_width; 253 } 254 val = *src & 0xf; 255 for (i = 0; i < MAGNIFY; i++) { 256 memcpy(dest, colors[val], dest_width); 257 dest += dest_width; 258 } 259 src++; 260 } 261 for (i = 1; i < MAGNIFY; i++) { 262 memcpy(dest, dest - dest_width * MAGNIFY * 128, 263 dest_width * 128 * MAGNIFY); 264 dest += dest_width * 128 * MAGNIFY; 265 } 266 } 267 s->redraw = 0; 268 dpy_gfx_update(s->con, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY); 269 } 270 271 static void ssd0323_invalidate_display(void * opaque) 272 { 273 ssd0323_state *s = (ssd0323_state *)opaque; 274 s->redraw = 1; 275 } 276 277 /* Command/data input. */ 278 static void ssd0323_cd(void *opaque, int n, int level) 279 { 280 ssd0323_state *s = (ssd0323_state *)opaque; 281 DPRINTF("%s mode\n", level ? "Data" : "Command"); 282 s->mode = level ? SSD0323_DATA : SSD0323_CMD; 283 } 284 285 static int ssd0323_post_load(void *opaque, int version_id) 286 { 287 ssd0323_state *s = (ssd0323_state *)opaque; 288 289 if (s->cmd_len > ARRAY_SIZE(s->cmd_data)) { 290 return -EINVAL; 291 } 292 if (s->row < 0 || s->row >= 80) { 293 return -EINVAL; 294 } 295 if (s->row_start < 0 || s->row_start >= 80) { 296 return -EINVAL; 297 } 298 if (s->row_end < 0 || s->row_end >= 80) { 299 return -EINVAL; 300 } 301 if (s->col < 0 || s->col >= 64) { 302 return -EINVAL; 303 } 304 if (s->col_start < 0 || s->col_start >= 64) { 305 return -EINVAL; 306 } 307 if (s->col_end < 0 || s->col_end >= 64) { 308 return -EINVAL; 309 } 310 if (s->mode != SSD0323_CMD && s->mode != SSD0323_DATA) { 311 return -EINVAL; 312 } 313 314 return 0; 315 } 316 317 static const VMStateDescription vmstate_ssd0323 = { 318 .name = "ssd0323_oled", 319 .version_id = 2, 320 .minimum_version_id = 2, 321 .post_load = ssd0323_post_load, 322 .fields = (VMStateField []) { 323 VMSTATE_UINT32(cmd_len, ssd0323_state), 324 VMSTATE_INT32(cmd, ssd0323_state), 325 VMSTATE_INT32_ARRAY(cmd_data, ssd0323_state, 8), 326 VMSTATE_INT32(row, ssd0323_state), 327 VMSTATE_INT32(row_start, ssd0323_state), 328 VMSTATE_INT32(row_end, ssd0323_state), 329 VMSTATE_INT32(col, ssd0323_state), 330 VMSTATE_INT32(col_start, ssd0323_state), 331 VMSTATE_INT32(col_end, ssd0323_state), 332 VMSTATE_INT32(redraw, ssd0323_state), 333 VMSTATE_INT32(remap, ssd0323_state), 334 VMSTATE_UINT32(mode, ssd0323_state), 335 VMSTATE_BUFFER(framebuffer, ssd0323_state), 336 VMSTATE_SSI_SLAVE(ssidev, ssd0323_state), 337 VMSTATE_END_OF_LIST() 338 } 339 }; 340 341 static const GraphicHwOps ssd0323_ops = { 342 .invalidate = ssd0323_invalidate_display, 343 .gfx_update = ssd0323_update_display, 344 }; 345 346 static void ssd0323_realize(SSISlave *d, Error **errp) 347 { 348 DeviceState *dev = DEVICE(d); 349 ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, d); 350 351 s->col_end = 63; 352 s->row_end = 79; 353 s->con = graphic_console_init(dev, 0, &ssd0323_ops, s); 354 qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY); 355 356 qdev_init_gpio_in(dev, ssd0323_cd, 1); 357 } 358 359 static void ssd0323_class_init(ObjectClass *klass, void *data) 360 { 361 DeviceClass *dc = DEVICE_CLASS(klass); 362 SSISlaveClass *k = SSI_SLAVE_CLASS(klass); 363 364 k->realize = ssd0323_realize; 365 k->transfer = ssd0323_transfer; 366 k->cs_polarity = SSI_CS_HIGH; 367 dc->vmsd = &vmstate_ssd0323; 368 } 369 370 static const TypeInfo ssd0323_info = { 371 .name = "ssd0323", 372 .parent = TYPE_SSI_SLAVE, 373 .instance_size = sizeof(ssd0323_state), 374 .class_init = ssd0323_class_init, 375 }; 376 377 static void ssd03232_register_types(void) 378 { 379 type_register_static(&ssd0323_info); 380 } 381 382 type_init(ssd03232_register_types) 383