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