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-connector"
18 
19 #include "drmconnector.h"
20 
21 #include <cutils/properties.h>
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <log/log.h>
25 #include <stdint.h>
26 #include <xf86drmMode.h>
27 
28 #include <array>
29 #include <sstream>
30 
31 #include "drmdevice.h"
32 
33 #ifndef DRM_MODE_CONNECTOR_WRITEBACK
34 #define DRM_MODE_CONNECTOR_WRITEBACK 18
35 #endif
36 
37 namespace android {
38 
39 constexpr size_t TYPES_COUNT = 18;
40 
DrmConnector(DrmDevice * drm,drmModeConnectorPtr c,DrmEncoder * current_encoder,std::vector<DrmEncoder * > & possible_encoders)41 DrmConnector::DrmConnector(DrmDevice *drm, drmModeConnectorPtr c,
42                            DrmEncoder *current_encoder,
43                            std::vector<DrmEncoder *> &possible_encoders)
44     : drm_(drm),
45       id_(c->connector_id),
46       encoder_(current_encoder),
47       display_(-1),
48       type_(c->connector_type),
49       type_id_(c->connector_type_id),
50       state_(c->connection),
51       mm_width_(c->mmWidth),
52       mm_height_(c->mmHeight),
53       possible_encoders_(possible_encoders) {
54 }
55 
Init()56 int DrmConnector::Init() {
57   int ret = drm_->GetConnectorProperty(*this, "DPMS", &dpms_property_);
58   if (ret) {
59     ALOGE("Could not get DPMS property\n");
60     return ret;
61   }
62   ret = drm_->GetConnectorProperty(*this, "CRTC_ID", &crtc_id_property_);
63   if (ret) {
64     ALOGE("Could not get CRTC_ID property\n");
65     return ret;
66   }
67   ret = drm_->GetConnectorProperty(*this, "EDID", &edid_property_);
68   if (ret) {
69     ALOGW("Could not get EDID property\n");
70   }
71   if (writeback()) {
72     ret = drm_->GetConnectorProperty(*this, "WRITEBACK_PIXEL_FORMATS",
73                                      &writeback_pixel_formats_);
74     if (ret) {
75       ALOGE("Could not get WRITEBACK_PIXEL_FORMATS connector_id = %d\n", id_);
76       return ret;
77     }
78     ret = drm_->GetConnectorProperty(*this, "WRITEBACK_FB_ID",
79                                      &writeback_fb_id_);
80     if (ret) {
81       ALOGE("Could not get WRITEBACK_FB_ID connector_id = %d\n", id_);
82       return ret;
83     }
84     ret = drm_->GetConnectorProperty(*this, "WRITEBACK_OUT_FENCE_PTR",
85                                      &writeback_out_fence_);
86     if (ret) {
87       ALOGE("Could not get WRITEBACK_OUT_FENCE_PTR connector_id = %d\n", id_);
88       return ret;
89     }
90   }
91 
92   ret = drm_->GetConnectorProperty(*this, "max_luminance", &max_luminance_);
93   if (ret) {
94     ALOGE("Could not get max_luminance property\n");
95   }
96 
97   ret = drm_->GetConnectorProperty(*this, "max_avg_luminance", &max_avg_luminance_);
98   if (ret) {
99     ALOGE("Could not get max_avg_luminance property\n");
100   }
101 
102   ret = drm_->GetConnectorProperty(*this, "min_luminance", &min_luminance_);
103   if (ret) {
104     ALOGE("Could not get min_luminance property\n");
105   }
106 
107   ret = drm_->GetConnectorProperty(*this, "hdr_formats", &hdr_formats_);
108   if (ret) {
109     ALOGE("Could not get hdr_formats property\n");
110   }
111 
112   ret = drm_->GetConnectorProperty(*this, "frame_interval", &frame_interval_);
113   if (ret) {
114     ALOGE("Could not get frame_interval property\n");
115   }
116 
117   ret = drm_->GetConnectorProperty(*this, "panel orientation", &orientation_);
118   if (ret) {
119     ALOGE("Could not get orientation property\n");
120   }
121 
122   ret = drm_->GetConnectorProperty(*this, "lp_mode", &lp_mode_property_);
123   if (!ret) {
124       UpdateLpMode();
125   } else {
126       ALOGE("Could not get lp_mode property\n");
127   }
128 
129   ret = drm_->GetConnectorProperty(*this, "brightness_capability", &brightness_cap_);
130   if (ret) {
131       ALOGE("Could not get brightness_capability property\n");
132   }
133 
134   ret = drm_->GetConnectorProperty(*this, "brightness_level", &brightness_level_);
135   if (ret) {
136       ALOGE("Could not get brightness_level property\n");
137   }
138 
139   ret = drm_->GetConnectorProperty(*this, "hbm_mode", &hbm_mode_);
140   if (ret) {
141       ALOGE("Could not get hbm_mode property\n");
142   }
143 
144   ret = drm_->GetConnectorProperty(*this, "dimming_on", &dimming_on_);
145   if (ret) {
146       ALOGE("Could not get dimming_on property\n");
147   }
148 
149   ret = drm_->GetConnectorProperty(*this, "local_hbm_mode", &lhbm_on_);
150   if (ret) {
151       ALOGE("Could not get local_hbm_mode property\n");
152   }
153 
154   ret = drm_->GetConnectorProperty(*this, "mipi_sync", &mipi_sync_);
155   if (ret) {
156       ALOGE("Could not get mipi_sync property\n");
157   }
158 
159   ret = drm_->GetConnectorProperty(*this, "panel_idle_support", &panel_idle_support_);
160   if (ret) {
161       ALOGE("Could not get panel_idle_support property\n");
162   }
163 
164   ret = drm_->GetConnectorProperty(*this, "rr_switch_duration", &rr_switch_duration_);
165   if (ret) {
166       ALOGE("Could not get rr_switch_duration property\n");
167   }
168 
169   ret = drm_->GetConnectorProperty(*this, "operation_rate", &operation_rate_);
170   if (ret) {
171     ALOGE("Could not get operation_rate property\n");
172   }
173 
174   ret = drm_->GetConnectorProperty(*this, "refresh_on_lp", &refresh_on_lp_);
175   if (ret) {
176     ALOGE("Could not get refresh_on_lp property\n");
177   }
178 
179   ret = drm_->GetConnectorProperty(*this, "Content Protection", &content_protection_);
180   if (ret) {
181     ALOGE("Could not get Content Protection property\n");
182   }
183 
184   properties_.push_back(&dpms_property_);
185   properties_.push_back(&crtc_id_property_);
186   properties_.push_back(&edid_property_);
187   if (writeback()) {
188       properties_.push_back(&writeback_pixel_formats_);
189       properties_.push_back(&writeback_fb_id_);
190       properties_.push_back(&writeback_out_fence_);
191   }
192   properties_.push_back(&max_luminance_);
193   properties_.push_back(&max_avg_luminance_);
194   properties_.push_back(&min_luminance_);
195   properties_.push_back(&hdr_formats_);
196   properties_.push_back(&frame_interval_);
197   properties_.push_back(&orientation_);
198   properties_.push_back(&lp_mode_property_);
199   properties_.push_back(&brightness_cap_);
200   properties_.push_back(&brightness_level_);
201   properties_.push_back(&hbm_mode_);
202   properties_.push_back(&dimming_on_);
203   properties_.push_back(&lhbm_on_);
204   properties_.push_back(&mipi_sync_);
205   properties_.push_back(&panel_idle_support_);
206   properties_.push_back(&rr_switch_duration_);
207   properties_.push_back(&operation_rate_);
208   properties_.push_back(&refresh_on_lp_);
209   properties_.push_back(&content_protection_);
210 
211   return 0;
212 }
213 
id() const214 uint32_t DrmConnector::id() const {
215   return id_;
216 }
217 
display() const218 int DrmConnector::display() const {
219   return display_;
220 }
221 
set_display(int display)222 void DrmConnector::set_display(int display) {
223   display_ = display;
224 }
225 
internal() const226 bool DrmConnector::internal() const {
227   return type_ == DRM_MODE_CONNECTOR_LVDS || type_ == DRM_MODE_CONNECTOR_eDP ||
228          type_ == DRM_MODE_CONNECTOR_DSI ||
229          type_ == DRM_MODE_CONNECTOR_VIRTUAL || type_ == DRM_MODE_CONNECTOR_DPI;
230 }
231 
external() const232 bool DrmConnector::external() const {
233   return type_ == DRM_MODE_CONNECTOR_HDMIA ||
234          type_ == DRM_MODE_CONNECTOR_DisplayPort ||
235          type_ == DRM_MODE_CONNECTOR_DVID || type_ == DRM_MODE_CONNECTOR_DVII ||
236          type_ == DRM_MODE_CONNECTOR_VGA;
237 }
238 
writeback() const239 bool DrmConnector::writeback() const {
240 #ifdef DRM_MODE_CONNECTOR_WRITEBACK
241   return type_ == DRM_MODE_CONNECTOR_WRITEBACK;
242 #else
243   return false;
244 #endif
245 }
246 
valid_type() const247 bool DrmConnector::valid_type() const {
248   return internal() || external() || writeback();
249 }
250 
name() const251 std::string DrmConnector::name() const {
252   constexpr std::array<const char *, TYPES_COUNT> names =
253       {"None",   "VGA",  "DVI-I",     "DVI-D",   "DVI-A", "Composite",
254        "SVIDEO", "LVDS", "Component", "DIN",     "DP",    "HDMI-A",
255        "HDMI-B", "TV",   "eDP",       "Virtual", "DSI",   "DPI"};
256 
257   if (type_ < TYPES_COUNT) {
258     std::ostringstream name_buf;
259     name_buf << names[type_] << "-" << type_id_;
260     return name_buf.str();
261   } else {
262     ALOGE("Unknown type in connector %d, could not make his name", id_);
263     return "None";
264   }
265 }
266 
UpdateModes(bool is_vrr_mode)267 int DrmConnector::UpdateModes(bool is_vrr_mode) {
268   std::lock_guard<std::recursive_mutex> lock(modes_lock_);
269 
270   int fd = drm_->fd();
271 
272   drmModeConnectorPtr c = drmModeGetConnector(fd, id_);
273   if (!c) {
274     ALOGE("Failed to get connector %d", id_);
275     return -ENODEV;
276   }
277 
278   if (state_ == DRM_MODE_CONNECTED &&
279       c->connection == DRM_MODE_CONNECTED && modes_.size() > 0) {
280     // no need to update modes
281     return 0;
282   }
283 
284   if (state_ == DRM_MODE_DISCONNECTED &&
285       c->connection == DRM_MODE_DISCONNECTED && modes_.size() == 0) {
286     // no need to update modes
287     return 0;
288   }
289 
290   state_ = c->connection;
291 
292   // Update mm_width_ and mm_height_ for xdpi/ydpi calculations
293   mm_width_ = c->mmWidth;
294   mm_height_ = c->mmHeight;
295 
296   bool preferred_mode_found = false;
297   std::vector<DrmMode> new_modes;
298   for (int i = 0; i < c->count_modes; ++i) {
299     bool exists = false;
300     for (const DrmMode &mode : modes_) {
301       if (mode == c->modes[i]) {
302         new_modes.push_back(mode);
303         exists = true;
304         break;
305       }
306     }
307     if (!exists) {
308       // Remove modes that mismatch with the VRR setting..
309       if (type_ == DRM_MODE_CONNECTOR_DSI &&
310           is_vrr_mode != ((c->modes[i].type & DRM_MODE_TYPE_VRR) != 0 || c->modes[i].vscan > 0)) {
311         continue;
312       }
313       DrmMode m(&c->modes[i]);
314       m.set_id(drm_->next_mode_id());
315       new_modes.push_back(m);
316     }
317     // Use only the first DRM_MODE_TYPE_PREFERRED mode found
318     if (!preferred_mode_found &&
319         (new_modes.back().type() & DRM_MODE_TYPE_PREFERRED)) {
320       preferred_mode_id_ = new_modes.back().id();
321       preferred_mode_found = true;
322     }
323   }
324   modes_.swap(new_modes);
325   if (!preferred_mode_found && modes_.size() != 0) {
326     preferred_mode_id_ = modes_[0].id();
327   }
328   return 1;
329 }
330 
UpdateEdidProperty()331 int DrmConnector::UpdateEdidProperty() {
332   return drm_->UpdateConnectorProperty(*this, &edid_property_);
333 }
334 
UpdateLuminanceAndHdrProperties()335 int DrmConnector::UpdateLuminanceAndHdrProperties() {
336   int res = 0;
337 
338   res = drm_->UpdateConnectorProperty(*this, &max_luminance_);
339   if (res)
340     return res;
341   res = drm_->UpdateConnectorProperty(*this, &max_avg_luminance_);
342   if (res)
343     return res;
344   res = drm_->UpdateConnectorProperty(*this, &min_luminance_);
345   if (res)
346     return res;
347   res = drm_->UpdateConnectorProperty(*this, &hdr_formats_);
348   if (res)
349     return res;
350   return 0;
351 }
352 
active_mode() const353 const DrmMode &DrmConnector::active_mode() const {
354   return active_mode_;
355 }
356 
set_active_mode(const DrmMode & mode)357 void DrmConnector::set_active_mode(const DrmMode &mode) {
358   active_mode_ = mode;
359 }
360 
dpms_property() const361 const DrmProperty &DrmConnector::dpms_property() const {
362   return dpms_property_;
363 }
364 
crtc_id_property() const365 const DrmProperty &DrmConnector::crtc_id_property() const {
366   return crtc_id_property_;
367 }
368 
edid_property() const369 const DrmProperty &DrmConnector::edid_property() const {
370   return edid_property_;
371 }
372 
writeback_pixel_formats() const373 const DrmProperty &DrmConnector::writeback_pixel_formats() const {
374   return writeback_pixel_formats_;
375 }
376 
writeback_fb_id() const377 const DrmProperty &DrmConnector::writeback_fb_id() const {
378   return writeback_fb_id_;
379 }
380 
writeback_out_fence() const381 const DrmProperty &DrmConnector::writeback_out_fence() const {
382   return writeback_out_fence_;
383 }
384 
max_luminance() const385 const DrmProperty &DrmConnector::max_luminance() const {
386   return max_luminance_;
387 }
388 
max_avg_luminance() const389 const DrmProperty &DrmConnector::max_avg_luminance() const {
390   return max_avg_luminance_;
391 }
392 
min_luminance() const393 const DrmProperty &DrmConnector::min_luminance() const {
394   return min_luminance_;
395 }
396 
brightness_cap() const397 const DrmProperty &DrmConnector::brightness_cap() const {
398     return brightness_cap_;
399 }
400 
brightness_level() const401 const DrmProperty &DrmConnector::brightness_level() const {
402     return brightness_level_;
403 }
404 
hbm_mode() const405 const DrmProperty &DrmConnector::hbm_mode() const {
406     return hbm_mode_;
407 }
408 
dimming_on() const409 const DrmProperty &DrmConnector::dimming_on() const {
410     return dimming_on_;
411 }
412 
lhbm_on() const413 const DrmProperty &DrmConnector::lhbm_on() const {
414     return lhbm_on_;
415 }
416 
mipi_sync() const417 const DrmProperty &DrmConnector::mipi_sync() const {
418     return mipi_sync_;
419 }
420 
hdr_formats() const421 const DrmProperty &DrmConnector::hdr_formats() const {
422   return hdr_formats_;
423 }
424 
orientation() const425 const DrmProperty &DrmConnector::orientation() const {
426   return orientation_;
427 }
428 
lp_mode() const429 const DrmMode &DrmConnector::lp_mode() const {
430     return lp_mode_;
431 }
432 
operation_rate() const433 const DrmProperty &DrmConnector::operation_rate() const {
434     return operation_rate_;
435 }
436 
refresh_on_lp() const437 const DrmProperty &DrmConnector::refresh_on_lp() const {
438     return refresh_on_lp_;
439 }
440 
UpdateLpMode()441 int DrmConnector::UpdateLpMode() {
442     auto [ret, blobId] = lp_mode_property_.value();
443     if (ret) {
444         ALOGE("Fail to get blob id for lp mode");
445         return ret;
446     }
447     drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(drm_->fd(), blobId);
448     if (!blob) {
449         ALOGE("Fail to get blob for lp mode(%" PRId64 ")", blobId);
450         return -ENOENT;
451     }
452 
453     auto modeInfoPtr = static_cast<drmModeModeInfoPtr>(blob->data);
454     lp_mode_ = DrmMode(modeInfoPtr);
455     drmModeFreePropertyBlob(blob);
456 
457     ALOGD("Updating LP mode to: %s", lp_mode_.name().c_str());
458 
459     return 0;
460 }
461 
ResetLpMode()462 int DrmConnector::ResetLpMode() {
463     int ret = drm_->UpdateConnectorProperty(*this, &lp_mode_property_);
464 
465     if (ret) {
466         return ret;
467     }
468 
469     UpdateLpMode();
470 
471     return 0;
472 }
473 
panel_idle_support() const474 const DrmProperty &DrmConnector::panel_idle_support() const {
475     return panel_idle_support_;
476 }
477 
rr_switch_duration() const478 const DrmProperty &DrmConnector::rr_switch_duration() const {
479     return rr_switch_duration_;
480 }
481 
content_protection() const482 const DrmProperty &DrmConnector::content_protection() const {
483     return content_protection_;
484 }
485 
frame_interval() const486 const DrmProperty &DrmConnector::frame_interval() const {
487   return frame_interval_;
488 }
489 
encoder() const490 DrmEncoder *DrmConnector::encoder() const {
491   return encoder_;
492 }
493 
set_encoder(DrmEncoder * encoder)494 void DrmConnector::set_encoder(DrmEncoder *encoder) {
495   encoder_ = encoder;
496 }
497 
state() const498 drmModeConnection DrmConnector::state() const {
499   return state_;
500 }
501 
mm_width() const502 uint32_t DrmConnector::mm_width() const {
503   return mm_width_;
504 }
505 
mm_height() const506 uint32_t DrmConnector::mm_height() const {
507   return mm_height_;
508 }
509 }  // namespace android
510