xref: /openbmc/qemu/hw/display/i2c-ddc.c (revision 650d103d3ea959212f826acb9d3fe80cf30e347b)
1 /* A simple I2C slave for returning monitor EDID data via DDC.
2  *
3  * Copyright (c) 2011 Linaro Limited
4  * Written by Peter Maydell
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License version 2 as
8  *  published by the Free Software Foundation.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "qemu/osdep.h"
20 #include "qemu/log.h"
21 #include "qemu/module.h"
22 #include "hw/i2c/i2c.h"
23 #include "migration/vmstate.h"
24 #include "hw/display/i2c-ddc.h"
25 
26 #ifndef DEBUG_I2CDDC
27 #define DEBUG_I2CDDC 0
28 #endif
29 
30 #define DPRINTF(fmt, ...) do {                                                 \
31     if (DEBUG_I2CDDC) {                                                        \
32         qemu_log("i2c-ddc: " fmt , ## __VA_ARGS__);                            \
33     }                                                                          \
34 } while (0)
35 
36 static void i2c_ddc_reset(DeviceState *ds)
37 {
38     I2CDDCState *s = I2CDDC(ds);
39 
40     s->firstbyte = false;
41     s->reg = 0;
42 }
43 
44 static int i2c_ddc_event(I2CSlave *i2c, enum i2c_event event)
45 {
46     I2CDDCState *s = I2CDDC(i2c);
47 
48     if (event == I2C_START_SEND) {
49         s->firstbyte = true;
50     }
51 
52     return 0;
53 }
54 
55 static uint8_t i2c_ddc_rx(I2CSlave *i2c)
56 {
57     I2CDDCState *s = I2CDDC(i2c);
58 
59     int value;
60     value = s->edid_blob[s->reg % sizeof(s->edid_blob)];
61     s->reg++;
62     return value;
63 }
64 
65 static int i2c_ddc_tx(I2CSlave *i2c, uint8_t data)
66 {
67     I2CDDCState *s = I2CDDC(i2c);
68     if (s->firstbyte) {
69         s->reg = data;
70         s->firstbyte = false;
71         DPRINTF("[EDID] Written new pointer: %u\n", data);
72         return 0;
73     }
74 
75     /* Ignore all writes */
76     s->reg++;
77     return 0;
78 }
79 
80 static void i2c_ddc_init(Object *obj)
81 {
82     I2CDDCState *s = I2CDDC(obj);
83 
84     qemu_edid_generate(s->edid_blob, sizeof(s->edid_blob), &s->edid_info);
85 }
86 
87 static const VMStateDescription vmstate_i2c_ddc = {
88     .name = TYPE_I2CDDC,
89     .version_id = 1,
90     .fields = (VMStateField[]) {
91         VMSTATE_BOOL(firstbyte, I2CDDCState),
92         VMSTATE_UINT8(reg, I2CDDCState),
93         VMSTATE_END_OF_LIST()
94     }
95 };
96 
97 static Property i2c_ddc_properties[] = {
98     DEFINE_EDID_PROPERTIES(I2CDDCState, edid_info),
99     DEFINE_PROP_END_OF_LIST(),
100 };
101 
102 static void i2c_ddc_class_init(ObjectClass *oc, void *data)
103 {
104     DeviceClass *dc = DEVICE_CLASS(oc);
105     I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc);
106 
107     dc->reset = i2c_ddc_reset;
108     dc->vmsd = &vmstate_i2c_ddc;
109     dc->props = i2c_ddc_properties;
110     isc->event = i2c_ddc_event;
111     isc->recv = i2c_ddc_rx;
112     isc->send = i2c_ddc_tx;
113 }
114 
115 static TypeInfo i2c_ddc_info = {
116     .name = TYPE_I2CDDC,
117     .parent = TYPE_I2C_SLAVE,
118     .instance_size = sizeof(I2CDDCState),
119     .instance_init = i2c_ddc_init,
120     .class_init = i2c_ddc_class_init
121 };
122 
123 static void ddc_register_devices(void)
124 {
125     type_register_static(&i2c_ddc_info);
126 }
127 
128 type_init(ddc_register_devices);
129