qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

ssd0303.c (9381B)


      1 /*
      2  * SSD0303 OLED controller with OSRAM Pictiva 96x16 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/i2c/i2c.h"
     16 #include "migration/vmstate.h"
     17 #include "qemu/module.h"
     18 #include "ui/console.h"
     19 #include "qom/object.h"
     20 
     21 //#define DEBUG_SSD0303 1
     22 
     23 #ifdef DEBUG_SSD0303
     24 #define DPRINTF(fmt, ...) \
     25 do { printf("ssd0303: " fmt , ## __VA_ARGS__); } while (0)
     26 #define BADF(fmt, ...) \
     27 do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
     28 #else
     29 #define DPRINTF(fmt, ...) do {} while(0)
     30 #define BADF(fmt, ...) \
     31 do { fprintf(stderr, "ssd0303: error: " fmt , ## __VA_ARGS__);} while (0)
     32 #endif
     33 
     34 /* Scaling factor for pixels.  */
     35 #define MAGNIFY 4
     36 
     37 enum ssd0303_mode
     38 {
     39     SSD0303_IDLE,
     40     SSD0303_DATA,
     41     SSD0303_CMD
     42 };
     43 
     44 enum ssd0303_cmd {
     45     SSD0303_CMD_NONE,
     46     SSD0303_CMD_SKIP1
     47 };
     48 
     49 #define TYPE_SSD0303 "ssd0303"
     50 OBJECT_DECLARE_SIMPLE_TYPE(ssd0303_state, SSD0303)
     51 
     52 struct ssd0303_state {
     53     I2CSlave parent_obj;
     54 
     55     QemuConsole *con;
     56     int row;
     57     int col;
     58     int start_line;
     59     int mirror;
     60     int flash;
     61     int enabled;
     62     int inverse;
     63     int redraw;
     64     enum ssd0303_mode mode;
     65     enum ssd0303_cmd cmd_state;
     66     uint8_t framebuffer[132*8];
     67 };
     68 
     69 static uint8_t ssd0303_recv(I2CSlave *i2c)
     70 {
     71     BADF("Reads not implemented\n");
     72     return 0xff;
     73 }
     74 
     75 static int ssd0303_send(I2CSlave *i2c, uint8_t data)
     76 {
     77     ssd0303_state *s = SSD0303(i2c);
     78     enum ssd0303_cmd old_cmd_state;
     79 
     80     switch (s->mode) {
     81     case SSD0303_IDLE:
     82         DPRINTF("byte 0x%02x\n", data);
     83         if (data == 0x80)
     84             s->mode = SSD0303_CMD;
     85         else if (data == 0x40)
     86             s->mode = SSD0303_DATA;
     87         else
     88             BADF("Unexpected byte 0x%x\n", data);
     89         break;
     90     case SSD0303_DATA:
     91         DPRINTF("data 0x%02x\n", data);
     92         if (s->col < 132) {
     93             s->framebuffer[s->col + s->row * 132] = data;
     94             s->col++;
     95             s->redraw = 1;
     96         }
     97         break;
     98     case SSD0303_CMD:
     99         old_cmd_state = s->cmd_state;
    100         s->cmd_state = SSD0303_CMD_NONE;
    101         switch (old_cmd_state) {
    102         case SSD0303_CMD_NONE:
    103             DPRINTF("cmd 0x%02x\n", data);
    104             s->mode = SSD0303_IDLE;
    105             switch (data) {
    106             case 0x00 ... 0x0f: /* Set lower column address.  */
    107                 s->col = (s->col & 0xf0) | (data & 0xf);
    108                 break;
    109             case 0x10 ... 0x20: /* Set higher column address.  */
    110                 s->col = (s->col & 0x0f) | ((data & 0xf) << 4);
    111                 break;
    112             case 0x40 ... 0x7f: /* Set start line.  */
    113                 s->start_line = 0;
    114                 break;
    115             case 0x81: /* Set contrast (Ignored).  */
    116                 s->cmd_state = SSD0303_CMD_SKIP1;
    117                 break;
    118             case 0xa0: /* Mirror off.  */
    119                 s->mirror = 0;
    120                 break;
    121             case 0xa1: /* Mirror off.  */
    122                 s->mirror = 1;
    123                 break;
    124             case 0xa4: /* Entire display off.  */
    125                 s->flash = 0;
    126                 break;
    127             case 0xa5: /* Entire display on.  */
    128                 s->flash = 1;
    129                 break;
    130             case 0xa6: /* Inverse off.  */
    131                 s->inverse = 0;
    132                 break;
    133             case 0xa7: /* Inverse on.  */
    134                 s->inverse = 1;
    135                 break;
    136             case 0xa8: /* Set multiplied ratio (Ignored).  */
    137                 s->cmd_state = SSD0303_CMD_SKIP1;
    138                 break;
    139             case 0xad: /* DC-DC power control.  */
    140                 s->cmd_state = SSD0303_CMD_SKIP1;
    141                 break;
    142             case 0xae: /* Display off.  */
    143                 s->enabled = 0;
    144                 break;
    145             case 0xaf: /* Display on.  */
    146                 s->enabled = 1;
    147                 break;
    148             case 0xb0 ... 0xbf: /* Set Page address.  */
    149                 s->row = data & 7;
    150                 break;
    151             case 0xc0 ... 0xc8: /* Set COM output direction (Ignored).  */
    152                 break;
    153             case 0xd3: /* Set display offset (Ignored).  */
    154                 s->cmd_state = SSD0303_CMD_SKIP1;
    155                 break;
    156             case 0xd5: /* Set display clock (Ignored).  */
    157                 s->cmd_state = SSD0303_CMD_SKIP1;
    158                 break;
    159             case 0xd8: /* Set color and power mode (Ignored).  */
    160                 s->cmd_state = SSD0303_CMD_SKIP1;
    161                 break;
    162             case 0xd9: /* Set pre-charge period (Ignored).  */
    163                 s->cmd_state = SSD0303_CMD_SKIP1;
    164                 break;
    165             case 0xda: /* Set COM pin configuration (Ignored).  */
    166                 s->cmd_state = SSD0303_CMD_SKIP1;
    167                 break;
    168             case 0xdb: /* Set VCOM dselect level (Ignored).  */
    169                 s->cmd_state = SSD0303_CMD_SKIP1;
    170                 break;
    171             case 0xe3: /* no-op.  */
    172                 break;
    173             default:
    174                 BADF("Unknown command: 0x%x\n", data);
    175             }
    176             break;
    177         case SSD0303_CMD_SKIP1:
    178             DPRINTF("skip 0x%02x\n", data);
    179             break;
    180         }
    181         break;
    182     }
    183     return 0;
    184 }
    185 
    186 static int ssd0303_event(I2CSlave *i2c, enum i2c_event event)
    187 {
    188     ssd0303_state *s = SSD0303(i2c);
    189 
    190     switch (event) {
    191     case I2C_FINISH:
    192         s->mode = SSD0303_IDLE;
    193         break;
    194     case I2C_START_RECV:
    195     case I2C_START_SEND:
    196     case I2C_NACK:
    197         /* Nothing to do.  */
    198         break;
    199     default:
    200         return -1;
    201     }
    202 
    203     return 0;
    204 }
    205 
    206 static void ssd0303_update_display(void *opaque)
    207 {
    208     ssd0303_state *s = (ssd0303_state *)opaque;
    209     DisplaySurface *surface = qemu_console_surface(s->con);
    210     uint8_t *dest;
    211     uint8_t *src;
    212     int x;
    213     int y;
    214     int line;
    215     char *colors[2];
    216     char colortab[MAGNIFY * 8];
    217     int dest_width;
    218     uint8_t mask;
    219 
    220     if (!s->redraw)
    221         return;
    222 
    223     switch (surface_bits_per_pixel(surface)) {
    224     case 0:
    225         return;
    226     case 15:
    227         dest_width = 2;
    228         break;
    229     case 16:
    230         dest_width = 2;
    231         break;
    232     case 24:
    233         dest_width = 3;
    234         break;
    235     case 32:
    236         dest_width = 4;
    237         break;
    238     default:
    239         BADF("Bad color depth\n");
    240         return;
    241     }
    242     dest_width *= MAGNIFY;
    243     memset(colortab, 0xff, dest_width);
    244     memset(colortab + dest_width, 0, dest_width);
    245     if (s->flash) {
    246         colors[0] = colortab;
    247         colors[1] = colortab;
    248     } else if (s->inverse) {
    249         colors[0] = colortab;
    250         colors[1] = colortab + dest_width;
    251     } else {
    252         colors[0] = colortab + dest_width;
    253         colors[1] = colortab;
    254     }
    255     dest = surface_data(surface);
    256     for (y = 0; y < 16; y++) {
    257         line = (y + s->start_line) & 63;
    258         src = s->framebuffer + 132 * (line >> 3) + 36;
    259         mask = 1 << (line & 7);
    260         for (x = 0; x < 96; x++) {
    261             memcpy(dest, colors[(*src & mask) != 0], dest_width);
    262             dest += dest_width;
    263             src++;
    264         }
    265         for (x = 1; x < MAGNIFY; x++) {
    266             memcpy(dest, dest - dest_width * 96, dest_width * 96);
    267             dest += dest_width * 96;
    268         }
    269     }
    270     s->redraw = 0;
    271     dpy_gfx_update(s->con, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
    272 }
    273 
    274 static void ssd0303_invalidate_display(void * opaque)
    275 {
    276     ssd0303_state *s = (ssd0303_state *)opaque;
    277     s->redraw = 1;
    278 }
    279 
    280 static const VMStateDescription vmstate_ssd0303 = {
    281     .name = "ssd0303_oled",
    282     .version_id = 1,
    283     .minimum_version_id = 1,
    284     .fields = (VMStateField[]) {
    285         VMSTATE_INT32(row, ssd0303_state),
    286         VMSTATE_INT32(col, ssd0303_state),
    287         VMSTATE_INT32(start_line, ssd0303_state),
    288         VMSTATE_INT32(mirror, ssd0303_state),
    289         VMSTATE_INT32(flash, ssd0303_state),
    290         VMSTATE_INT32(enabled, ssd0303_state),
    291         VMSTATE_INT32(inverse, ssd0303_state),
    292         VMSTATE_INT32(redraw, ssd0303_state),
    293         VMSTATE_UINT32(mode, ssd0303_state),
    294         VMSTATE_UINT32(cmd_state, ssd0303_state),
    295         VMSTATE_BUFFER(framebuffer, ssd0303_state),
    296         VMSTATE_I2C_SLAVE(parent_obj, ssd0303_state),
    297         VMSTATE_END_OF_LIST()
    298     }
    299 };
    300 
    301 static const GraphicHwOps ssd0303_ops = {
    302     .invalidate  = ssd0303_invalidate_display,
    303     .gfx_update  = ssd0303_update_display,
    304 };
    305 
    306 static void ssd0303_realize(DeviceState *dev, Error **errp)
    307 {
    308     ssd0303_state *s = SSD0303(dev);
    309 
    310     s->con = graphic_console_init(dev, 0, &ssd0303_ops, s);
    311     qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY);
    312 }
    313 
    314 static void ssd0303_class_init(ObjectClass *klass, void *data)
    315 {
    316     DeviceClass *dc = DEVICE_CLASS(klass);
    317     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
    318 
    319     dc->realize = ssd0303_realize;
    320     k->event = ssd0303_event;
    321     k->recv = ssd0303_recv;
    322     k->send = ssd0303_send;
    323     dc->vmsd = &vmstate_ssd0303;
    324 }
    325 
    326 static const TypeInfo ssd0303_info = {
    327     .name          = TYPE_SSD0303,
    328     .parent        = TYPE_I2C_SLAVE,
    329     .instance_size = sizeof(ssd0303_state),
    330     .class_init    = ssd0303_class_init,
    331 };
    332 
    333 static void ssd0303_register_types(void)
    334 {
    335     type_register_static(&ssd0303_info);
    336 }
    337 
    338 type_init(ssd0303_register_types)