1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "hwc-drm-device"
18 
19 #include "drmdevice.h"
20 #include "drmconnector.h"
21 #include "drmcrtc.h"
22 #include "drmencoder.h"
23 #include "drmeventlistener.h"
24 #include "drmplane.h"
25 
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <stdint.h>
29 #include <xf86drm.h>
30 #include <xf86drmMode.h>
31 #include <cinttypes>
32 
33 #include <cutils/properties.h>
34 #include <log/log.h>
35 
36 #ifndef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS
37 #define DRM_CLIENT_CAP_WRITEBACK_CONNECTORS 5
38 #endif
39 
40 namespace android {
41 
DrmDevice()42 DrmDevice::DrmDevice() : event_listener_(this) {
43 }
44 
~DrmDevice()45 DrmDevice::~DrmDevice() {
46   event_listener_.Exit();
47 }
48 
Init(const char * path,int num_displays)49 std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) {
50   /* TODO: Use drmOpenControl here instead */
51   fd_.Set(open(path, O_RDWR));
52   if (fd() < 0) {
53     ALOGE("Failed to open dri %s: %s", path, strerror(errno));
54     return std::make_tuple(-ENODEV, 0);
55   }
56 
57   int ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
58   if (ret) {
59     ALOGE("Failed to set universal plane cap %d", ret);
60     return std::make_tuple(ret, 0);
61   }
62 
63   ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_ATOMIC, 1);
64   if (ret) {
65     ALOGE("Failed to set atomic cap %d", ret);
66     return std::make_tuple(ret, 0);
67   }
68 
69 #ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS
70   ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
71   if (ret) {
72     ALOGI("Failed to set writeback cap %d", ret);
73     ret = 0;
74   }
75 #endif
76 
77   drmModeResPtr res = drmModeGetResources(fd());
78   if (!res) {
79     ALOGE("Failed to get DrmDevice resources");
80     return std::make_tuple(-ENODEV, 0);
81   }
82 
83   min_resolution_ = std::pair<uint32_t, uint32_t>(res->min_width,
84                                                   res->min_height);
85   max_resolution_ = std::pair<uint32_t, uint32_t>(res->max_width,
86                                                   res->max_height);
87 
88   // Assumes that the primary display will always be in the first
89   // drm_device opened.
90   bool found_primary = num_displays != 0;
91 
92   for (int i = 0; !ret && i < res->count_crtcs; ++i) {
93     drmModeCrtcPtr c = drmModeGetCrtc(fd(), res->crtcs[i]);
94     if (!c) {
95       ALOGE("Failed to get crtc %d", res->crtcs[i]);
96       ret = -ENODEV;
97       break;
98     }
99 
100     std::unique_ptr<DrmCrtc> crtc(new DrmCrtc(this, c, i));
101     drmModeFreeCrtc(c);
102 
103     ret = crtc->Init();
104     if (ret) {
105       ALOGE("Failed to initialize crtc %d", res->crtcs[i]);
106       break;
107     }
108     crtcs_.emplace_back(std::move(crtc));
109   }
110 
111   std::vector<int> possible_clones;
112   for (int i = 0; !ret && i < res->count_encoders; ++i) {
113     drmModeEncoderPtr e = drmModeGetEncoder(fd(), res->encoders[i]);
114     if (!e) {
115       ALOGE("Failed to get encoder %d", res->encoders[i]);
116       ret = -ENODEV;
117       break;
118     }
119 
120     std::vector<DrmCrtc *> possible_crtcs;
121     DrmCrtc *current_crtc = NULL;
122     for (auto &crtc : crtcs_) {
123       if ((1 << crtc->pipe()) & e->possible_crtcs)
124         possible_crtcs.push_back(crtc.get());
125 
126       if (crtc->id() == e->crtc_id)
127         current_crtc = crtc.get();
128     }
129 
130     std::unique_ptr<DrmEncoder> enc(
131         new DrmEncoder(e, current_crtc, possible_crtcs));
132     possible_clones.push_back(e->possible_clones);
133     drmModeFreeEncoder(e);
134 
135     encoders_.emplace_back(std::move(enc));
136   }
137 
138   for (unsigned int i = 0; i < encoders_.size(); i++) {
139     for (unsigned int j = 0; j < encoders_.size(); j++)
140       if (possible_clones[i] & (1 << j))
141         encoders_[i]->AddPossibleClone(encoders_[j].get());
142   }
143 
144   for (int i = 0; !ret && i < res->count_connectors; ++i) {
145     drmModeConnectorPtr c = drmModeGetConnector(fd(), res->connectors[i]);
146     if (!c) {
147       ALOGE("Failed to get connector %d", res->connectors[i]);
148       ret = -ENODEV;
149       break;
150     }
151 
152     std::vector<DrmEncoder *> possible_encoders;
153     DrmEncoder *current_encoder = NULL;
154     for (int j = 0; j < c->count_encoders; ++j) {
155       for (auto &encoder : encoders_) {
156         if (encoder->id() == c->encoders[j])
157           possible_encoders.push_back(encoder.get());
158         if (encoder->id() == c->encoder_id)
159           current_encoder = encoder.get();
160       }
161     }
162 
163     std::unique_ptr<DrmConnector> conn(
164         new DrmConnector(this, c, current_encoder, possible_encoders));
165 
166     drmModeFreeConnector(c);
167 
168     ret = conn->Init();
169     if (ret) {
170       ALOGE("Init connector %d failed", res->connectors[i]);
171       break;
172     }
173 
174     if (conn->writeback())
175       writeback_connectors_.emplace_back(std::move(conn));
176     else
177       connectors_.emplace_back(std::move(conn));
178   }
179 
180   // First look for primary amongst internal connectors
181   for (auto &conn : connectors_) {
182     if (conn->internal() && !found_primary) {
183       conn->set_display(num_displays);
184       displays_[num_displays] = num_displays;
185       ++num_displays;
186       found_primary = true;
187       break;
188     }
189   }
190 
191   // Then pick first available as primary and for the others assign
192   // consecutive display_numbers.
193   for (auto &conn : connectors_) {
194     if (conn->external() || conn->internal()) {
195       if (!found_primary) {
196         conn->set_display(num_displays);
197         displays_[num_displays] = num_displays;
198         found_primary = true;
199         ++num_displays;
200       } else if (conn->display() < 0) {
201         conn->set_display(num_displays);
202         displays_[num_displays] = num_displays;
203         ++num_displays;
204       }
205     }
206   }
207 
208   if (res)
209     drmModeFreeResources(res);
210 
211   // Catch-all for the above loops
212   if (ret)
213     return std::make_tuple(ret, 0);
214 
215   drmModePlaneResPtr plane_res = drmModeGetPlaneResources(fd());
216   if (!plane_res) {
217     ALOGE("Failed to get plane resources");
218     return std::make_tuple(-ENOENT, 0);
219   }
220 
221   for (uint32_t i = 0; i < plane_res->count_planes; ++i) {
222     drmModePlanePtr p = drmModeGetPlane(fd(), plane_res->planes[i]);
223     if (!p) {
224       ALOGE("Failed to get plane %d", plane_res->planes[i]);
225       ret = -ENODEV;
226       break;
227     }
228 
229     std::unique_ptr<DrmPlane> plane(new DrmPlane(this, p));
230 
231     drmModeFreePlane(p);
232 
233     ret = plane->Init();
234     if (ret) {
235       ALOGE("Init plane %d failed", plane_res->planes[i]);
236       break;
237     }
238 
239     planes_.emplace_back(std::move(plane));
240   }
241   drmModeFreePlaneResources(plane_res);
242   if (ret)
243     return std::make_tuple(ret, 0);
244 
245   ret = event_listener_.Init();
246   if (ret) {
247     ALOGE("Can't initialize event listener %d", ret);
248     return std::make_tuple(ret, 0);
249   }
250 
251   for (auto &conn : connectors_) {
252     ret = CreateDisplayPipe(conn.get());
253     if (ret) {
254       ALOGE("Failed CreateDisplayPipe %d with %d", conn->id(), ret);
255       return std::make_tuple(ret, 0);
256     }
257     if (!AttachWriteback(conn.get())) {
258       ALOGI("Display %d has writeback attach to it", conn->display());
259     }
260   }
261   return std::make_tuple(ret, displays_.size());
262 }
263 
HandlesDisplay(int display) const264 bool DrmDevice::HandlesDisplay(int display) const {
265   return displays_.find(display) != displays_.end();
266 }
267 
GetConnectorForDisplay(int display) const268 DrmConnector *DrmDevice::GetConnectorForDisplay(int display) const {
269   for (auto &conn : connectors_) {
270     if (conn->display() == display)
271       return conn.get();
272   }
273   return NULL;
274 }
275 
GetWritebackConnectorForDisplay(int display) const276 DrmConnector *DrmDevice::GetWritebackConnectorForDisplay(int display) const {
277   for (auto &conn : writeback_connectors_) {
278     if (conn->display() == display)
279       return conn.get();
280   }
281   return NULL;
282 }
283 
284 // TODO what happens when hotplugging
AvailableWritebackConnector(int display) const285 DrmConnector *DrmDevice::AvailableWritebackConnector(int display) const {
286   DrmConnector *writeback_conn = GetWritebackConnectorForDisplay(display);
287   DrmConnector *display_conn = GetConnectorForDisplay(display);
288   // If we have a writeback already attached to the same CRTC just use that,
289   // if possible.
290   if (display_conn && writeback_conn &&
291       writeback_conn->encoder()->CanClone(display_conn->encoder()))
292     return writeback_conn;
293 
294   // Use another CRTC if available and doesn't have any connector
295   for (auto &crtc : crtcs_) {
296     if (crtc->has_display(display))
297       continue;
298     for (auto it: crtc->displays()) {
299       display_conn = GetConnectorForDisplay(it);
300       // If we have a display connected don't use it for writeback
301       if (display_conn && display_conn->state() == DRM_MODE_CONNECTED)
302         continue;
303       writeback_conn = GetWritebackConnectorForDisplay(it);
304       if (writeback_conn)
305         return writeback_conn;
306     }
307   }
308   return NULL;
309 }
310 
GetCrtcForDisplay(int display) const311 DrmCrtc *DrmDevice::GetCrtcForDisplay(int display) const {
312   for (auto &crtc : crtcs_) {
313     if (crtc->has_display(display))
314       return crtc.get();
315   }
316   return NULL;
317 }
318 
GetPlane(uint32_t id) const319 DrmPlane *DrmDevice::GetPlane(uint32_t id) const {
320   for (auto &plane : planes_) {
321     if (plane->id() == id)
322       return plane.get();
323   }
324   return NULL;
325 }
326 
crtcs() const327 const std::vector<std::unique_ptr<DrmCrtc>> &DrmDevice::crtcs() const {
328   return crtcs_;
329 }
330 
next_mode_id()331 uint32_t DrmDevice::next_mode_id() {
332   return ++mode_id_;
333 }
334 
TryEncoderForDisplay(int display,DrmEncoder * enc)335 int DrmDevice::TryEncoderForDisplay(int display, DrmEncoder *enc) {
336   /* First try to use the currently-bound crtc */
337   DrmCrtc *crtc = enc->crtc();
338   if (crtc && crtc->can_bind(display)) {
339     crtc->set_display(display);
340     enc->set_crtc(crtc, display);
341     return 0;
342   }
343 
344   /* Try to find a possible crtc which will work */
345   for (DrmCrtc *crtc : enc->possible_crtcs()) {
346     /* We've already tried this earlier */
347     if (crtc == enc->crtc())
348       continue;
349 
350     if (crtc->can_bind(display)) {
351       crtc->set_display(display);
352       enc->set_crtc(crtc, display);
353       return 0;
354     }
355   }
356 
357   /* We can't use the encoder, but nothing went wrong, try another one */
358   return -EAGAIN;
359 }
360 
CreateDisplayPipe(DrmConnector * connector)361 int DrmDevice::CreateDisplayPipe(DrmConnector *connector) {
362   int display = connector->display();
363   /* Try to use current setup first */
364   if (connector->encoder()) {
365     int ret = TryEncoderForDisplay(display, connector->encoder());
366     if (!ret) {
367       return 0;
368     } else if (ret != -EAGAIN) {
369       ALOGE("Could not set mode %d/%d", display, ret);
370       return ret;
371     }
372   }
373 
374   for (DrmEncoder *enc : connector->possible_encoders()) {
375     int ret = TryEncoderForDisplay(display, enc);
376     if (!ret) {
377       connector->set_encoder(enc);
378       return 0;
379     } else if (ret != -EAGAIN) {
380       ALOGE("Could not set mode %d/%d", display, ret);
381       return ret;
382     }
383   }
384 
385   /* Create pipe with the first crtc */
386   for (DrmEncoder *enc : connector->possible_encoders()) {
387     if (!enc->can_bind(display))
388       continue;
389     if (enc->possible_crtcs().size() > 0) {
390       DrmCrtc *crtc = enc->possible_crtcs().at(0);
391       crtc->set_display(display);
392       enc->set_crtc(crtc, display);
393       ALOGD("crtc is connected to multiple connector (%zu)",
394           crtc->displays().size());
395       connector->set_encoder(enc);
396       return 0;
397     }
398   }
399 
400   ALOGE("Could not find a suitable encoder/crtc for display %d",
401         connector->display());
402   return -ENODEV;
403 }
404 
405 // Attach writeback connector to the CRTC linked to the display_conn
AttachWriteback(DrmConnector * display_conn)406 int DrmDevice::AttachWriteback(DrmConnector *display_conn) {
407   DrmCrtc *display_crtc = display_conn->encoder()->crtc();
408 
409   for (auto it: display_crtc->displays()) {
410     if (GetWritebackConnectorForDisplay(it) != NULL) {
411       ALOGE("Display already has writeback attach to it");
412       return -EINVAL;
413     }
414   }
415 
416   for (auto it: display_crtc->displays()) {
417     for (auto &writeback_conn : writeback_connectors_) {
418       if (writeback_conn->display() >= 0)
419         continue;
420       for (DrmEncoder *writeback_enc : writeback_conn->possible_encoders()) {
421         for (DrmCrtc *possible_crtc : writeback_enc->possible_crtcs()) {
422           if (possible_crtc != display_crtc)
423             continue;
424           // Use just encoders which had not been bound already
425           if (writeback_enc->can_bind(it)) {
426             writeback_enc->set_crtc(display_crtc, it);
427             writeback_conn->set_encoder(writeback_enc);
428             writeback_conn->set_display(it);
429             writeback_conn->UpdateModes();
430             return 0;
431           }
432         }
433       }
434     }
435   }
436   return -EINVAL;
437 }
438 
CreatePropertyBlob(void * data,size_t length,uint32_t * blob_id)439 int DrmDevice::CreatePropertyBlob(void *data, size_t length,
440                                   uint32_t *blob_id) {
441   struct drm_mode_create_blob create_blob;
442   memset(&create_blob, 0, sizeof(create_blob));
443   create_blob.length = length;
444   create_blob.data = (__u64)data;
445 
446   int ret = drmIoctl(fd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob);
447   if (ret) {
448     ALOGE("Failed to create mode property blob %d", ret);
449     return ret;
450   }
451   *blob_id = create_blob.blob_id;
452   return 0;
453 }
454 
DestroyPropertyBlob(uint32_t blob_id)455 int DrmDevice::DestroyPropertyBlob(uint32_t blob_id) {
456   if (!blob_id)
457     return 0;
458 
459   struct drm_mode_destroy_blob destroy_blob;
460   memset(&destroy_blob, 0, sizeof(destroy_blob));
461   destroy_blob.blob_id = (__u32)blob_id;
462   int ret = drmIoctl(fd(), DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy_blob);
463   if (ret) {
464     ALOGE("Failed to destroy mode property blob %" PRIu32 "/%d", blob_id, ret);
465     return ret;
466   }
467   return 0;
468 }
469 
event_listener()470 DrmEventListener *DrmDevice::event_listener() {
471   return &event_listener_;
472 }
473 
GetProperty(uint32_t obj_id,uint32_t obj_type,const char * prop_name,DrmProperty * property)474 int DrmDevice::GetProperty(uint32_t obj_id, uint32_t obj_type,
475                            const char *prop_name, DrmProperty *property) {
476   drmModeObjectPropertiesPtr props;
477 
478   props = drmModeObjectGetProperties(fd(), obj_id, obj_type);
479   if (!props) {
480     ALOGE("Failed to get properties for %d/%x", obj_id, obj_type);
481     return -ENODEV;
482   }
483 
484   bool found = false;
485   for (int i = 0; !found && (size_t)i < props->count_props; ++i) {
486     drmModePropertyPtr p = drmModeGetProperty(fd(), props->props[i]);
487     if (!strcmp(p->name, prop_name)) {
488       property->Init(p, props->prop_values[i]);
489       found = true;
490     }
491     drmModeFreeProperty(p);
492   }
493 
494   if (!found)
495       property->SetName(prop_name);
496 
497   drmModeFreeObjectProperties(props);
498   return found ? 0 : -ENOENT;
499 }
500 
GetPlaneProperty(const DrmPlane & plane,const char * prop_name,DrmProperty * property)501 int DrmDevice::GetPlaneProperty(const DrmPlane &plane, const char *prop_name,
502                                 DrmProperty *property) {
503   return GetProperty(plane.id(), DRM_MODE_OBJECT_PLANE, prop_name, property);
504 }
505 
GetCrtcProperty(const DrmCrtc & crtc,const char * prop_name,DrmProperty * property)506 int DrmDevice::GetCrtcProperty(const DrmCrtc &crtc, const char *prop_name,
507                                DrmProperty *property) {
508   return GetProperty(crtc.id(), DRM_MODE_OBJECT_CRTC, prop_name, property);
509 }
510 
GetConnectorProperty(const DrmConnector & connector,const char * prop_name,DrmProperty * property)511 int DrmDevice::GetConnectorProperty(const DrmConnector &connector,
512                                     const char *prop_name,
513                                     DrmProperty *property) {
514   return GetProperty(connector.id(), DRM_MODE_OBJECT_CONNECTOR, prop_name,
515                      property);
516 }
517 
UpdateCrtcProperty(const DrmCrtc & crtc,DrmProperty * property)518 int DrmDevice::UpdateCrtcProperty(const DrmCrtc &crtc, DrmProperty *property) {
519     drmModeObjectPropertiesPtr props;
520     props = drmModeObjectGetProperties(fd(), crtc.id(), DRM_MODE_OBJECT_CRTC);
521     if (!props) {
522         ALOGE("Failed to get properties for crtc %s", property->name().c_str());
523         return -ENODEV;
524     }
525     bool found = false;
526     for (int i = 0; !found && (size_t)i < props->count_props; ++i) {
527         drmModePropertyPtr p = drmModeGetProperty(fd(), props->props[i]);
528         if (props->props[i] == property->id()) {
529             property->UpdateValue(props->prop_values[i]);
530             found = true;
531         }
532         drmModeFreeProperty(p);
533     }
534     drmModeFreeObjectProperties(props);
535     return found ? 0 : -ENOENT;
536 }
537 }  // namespace android
538