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