qemu/hw/display/sii9022.c
<<
>>
Prefs
   1/*
   2 * Silicon Image SiI9022
   3 *
   4 * This is a pretty hollow emulation: all we do is acknowledge that we
   5 * exist (chip ID) and confirm that we get switched over into DDC mode
   6 * so the emulated host can proceed to read out EDID data. All subsequent
   7 * set-up of connectors etc will be acknowledged and ignored.
   8 *
   9 * Copyright (C) 2018 Linus Walleij
  10 *
  11 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  12 * See the COPYING file in the top-level directory.
  13 * SPDX-License-Identifier: GPL-2.0-or-later
  14 */
  15
  16#include "qemu/osdep.h"
  17#include "qemu/module.h"
  18#include "hw/i2c/i2c.h"
  19#include "migration/vmstate.h"
  20#include "hw/display/i2c-ddc.h"
  21#include "trace.h"
  22#include "qom/object.h"
  23
  24#define SII9022_SYS_CTRL_DATA 0x1a
  25#define SII9022_SYS_CTRL_PWR_DWN 0x10
  26#define SII9022_SYS_CTRL_AV_MUTE 0x08
  27#define SII9022_SYS_CTRL_DDC_BUS_REQ 0x04
  28#define SII9022_SYS_CTRL_DDC_BUS_GRTD 0x02
  29#define SII9022_SYS_CTRL_OUTPUT_MODE 0x01
  30#define SII9022_SYS_CTRL_OUTPUT_HDMI 1
  31#define SII9022_SYS_CTRL_OUTPUT_DVI 0
  32#define SII9022_REG_CHIPID 0x1b
  33#define SII9022_INT_ENABLE 0x3c
  34#define SII9022_INT_STATUS 0x3d
  35#define SII9022_INT_STATUS_HOTPLUG 0x01;
  36#define SII9022_INT_STATUS_PLUGGED 0x04;
  37
  38#define TYPE_SII9022 "sii9022"
  39OBJECT_DECLARE_SIMPLE_TYPE(sii9022_state, SII9022)
  40
  41struct sii9022_state {
  42    I2CSlave parent_obj;
  43    uint8_t ptr;
  44    bool addr_byte;
  45    bool ddc_req;
  46    bool ddc_skip_finish;
  47    bool ddc;
  48};
  49
  50static const VMStateDescription vmstate_sii9022 = {
  51    .name = "sii9022",
  52    .version_id = 1,
  53    .minimum_version_id = 1,
  54    .fields = (VMStateField[]) {
  55        VMSTATE_I2C_SLAVE(parent_obj, sii9022_state),
  56        VMSTATE_UINT8(ptr, sii9022_state),
  57        VMSTATE_BOOL(addr_byte, sii9022_state),
  58        VMSTATE_BOOL(ddc_req, sii9022_state),
  59        VMSTATE_BOOL(ddc_skip_finish, sii9022_state),
  60        VMSTATE_BOOL(ddc, sii9022_state),
  61        VMSTATE_END_OF_LIST()
  62    }
  63};
  64
  65static int sii9022_event(I2CSlave *i2c, enum i2c_event event)
  66{
  67    sii9022_state *s = SII9022(i2c);
  68
  69    switch (event) {
  70    case I2C_START_SEND:
  71        s->addr_byte = true;
  72        break;
  73    case I2C_START_RECV:
  74        break;
  75    case I2C_FINISH:
  76        break;
  77    case I2C_NACK:
  78        break;
  79    }
  80
  81    return 0;
  82}
  83
  84static uint8_t sii9022_rx(I2CSlave *i2c)
  85{
  86    sii9022_state *s = SII9022(i2c);
  87    uint8_t res = 0x00;
  88
  89    switch (s->ptr) {
  90    case SII9022_SYS_CTRL_DATA:
  91        if (s->ddc_req) {
  92            /* Acknowledge DDC bus request */
  93            res = SII9022_SYS_CTRL_DDC_BUS_GRTD | SII9022_SYS_CTRL_DDC_BUS_REQ;
  94        }
  95        break;
  96    case SII9022_REG_CHIPID:
  97        res = 0xb0;
  98        break;
  99    case SII9022_INT_STATUS:
 100        /* Something is cold-plugged in, no interrupts */
 101        res = SII9022_INT_STATUS_PLUGGED;
 102        break;
 103    default:
 104        break;
 105    }
 106
 107    trace_sii9022_read_reg(s->ptr, res);
 108    s->ptr++;
 109
 110    return res;
 111}
 112
 113static int sii9022_tx(I2CSlave *i2c, uint8_t data)
 114{
 115    sii9022_state *s = SII9022(i2c);
 116
 117    if (s->addr_byte) {
 118        s->ptr = data;
 119        s->addr_byte = false;
 120        return 0;
 121    }
 122
 123    switch (s->ptr) {
 124    case SII9022_SYS_CTRL_DATA:
 125        if (data & SII9022_SYS_CTRL_DDC_BUS_REQ) {
 126            s->ddc_req = true;
 127            if (data & SII9022_SYS_CTRL_DDC_BUS_GRTD) {
 128                s->ddc = true;
 129                /* Skip this finish since we just switched to DDC */
 130                s->ddc_skip_finish = true;
 131                trace_sii9022_switch_mode("DDC");
 132            }
 133        } else {
 134            s->ddc_req = false;
 135            s->ddc = false;
 136            trace_sii9022_switch_mode("normal");
 137        }
 138        break;
 139    default:
 140        break;
 141    }
 142
 143    trace_sii9022_write_reg(s->ptr, data);
 144    s->ptr++;
 145
 146    return 0;
 147}
 148
 149static void sii9022_reset(DeviceState *dev)
 150{
 151    sii9022_state *s = SII9022(dev);
 152
 153    s->ptr = 0;
 154    s->addr_byte = false;
 155    s->ddc_req = false;
 156    s->ddc_skip_finish = false;
 157    s->ddc = false;
 158}
 159
 160static void sii9022_realize(DeviceState *dev, Error **errp)
 161{
 162    I2CBus *bus;
 163
 164    bus = I2C_BUS(qdev_get_parent_bus(dev));
 165    i2c_slave_create_simple(bus, TYPE_I2CDDC, 0x50);
 166}
 167
 168static void sii9022_class_init(ObjectClass *klass, void *data)
 169{
 170    DeviceClass *dc = DEVICE_CLASS(klass);
 171    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
 172
 173    k->event = sii9022_event;
 174    k->recv = sii9022_rx;
 175    k->send = sii9022_tx;
 176    dc->reset = sii9022_reset;
 177    dc->realize = sii9022_realize;
 178    dc->vmsd = &vmstate_sii9022;
 179}
 180
 181static const TypeInfo sii9022_info = {
 182    .name          = TYPE_SII9022,
 183    .parent        = TYPE_I2C_SLAVE,
 184    .instance_size = sizeof(sii9022_state),
 185    .class_init    = sii9022_class_init,
 186};
 187
 188static void sii9022_register_types(void)
 189{
 190    type_register_static(&sii9022_info);
 191}
 192
 193type_init(sii9022_register_types)
 194