178c71af8SPeter Maydell /* A simple I2C slave for returning monitor EDID data via DDC. 278c71af8SPeter Maydell * 378c71af8SPeter Maydell * Copyright (c) 2011 Linaro Limited 478c71af8SPeter Maydell * Written by Peter Maydell 578c71af8SPeter Maydell * 678c71af8SPeter Maydell * This program is free software; you can redistribute it and/or modify 778c71af8SPeter Maydell * it under the terms of the GNU General Public License version 2 as 878c71af8SPeter Maydell * published by the Free Software Foundation. 978c71af8SPeter Maydell * 1078c71af8SPeter Maydell * This program is distributed in the hope that it will be useful, 1178c71af8SPeter Maydell * but WITHOUT ANY WARRANTY; without even the implied warranty of 1278c71af8SPeter Maydell * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1378c71af8SPeter Maydell * GNU General Public License for more details. 1478c71af8SPeter Maydell * 1578c71af8SPeter Maydell * You should have received a copy of the GNU General Public License along 1678c71af8SPeter Maydell * with this program; if not, see <http://www.gnu.org/licenses/>. 1778c71af8SPeter Maydell */ 1878c71af8SPeter Maydell 1978c71af8SPeter Maydell #include "qemu/osdep.h" 2078c71af8SPeter Maydell #include "qemu/log.h" 210b8fa32fSMarkus Armbruster #include "qemu/module.h" 2278c71af8SPeter Maydell #include "hw/i2c/i2c.h" 23a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 24d6454270SMarkus Armbruster #include "migration/vmstate.h" 256306cae2SPaolo Bonzini #include "hw/display/i2c-ddc.h" 2678c71af8SPeter Maydell 2778c71af8SPeter Maydell #ifndef DEBUG_I2CDDC 2878c71af8SPeter Maydell #define DEBUG_I2CDDC 0 2978c71af8SPeter Maydell #endif 3078c71af8SPeter Maydell 3178c71af8SPeter Maydell #define DPRINTF(fmt, ...) do { \ 3278c71af8SPeter Maydell if (DEBUG_I2CDDC) { \ 3378c71af8SPeter Maydell qemu_log("i2c-ddc: " fmt , ## __VA_ARGS__); \ 3478c71af8SPeter Maydell } \ 352562755eSEric Blake } while (0) 3678c71af8SPeter Maydell 3778c71af8SPeter Maydell static void i2c_ddc_reset(DeviceState *ds) 3878c71af8SPeter Maydell { 3978c71af8SPeter Maydell I2CDDCState *s = I2CDDC(ds); 4078c71af8SPeter Maydell 4178c71af8SPeter Maydell s->firstbyte = false; 4278c71af8SPeter Maydell s->reg = 0; 4378c71af8SPeter Maydell } 4478c71af8SPeter Maydell 45d307c28cSCorey Minyard static int i2c_ddc_event(I2CSlave *i2c, enum i2c_event event) 4678c71af8SPeter Maydell { 4778c71af8SPeter Maydell I2CDDCState *s = I2CDDC(i2c); 4878c71af8SPeter Maydell 4978c71af8SPeter Maydell if (event == I2C_START_SEND) { 5078c71af8SPeter Maydell s->firstbyte = true; 5178c71af8SPeter Maydell } 52d307c28cSCorey Minyard 53d307c28cSCorey Minyard return 0; 5478c71af8SPeter Maydell } 5578c71af8SPeter Maydell 562ac4c5f4SCorey Minyard static uint8_t i2c_ddc_rx(I2CSlave *i2c) 5778c71af8SPeter Maydell { 5878c71af8SPeter Maydell I2CDDCState *s = I2CDDC(i2c); 5978c71af8SPeter Maydell 6078c71af8SPeter Maydell int value; 61b05b2678SGerd Hoffmann value = s->edid_blob[s->reg % sizeof(s->edid_blob)]; 6278c71af8SPeter Maydell s->reg++; 6378c71af8SPeter Maydell return value; 6478c71af8SPeter Maydell } 6578c71af8SPeter Maydell 6678c71af8SPeter Maydell static int i2c_ddc_tx(I2CSlave *i2c, uint8_t data) 6778c71af8SPeter Maydell { 6878c71af8SPeter Maydell I2CDDCState *s = I2CDDC(i2c); 6978c71af8SPeter Maydell if (s->firstbyte) { 7078c71af8SPeter Maydell s->reg = data; 7178c71af8SPeter Maydell s->firstbyte = false; 7278c71af8SPeter Maydell DPRINTF("[EDID] Written new pointer: %u\n", data); 73839a2b28SLinus Walleij return 0; 7478c71af8SPeter Maydell } 7578c71af8SPeter Maydell 7678c71af8SPeter Maydell /* Ignore all writes */ 7778c71af8SPeter Maydell s->reg++; 78839a2b28SLinus Walleij return 0; 7978c71af8SPeter Maydell } 8078c71af8SPeter Maydell 8178c71af8SPeter Maydell static void i2c_ddc_init(Object *obj) 8278c71af8SPeter Maydell { 8378c71af8SPeter Maydell I2CDDCState *s = I2CDDC(obj); 84715eb05bSGerd Hoffmann 85715eb05bSGerd Hoffmann qemu_edid_generate(s->edid_blob, sizeof(s->edid_blob), &s->edid_info); 8678c71af8SPeter Maydell } 8778c71af8SPeter Maydell 8878c71af8SPeter Maydell static const VMStateDescription vmstate_i2c_ddc = { 8978c71af8SPeter Maydell .name = TYPE_I2CDDC, 9078c71af8SPeter Maydell .version_id = 1, 91*f0613160SRichard Henderson .fields = (const VMStateField[]) { 9278c71af8SPeter Maydell VMSTATE_BOOL(firstbyte, I2CDDCState), 9378c71af8SPeter Maydell VMSTATE_UINT8(reg, I2CDDCState), 9478c71af8SPeter Maydell VMSTATE_END_OF_LIST() 9578c71af8SPeter Maydell } 9678c71af8SPeter Maydell }; 9778c71af8SPeter Maydell 98715eb05bSGerd Hoffmann static Property i2c_ddc_properties[] = { 99715eb05bSGerd Hoffmann DEFINE_EDID_PROPERTIES(I2CDDCState, edid_info), 100715eb05bSGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 101715eb05bSGerd Hoffmann }; 102715eb05bSGerd Hoffmann 10378c71af8SPeter Maydell static void i2c_ddc_class_init(ObjectClass *oc, void *data) 10478c71af8SPeter Maydell { 10578c71af8SPeter Maydell DeviceClass *dc = DEVICE_CLASS(oc); 10678c71af8SPeter Maydell I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc); 10778c71af8SPeter Maydell 10878c71af8SPeter Maydell dc->reset = i2c_ddc_reset; 10978c71af8SPeter Maydell dc->vmsd = &vmstate_i2c_ddc; 1104f67d30bSMarc-André Lureau device_class_set_props(dc, i2c_ddc_properties); 11178c71af8SPeter Maydell isc->event = i2c_ddc_event; 11278c71af8SPeter Maydell isc->recv = i2c_ddc_rx; 11378c71af8SPeter Maydell isc->send = i2c_ddc_tx; 11478c71af8SPeter Maydell } 11578c71af8SPeter Maydell 1165e78c98bSBernhard Beschow static const TypeInfo i2c_ddc_info = { 11778c71af8SPeter Maydell .name = TYPE_I2CDDC, 11878c71af8SPeter Maydell .parent = TYPE_I2C_SLAVE, 11978c71af8SPeter Maydell .instance_size = sizeof(I2CDDCState), 12078c71af8SPeter Maydell .instance_init = i2c_ddc_init, 12178c71af8SPeter Maydell .class_init = i2c_ddc_class_init 12278c71af8SPeter Maydell }; 12378c71af8SPeter Maydell 12478c71af8SPeter Maydell static void ddc_register_devices(void) 12578c71af8SPeter Maydell { 12678c71af8SPeter Maydell type_register_static(&i2c_ddc_info); 12778c71af8SPeter Maydell } 12878c71af8SPeter Maydell 12978c71af8SPeter Maydell type_init(ddc_register_devices); 130