1 /*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2016 Roman Gilg <subdiff@gmail.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 #include "drm_object.h"
10
11 #include <errno.h>
12
13 #include "drm_gpu.h"
14 #include "drm_pointer.h"
15 #include "logging.h"
16
17 namespace KWin
18 {
19
20 /*
21 * Definitions for class DrmObject
22 */
23
DrmObject(DrmGpu * gpu,uint32_t objectId,const QVector<PropertyDefinition> && vector,uint32_t objectType)24 DrmObject::DrmObject(DrmGpu *gpu, uint32_t objectId, const QVector<PropertyDefinition> &&vector, uint32_t objectType)
25 : m_gpu(gpu)
26 , m_id(objectId)
27 , m_objectType(objectType)
28 , m_propertyDefinitions(vector)
29 {
30 m_props.resize(m_propertyDefinitions.count());
31 }
32
~DrmObject()33 DrmObject::~DrmObject()
34 {
35 qDeleteAll(m_props);
36 }
37
initProps()38 bool DrmObject::initProps()
39 {
40 if (!updateProperties()) {
41 return false;
42 }
43 if (KWIN_DRM().isDebugEnabled() && m_gpu->atomicModeSetting()) {
44 auto debug = QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC, KWIN_DRM().categoryName()).debug().nospace();
45 switch(m_objectType) {
46 case DRM_MODE_OBJECT_CONNECTOR:
47 debug << "Connector ";
48 break;
49 case DRM_MODE_OBJECT_CRTC:
50 debug << "Crtc ";
51 break;
52 case DRM_MODE_OBJECT_PLANE:
53 debug << "Plane ";
54 break;
55 default:
56 Q_UNREACHABLE();
57 }
58 debug << m_id << " has properties ";
59 for (int i = 0; i < m_props.count(); i++) {
60 if (i > 0) {
61 debug << ", ";
62 }
63 const auto &prop = m_props[i];
64 if (prop) {
65 debug << prop->name() << "=";
66 if (m_propertyDefinitions[i].enumNames.isEmpty()) {
67 debug << prop->current();
68 } else {
69 if (prop->hasEnum(prop->current())) {
70 debug << prop->enumNames().at(prop->enumForValue<uint32_t>(prop->current()));
71 } else {
72 debug << "invalid value: " << prop->current();
73 }
74 }
75 } else {
76 debug << m_propertyDefinitions[i].name << " not found";
77 }
78 }
79 }
80 return true;
81 }
82
atomicPopulate(drmModeAtomicReq * req) const83 bool DrmObject::atomicPopulate(drmModeAtomicReq *req) const
84 {
85 for (const auto &property : qAsConst(m_props)) {
86 if (property && !property->isImmutable() && !property->isLegacy() && property->needsCommit()) {
87 if (drmModeAtomicAddProperty(req, m_id, property->propId(), property->pending()) <= 0) {
88 qCWarning(KWIN_DRM) << "Adding property" << property->name() << "->" << property->pending()
89 << "to atomic commit failed for object" << this << "with error" << strerror(errno);
90 return false;
91 }
92 }
93 }
94 return true;
95 }
96
properties()97 QVector<DrmObject::Property*> DrmObject::properties()
98 {
99 return m_props;
100 }
101
commit()102 void DrmObject::commit()
103 {
104 for (const auto &prop : qAsConst(m_props)) {
105 if (prop) {
106 prop->commit();
107 }
108 }
109 }
110
commitPending()111 void DrmObject::commitPending()
112 {
113 for (const auto &prop : qAsConst(m_props)) {
114 if (prop) {
115 prop->commitPending();
116 }
117 }
118 }
119
rollbackPending()120 void DrmObject::rollbackPending()
121 {
122 for (const auto &prop : qAsConst(m_props)) {
123 if (prop) {
124 prop->rollbackPending();
125 }
126 }
127 }
128
needsCommit() const129 bool DrmObject::needsCommit() const
130 {
131 for (const auto &prop : qAsConst(m_props)) {
132 if (prop && prop->needsCommit()) {
133 return true;
134 }
135 }
136 return false;
137 }
138
updateProperties()139 bool DrmObject::updateProperties()
140 {
141 DrmScopedPointer<drmModeObjectProperties> properties(drmModeObjectGetProperties(m_gpu->fd(), m_id, m_objectType));
142 if (!properties) {
143 qCWarning(KWIN_DRM) << "Failed to get properties for object" << m_id;
144 return false;
145 }
146 for (int propIndex = 0; propIndex < m_propertyDefinitions.count(); propIndex++) {
147 const PropertyDefinition &def = m_propertyDefinitions[propIndex];
148 bool found = false;
149 for (uint32_t drmPropIndex = 0; drmPropIndex < properties->count_props; drmPropIndex++) {
150 DrmScopedPointer<drmModePropertyRes> prop(drmModeGetProperty(m_gpu->fd(), properties->props[drmPropIndex]));
151 if (!prop) {
152 qCWarning(KWIN_DRM, "Getting property %d of object %d failed!", drmPropIndex, m_id);
153 continue;
154 }
155 if (def.name == prop->name) {
156 if (m_props[propIndex]) {
157 m_props[propIndex]->setCurrent(properties->prop_values[drmPropIndex]);
158 } else {
159 m_props[propIndex] = new Property(m_gpu, prop.data(), properties->prop_values[drmPropIndex], def.enumNames);
160 }
161 found = true;
162 break;
163 }
164 }
165 if (!found) {
166 deleteProp(propIndex);
167 }
168 }
169 for (int i = 0; i < m_propertyDefinitions.count(); i++) {
170 if (m_gpu->atomicModeSetting() && m_propertyDefinitions[i].requirement == Requirement::Required && !m_props[i]) {
171 qCWarning(KWIN_DRM, "Required property %s for object %d not found!", qPrintable(m_propertyDefinitions[i].name), m_id);
172 return false;
173 }
174 }
175 return true;
176 }
177
178 /*
179 * Definitions for DrmObject::Property
180 */
181
Property(DrmGpu * gpu,drmModePropertyRes * prop,uint64_t val,const QVector<QByteArray> & enumNames)182 DrmObject::Property::Property(DrmGpu *gpu, drmModePropertyRes *prop, uint64_t val, const QVector<QByteArray> &enumNames)
183 : m_propId(prop->prop_id)
184 , m_propName(prop->name)
185 , m_pending(val)
186 , m_next(val)
187 , m_current(val)
188 , m_immutable(prop->flags & DRM_MODE_PROP_IMMUTABLE)
189 , m_gpu(gpu)
190 {
191 if (!enumNames.isEmpty()) {
192 m_enumNames = enumNames;
193 initEnumMap(prop);
194 }
195 }
196
197 DrmObject::Property::~Property() = default;
198
setPending(uint64_t value)199 void DrmObject::Property::setPending(uint64_t value)
200 {
201 m_pending = value;
202 }
203
pending() const204 uint64_t DrmObject::Property::pending() const
205 {
206 return m_pending;
207 }
208
setPendingBlob(void * blob,size_t length)209 bool DrmObject::Property::setPendingBlob(void *blob, size_t length)
210 {
211 if (!blob && !m_pendingBlob) {
212 return true;
213 }
214 if (blob && m_pendingBlob && length == m_pendingBlob->length && memcmp(blob, m_pendingBlob->data, length) == 0) {
215 return true;
216 }
217 uint32_t id = 0;
218 if (blob != nullptr) {
219 if (drmModeCreatePropertyBlob(m_gpu->fd(), blob, length, &id) != 0) {
220 qCWarning(KWIN_DRM) << "Creating property blob failed!" << strerror(errno);
221 return false;
222 }
223 }
224 if (m_pendingBlob && m_pendingBlob != m_currentBlob && m_pendingBlob != m_nextBlob) {
225 drmModeDestroyPropertyBlob(m_gpu->fd(), m_pendingBlob->id);
226 drmModeFreePropertyBlob(m_pendingBlob);
227 }
228 m_pending = id;
229 m_pendingBlob = drmModeGetPropertyBlob(m_gpu->fd(), m_pending);
230 return true;
231 }
232
setCurrent(uint64_t value)233 void DrmObject::Property::setCurrent(uint64_t value)
234 {
235 m_current = value;
236 }
237
current() const238 uint64_t DrmObject::Property::current() const
239 {
240 return m_current;
241 }
242
setCurrentBlob(drmModePropertyBlobRes * blob)243 void DrmObject::Property::setCurrentBlob(drmModePropertyBlobRes *blob)
244 {
245 if (m_currentBlob && m_currentBlob != m_pendingBlob && m_currentBlob != m_nextBlob && m_currentBlob != blob) {
246 drmModeDestroyPropertyBlob(m_gpu->fd(), m_currentBlob->id);
247 drmModeFreePropertyBlob(m_currentBlob);
248 }
249 m_currentBlob = blob;
250 m_current = blob ? blob->id : 0;
251 }
252
commit()253 void DrmObject::Property::commit()
254 {
255 if (m_immutable || m_current == m_pending) {
256 return;
257 }
258 if (m_pendingBlob || m_currentBlob) {
259 setCurrentBlob(m_pendingBlob);
260 } else {
261 setCurrent(m_pending);
262 }
263 }
264
commitPending()265 void DrmObject::Property::commitPending()
266 {
267 if (m_immutable || m_next == m_pending) {
268 return;
269 }
270 if (m_pendingBlob || m_nextBlob) {
271 if (m_nextBlob && m_nextBlob != m_currentBlob) {
272 drmModeDestroyPropertyBlob(m_gpu->fd(), m_nextBlob->id);
273 drmModeFreePropertyBlob(m_nextBlob);
274 }
275 m_next = m_pending;
276 m_nextBlob = m_pendingBlob;
277 } else {
278 m_next = m_pending;
279 }
280 }
281
rollbackPending()282 void DrmObject::Property::rollbackPending()
283 {
284 if (m_immutable || m_next == m_pending) {
285 return;
286 }
287 if (m_pendingBlob || m_nextBlob) {
288 if (m_pendingBlob && m_pendingBlob != m_currentBlob) {
289 drmModeDestroyPropertyBlob(m_gpu->fd(), m_pendingBlob->id);
290 drmModeFreePropertyBlob(m_pendingBlob);
291 }
292 m_pending = m_next;
293 m_pendingBlob = m_nextBlob;
294 } else {
295 m_pending = m_next;
296 }
297 }
298
needsCommit() const299 bool DrmObject::Property::needsCommit() const
300 {
301 return m_pending != m_current;
302 }
303
initEnumMap(drmModePropertyRes * prop)304 void DrmObject::Property::initEnumMap(drmModePropertyRes *prop)
305 {
306 if ( ( !(prop->flags & DRM_MODE_PROP_ENUM) && !(prop->flags & DRM_MODE_PROP_BITMASK) )
307 || prop->count_enums < 1 ) {
308 qCWarning(KWIN_DRM) << "Property '" << prop->name << "' ( id ="
309 << m_propId << ") should be enum valued, but it is not.";
310 return;
311 }
312
313 for (int i = 0; i < prop->count_enums; i++) {
314 struct drm_mode_property_enum *en = &prop->enums[i];
315 int j = m_enumNames.indexOf(QByteArray(en->name));
316 if (j >= 0) {
317 m_enumMap[j] = en->value;
318 } else {
319 qCWarning(KWIN_DRM, "%s has unrecognized enum '%s'", qPrintable(m_propName), en->name);
320 }
321 }
322 }
323
324 }
325
operator <<(QDebug s,const KWin::DrmObject * obj)326 QDebug operator<<(QDebug s, const KWin::DrmObject *obj)
327 {
328 QDebugStateSaver saver(s);
329 if (obj) {
330 s.nospace() << "DrmObject(id=" << obj->id() << ", gpu="<< obj->gpu() << ')';
331 } else {
332 s << "DrmObject(0x0)";
333 }
334 return s;
335 }
336