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 "hw/display/i2c-ddc.h"
  20#include "trace.h"
  21
  22#define SII9022_SYS_CTRL_DATA 0x1a
  23#define SII9022_SYS_CTRL_PWR_DWN 0x10
  24#define SII9022_SYS_CTRL_AV_MUTE 0x08
  25#define SII9022_SYS_CTRL_DDC_BUS_REQ 0x04
  26#define SII9022_SYS_CTRL_DDC_BUS_GRTD 0x02
  27#define SII9022_SYS_CTRL_OUTPUT_MODE 0x01
  28#define SII9022_SYS_CTRL_OUTPUT_HDMI 1
  29#define SII9022_SYS_CTRL_OUTPUT_DVI 0
  30#define SII9022_REG_CHIPID 0x1b
  31#define SII9022_INT_ENABLE 0x3c
  32#define SII9022_INT_STATUS 0x3d
  33#define SII9022_INT_STATUS_HOTPLUG 0x01;
  34#define SII9022_INT_STATUS_PLUGGED 0x04;
  35
  36#define TYPE_SII9022 "sii9022"
  37#define SII9022(obj) OBJECT_CHECK(sii9022_state, (obj), TYPE_SII9022)
  38
  39typedef struct sii9022_state {
  40    I2CSlave parent_obj;
  41    uint8_t ptr;
  42    bool addr_byte;
  43    bool ddc_req;
  44    bool ddc_skip_finish;
  45    bool ddc;
  46} sii9022_state;
  47
  48static const VMStateDescription vmstate_sii9022 = {
  49    .name = "sii9022",
  50    .version_id = 1,
  51    .minimum_version_id = 1,
  52    .fields = (VMStateField[]) {
  53        VMSTATE_I2C_SLAVE(parent_obj, sii9022_state),
  54        VMSTATE_UINT8(ptr, sii9022_state),
  55        VMSTATE_BOOL(addr_byte, sii9022_state),
  56        VMSTATE_BOOL(ddc_req, sii9022_state),
  57        VMSTATE_BOOL(ddc_skip_finish, sii9022_state),
  58        VMSTATE_BOOL(ddc, sii9022_state),
  59        VMSTATE_END_OF_LIST()
  60    }
  61};
  62
  63static int sii9022_event(I2CSlave *i2c, enum i2c_event event)
  64{
  65    sii9022_state *s = SII9022(i2c);
  66
  67    switch (event) {
  68    case I2C_START_SEND:
  69        s->addr_byte = true;
  70        break;
  71    case I2C_START_RECV:
  72        break;
  73    case I2C_FINISH:
  74        break;
  75    case I2C_NACK:
  76        break;
  77    }
  78
  79    return 0;
  80}
  81
  82static uint8_t sii9022_rx(I2CSlave *i2c)
  83{
  84    sii9022_state *s = SII9022(i2c);
  85    uint8_t res = 0x00;
  86
  87    switch (s->ptr) {
  88    case SII9022_SYS_CTRL_DATA:
  89        if (s->ddc_req) {
  90            /* Acknowledge DDC bus request */
  91            res = SII9022_SYS_CTRL_DDC_BUS_GRTD | SII9022_SYS_CTRL_DDC_BUS_REQ;
  92        }
  93        break;
  94    case SII9022_REG_CHIPID:
  95        res = 0xb0;
  96        break;
  97    case SII9022_INT_STATUS:
  98        /* Something is cold-plugged in, no interrupts */
  99        res = SII9022_INT_STATUS_PLUGGED;
 100        break;
 101    default:
 102        break;
 103    }
 104
 105    trace_sii9022_read_reg(s->ptr, res);
 106    s->ptr++;
 107
 108    return res;
 109}
 110
 111static int sii9022_tx(I2CSlave *i2c, uint8_t data)
 112{
 113    sii9022_state *s = SII9022(i2c);
 114
 115    if (s->addr_byte) {
 116        s->ptr = data;
 117        s->addr_byte = false;
 118        return 0;
 119    }
 120
 121    switch (s->ptr) {
 122    case SII9022_SYS_CTRL_DATA:
 123        if (data & SII9022_SYS_CTRL_DDC_BUS_REQ) {
 124            s->ddc_req = true;
 125            if (data & SII9022_SYS_CTRL_DDC_BUS_GRTD) {
 126                s->ddc = true;
 127                /* Skip this finish since we just switched to DDC */
 128                s->ddc_skip_finish = true;
 129                trace_sii9022_switch_mode("DDC");
 130            }
 131        } else {
 132            s->ddc_req = false;
 133            s->ddc = false;
 134            trace_sii9022_switch_mode("normal");
 135        }
 136        break;
 137    default:
 138        break;
 139    }
 140
 141    trace_sii9022_write_reg(s->ptr, data);
 142    s->ptr++;
 143
 144    return 0;
 145}
 146
 147static void sii9022_reset(DeviceState *dev)
 148{
 149    sii9022_state *s = SII9022(dev);
 150
 151    s->ptr = 0;
 152    s->addr_byte = false;
 153    s->ddc_req = false;
 154    s->ddc_skip_finish = false;
 155    s->ddc = false;
 156}
 157
 158static void sii9022_realize(DeviceState *dev, Error **errp)
 159{
 160    I2CBus *bus;
 161
 162    bus = I2C_BUS(qdev_get_parent_bus(dev));
 163    i2c_create_slave(bus, TYPE_I2CDDC, 0x50);
 164}
 165
 166static void sii9022_class_init(ObjectClass *klass, void *data)
 167{
 168    DeviceClass *dc = DEVICE_CLASS(klass);
 169    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
 170
 171    k->event = sii9022_event;
 172    k->recv = sii9022_rx;
 173    k->send = sii9022_tx;
 174    dc->reset = sii9022_reset;
 175    dc->realize = sii9022_realize;
 176    dc->vmsd = &vmstate_sii9022;
 177}
 178
 179static const TypeInfo sii9022_info = {
 180    .name          = TYPE_SII9022,
 181    .parent        = TYPE_I2C_SLAVE,
 182    .instance_size = sizeof(sii9022_state),
 183    .class_init    = sii9022_class_init,
 184};
 185
 186static void sii9022_register_types(void)
 187{
 188    type_register_static(&sii9022_info);
 189}
 190
 191type_init(sii9022_register_types)
 192