File:  [Qemu by Fabrice Bellard] / qemu / hw / ssd0323.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:40:14 2018 UTC (2 years, 11 months ago) by root
Branches: qemu, MAIN
CVS tags: qemu0125, qemu0124, qemu0123, qemu0122, qemu0121, qemu0120, HEAD
qemu 0.12.0

    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 "ssi.h"
   14: #include "console.h"
   15: 
   16: //#define DEBUG_SSD0323 1
   17: 
   18: #ifdef DEBUG_SSD0323
   19: #define DPRINTF(fmt, ...) \
   20: do { printf("ssd0323: " fmt , ## __VA_ARGS__); } while (0)
   21: #define BADF(fmt, ...) \
   22: do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
   23: #else
   24: #define DPRINTF(fmt, ...) do {} while(0)
   25: #define BADF(fmt, ...) \
   26: do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__);} while (0)
   27: #endif
   28: 
   29: /* Scaling factor for pixels.  */
   30: #define MAGNIFY 4
   31: 
   32: #define REMAP_SWAP_COLUMN 0x01
   33: #define REMAP_SWAP_NYBBLE 0x02
   34: #define REMAP_VERTICAL    0x04
   35: #define REMAP_SWAP_COM    0x10
   36: #define REMAP_SPLIT_COM   0x40
   37: 
   38: enum ssd0323_mode
   39: {
   40:     SSD0323_CMD,
   41:     SSD0323_DATA
   42: };
   43: 
   44: typedef struct {
   45:     SSISlave ssidev;
   46:     DisplayState *ds;
   47: 
   48:     int cmd_len;
   49:     int cmd;
   50:     int cmd_data[8];
   51:     int row;
   52:     int row_start;
   53:     int row_end;
   54:     int col;
   55:     int col_start;
   56:     int col_end;
   57:     int redraw;
   58:     int remap;
   59:     enum ssd0323_mode mode;
   60:     uint8_t framebuffer[128 * 80 / 2];
   61: } ssd0323_state;
   62: 
   63: static uint32_t ssd0323_transfer(SSISlave *dev, uint32_t data)
   64: {
   65:     ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev);
   66: 
   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: static int ssd0323_init(SSISlave *dev)
  326: {
  327:     ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev);
  328: 
  329:     s->col_end = 63;
  330:     s->row_end = 79;
  331:     s->ds = graphic_console_init(ssd0323_update_display,
  332:                                  ssd0323_invalidate_display,
  333:                                  NULL, NULL, s);
  334:     qemu_console_resize(s->ds, 128 * MAGNIFY, 64 * MAGNIFY);
  335: 
  336:     qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1);
  337: 
  338:     register_savevm("ssd0323_oled", -1, 1, ssd0323_save, ssd0323_load, s);
  339:     return 0;
  340: }
  341: 
  342: static SSISlaveInfo ssd0323_info = {
  343:     .qdev.name = "ssd0323",
  344:     .qdev.size = sizeof(ssd0323_state),
  345:     .init = ssd0323_init,
  346:     .transfer = ssd0323_transfer
  347: };
  348: 
  349: static void ssd03232_register_devices(void)
  350: {
  351:     ssi_register_slave(&ssd0323_info);
  352: }
  353: 
  354: device_init(ssd03232_register_devices)

unix.superglobalmegacorp.com