Peter Maydell | 78c71af | 2016-06-14 15:59:15 +0100 | [diff] [blame^] | 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 "hw/i2c/i2c.h" |
| 22 | #include "hw/i2c/i2c-ddc.h" |
| 23 | |
| 24 | #ifndef DEBUG_I2CDDC |
| 25 | #define DEBUG_I2CDDC 0 |
| 26 | #endif |
| 27 | |
| 28 | #define DPRINTF(fmt, ...) do { \ |
| 29 | if (DEBUG_I2CDDC) { \ |
| 30 | qemu_log("i2c-ddc: " fmt , ## __VA_ARGS__); \ |
| 31 | } \ |
| 32 | } while (0); |
| 33 | |
| 34 | /* Structure defining a monitor's characteristics in a |
| 35 | * readable format: this should be passed to build_edid_blob() |
| 36 | * to convert it into the 128 byte binary EDID blob. |
| 37 | * Not all bits of the EDID are customisable here. |
| 38 | */ |
| 39 | struct EDIDData { |
| 40 | char manuf_id[3]; /* three upper case letters */ |
| 41 | uint16_t product_id; |
| 42 | uint32_t serial_no; |
| 43 | uint8_t manuf_week; |
| 44 | int manuf_year; |
| 45 | uint8_t h_cm; |
| 46 | uint8_t v_cm; |
| 47 | uint8_t gamma; |
| 48 | char monitor_name[14]; |
| 49 | char serial_no_string[14]; |
| 50 | /* Range limits */ |
| 51 | uint8_t vmin; /* Hz */ |
| 52 | uint8_t vmax; /* Hz */ |
| 53 | uint8_t hmin; /* kHz */ |
| 54 | uint8_t hmax; /* kHz */ |
| 55 | uint8_t pixclock; /* MHz / 10 */ |
| 56 | uint8_t timing_data[18]; |
| 57 | }; |
| 58 | |
| 59 | typedef struct EDIDData EDIDData; |
| 60 | |
| 61 | /* EDID data for a simple LCD monitor */ |
| 62 | static const EDIDData lcd_edid = { |
| 63 | /* The manuf_id ought really to be an assigned EISA ID */ |
| 64 | .manuf_id = "QMU", |
| 65 | .product_id = 0, |
| 66 | .serial_no = 1, |
| 67 | .manuf_week = 1, |
| 68 | .manuf_year = 2011, |
| 69 | .h_cm = 40, |
| 70 | .v_cm = 30, |
| 71 | .gamma = 0x78, |
| 72 | .monitor_name = "QEMU monitor", |
| 73 | .serial_no_string = "1", |
| 74 | .vmin = 40, |
| 75 | .vmax = 120, |
| 76 | .hmin = 30, |
| 77 | .hmax = 100, |
| 78 | .pixclock = 18, |
| 79 | .timing_data = { |
| 80 | /* Borrowed from a 21" LCD */ |
| 81 | 0x48, 0x3f, 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, |
| 82 | 0xc0, 0x13, 0x00, 0x98, 0x32, 0x11, 0x00, 0x00, 0x1e |
| 83 | } |
| 84 | }; |
| 85 | |
| 86 | static uint8_t manuf_char_to_int(char c) |
| 87 | { |
| 88 | return (c - 'A') & 0x1f; |
| 89 | } |
| 90 | |
| 91 | static void write_ascii_descriptor_block(uint8_t *descblob, uint8_t blocktype, |
| 92 | const char *string) |
| 93 | { |
| 94 | /* Write an EDID Descriptor Block of the "ascii string" type */ |
| 95 | int i; |
| 96 | descblob[0] = descblob[1] = descblob[2] = descblob[4] = 0; |
| 97 | descblob[3] = blocktype; |
| 98 | /* The rest is 13 bytes of ASCII; if less then the rest must |
| 99 | * be filled with newline then spaces |
| 100 | */ |
| 101 | for (i = 5; i < 19; i++) { |
| 102 | descblob[i] = string[i - 5]; |
| 103 | if (!descblob[i]) { |
| 104 | break; |
| 105 | } |
| 106 | } |
| 107 | if (i < 19) { |
| 108 | descblob[i++] = '\n'; |
| 109 | } |
| 110 | for ( ; i < 19; i++) { |
| 111 | descblob[i] = ' '; |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | static void write_range_limits_descriptor(const EDIDData *edid, |
| 116 | uint8_t *descblob) |
| 117 | { |
| 118 | int i; |
| 119 | descblob[0] = descblob[1] = descblob[2] = descblob[4] = 0; |
| 120 | descblob[3] = 0xfd; |
| 121 | descblob[5] = edid->vmin; |
| 122 | descblob[6] = edid->vmax; |
| 123 | descblob[7] = edid->hmin; |
| 124 | descblob[8] = edid->hmax; |
| 125 | descblob[9] = edid->pixclock; |
| 126 | descblob[10] = 0; |
| 127 | descblob[11] = 0xa; |
| 128 | for (i = 12; i < 19; i++) { |
| 129 | descblob[i] = 0x20; |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | static void build_edid_blob(const EDIDData *edid, uint8_t *blob) |
| 134 | { |
| 135 | /* Write an EDID 1.3 format blob (128 bytes) based |
| 136 | * on the EDIDData structure. |
| 137 | */ |
| 138 | int i; |
| 139 | uint8_t cksum; |
| 140 | |
| 141 | /* 00-07 : header */ |
| 142 | blob[0] = blob[7] = 0; |
| 143 | for (i = 1 ; i < 7; i++) { |
| 144 | blob[i] = 0xff; |
| 145 | } |
| 146 | /* 08-09 : manufacturer ID */ |
| 147 | blob[8] = (manuf_char_to_int(edid->manuf_id[0]) << 2) |
| 148 | | (manuf_char_to_int(edid->manuf_id[1]) >> 3); |
| 149 | blob[9] = (manuf_char_to_int(edid->manuf_id[1]) << 5) |
| 150 | | manuf_char_to_int(edid->manuf_id[2]); |
| 151 | /* 10-11 : product ID code */ |
| 152 | blob[10] = edid->product_id; |
| 153 | blob[11] = edid->product_id >> 8; |
| 154 | blob[12] = edid->serial_no; |
| 155 | blob[13] = edid->serial_no >> 8; |
| 156 | blob[14] = edid->serial_no >> 16; |
| 157 | blob[15] = edid->serial_no >> 24; |
| 158 | /* 16 : week of manufacture */ |
| 159 | blob[16] = edid->manuf_week; |
| 160 | /* 17 : year of manufacture - 1990 */ |
| 161 | blob[17] = edid->manuf_year - 1990; |
| 162 | /* 18, 19 : EDID version and revision */ |
| 163 | blob[18] = 1; |
| 164 | blob[19] = 3; |
| 165 | /* 20 - 24 : basic display parameters */ |
| 166 | /* We are always a digital display */ |
| 167 | blob[20] = 0x80; |
| 168 | /* 21, 22 : max h/v size in cm */ |
| 169 | blob[21] = edid->h_cm; |
| 170 | blob[22] = edid->v_cm; |
| 171 | /* 23 : gamma (divide by 100 then add 1 for actual value) */ |
| 172 | blob[23] = edid->gamma; |
| 173 | /* 24 feature support: no power management, RGB, preferred timing mode, |
| 174 | * standard colour space |
| 175 | */ |
| 176 | blob[24] = 0x0e; |
| 177 | /* 25 - 34 : chromaticity coordinates. These are the |
| 178 | * standard sRGB chromaticity values |
| 179 | */ |
| 180 | blob[25] = 0xee; |
| 181 | blob[26] = 0x91; |
| 182 | blob[27] = 0xa3; |
| 183 | blob[28] = 0x54; |
| 184 | blob[29] = 0x4c; |
| 185 | blob[30] = 0x99; |
| 186 | blob[31] = 0x26; |
| 187 | blob[32] = 0x0f; |
| 188 | blob[33] = 0x50; |
| 189 | blob[34] = 0x54; |
| 190 | /* 35, 36 : Established timings: claim to support everything */ |
| 191 | blob[35] = blob[36] = 0xff; |
| 192 | /* 37 : manufacturer's reserved timing: none */ |
| 193 | blob[37] = 0; |
| 194 | /* 38 - 53 : standard timing identification |
| 195 | * don't claim anything beyond what the 'established timings' |
| 196 | * already provide. Unused slots must be (0x1, 0x1) |
| 197 | */ |
| 198 | for (i = 38; i < 54; i++) { |
| 199 | blob[i] = 0x1; |
| 200 | } |
| 201 | /* 54 - 71 : descriptor block 1 : must be preferred timing data */ |
| 202 | memcpy(blob + 54, edid->timing_data, 18); |
| 203 | /* 72 - 89, 90 - 107, 108 - 125 : descriptor block 2, 3, 4 |
| 204 | * Order not important, but we must have a monitor name and a |
| 205 | * range limits descriptor. |
| 206 | */ |
| 207 | write_range_limits_descriptor(edid, blob + 72); |
| 208 | write_ascii_descriptor_block(blob + 90, 0xfc, edid->monitor_name); |
| 209 | write_ascii_descriptor_block(blob + 108, 0xff, edid->serial_no_string); |
| 210 | |
| 211 | /* 126 : extension flag */ |
| 212 | blob[126] = 0; |
| 213 | |
| 214 | cksum = 0; |
| 215 | for (i = 0; i < 127; i++) { |
| 216 | cksum += blob[i]; |
| 217 | } |
| 218 | /* 127 : checksum */ |
| 219 | blob[127] = -cksum; |
| 220 | if (DEBUG_I2CDDC) { |
| 221 | qemu_hexdump((char *)blob, stdout, "", 128); |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | static void i2c_ddc_reset(DeviceState *ds) |
| 226 | { |
| 227 | I2CDDCState *s = I2CDDC(ds); |
| 228 | |
| 229 | s->firstbyte = false; |
| 230 | s->reg = 0; |
| 231 | } |
| 232 | |
| 233 | static void i2c_ddc_event(I2CSlave *i2c, enum i2c_event event) |
| 234 | { |
| 235 | I2CDDCState *s = I2CDDC(i2c); |
| 236 | |
| 237 | if (event == I2C_START_SEND) { |
| 238 | s->firstbyte = true; |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | static int i2c_ddc_rx(I2CSlave *i2c) |
| 243 | { |
| 244 | I2CDDCState *s = I2CDDC(i2c); |
| 245 | |
| 246 | int value; |
| 247 | value = s->edid_blob[s->reg]; |
| 248 | s->reg++; |
| 249 | return value; |
| 250 | } |
| 251 | |
| 252 | static int i2c_ddc_tx(I2CSlave *i2c, uint8_t data) |
| 253 | { |
| 254 | I2CDDCState *s = I2CDDC(i2c); |
| 255 | if (s->firstbyte) { |
| 256 | s->reg = data; |
| 257 | s->firstbyte = false; |
| 258 | DPRINTF("[EDID] Written new pointer: %u\n", data); |
| 259 | return 1; |
| 260 | } |
| 261 | |
| 262 | /* Ignore all writes */ |
| 263 | s->reg++; |
| 264 | return 1; |
| 265 | } |
| 266 | |
| 267 | static void i2c_ddc_init(Object *obj) |
| 268 | { |
| 269 | I2CDDCState *s = I2CDDC(obj); |
| 270 | build_edid_blob(&lcd_edid, s->edid_blob); |
| 271 | } |
| 272 | |
| 273 | static const VMStateDescription vmstate_i2c_ddc = { |
| 274 | .name = TYPE_I2CDDC, |
| 275 | .version_id = 1, |
| 276 | .fields = (VMStateField[]) { |
| 277 | VMSTATE_BOOL(firstbyte, I2CDDCState), |
| 278 | VMSTATE_UINT8(reg, I2CDDCState), |
| 279 | VMSTATE_END_OF_LIST() |
| 280 | } |
| 281 | }; |
| 282 | |
| 283 | static void i2c_ddc_class_init(ObjectClass *oc, void *data) |
| 284 | { |
| 285 | DeviceClass *dc = DEVICE_CLASS(oc); |
| 286 | I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc); |
| 287 | |
| 288 | dc->reset = i2c_ddc_reset; |
| 289 | dc->vmsd = &vmstate_i2c_ddc; |
| 290 | isc->event = i2c_ddc_event; |
| 291 | isc->recv = i2c_ddc_rx; |
| 292 | isc->send = i2c_ddc_tx; |
| 293 | } |
| 294 | |
| 295 | static TypeInfo i2c_ddc_info = { |
| 296 | .name = TYPE_I2CDDC, |
| 297 | .parent = TYPE_I2C_SLAVE, |
| 298 | .instance_size = sizeof(I2CDDCState), |
| 299 | .instance_init = i2c_ddc_init, |
| 300 | .class_init = i2c_ddc_class_init |
| 301 | }; |
| 302 | |
| 303 | static void ddc_register_devices(void) |
| 304 | { |
| 305 | type_register_static(&i2c_ddc_info); |
| 306 | } |
| 307 | |
| 308 | type_init(ddc_register_devices); |