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