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