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