KONRAD Frederic | e27ed1b | 2016-06-14 15:59:15 +0100 | [diff] [blame] | 1 | /* |
Philippe Mathieu-Daudé | 91e7fd3 | 2020-05-26 08:22:45 +0200 | [diff] [blame] | 2 | * Xilinx Display Port Control Data |
KONRAD Frederic | e27ed1b | 2016-06-14 15:59:15 +0100 | [diff] [blame] | 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" |
Markus Armbruster | 0b8fa32 | 2019-05-23 16:35:07 +0200 | [diff] [blame] | 31 | #include "qemu/module.h" |
Peter Maydell | e0dadc1 | 2016-07-07 13:47:01 +0100 | [diff] [blame] | 32 | #include "hw/misc/auxbus.h" |
Markus Armbruster | d645427 | 2019-08-12 07:23:45 +0200 | [diff] [blame] | 33 | #include "migration/vmstate.h" |
KONRAD Frederic | e27ed1b | 2016-06-14 15:59:15 +0100 | [diff] [blame] | 34 | #include "hw/display/dpcd.h" |
Philippe Mathieu-Daudé | eeb1168 | 2020-05-26 08:22:46 +0200 | [diff] [blame] | 35 | #include "trace.h" |
KONRAD Frederic | e27ed1b | 2016-06-14 15:59:15 +0100 | [diff] [blame] | 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 | } |
Philippe Mathieu-Daudé | eeb1168 | 2020-05-26 08:22:46 +0200 | [diff] [blame] | 64 | trace_dpcd_read(offset, ret); |
KONRAD Frederic | e27ed1b | 2016-06-14 15:59:15 +0100 | [diff] [blame] | 65 | |
KONRAD Frederic | e27ed1b | 2016-06-14 15:59:15 +0100 | [diff] [blame] | 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 | |
Philippe Mathieu-Daudé | eeb1168 | 2020-05-26 08:22:46 +0200 | [diff] [blame] | 74 | trace_dpcd_write(offset, value); |
KONRAD Frederic | e27ed1b | 2016-06-14 15:59:15 +0100 | [diff] [blame] | 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 | |
Philippe Mathieu-Daudé | 91e7fd3 | 2020-05-26 08:22:45 +0200 | [diff] [blame] | 130 | memory_region_init_io(&s->iomem, obj, &aux_ops, s, TYPE_DPCD, 0x80000); |
KONRAD Frederic | e27ed1b | 2016-06-14 15:59:15 +0100 | [diff] [blame] | 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, |
Richard Henderson | f061316 | 2023-12-21 14:16:07 +1100 | [diff] [blame] | 138 | .fields = (const VMStateField[]) { |
KONRAD Frederic | e27ed1b | 2016-06-14 15:59:15 +0100 | [diff] [blame] | 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 | |
Peter Maydell | e3d0814 | 2024-09-13 15:31:44 +0100 | [diff] [blame] | 148 | device_class_set_legacy_reset(dc, dpcd_reset); |
KONRAD Frederic | e27ed1b | 2016-06-14 15:59:15 +0100 | [diff] [blame] | 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) |