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