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