qemu/hw/ssd0323.c
<<
>>
Prefs
   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#include "ssi.h"
  14#include "console.h"
  15
  16//#define DEBUG_SSD0323 1
  17
  18#ifdef DEBUG_SSD0323
  19#define DPRINTF(fmt, ...) \
  20do { printf("ssd0323: " fmt , ## __VA_ARGS__); } while (0)
  21#define BADF(fmt, ...) \
  22do { fprintf(stderr, "ssd0323: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
  23#else
  24#define DPRINTF(fmt, ...) do {} while(0)
  25#define BADF(fmt, ...) \
  26do { 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
  38enum ssd0323_mode
  39{
  40    SSD0323_CMD,
  41    SSD0323_DATA
  42};
  43
  44typedef 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
  63static 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
 173static 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
 264static void ssd0323_invalidate_display(void * opaque)
 265{
 266    ssd0323_state *s = (ssd0323_state *)opaque;
 267    s->redraw = 1;
 268}
 269
 270/* Command/data input.  */
 271static 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
 278static 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
 299static 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
 325static 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(&dev->qdev, "ssd0323_oled", -1, 1,
 339                    ssd0323_save, ssd0323_load, s);
 340    return 0;
 341}
 342
 343static void ssd0323_class_init(ObjectClass *klass, void *data)
 344{
 345    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);
 346
 347    k->init = ssd0323_init;
 348    k->transfer = ssd0323_transfer;
 349}
 350
 351static TypeInfo ssd0323_info = {
 352    .name          = "ssd0323",
 353    .parent        = TYPE_SSI_SLAVE,
 354    .instance_size = sizeof(ssd0323_state),
 355    .class_init    = ssd0323_class_init,
 356};
 357
 358static void ssd03232_register_types(void)
 359{
 360    type_register_static(&ssd0323_info);
 361}
 362
 363type_init(ssd03232_register_types)
 364