xref: /openbmc/qemu/hw/display/dpcd.c (revision c16de0d9)
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     dc->reset = 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