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 "hw/display/dpcd.h" 34 35 #ifndef DEBUG_DPCD 36 #define DEBUG_DPCD 0 37 #endif 38 39 #define DPRINTF(fmt, ...) do { \ 40 if (DEBUG_DPCD) { \ 41 qemu_log("dpcd: " fmt, ## __VA_ARGS__); \ 42 } \ 43 } while (0) 44 45 #define DPCD_READABLE_AREA 0x600 46 47 struct DPCDState { 48 /*< private >*/ 49 AUXSlave parent_obj; 50 51 /*< public >*/ 52 /* 53 * The DCPD is 0x7FFFF length but read as 0 after offset 0x5FF. 54 */ 55 uint8_t dpcd_info[DPCD_READABLE_AREA]; 56 57 MemoryRegion iomem; 58 }; 59 60 static uint64_t dpcd_read(void *opaque, hwaddr offset, unsigned size) 61 { 62 uint8_t ret; 63 DPCDState *e = DPCD(opaque); 64 65 if (offset < DPCD_READABLE_AREA) { 66 ret = e->dpcd_info[offset]; 67 } else { 68 qemu_log_mask(LOG_GUEST_ERROR, "dpcd: Bad offset 0x%" HWADDR_PRIX "\n", 69 offset); 70 ret = 0; 71 } 72 73 DPRINTF("read 0x%" PRIX8 " @0x%" HWADDR_PRIX "\n", ret, offset); 74 return ret; 75 } 76 77 static void dpcd_write(void *opaque, hwaddr offset, uint64_t value, 78 unsigned size) 79 { 80 DPCDState *e = DPCD(opaque); 81 82 DPRINTF("write 0x%" PRIX8 " @0x%" HWADDR_PRIX "\n", (uint8_t)value, offset); 83 84 if (offset < DPCD_READABLE_AREA) { 85 e->dpcd_info[offset] = value; 86 } else { 87 qemu_log_mask(LOG_GUEST_ERROR, "dpcd: Bad offset 0x%" HWADDR_PRIX "\n", 88 offset); 89 } 90 } 91 92 static const MemoryRegionOps aux_ops = { 93 .read = dpcd_read, 94 .write = dpcd_write, 95 .valid = { 96 .min_access_size = 1, 97 .max_access_size = 1, 98 }, 99 .impl = { 100 .min_access_size = 1, 101 .max_access_size = 1, 102 }, 103 }; 104 105 static void dpcd_reset(DeviceState *dev) 106 { 107 DPCDState *s = DPCD(dev); 108 109 memset(&(s->dpcd_info), 0, sizeof(s->dpcd_info)); 110 111 s->dpcd_info[DPCD_REVISION] = DPCD_REV_1_0; 112 s->dpcd_info[DPCD_MAX_LINK_RATE] = DPCD_5_4GBPS; 113 s->dpcd_info[DPCD_MAX_LANE_COUNT] = DPCD_FOUR_LANES; 114 s->dpcd_info[DPCD_RECEIVE_PORT0_CAP_0] = DPCD_EDID_PRESENT; 115 /* buffer size */ 116 s->dpcd_info[DPCD_RECEIVE_PORT0_CAP_1] = 0xFF; 117 118 s->dpcd_info[DPCD_LANE0_1_STATUS] = DPCD_LANE0_CR_DONE 119 | DPCD_LANE0_CHANNEL_EQ_DONE 120 | DPCD_LANE0_SYMBOL_LOCKED 121 | DPCD_LANE1_CR_DONE 122 | DPCD_LANE1_CHANNEL_EQ_DONE 123 | DPCD_LANE1_SYMBOL_LOCKED; 124 s->dpcd_info[DPCD_LANE2_3_STATUS] = DPCD_LANE2_CR_DONE 125 | DPCD_LANE2_CHANNEL_EQ_DONE 126 | DPCD_LANE2_SYMBOL_LOCKED 127 | DPCD_LANE3_CR_DONE 128 | DPCD_LANE3_CHANNEL_EQ_DONE 129 | DPCD_LANE3_SYMBOL_LOCKED; 130 131 s->dpcd_info[DPCD_LANE_ALIGN_STATUS_UPDATED] = DPCD_INTERLANE_ALIGN_DONE; 132 s->dpcd_info[DPCD_SINK_STATUS] = DPCD_RECEIVE_PORT_0_STATUS; 133 } 134 135 static void dpcd_init(Object *obj) 136 { 137 DPCDState *s = DPCD(obj); 138 139 memory_region_init_io(&s->iomem, obj, &aux_ops, s, TYPE_DPCD, 0x7FFFF); 140 aux_init_mmio(AUX_SLAVE(obj), &s->iomem); 141 } 142 143 static const VMStateDescription vmstate_dpcd = { 144 .name = TYPE_DPCD, 145 .version_id = 0, 146 .minimum_version_id = 0, 147 .fields = (VMStateField[]) { 148 VMSTATE_UINT8_ARRAY_V(dpcd_info, DPCDState, DPCD_READABLE_AREA, 0), 149 VMSTATE_END_OF_LIST() 150 } 151 }; 152 153 static void dpcd_class_init(ObjectClass *oc, void *data) 154 { 155 DeviceClass *dc = DEVICE_CLASS(oc); 156 157 dc->reset = dpcd_reset; 158 dc->vmsd = &vmstate_dpcd; 159 } 160 161 static const TypeInfo dpcd_info = { 162 .name = TYPE_DPCD, 163 .parent = TYPE_AUX_SLAVE, 164 .instance_size = sizeof(DPCDState), 165 .class_init = dpcd_class_init, 166 .instance_init = dpcd_init, 167 }; 168 169 static void dpcd_register_types(void) 170 { 171 type_register_static(&dpcd_info); 172 } 173 174 type_init(dpcd_register_types) 175