xref: /openbmc/qemu/hw/display/i2c-ddc.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
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