1 /* 2 * dpcd.c 3 * 4 * Copyright (C) 2015 : GreenSocs Ltd 5 * http://www.greensocs.com/ , email: info@greensocs.com 6 * 7 * Developed by : 8 * Frederic Konrad <fred.konrad@greensocs.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation, either version 2 of the License, or 13 * (at your option)any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License along 21 * with this program; if not, see <http://www.gnu.org/licenses/>. 22 * 23 */ 24 25 /* 26 * This is a simple AUX slave which emulates a connected screen. 27 */ 28 29 #include "qemu/osdep.h" 30 #include "qemu/log.h" 31 #include "qemu/module.h" 32 #include "hw/misc/auxbus.h" 33 #include "migration/vmstate.h" 34 #include "hw/display/dpcd.h" 35 36 #ifndef DEBUG_DPCD 37 #define DEBUG_DPCD 0 38 #endif 39 40 #define DPRINTF(fmt, ...) do { \ 41 if (DEBUG_DPCD) { \ 42 qemu_log("dpcd: " fmt, ## __VA_ARGS__); \ 43 } \ 44 } while (0) 45 46 #define DPCD_READABLE_AREA 0x600 47 48 struct DPCDState { 49 /*< private >*/ 50 AUXSlave parent_obj; 51 52 /*< public >*/ 53 /* 54 * The DCPD is 0x7FFFF length but read as 0 after offset 0x5FF. 55 */ 56 uint8_t dpcd_info[DPCD_READABLE_AREA]; 57 58 MemoryRegion iomem; 59 }; 60 61 static uint64_t dpcd_read(void *opaque, hwaddr offset, unsigned size) 62 { 63 uint8_t ret; 64 DPCDState *e = DPCD(opaque); 65 66 if (offset < DPCD_READABLE_AREA) { 67 ret = e->dpcd_info[offset]; 68 } else { 69 qemu_log_mask(LOG_GUEST_ERROR, "dpcd: Bad offset 0x%" HWADDR_PRIX "\n", 70 offset); 71 ret = 0; 72 } 73 74 DPRINTF("read 0x%" PRIX8 " @0x%" HWADDR_PRIX "\n", ret, offset); 75 return ret; 76 } 77 78 static void dpcd_write(void *opaque, hwaddr offset, uint64_t value, 79 unsigned size) 80 { 81 DPCDState *e = DPCD(opaque); 82 83 DPRINTF("write 0x%" PRIX8 " @0x%" HWADDR_PRIX "\n", (uint8_t)value, offset); 84 85 if (offset < DPCD_READABLE_AREA) { 86 e->dpcd_info[offset] = value; 87 } else { 88 qemu_log_mask(LOG_GUEST_ERROR, "dpcd: Bad offset 0x%" HWADDR_PRIX "\n", 89 offset); 90 } 91 } 92 93 static const MemoryRegionOps aux_ops = { 94 .read = dpcd_read, 95 .write = dpcd_write, 96 .valid = { 97 .min_access_size = 1, 98 .max_access_size = 1, 99 }, 100 .impl = { 101 .min_access_size = 1, 102 .max_access_size = 1, 103 }, 104 }; 105 106 static void dpcd_reset(DeviceState *dev) 107 { 108 DPCDState *s = DPCD(dev); 109 110 memset(&(s->dpcd_info), 0, sizeof(s->dpcd_info)); 111 112 s->dpcd_info[DPCD_REVISION] = DPCD_REV_1_0; 113 s->dpcd_info[DPCD_MAX_LINK_RATE] = DPCD_5_4GBPS; 114 s->dpcd_info[DPCD_MAX_LANE_COUNT] = DPCD_FOUR_LANES; 115 s->dpcd_info[DPCD_RECEIVE_PORT0_CAP_0] = DPCD_EDID_PRESENT; 116 /* buffer size */ 117 s->dpcd_info[DPCD_RECEIVE_PORT0_CAP_1] = 0xFF; 118 119 s->dpcd_info[DPCD_LANE0_1_STATUS] = DPCD_LANE0_CR_DONE 120 | DPCD_LANE0_CHANNEL_EQ_DONE 121 | DPCD_LANE0_SYMBOL_LOCKED 122 | DPCD_LANE1_CR_DONE 123 | DPCD_LANE1_CHANNEL_EQ_DONE 124 | DPCD_LANE1_SYMBOL_LOCKED; 125 s->dpcd_info[DPCD_LANE2_3_STATUS] = DPCD_LANE2_CR_DONE 126 | DPCD_LANE2_CHANNEL_EQ_DONE 127 | DPCD_LANE2_SYMBOL_LOCKED 128 | DPCD_LANE3_CR_DONE 129 | DPCD_LANE3_CHANNEL_EQ_DONE 130 | DPCD_LANE3_SYMBOL_LOCKED; 131 132 s->dpcd_info[DPCD_LANE_ALIGN_STATUS_UPDATED] = DPCD_INTERLANE_ALIGN_DONE; 133 s->dpcd_info[DPCD_SINK_STATUS] = DPCD_RECEIVE_PORT_0_STATUS; 134 } 135 136 static void dpcd_init(Object *obj) 137 { 138 DPCDState *s = DPCD(obj); 139 140 memory_region_init_io(&s->iomem, obj, &aux_ops, s, TYPE_DPCD, 0x7FFFF); 141 aux_init_mmio(AUX_SLAVE(obj), &s->iomem); 142 } 143 144 static const VMStateDescription vmstate_dpcd = { 145 .name = TYPE_DPCD, 146 .version_id = 0, 147 .minimum_version_id = 0, 148 .fields = (VMStateField[]) { 149 VMSTATE_UINT8_ARRAY_V(dpcd_info, DPCDState, DPCD_READABLE_AREA, 0), 150 VMSTATE_END_OF_LIST() 151 } 152 }; 153 154 static void dpcd_class_init(ObjectClass *oc, void *data) 155 { 156 DeviceClass *dc = DEVICE_CLASS(oc); 157 158 dc->reset = dpcd_reset; 159 dc->vmsd = &vmstate_dpcd; 160 } 161 162 static const TypeInfo dpcd_info = { 163 .name = TYPE_DPCD, 164 .parent = TYPE_AUX_SLAVE, 165 .instance_size = sizeof(DPCDState), 166 .class_init = dpcd_class_init, 167 .instance_init = dpcd_init, 168 }; 169 170 static void dpcd_register_types(void) 171 { 172 type_register_static(&dpcd_info); 173 } 174 175 type_init(dpcd_register_types) 176