16306cae2SPaolo Bonzini /* A simple I2C slave for returning monitor EDID data via DDC.
26306cae2SPaolo Bonzini *
36306cae2SPaolo Bonzini * Copyright (c) 2011 Linaro Limited
46306cae2SPaolo Bonzini * Written by Peter Maydell
56306cae2SPaolo Bonzini *
66306cae2SPaolo Bonzini * This program is free software; you can redistribute it and/or modify
76306cae2SPaolo Bonzini * it under the terms of the GNU General Public License version 2 as
86306cae2SPaolo Bonzini * published by the Free Software Foundation.
96306cae2SPaolo Bonzini *
106306cae2SPaolo Bonzini * This program is distributed in the hope that it will be useful,
116306cae2SPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of
126306cae2SPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
136306cae2SPaolo Bonzini * GNU General Public License for more details.
146306cae2SPaolo Bonzini *
156306cae2SPaolo Bonzini * You should have received a copy of the GNU General Public License along
166306cae2SPaolo Bonzini * with this program; if not, see <http://www.gnu.org/licenses/>.
176306cae2SPaolo Bonzini */
186306cae2SPaolo Bonzini
196306cae2SPaolo Bonzini #include "qemu/osdep.h"
206306cae2SPaolo Bonzini #include "qemu/log.h"
210b8fa32fSMarkus Armbruster #include "qemu/module.h"
226306cae2SPaolo Bonzini #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"
266306cae2SPaolo Bonzini
276306cae2SPaolo Bonzini #ifndef DEBUG_I2CDDC
286306cae2SPaolo Bonzini #define DEBUG_I2CDDC 0
296306cae2SPaolo Bonzini #endif
306306cae2SPaolo Bonzini
316306cae2SPaolo Bonzini #define DPRINTF(fmt, ...) do { \
326306cae2SPaolo Bonzini if (DEBUG_I2CDDC) { \
336306cae2SPaolo Bonzini qemu_log("i2c-ddc: " fmt , ## __VA_ARGS__); \
346306cae2SPaolo Bonzini } \
356306cae2SPaolo Bonzini } while (0)
366306cae2SPaolo Bonzini
i2c_ddc_reset(DeviceState * ds)376306cae2SPaolo Bonzini static void i2c_ddc_reset(DeviceState *ds)
386306cae2SPaolo Bonzini {
396306cae2SPaolo Bonzini I2CDDCState *s = I2CDDC(ds);
406306cae2SPaolo Bonzini
416306cae2SPaolo Bonzini s->firstbyte = false;
426306cae2SPaolo Bonzini s->reg = 0;
436306cae2SPaolo Bonzini }
446306cae2SPaolo Bonzini
i2c_ddc_event(I2CSlave * i2c,enum i2c_event event)456306cae2SPaolo Bonzini static int i2c_ddc_event(I2CSlave *i2c, enum i2c_event event)
466306cae2SPaolo Bonzini {
476306cae2SPaolo Bonzini I2CDDCState *s = I2CDDC(i2c);
486306cae2SPaolo Bonzini
496306cae2SPaolo Bonzini if (event == I2C_START_SEND) {
506306cae2SPaolo Bonzini s->firstbyte = true;
516306cae2SPaolo Bonzini }
526306cae2SPaolo Bonzini
536306cae2SPaolo Bonzini return 0;
546306cae2SPaolo Bonzini }
556306cae2SPaolo Bonzini
i2c_ddc_rx(I2CSlave * i2c)566306cae2SPaolo Bonzini static uint8_t i2c_ddc_rx(I2CSlave *i2c)
576306cae2SPaolo Bonzini {
586306cae2SPaolo Bonzini I2CDDCState *s = I2CDDC(i2c);
596306cae2SPaolo Bonzini
606306cae2SPaolo Bonzini int value;
616306cae2SPaolo Bonzini value = s->edid_blob[s->reg % sizeof(s->edid_blob)];
626306cae2SPaolo Bonzini s->reg++;
636306cae2SPaolo Bonzini return value;
646306cae2SPaolo Bonzini }
656306cae2SPaolo Bonzini
i2c_ddc_tx(I2CSlave * i2c,uint8_t data)666306cae2SPaolo Bonzini static int i2c_ddc_tx(I2CSlave *i2c, uint8_t data)
676306cae2SPaolo Bonzini {
686306cae2SPaolo Bonzini I2CDDCState *s = I2CDDC(i2c);
696306cae2SPaolo Bonzini if (s->firstbyte) {
706306cae2SPaolo Bonzini s->reg = data;
716306cae2SPaolo Bonzini s->firstbyte = false;
726306cae2SPaolo Bonzini DPRINTF("[EDID] Written new pointer: %u\n", data);
736306cae2SPaolo Bonzini return 0;
746306cae2SPaolo Bonzini }
756306cae2SPaolo Bonzini
766306cae2SPaolo Bonzini /* Ignore all writes */
776306cae2SPaolo Bonzini s->reg++;
786306cae2SPaolo Bonzini return 0;
796306cae2SPaolo Bonzini }
806306cae2SPaolo Bonzini
i2c_ddc_init(Object * obj)816306cae2SPaolo Bonzini static void i2c_ddc_init(Object *obj)
826306cae2SPaolo Bonzini {
836306cae2SPaolo Bonzini I2CDDCState *s = I2CDDC(obj);
846306cae2SPaolo Bonzini
856306cae2SPaolo Bonzini qemu_edid_generate(s->edid_blob, sizeof(s->edid_blob), &s->edid_info);
866306cae2SPaolo Bonzini }
876306cae2SPaolo Bonzini
886306cae2SPaolo Bonzini static const VMStateDescription vmstate_i2c_ddc = {
896306cae2SPaolo Bonzini .name = TYPE_I2CDDC,
906306cae2SPaolo Bonzini .version_id = 1,
91f0613160SRichard Henderson .fields = (const VMStateField[]) {
926306cae2SPaolo Bonzini VMSTATE_BOOL(firstbyte, I2CDDCState),
936306cae2SPaolo Bonzini VMSTATE_UINT8(reg, I2CDDCState),
946306cae2SPaolo Bonzini VMSTATE_END_OF_LIST()
956306cae2SPaolo Bonzini }
966306cae2SPaolo Bonzini };
976306cae2SPaolo Bonzini
986306cae2SPaolo Bonzini static Property i2c_ddc_properties[] = {
996306cae2SPaolo Bonzini DEFINE_EDID_PROPERTIES(I2CDDCState, edid_info),
1006306cae2SPaolo Bonzini DEFINE_PROP_END_OF_LIST(),
1016306cae2SPaolo Bonzini };
1026306cae2SPaolo Bonzini
i2c_ddc_class_init(ObjectClass * oc,void * data)1036306cae2SPaolo Bonzini static void i2c_ddc_class_init(ObjectClass *oc, void *data)
1046306cae2SPaolo Bonzini {
1056306cae2SPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(oc);
1066306cae2SPaolo Bonzini I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc);
1076306cae2SPaolo Bonzini
108*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, i2c_ddc_reset);
1096306cae2SPaolo Bonzini dc->vmsd = &vmstate_i2c_ddc;
1104f67d30bSMarc-André Lureau device_class_set_props(dc, i2c_ddc_properties);
1116306cae2SPaolo Bonzini isc->event = i2c_ddc_event;
1126306cae2SPaolo Bonzini isc->recv = i2c_ddc_rx;
1136306cae2SPaolo Bonzini isc->send = i2c_ddc_tx;
1146306cae2SPaolo Bonzini }
1156306cae2SPaolo Bonzini
1165e78c98bSBernhard Beschow static const TypeInfo i2c_ddc_info = {
1176306cae2SPaolo Bonzini .name = TYPE_I2CDDC,
1186306cae2SPaolo Bonzini .parent = TYPE_I2C_SLAVE,
1196306cae2SPaolo Bonzini .instance_size = sizeof(I2CDDCState),
1206306cae2SPaolo Bonzini .instance_init = i2c_ddc_init,
1216306cae2SPaolo Bonzini .class_init = i2c_ddc_class_init
1226306cae2SPaolo Bonzini };
1236306cae2SPaolo Bonzini
ddc_register_devices(void)1246306cae2SPaolo Bonzini static void ddc_register_devices(void)
1256306cae2SPaolo Bonzini {
1266306cae2SPaolo Bonzini type_register_static(&i2c_ddc_info);
1276306cae2SPaolo Bonzini }
1286306cae2SPaolo Bonzini
1296306cae2SPaolo Bonzini type_init(ddc_register_devices);
130